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