From 8f8e182856fed32ce0a67a04cc054bb7e88d1e14 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Thu, 25 Feb 2010 20:36:47 -0500 Subject: Rewrite the type lookup using flags where possible, eliminating need for "is_py3k" hack --- libpython.py | 102 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 43 deletions(-) diff --git a/libpython.py b/libpython.py index 1ede863..2f1e0ec 100644 --- a/libpython.py +++ b/libpython.py @@ -49,6 +49,16 @@ TODO: better handling of "instance" import gdb +Py_TPFLAGS_INT_SUBCLASS = (1L<<23) +Py_TPFLAGS_LONG_SUBCLASS = (1L<<24) +Py_TPFLAGS_LIST_SUBCLASS = (1L<<25) +Py_TPFLAGS_TUPLE_SUBCLASS = (1L<<26) +Py_TPFLAGS_STRING_SUBCLASS = (1L<<27) +Py_TPFLAGS_UNICODE_SUBCLASS = (1L<<28) +Py_TPFLAGS_DICT_SUBCLASS = (1L<<29) +Py_TPFLAGS_BASE_EXC_SUBCLASS = (1L<<30) +Py_TPFLAGS_TYPE_SUBCLASS = (1L<<31) + class NullPyObjectPtr(RuntimeError): pass @@ -63,17 +73,6 @@ def safe_range(val): # threshold in case the data was corrupted return xrange(safety_limit(val)) -def is_py3k(): - # This code assumes that a libpython's DWARF data has actually been - # loaded by the point that this function is called - sym = gdb.lookup_symbol('PyBytes_Type')[0] - if sym: - #...then PyBytes_Type exists, assume this is libpython3.* - return True - else: - #...then PyBytes_Type doesn't exist, assume this is libpython2.* - return False - class PyObjectPtr(object): """ Class wrapping a gdb.Value that's a either a (PyObject*) within the @@ -168,31 +167,62 @@ class PyObjectPtr(object): long(self._gdbval)) @classmethod - def subclass_for_tp_name(cls, tp_name): - if tp_name == 'str': - if is_py3k(): - return PyUnicodeObjectPtr - else: - return PyStringObjectPtr - if tp_name == 'int': - if is_py3k(): - return PyLongObjectPtr - else: - return PyIntObjectPtr + def subclass_from_type(cls, t): + ''' + Given a PyTypeObjectPtr instance wrapping a gdb.Value that's a + (PyTypeObject*), determine the corresponding subclass of PyObjectPtr + to use + + Ideally, we would look up the symbols for the global types, but that + isn't working yet: + (gdb) python print gdb.lookup_symbol('PyList_Type')[0].value + Traceback (most recent call last): + File "", line 1, in + NotImplementedError: Symbol type not yet supported in Python scripts. + Error while executing Python code. + + For now, we use tp_flags, after doing some string comparisons on the + tp_name for some special-cases that don't seem to be visible through flags + ''' + try: + tp_name = t.field('tp_name').string() + tp_flags = int(t.field('tp_flags')) + except RuntimeError: + # Handle any kind of error e.g. NULL ptrs by simply using the base + # class + return cls + + #print 'tp_flags = 0x%08x' % tp_flags + #print 'tp_name = %r' % tp_name name_map = {'bool' : PyBoolObjectPtr, 'classobj': PyClassObjectPtr, - 'dict': PyDictObjectPtr, 'instance': PyInstanceObjectPtr, - 'list': PyListObjectPtr, - 'long': PyLongObjectPtr, 'NoneType': PyNoneStructPtr, - 'tuple': PyTupleObjectPtr, 'frame': PyFrameObjectPtr, - 'unicode': PyUnicodeObjectPtr, } if tp_name in name_map: return name_map[tp_name] + + if tp_flags & Py_TPFLAGS_INT_SUBCLASS: + return PyIntObjectPtr + if tp_flags & Py_TPFLAGS_LONG_SUBCLASS: + return PyLongObjectPtr + if tp_flags & Py_TPFLAGS_LIST_SUBCLASS: + return PyListObjectPtr + if tp_flags & Py_TPFLAGS_TUPLE_SUBCLASS: + return PyTupleObjectPtr + if tp_flags & Py_TPFLAGS_STRING_SUBCLASS: + return PyStringObjectPtr + if tp_flags & Py_TPFLAGS_UNICODE_SUBCLASS: + 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_TYPE_SUBCLASS: + # return PyTypeObjectPtr + # Use the base class: return cls @@ -200,26 +230,12 @@ class PyObjectPtr(object): def from_pyobject_ptr(cls, gdbval): ''' Try to locate the appropriate derived class dynamically, and cast - the pointer accordingly: - For now, we just do string comparison on the tp_name - Other approaches: - (i) look up the symbols for the global types, but that isn't working yet: - (gdb) python print gdb.lookup_symbol('PyList_Type')[0].value - Traceback (most recent call last): - File "", line 1, in - NotImplementedError: Symbol type not yet supported in Python scripts. - Error while executing Python code. - (ii) look at tp_flags, looking e.g. for Py_TPFLAGS_LIST_SUBCLASS however - this would rely on the values of those flags. - - So we go with the simple approach of looking at tp_name + the pointer accordingly. ''' # try: p = PyObjectPtr(gdbval) - t = p.type() - tp_name = t.field('tp_name').string() - cls = cls.subclass_for_tp_name(tp_name) + cls = cls.subclass_from_type(p.type()) return cls(gdbval, cast_to=cls.get_gdb_type()) except RuntimeError: # Handle any kind of error e.g. NULL ptrs by simply using the base -- cgit