Skip to content

Instantly share code, notes, and snippets.

@goldshtn
Last active September 13, 2018 16:53
Show Gist options
  • Select an option

  • Save goldshtn/56d3daae842ccad44612cdacd68bac29 to your computer and use it in GitHub Desktop.

Select an option

Save goldshtn/56d3daae842ccad44612cdacd68bac29 to your computer and use it in GitHub Desktop.

Revisions

  1. goldshtn renamed this gist Feb 8, 2018. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. goldshtn revised this gist Feb 8, 2018. 1 changed file with 12 additions and 1 deletion.
    13 changes: 12 additions & 1 deletion place-probe.py
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,14 @@
    #!/usr/bin/env python
    #
    # USAGE: place-probe [-h] [--dry-run] [--debug] PID METHOD
    #
    # This tool helps place dynamic probes on .NET methods that were
    # CrossGen-generated (compiled ahead of time). To use the tool,
    # the CrossGen-generated assemblies need to have perfmaps generated
    # by CrossGen /CreatePerfMap, expected in the /tmp directory.
    #
    # Copyright (C) 2018, Sasha Goldshtein
    # Licensed under the MIT License

    import argparse
    import os
    @@ -41,7 +51,8 @@ def place_probe(path, offset):
    subprocess.check_call(command, shell=True)

    parser = argparse.ArgumentParser(description="Place dynamic tracing probes on a managed method " +
    "that resides in a crossgen-compiled assembly. For .NET Core on Linux.")
    "that resides in a crossgen-compiled assembly. For .NET Core on Linux.",
    epilog="EXAMPLE: ./place-probe.py 1234 'Thread::Sleep'")
    parser.add_argument("pid", type=int, help="the dotnet process id")
    parser.add_argument("symbol", type=str, help="the symbol on which to place the probe")
    parser.add_argument("--dry-run", action="store_true",
  3. goldshtn revised this gist Feb 8, 2018. 1 changed file with 20 additions and 0 deletions.
    20 changes: 20 additions & 0 deletions Makefile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,20 @@
    create:
    sudo docker run -v $(CURDIR):/app -it --rm microsoft/dotnet:sdk dotnet new console -o /app

    build:
    sudo docker run -it --rm -v $(CURDIR):/app microsoft/dotnet:sdk bash -c \
    "cd /app && dotnet publish -c Release -o /app/out -r linux-x64"
    out/crossgen /Platform_Assemblies_Paths out out/app.dll
    mv out/app.ni.exe out/app.dll

    run:
    COMPlus_PerfMapEnabled=1 $(CURDIR)/out/app

    generate:
    ./dotnet-mapgen-v3.py generate `pidof app`

    record:
    sudo perf record -e probe_System:* -p `pidof app` -g -- sleep 10
    sudo perf probe --del=*
    sudo chown ubuntu perf.data
    perf script
  4. goldshtn created this gist Feb 8, 2018.
    76 changes: 76 additions & 0 deletions place-probe.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,76 @@
    #!/usr/bin/env python

    import argparse
    import os
    import re
    import subprocess

    class Section(object):
    def __init__(self, start, perms, offset, path):
    self.start = int(start, 16)
    self.perms = perms
    self.offset = int(offset, 16)
    self.path = path

    def assembly_from_map_file(map_file):
    return re.match("/tmp/(.*)\.ni\.{.*}.map", map_file).group(1) + ".dll"

    def all_sections(pid):
    sections = {}
    with open("/proc/%d/maps" % pid, "r") as maps:
    for line in maps:
    match = re.match(r"(\S+)-\S+\s+(\S+)\s+(\S+)\s+\S+\s+\S+\s+(\S+)", line.strip())
    if match is None:
    continue
    start, perms, offset, path = match.group(1, 2, 3, 4)
    if '/' not in path:
    continue
    filename = os.path.basename(path)
    section = Section(start, perms, offset, path)
    if filename in sections:
    sections[filename].append(section)
    else:
    sections[filename] = [section]
    return sections

    def place_probe(path, offset):
    command = "sudo perf probe -x %s --add 0x%x" % (path, offset)
    if args.dry_run:
    print(command)
    else:
    subprocess.check_call(command, shell=True)

    parser = argparse.ArgumentParser(description="Place dynamic tracing probes on a managed method " +
    "that resides in a crossgen-compiled assembly. For .NET Core on Linux.")
    parser.add_argument("pid", type=int, help="the dotnet process id")
    parser.add_argument("symbol", type=str, help="the symbol on which to place the probe")
    parser.add_argument("--dry-run", action="store_true",
    help="print the symbol and the command but don't place the probe")
    parser.add_argument("--debug", action="store_true", help="print diagnostic information")
    args = parser.parse_args()

    sections = all_sections(args.pid)

    output = subprocess.check_output("grep '%s' /tmp/*.ni.*.map" % args.symbol,
    shell=True)
    for line in output.strip().split('\n'):
    parts = line.split()
    map_file, address = parts[0].split(':')
    address = int(address, 16)
    assembly = assembly_from_map_file(map_file)

    symbol = str.join(' ', parts[2:])
    if args.dry_run:
    print("\n" + symbol)

    first_section = sections[assembly][0]
    exec_section = [section for section in sections[assembly] if 'r-xp' == section.perms][0]
    offset_from_first = exec_section.start - first_section.start
    offset_in_file = exec_section.offset
    final_address = address - offset_from_first + offset_in_file

    if args.debug:
    print("address: %x, offset_from_first: %x, offset_in_file: %x, final_address: %x" %
    (address, offset_from_first, offset_in_file, final_address))

    place_probe(exec_section.path, final_address)