summaryrefslogtreecommitdiffstats
path: root/libpython.py
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2010-02-27 03:01:12 -0500
committerDavid Malcolm <dmalcolm@redhat.com>2010-02-27 03:01:12 -0500
commit6bce74d590343ad39dc5b2beaeadfcfc2d201462 (patch)
tree1796b9b8bc1b942a374c39c2ad27013bd11c880e /libpython.py
parente0fef4a512f975c30df50f68f24e7c5410ec1fe6 (diff)
downloadlibpython-6bce74d590343ad39dc5b2beaeadfcfc2d201462.tar.gz
libpython-6bce74d590343ad39dc5b2beaeadfcfc2d201462.tar.xz
libpython-6bce74d590343ad39dc5b2beaeadfcfc2d201462.zip
Add initial support for new-style classes
Diffstat (limited to 'libpython.py')
-rw-r--r--libpython.py112
1 files changed, 86 insertions, 26 deletions
diff --git a/libpython.py b/libpython.py
index c574561..e994af2 100644
--- a/libpython.py
+++ b/libpython.py
@@ -45,6 +45,16 @@ TODO: better handling of "instance"
import gdb
+# Look up the gdb.Type for some standard types:
+_type_char_ptr = gdb.lookup_type('char').pointer() # char*
+_type_void_ptr = gdb.lookup_type('void').pointer() # void*
+_type_size_t = gdb.lookup_type('size_t')
+
+SIZEOF_VOID_P = _type_void_ptr.sizeof
+
+
+Py_TPFLAGS_HEAPTYPE = (1L << 9)
+
Py_TPFLAGS_INT_SUBCLASS = (1L << 23)
Py_TPFLAGS_LONG_SUBCLASS = (1L << 24)
Py_TPFLAGS_LIST_SUBCLASS = (1L << 25)
@@ -132,6 +142,16 @@ class PyObjectPtr(object):
def is_null(self):
return 0 == long(self._gdbval)
+ def safe_tp_name(self):
+ try:
+ return self.type().field('tp_name').string()
+ except NullPyObjectPtr:
+ # NULL tp_name?
+ return 'unknown'
+ except RuntimeError:
+ # Can't even read the object at all?
+ return 'unknown'
+
def proxyval(self):
'''
Scrape a value from the inferior process, and try to represent it
@@ -158,15 +178,7 @@ class PyObjectPtr(object):
def __repr__(self):
return '<%s at remote 0x%x>' % (self.tp_name, self.address)
- try:
- tp_name = self.type().field('tp_name').string()
- except NullPyObjectPtr:
- # NULL tp_name?
- tp_name = 'unknown'
- except RuntimeError:
- # Can't even read the object at all?
- tp_name = 'unknown'
- return FakeRepr(tp_name,
+ return FakeRepr(self.safe_tp_name(),
long(self._gdbval))
@classmethod
@@ -208,6 +220,9 @@ class PyObjectPtr(object):
if tp_name in name_map:
return name_map[tp_name]
+ if tp_flags & Py_TPFLAGS_HEAPTYPE:
+ return HeapTypeObjectPtr
+
if tp_flags & Py_TPFLAGS_INT_SUBCLASS:
return PyIntObjectPtr
if tp_flags & Py_TPFLAGS_LONG_SUBCLASS:
@@ -251,6 +266,67 @@ class PyObjectPtr(object):
return gdb.lookup_type(cls._typename).pointer()
+class InstanceProxy(object):
+
+ def __init__(self, cl_name, attrdict, address):
+ self.cl_name = cl_name
+ self.attrdict = attrdict
+ self.address = address
+
+ def __repr__(self):
+ kwargs = ', '.join(["%s=%r" % (arg, val)
+ for arg, val in self.attrdict.iteritems()])
+ return '<%s(%s) at remote 0x%x>' % (self.cl_name,
+ kwargs, self.address)
+
+
+def _PyObject_VAR_SIZE(typeobj, nitems):
+ return ( ( typeobj.field('tp_basicsize') +
+ nitems * typeobj.field('tp_itemsize') +
+ (SIZEOF_VOID_P - 1)
+ ) & ~(SIZEOF_VOID_P - 1)
+ ).cast(_type_size_t)
+
+class HeapTypeObjectPtr(PyObjectPtr):
+ _typename = 'PyObject'
+
+ def proxyval(self):
+ '''
+ Support for new-style classes.
+
+ Currently we just locate the dictionary using _PyObject_GetDictPtr,
+ ignoring descriptors
+ '''
+ attr_dict = {}
+
+ try:
+ typeobj = self.type()
+ dictoffset = int_from_int(typeobj.field('tp_dictoffset'))
+ if dictoffset != 0:
+ if dictoffset < 0:
+ type_PyVarObject_ptr = gdb.lookup_type('PyVarObject').pointer()
+ tsize = int_from_int(self._gdbval.cast(type_PyVarObject_ptr)['ob_size'])
+ if tsize < 0:
+ tsize = -tsize
+ size = _PyObject_VAR_SIZE(typeobj, tsize)
+ dictoffset += size
+ assert dictoffset > 0
+ assert dictoffset % SIZEOF_VOID_P == 0
+
+ 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()
+ except RuntimeError:
+ # Corrupt data somewhere; fail safe
+ pass
+
+ tp_name = self.safe_tp_name()
+
+ # New-style class:
+ return InstanceProxy(tp_name, attr_dict, long(self._gdbval))
+
+
class PyBoolObjectPtr(PyObjectPtr):
"""
Class wrapping a gdb.Value that's a PyBoolObject* i.e. one of the two
@@ -303,20 +379,6 @@ class PyInstanceObjectPtr(PyObjectPtr):
_typename = 'PyInstanceObject'
def proxyval(self):
-
- class InstanceProxy(object):
-
- def __init__(self, cl_name, attrdict, address):
- self.cl_name = cl_name
- self.attrdict = attrdict
- self.address = address
-
- def __repr__(self):
- kwargs = ', '.join(["%s=%r" % (arg, val)
- for arg, val in self.attrdict.iteritems()])
- return '<%s(%s) at remote 0x%x>' % (self.cl_name,
- kwargs, self.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()
@@ -324,6 +386,7 @@ class PyInstanceObjectPtr(PyObjectPtr):
# Get dictionary of instance attributes:
in_dict = PyObjectPtr.from_pyobject_ptr(self.field('in_dict')).proxyval()
+ # Old-style class:
return InstanceProxy(cl_name, in_dict, long(self._gdbval))
@@ -413,9 +476,6 @@ class PyStringObjectPtr(PyObjectPtr):
_typename = 'PyStringObject'
def __str__(self):
- # Lookup the gdb.Type for "char*"
- _type_char_ptr = gdb.lookup_type('char').pointer()
-
field_ob_sval = self.field('ob_sval')
char_ptr = field_ob_sval.address.cast(_type_char_ptr)
return char_ptr.string()