rpms/python/devel python-gdb.py,1.1,1.2 python.spec,1.179,1.180

dmalcolm dmalcolm at fedoraproject.org
Thu Mar 25 20:35:17 UTC 2010


Author: dmalcolm

Update of /cvs/pkgs/rpms/python/devel
In directory cvs01.phx2.fedoraproject.org:/tmp/cvs-serv11128

Modified Files:
	python-gdb.py python.spec 
Log Message:
update python-gdb.py from v3 to v4 (fixing infinite recursion on reference
cycles and tracebacks on bytes 0x80-0xff in strings, adding handlers for sets
and exceptions)



Index: python-gdb.py
===================================================================
RCS file: /cvs/pkgs/rpms/python/devel/python-gdb.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -p -r1.1 -r1.2
--- python-gdb.py	24 Mar 2010 18:30:06 -0000	1.1
+++ python-gdb.py	25 Mar 2010 20:35:16 -0000	1.2
@@ -35,6 +35,7 @@ import gdb
 
 # Look up the gdb.Type for some standard types:
 _type_char_ptr = gdb.lookup_type('char').pointer() # char*
+_type_unsigned_char_ptr = gdb.lookup_type('unsigned char').pointer() # unsigned char*
 _type_void_ptr = gdb.lookup_type('void').pointer() # void*
 _type_size_t = gdb.lookup_type('size_t')
 
@@ -140,7 +141,7 @@ class PyObjectPtr(object):
             # Can't even read the object at all?
             return 'unknown'
 
-    def proxyval(self):
+    def proxyval(self, visited):
         '''
         Scrape a value from the inferior process, and try to represent it
         within the gdb process, whilst (hopefully) avoiding crashes when
@@ -150,6 +151,11 @@ class PyObjectPtr(object):
 
         For example, a PyIntObject* with ob_ival 42 in the inferior process
         should result in an int(42) in this process.
+
+        visited: a set of all gdb.Value pyobject pointers already visited
+        whilst generating this value (to guard against infinite recursion when
+        visiting object graphs with loops).  Analogous to Py_ReprEnter and
+        Py_ReprLeave
         '''
 
         class FakeRepr(object):
@@ -209,6 +215,8 @@ class PyObjectPtr(object):
                     'instance': PyInstanceObjectPtr,
                     'NoneType': PyNoneStructPtr,
                     'frame': PyFrameObjectPtr,
+                    'set' : PySetObjectPtr,
+                    'frozenset' : PySetObjectPtr,
                     }
         if tp_name in name_map:
             return name_map[tp_name]
@@ -230,8 +238,8 @@ class PyObjectPtr(object):
             return PyUnicodeObjectPtr
         if tp_flags & Py_TPFLAGS_DICT_SUBCLASS:
             return PyDictObjectPtr
-        #if tp_flags & Py_TPFLAGS_BASE_EXC_SUBCLASS:
-        #    return something
+        if tp_flags & Py_TPFLAGS_BASE_EXC_SUBCLASS:
+            return PyBaseExceptionObjectPtr
         #if tp_flags & Py_TPFLAGS_TYPE_SUBCLASS:
         #    return PyTypeObjectPtr
 
@@ -258,6 +266,22 @@ class PyObjectPtr(object):
     def get_gdb_type(cls):
         return gdb.lookup_type(cls._typename).pointer()
 
+    def as_address(self):
+        return long(self._gdbval)
+
+
+class ProxyAlreadyVisited(object):
+    '''
+    Placeholder proxy to use when protecting against infinite recursion due to
+    loops in the object graph.
+
+    Analogous to the values emitted by the users of Py_ReprEnter and Py_ReprLeave
+    '''
+    def __init__(self, rep):
+        self._rep = rep
+    
+    def __repr__(self):
+        return self._rep
 
 class InstanceProxy(object):
 
@@ -287,15 +311,19 @@ def _PyObject_VAR_SIZE(typeobj, nitems):
 class HeapTypeObjectPtr(PyObjectPtr):
     _typename = 'PyObject'
 
