Skip to content
Permalink
Browse files

Add command to display python local variables. Add each call as a com…

…mand for use directly within lldb.

git-svn-id: https://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk@14999 e27351fd-9f3e-4f54-a53b-843176b1656c
  • Loading branch information...
cyrusdaboo committed Jul 26, 2015
1 parent 46f9524 commit 98642cad9d3b38ee3064d3efb3aab432d91677ad
Showing with 138 additions and 13 deletions.
  1. +138 −13 contrib/tools/lldb_utils.py
@@ -23,37 +23,162 @@
> lldb
(lldb) target symbols add <path to Python.framework.dSYM>
(lldb) command script import contrib/tools/lldb_utils.py
Run commands using:
(lldb) pybt
...
(lldb) pybtall
...
(lldb) pylocals
...
or inside the python shell:
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> lldb_utils.pybt()
...
>>> lldb_utils.pybtall()
...
>>> lldb_utils.pylocals()
...
pybt - generate a python function call backtrace of the currently selected thread
pybtall - generate a python function call backtrace of all threads
pybt - generate a python function call back trace of the currently selected thread
pybtall - generate a python function call back trace of all threads
pylocals - generate a list of the name and values of all locals in the current
python frame (only works when the currently selected frame is a Python call
frame as found by the pybt command).
"""

import lldb #@UnresolvedImport

def pybt(thread=None):
if thread is None:
thread = lldb.thread
def _toStr(obj, pystring_t):
return obj.Cast(pystring_t).GetValueForExpressionPath("->ob_sval").summary



def pybt(debugger=None, command=None, result=None, dict=None, thread=None):
"""
An lldb command that prints a Python call back trace for the specified
thread or the currently selected thread.
@param debugger: debugger to use
@type debugger: L{lldb.SBDebugger}
@param command: ignored
@type command: ignored
@param result: ignored
@type result: ignored
@param dict: ignored
@type dict: ignored
@param thread: the specific thread to target
@type thread: L{lldb.SBThread}
"""

if debugger is None:
debugger = lldb.debugger
target = debugger.GetSelectedTarget()
if not isinstance(thread, lldb.SBThread):
thread = target.GetProcess().GetSelectedThread()

pystring_t = target.FindFirstType("PyStringObject").GetPointerType()

num_frames = thread.GetNumFrames()
pystring_t = lldb.target.FindFirstType("PyStringObject").GetPointerType()
for i in range(num_frames - 1):
fr = thread.GetFrameAtIndex(i)
if fr.GetFunctionName() == "PyEval_EvalFrameEx":
fr_next = thread.GetFrameAtIndex(i + 1)
if fr_next.GetFunctionName() == "PyEval_EvalCodeEx":
f = fr.GetValueForVariablePath("f")
filename = f.GetValueForExpressionPath("->f_code->co_filename").Cast(pystring_t).GetValueForExpressionPath("->ob_sval")
name = f.GetValueForExpressionPath(".f_code->co_name").Cast(pystring_t).GetValueForExpressionPath("->ob_sval")
print("{} - {}".format(filename.summary, name.summary))
filename = _toStr(f.GetValueForExpressionPath("->f_code->co_filename"), pystring_t)
name = _toStr(f.GetValueForExpressionPath("->f_code->co_name"), pystring_t)
lineno = f.GetValueForExpressionPath("->f_lineno").GetValue()
print("#{}: {} - {}:{}".format(
fr.GetFrameID(),
filename[1:-1] if filename else ".",
name[1:-1] if name else ".",
lineno if lineno else ".",
))



def pybtall(debugger=None, command=None, result=None, dict=None):
"""
An lldb command that prints a Python call back trace for all threads.
def pybtall():
numthreads = lldb.process.GetNumThreads()
@param debugger: debugger to use
@type debugger: L{lldb.SBDebugger}
@param command: ignored
@type command: ignored
@param result: ignored
@type result: ignored
@param dict: ignored
@type dict: ignored
"""
if debugger is None:
debugger = lldb.debugger
process = debugger.GetSelectedTarget().GetProcess()
numthreads = process.GetNumThreads()
for i in range(numthreads):
thread = lldb.process.GetThreadAtIndex(i)
thread = process.GetThreadAtIndex(i)
print("----- Thread: {} -----".format(i + 1))
pybt(thread)
pybt(debugger=debugger, thread=thread)



def pylocals(debugger=None, command=None, result=None, dict=None):
"""
An lldb command that prints a list of Python local variables for the
currently selected frame.
@param debugger: debugger to use
@type debugger: L{lldb.SBDebugger}
@param command: ignored
@type command: ignored
@param result: ignored
@type result: ignored
@param dict: ignored
@type dict: ignored
"""
if debugger is None:
debugger = lldb.debugger
target = debugger.GetSelectedTarget()
frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()

pystring_t = target.FindFirstType("PyStringObject").GetPointerType()
pytuple_t = target.FindFirstType("PyTupleObject").GetPointerType()

f = frame.GetValueForVariablePath("f")
try:
numlocals = int(f.GetValueForExpressionPath("->f_code->co_nlocals").GetValue())
except TypeError:
print("Current frame is not a Python function")
return
print("Locals in frame #{}".format(frame.GetFrameID()))
names = f.GetValueForExpressionPath("->f_code->co_varnames").Cast(pytuple_t)
for i in range(numlocals):
localname = _toStr(names.GetValueForExpressionPath("->ob_item[{}]".format(i)), pystring_t)
local = frame.EvaluateExpression("PyString_AsString(PyObject_Repr(f->f_localsplus[{}]))".format(i)).summary
print(" {} = {}".format(
localname[1:-1] if localname else ".",
local[1:-1] if local else ".",
))


CMDS = ("pybt", "pybtall", "pylocals",)


def __lldb_init_module(debugger, dict):
"""
Register each command with lldb so they are available directly within lldb as
well as within its Python script shell.
@param debugger: debugger to use
@type debugger: L{lldb.SBDebugger}
@param dict: ignored
@type dict: ignored
"""
for cmd in CMDS:
debugger.HandleCommand(
"command script add -f lldb_utils.{cmd} {cmd}".format(cmd=cmd)
)

0 comments on commit 98642ca

Please sign in to comment.
You can’t perform that action at this time.