[CalendarServer-changes] [14999] CalendarServer/trunk/contrib/tools/lldb_utils.py

source_changes at macosforge.org source_changes at macosforge.org
Sun Jul 26 08:18:58 PDT 2015


Revision: 14999
          http://trac.calendarserver.org//changeset/14999
Author:   cdaboo at apple.com
Date:     2015-07-26 08:18:57 -0700 (Sun, 26 Jul 2015)
Log Message:
-----------
Add command to display python local variables. Add each call as a command for use directly within lldb.

Modified Paths:
--------------
    CalendarServer/trunk/contrib/tools/lldb_utils.py

Modified: CalendarServer/trunk/contrib/tools/lldb_utils.py
===================================================================
--- CalendarServer/trunk/contrib/tools/lldb_utils.py	2015-07-25 15:34:39 UTC (rev 14998)
+++ CalendarServer/trunk/contrib/tools/lldb_utils.py	2015-07-26 15:18:57 UTC (rev 14999)
@@ -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():
-    numthreads = lldb.process.GetNumThreads()
+def pybtall(debugger=None, command=None, result=None, dict=None):
+    """
+    An lldb command that prints a Python call back trace for all threads.
+
+    @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)
+        )
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150726/a19b6df0/attachment.html>


More information about the calendarserver-changes mailing list