Skip to content

Instantly share code, notes, and snippets.

@goldshtn
Created March 4, 2017 15:22
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save goldshtn/242dfd395710c378b08564492e9f7fa4 to your computer and use it in GitHub Desktop.
Save goldshtn/242dfd395710c378b08564492e9f7fa4 to your computer and use it in GitHub Desktop.
.NET Core on Linux LLDB analysis scripts
#!/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))
# 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