-    def proxyval(self):
+    def proxyval(self, visited):
         '''
         Support for new-style classes.
 
         Currently we just locate the dictionary using a transliteration to
         python of _PyObject_GetDictPtr, ignoring descriptors
         '''
-        attr_dict = {}
+        # Guard against infinite loops:
+        if self.as_address() in visited:
+            return ProxyAlreadyVisited('<...>')
+        visited.add(self.as_address())
 
+        attr_dict = {}
         try:
             typeobj = self.type()
             dictoffset = int_from_int(typeobj.field('tp_dictoffset'))
@@ -313,16 +341,39 @@ class HeapTypeObjectPtr(PyObjectPtr):
                 dictptr = self._gdbval.cast(_type_char_ptr) + dictoffset
                 PyObjectPtrPtr = PyObjectPtr.get_gdb_type().pointer()
                 dictptr = dictptr.cast(PyObjectPtrPtr)
-                attr_dict = PyObjectPtr.from_pyobject_ptr(dictptr.dereference()).proxyval()
+                attr_dict = PyObjectPtr.from_pyobject_ptr(dictptr.dereference()).proxyval(visited)
         except RuntimeError:
             # Corrupt data somewhere; fail safe
-            pass    
+            pass
 
         tp_name = self.safe_tp_name()
 
         # New-style class:
         return InstanceProxy(tp_name, attr_dict, long(self._gdbval))
 
+class ProxyException(Exception):
+    def __init__(self, tp_name, args):
+        self.tp_name = tp_name
+        self.args = args
+
+    def __repr__(self):
+        return '%s%r' % (self.tp_name, self.args)
+
+class PyBaseExceptionObjectPtr(PyObjectPtr):
+    """
+    Class wrapping a gdb.Value that's a PyBaseExceptionObject* i.e. an exception
+    within the process being debugged.
+    """
+    _typename = 'PyBaseExceptionObject'
+
+    def proxyval(self, visited):
+        # Guard against infinite loops:
+        if self.as_address() in visited:
+            return ProxyAlreadyVisited('(...)')
+        visited.add(self.as_address())
+        arg_proxy = PyObjectPtr.from_pyobject_ptr(self.field('args')).proxyval(visited)
+        return ProxyException(self.safe_tp_name(),
+                              arg_proxy)
 
 class PyBoolObjectPtr(PyObjectPtr):
     """
@@ -331,7 +382,7 @@ class PyBoolObjectPtr(PyObjectPtr):
     """
     _typename = 'PyBoolObject'
 
-    def proxyval(self):
+    def proxyval(self, visited):
         if int_from_int(self.field('ob_ival')):
             return True
         else:
@@ -360,7 +411,7 @@ class PyCodeObjectPtr(PyObjectPtr):
         Analogous to PyCode_Addr2Line; translated from pseudocode in
         Objects/lnotab_notes.txt
         '''
-        co_lnotab = PyObjectPtr.from_pyobject_ptr(self.field('co_lnotab')).proxyval()
+        co_lnotab = PyObjectPtr.from_pyobject_ptr(self.field('co_lnotab')).proxyval(set())
 
         # Initialize lineno to co_firstlineno as per PyCode_Addr2Line
         # not 0, as lnotab_notes.txt has it:
@@ -381,27 +432,37 @@ class PyDictObjectPtr(PyObjectPtr):
     """
     _typename = 'PyDictObject'
 
-    def proxyval(self):
+    def proxyval(self, visited):
+        # Guard against infinite loops:
+        if self.as_address() in visited:
+            return ProxyAlreadyVisited('{...}')
+        visited.add(self.as_address())
+
         result = {}
         for i in safe_range(self.field('ma_mask') + 1):
             ep = self.field('ma_table') + i
             pvalue = PyObjectPtr.from_pyobject_ptr(ep['me_value'])
             if not pvalue.is_null():
                 pkey = PyObjectPtr.from_pyobject_ptr(ep['me_key'])
-                result[pkey.proxyval()] = pvalue.proxyval()
+                result[pkey.proxyval(visited)] = pvalue.proxyval(visited)
         return result
 
 
 class PyInstanceObjectPtr(PyObjectPtr):
     _typename = 'PyInstanceObject'
 
-    def proxyval(self):
+    def proxyval(self, visited):
+        # Guard against infinite loops:
+        if self.as_address() in visited:
+            return ProxyAlreadyVisited('<...>')
+        visited.add(self.as_address())
+
         # Get name of class:
         in_class = PyObjectPtr.from_pyobject_ptr(self.field('in_class'))
-        cl_name = PyObjectPtr.from_pyobject_ptr(in_class.field('cl_name')).proxyval()
+        cl_name = PyObjectPtr.from_pyobject_ptr(in_class.field('cl_name')).proxyval(visited)
 
         # Get dictionary of instance attributes:
-        in_dict = PyObjectPtr.from_pyobject_ptr(self.field('in_dict')).proxyval()
+        in_dict = PyObjectPtr.from_pyobject_ptr(self.field('in_dict')).proxyval(visited)
 
         # Old-style class:
         return InstanceProxy(cl_name, in_dict, long(self._gdbval))
@@ -410,11 +471,10 @@ class PyInstanceObjectPtr(PyObjectPtr):
 class PyIntObjectPtr(PyObjectPtr):
     _typename = 'PyIntObject'
 
-    def proxyval(self):
+    def proxyval(self, visited):
         result = int_from_int(self.field('ob_ival'))
         return result
 
-
 class PyListObjectPtr(PyObjectPtr):
     _typename = 'PyListObject'
 
@@ -423,8 +483,13 @@ class PyListObjectPtr(PyObjectPtr):
         field_ob_item = self.field('ob_item')
         return field_ob_item[i]
 
-    def proxyval(self):
-        result = [PyObjectPtr.from_pyobject_ptr(self[i]).proxyval()
+    def proxyval(self, visited):
+        # Guard against infinite loops:
+        if self.as_address() in visited:
+            return ProxyAlreadyVisited('[...]')
+        visited.add(self.as_address())
+        
+        result = [PyObjectPtr.from_pyobject_ptr(self[i]).proxyval(visited)
                   for i in safe_range(int_from_int(self.field('ob_size')))]
         return result
 
@@ -432,7 +497,7 @@ class PyListObjectPtr(PyObjectPtr):
 class PyLongObjectPtr(PyObjectPtr):
     _typename = 'PyLongObject'
 
-    def proxyval(self):
+    def proxyval(self, visited):
         '''
         Python's Include/longobjrep.h has this declaration:
            struct _longobject {
@@ -477,7 +542,7 @@ class PyNoneStructPtr(PyObjectPtr):
     """
     _typename = 'PyObject'
 
-    def proxyval(self):
+    def proxyval(self, visited):
         return None
 
 
@@ -489,16 +554,39 @@ class PyFrameObjectPtr(PyObjectPtr):
         return str(fi)
 
 
+class PySetObjectPtr(PyObjectPtr):
+    _typename = 'PySetObject'
+
+    def proxyval(self, visited):
+        # Guard against infinite loops:
+        if self.as_address() in visited:
+            return ProxyAlreadyVisited('%s(...)' % self.safe_tp_name())
+        visited.add(self.as_address())
+
+        members = []
+        table = self.field('table')
+        for i in safe_range(self.field('mask')+1):
+            setentry = table[i]
+            key = setentry['key']
+            if key != 0:
+                key_proxy = PyObjectPtr.from_pyobject_ptr(key).proxyval(visited)
+                if key_proxy != '<dummy key>':
+                    members.append(key_proxy)
+        if self.safe_tp_name() == 'frozenset':
+            return frozenset(members)
+        else:
+            return set(members)
+
 class PyStringObjectPtr(PyObjectPtr):
     _typename = 'PyStringObject'
 
     def __str__(self):
         field_ob_size = self.field('ob_size')
         field_ob_sval = self.field('ob_sval')
-        char_ptr = field_ob_sval.address.cast(_type_char_ptr)
-        return ''.join([chr(field_ob_sval[i]) for i in safe_range(field_ob_size)])
+        char_ptr = field_ob_sval.address.cast(_type_unsigned_char_ptr)
+        return ''.join([chr(char_ptr[i]) for i in safe_range(field_ob_size)])
 
-    def proxyval(self):
+    def proxyval(self, visited):
         return str(self)
 
 
@@ -510,8 +598,13 @@ class PyTupleObjectPtr(PyObjectPtr):
         field_ob_item = self.field('ob_item')
         return field_ob_item[i]
 
-    def proxyval(self):
-        result = tuple([PyObjectPtr.from_pyobject_ptr(self[i]).proxyval()
+    def proxyval(self, visited):
+        # Guard against infinite loops:
+        if self.as_address() in visited:
+            return ProxyAlreadyVisited('(...)')
+        visited.add(self.as_address())
+
+        result = tuple([PyObjectPtr.from_pyobject_ptr(self[i]).proxyval(visited)
                         for i in safe_range(int_from_int(self.field('ob_size')))])
         return result
 
@@ -523,7 +616,7 @@ class PyTypeObjectPtr(PyObjectPtr):
 class PyUnicodeObjectPtr(PyObjectPtr):
     _typename = 'PyUnicodeObject'
 
-    def proxyval(self):
+    def proxyval(self, visited):
         # From unicodeobject.h:
         #     Py_ssize_t length;  /* Length of raw Unicode data in buffer */
         #     Py_UNICODE *str;    /* Raw Unicode buffer */
@@ -576,13 +669,13 @@ class FrameInfo:
             if not value.is_null():
                 name = PyObjectPtr.from_pyobject_ptr(self.co_varnames[i])
                 #print 'name=%s' % name
-                value = value.proxyval()
+                value = value.proxyval(set())
                 #print 'value=%s' % value
                 self.locals.append((str(name), value))
 
     def filename(self):
         '''Get the path of the current Python source file, as a string'''
-        return self.co_filename.proxyval()
+        return self.co_filename.proxyval(set())
 
     def current_line_num(self):
         '''Get current line number as an integer (1-based)
@@ -626,7 +719,7 @@ class PyObjectPtrPrinter:
         self.gdbval = gdbval
 
     def to_string (self):
-        proxyval = PyObjectPtr.from_pyobject_ptr(self.gdbval).proxyval()
+        proxyval = PyObjectPtr.from_pyobject_ptr(self.gdbval).proxyval(set())
         return stringify(proxyval)
 
 


Index: python.spec
===================================================================
RCS file: /cvs/pkgs/rpms/python/devel/python.spec,v
retrieving revision 1.179
retrieving revision 1.180
diff -u -p -r1.179 -r1.180
--- python.spec	24 Mar 2010 18:30:06 -0000	1.179
+++ python.spec	25 Mar 2010 20:35:17 -0000	1.180
@@ -53,7 +53,7 @@ Summary: An interpreted, interactive, ob
 Name: %{python}
 # Remember to also rebase python-docs when changing this:
 Version: 2.6.5
-Release: 3%{?dist}
+Release: 4%{?dist}
 License: Python
 Group: Development/Languages
 Provides: python-abi = %{pybasever}
@@ -75,7 +75,7 @@ Source: http://www.python.org/ftp/python
 #
 # Downloaded from:
 #  http://bugs.python.org/issue8032
-# This is Tools/gdb/libpython.py from v3 of the patch
+# This is Tools/gdb/libpython.py from v4 of the patch
 Source1: python-gdb.py
 
 # Work around bug 562906 until it's fixed in rpm-build by providing a fixed
@@ -954,6 +954,11 @@ rm -fr %{buildroot}
 # payload file would be unpackaged)
 
 %changelog
+* Thu Mar 25 2010 David Malcolm <dmalcolm at redhat.com> - 2.6.5-4
+- update python-gdb.py from v3 to v4 (fixing infinite recursion on reference
+cycles and tracebacks on bytes 0x80-0xff in strings, adding handlers for sets
+and exceptions)
+
 * Wed Mar 24 2010 David Malcolm <dmalcolm at redhat.com> - 2.6.5-3
 - refresh gdb hooks to v3 (reworking how they are packaged)
 



More information about the scm-commits mailing list