Last active
September 13, 2018 16:53
-
-
Save goldshtn/56d3daae842ccad44612cdacd68bac29 to your computer and use it in GitHub Desktop.
Place a dynamic probe on a .NET Core method
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 | |
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.", | |
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", | |
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment