Created
March 4, 2017 15:22
-
-
Save goldshtn/242dfd395710c378b08564492e9f7fa4 to your computer and use it in GitHub Desktop.
.NET Core on Linux LLDB analysis scripts
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 | |
# | |
# analyze.py Example of an LLDB script that loads SOS and runs a command | |
# for analysis of a .NET Core application on Linux/macOS. | |
# Requires LLDB matching the version of libsosplugin.so for your | |
# CoreCLR version, and gdb. | |
# | |
# USAGE: analyze.py [--memory] [--stacks] COREFILE | |
# | |
# NOTE: To run this as stand-alone, you might need to fix some bad symlinks | |
# created by the LLDB installation, so that the _lldb.so library can be | |
# found by the Python lldb module. These are in /usr/lib/llvm-3.5/lib/ | |
# python/site-packages/lldb -- just fix the symlinks to remove the non- | |
# existing x86_64-linux-gnu directory from the link. | |
# SEE: http://stackoverflow.com/a/31005690/587772 | |
# | |
# Copyright (C) Sasha Goldshtein, 2017 | |
# Licensed under the MIT License | |
import argparse | |
import lldb | |
import os | |
import subprocess | |
import tempfile | |
def truncate(s, n): | |
return s[:n-3] + "..." if len(s) >= n-3 else s | |
def run_command_get_output_file(cmd): | |
oldout = debugger.GetOutputFileHandle() | |
fd, path = tempfile.mkstemp(prefix="lldb-out") | |
with os.fdopen(fd, "w") as f: | |
debugger.SetOutputFileHandle(f, False) | |
debugger.HandleCommand(cmd) | |
debugger.SetOutputFileHandle(oldout, False) | |
return path | |
parser = argparse.ArgumentParser() | |
parser.add_argument("core", help="the core file to analyze") | |
parser.add_argument("--memory", action="store_true", | |
help="display managed memory statistics") | |
parser.add_argument("--stacks", action="store_true", | |
help="display managed stacks and exceptions") | |
args = parser.parse_args() | |
dotnetlocation = subprocess.check_output("which dotnet", shell=True).strip() | |
soslocation = subprocess.check_output( | |
"find /usr/share/dotnet/shared -name libsosplugin.so", | |
shell=True).strip() | |
clrdir = os.path.dirname(soslocation) | |
print("'dotnet' location: " + dotnetlocation) | |
print("SOS plugin location: " + soslocation) | |
print("libcoreclr location: " + clrdir) | |
debugger = lldb.SBDebugger.Create() | |
target = debugger.CreateTarget(dotnetlocation) | |
process = target.LoadCore(args.core) | |
def get_thread_idx_to_osid(): | |
gdbcmd = "gdb %s -c %s" % (dotnetlocation, args.core) | |
gdbout = subprocess.check_output(gdbcmd + | |
" -ex 'pi for t in gdb.selected_thread().inferior.threads(): " + | |
"print(\"<<TID>> %d %d\" % (t.num, t.ptid[1]))' -ex 'quit' 2>/dev/null", | |
shell=True) | |
threadids = {} | |
for line in gdbout.split('\n'): | |
if "<<TID>>" not in line: continue | |
_, idx, osid = line.split() | |
threadids[int(idx)] = int(osid) | |
return threadids | |
debugger.HandleCommand("plugin load " + soslocation) | |
debugger.HandleCommand("setclrpath " + clrdir) | |
if args.stacks: | |
for idx, osid in get_thread_idx_to_osid().items(): | |
debugger.HandleCommand("setsostid %x %x" % (osid, idx)) | |
debugger.HandleCommand("sos PrintException -nested") | |
debugger.HandleCommand("sos ClrStack") | |
if args.memory: | |
path = run_command_get_output_file("sos DumpHeap -stat") | |
stats = {} | |
with open(path, "r") as f: | |
for line in f: | |
parts = line.split() | |
if len(parts) < 4 or parts[3] == "UNKNOWN": | |
continue | |
try: | |
mt = int(parts[0], 16) | |
except ValueError: | |
continue | |
stats[str.join(' ', parts[3:])] = int(parts[2]) | |
print("%-60s %12s" % ("TYPE", "SIZE (MB)")) | |
for classname, size in sorted(stats.items(), key=lambda kv: -kv[1])[:10]: | |
print("%-60s %12.3f" % (truncate(classname, 60), size/1048576.0)) |
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
# lldbsos.py Set up LLDB for analysis of a .NET process or a core dump by | |
# loading the SOS plugin and setting the CLR path appropriately. | |
# | |
# Example usage: | |
# lldb-3.5 /usr/bin/dotnet -c core.25315 -o "script import lldbsos" | |
# | |
# Copyright (C) Sasha Goldshtein, 2017 | |
# Licensed under the MIT License | |
import lldb | |
import os | |
import subprocess | |
soslocation = subprocess.check_output( | |
"find /usr/share/dotnet/shared -name libsosplugin.so", | |
shell=True).strip() | |
clrdir = os.path.dirname(soslocation) | |
lldb.debugger.HandleCommand("plugin load " + soslocation) | |
lldb.debugger.HandleCommand("setclrpath " + clrdir) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment