You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
7.7 KiB
207 lines
7.7 KiB
--- Tools/gdb/libpython.py.orig 2013-10-09 10:54:59.894701668 +0200 |
|
+++ Tools/gdb/libpython.py 2013-10-09 11:09:30.278703290 +0200 |
|
@@ -1194,39 +1194,113 @@ |
|
iter_frame = iter_frame.newer() |
|
return index |
|
|
|
+ # We divide frames into: |
|
+ # - "python frames": |
|
+ # - "bytecode frames" i.e. PyEval_EvalFrameEx |
|
+ # - "other python frames": things that are of interest from a python |
|
+ # POV, but aren't bytecode (e.g. GC, GIL) |
|
+ # - everything else |
|
+ |
|
+ def is_python_frame(self): |
|
+ '''Is this a PyEval_EvalFrameEx frame, or some other important |
|
+ frame? (see is_other_python_frame for what "important" means in this |
|
+ context)''' |
|
+ if self.is_evalframeex(): |
|
+ return True |
|
+ if self.is_other_python_frame(): |
|
+ return True |
|
+ return False |
|
+ |
|
def is_evalframeex(self): |
|
- '''Is this a PyEval_EvalFrameEx frame?''' |
|
- if self._gdbframe.name() == 'PyEval_EvalFrameEx': |
|
- ''' |
|
- I believe we also need to filter on the inline |
|
- struct frame_id.inline_depth, only regarding frames with |
|
- an inline depth of 0 as actually being this function |
|
- |
|
- So we reject those with type gdb.INLINE_FRAME |
|
- ''' |
|
- if self._gdbframe.type() == gdb.NORMAL_FRAME: |
|
- # We have a PyEval_EvalFrameEx frame: |
|
- return True |
|
+ if self._gdbframe.function(): |
|
+ if self._gdbframe.function().name == 'PyEval_EvalFrameEx': |
|
+ ''' |
|
+ I believe we also need to filter on the inline |
|
+ struct frame_id.inline_depth, only regarding frames with |
|
+ an inline depth of 0 as actually being this function |
|
+ |
|
+ So we reject those with type gdb.INLINE_FRAME |
|
+ ''' |
|
+ if self._gdbframe.type() == gdb.NORMAL_FRAME: |
|
+ # We have a PyEval_EvalFrameEx frame: |
|
+ return True |
|
+ |
|
+ return False |
|
+ |
|
+ def is_other_python_frame(self): |
|
+ '''Is this frame worth displaying in python backtraces? |
|
+ Examples: |
|
+ - waiting on the GIL |
|
+ - garbage-collecting |
|
+ - within a CFunction |
|
+ If it is, return a descriptive string |
|
+ For other frames, return False |
|
+ ''' |
|
+ if self.is_waiting_for_gil(): |
|
+ return 'Waiting for a lock (e.g. GIL)' |
|
+ elif self.is_gc_collect(): |
|
+ return 'Garbage-collecting' |
|
+ else: |
|
+ # Detect invocations of PyCFunction instances: |
|
+ if self._gdbframe.name() == 'PyCFunction_Call': |
|
+ try: |
|
+ func = self._gdbframe.read_var('func') |
|
+ # Use the prettyprinter for the func: |
|
+ return str(func) |
|
+ except RuntimeError: |
|
+ return 'PyCFunction invocation (unable to read "func")' |
|
+ older = self.older() |
|
+ if older and older._gdbframe.name() == 'call_function': |
|
+ # Within that frame: |
|
+ # 'call_function' contains, amongst other things, a |
|
+ # hand-inlined copy of PyCFunction_Call. |
|
+ # "func" is the local containing the PyObject* of the |
|
+ # callable instance |
|
+ # Report it, but only if it's a PyCFunction (since otherwise |
|
+ # we'd be reporting an implementation detail of every other |
|
+ # function invocation) |
|
+ try: |
|
+ func = older._gdbframe.read_var('func') |
|
+ funcobj = PyObjectPtr.from_pyobject_ptr(func) |
|
+ if isinstance(funcobj, PyCFunctionObjectPtr): |
|
+ # Use the prettyprinter for the func: |
|
+ return str(func) |
|
+ except RuntimeError: |
|
+ return False |
|
|
|
+ # This frame isn't worth reporting: |
|
return False |
|
|
|
+ def is_waiting_for_gil(self): |
|
+ '''Is this frame waiting for a lock?''' |
|
+ framename = self._gdbframe.name() |
|
+ if framename: |
|
+ return 'pthread_cond_timedwait' in framename or \ |
|
+ 'PyThread_acquire_lock' in framename |
|
+ |
|
+ def is_gc_collect(self): |
|
+ '''Is this frame "collect" within the the garbage-collector?''' |
|
+ return self._gdbframe.name() == 'collect' |
|
+ |
|
def get_pyop(self): |
|
try: |
|
f = self._gdbframe.read_var('f') |
|
- frame = PyFrameObjectPtr.from_pyobject_ptr(f) |
|
- if not frame.is_optimized_out(): |
|
- return frame |
|
- # gdb is unable to get the "f" argument of PyEval_EvalFrameEx() |
|
- # because it was "optimized out". Try to get "f" from the frame |
|
- # of the caller, PyEval_EvalCodeEx(). |
|
- orig_frame = frame |
|
- caller = self._gdbframe.older() |
|
- if caller: |
|
- f = caller.read_var('f') |
|
- frame = PyFrameObjectPtr.from_pyobject_ptr(f) |
|
- if not frame.is_optimized_out(): |
|
- return frame |
|
- return orig_frame |
|
+ obj = PyFrameObjectPtr.from_pyobject_ptr(f) |
|
+ if isinstance(obj, PyFrameObjectPtr): |
|
+ return obj |
|
+ else: |
|
+ return None |
|
+ except ValueError: |
|
+ return None |
|
+ |
|
+ def get_py_co(self): |
|
+ try: |
|
+ co = self._gdbframe.read_var('co') |
|
+ obj = PyCodeObjectPtr.from_pyobject_ptr(co) |
|
+ if isinstance(obj, PyCodeObjectPtr): |
|
+ return obj |
|
+ else: |
|
+ return None |
|
except ValueError: |
|
return None |
|
|
|
@@ -1239,8 +1313,22 @@ |
|
|
|
@classmethod |
|
def get_selected_python_frame(cls): |
|
- '''Try to obtain the Frame for the python code in the selected frame, |
|
- or None''' |
|
+ '''Try to obtain the Frame for the python-related code in the selected |
|
+ frame, or None''' |
|
+ frame = cls.get_selected_frame() |
|
+ |
|
+ while frame: |
|
+ if frame.is_python_frame(): |
|
+ return frame |
|
+ frame = frame.older() |
|
+ |
|
+ # Not found: |
|
+ return None |
|
+ |
|
+ @classmethod |
|
+ def get_selected_bytecode_frame(cls): |
|
+ '''Try to obtain the Frame for the python bytecode interpreter in the |
|
+ selected GDB frame, or None''' |
|
frame = cls.get_selected_frame() |
|
|
|
while frame: |
|
@@ -1265,7 +1353,11 @@ |
|
else: |
|
sys.stdout.write('#%i (unable to read python frame information)\n' % self.get_index()) |
|
else: |
|
- sys.stdout.write('#%i\n' % self.get_index()) |
|
+ info = self.is_other_python_frame() |
|
+ if info: |
|
+ sys.stdout.write('#%i %s\n' % (self.get_index(), info)) |
|
+ else: |
|
+ sys.stdout.write('#%i\n' % self.get_index()) |
|
|
|
class PyList(gdb.Command): |
|
'''List the current Python source code, if any |
|
@@ -1301,7 +1393,7 @@ |
|
if m: |
|
start, end = map(int, m.groups()) |
|
|
|
- frame = Frame.get_selected_python_frame() |
|
+ frame = Frame.get_selected_bytecode_frame() |
|
if not frame: |
|
print 'Unable to locate python frame' |
|
return |
|
@@ -1353,7 +1445,7 @@ |
|
if not iter_frame: |
|
break |
|
|
|
- if iter_frame.is_evalframeex(): |
|
+ if iter_frame.is_python_frame(): |
|
# Result: |
|
if iter_frame.select(): |
|
iter_frame.print_summary() |
|
@@ -1407,7 +1499,7 @@ |
|
def invoke(self, args, from_tty): |
|
frame = Frame.get_selected_python_frame() |
|
while frame: |
|
- if frame.is_evalframeex(): |
|
+ if frame.is_python_frame(): |
|
frame.print_summary() |
|
frame = frame.older() |
|
|
|
|