- 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)
This commit is contained in:
dmalcolm 2010-03-25 20:38:13 +00:00
parent 485fd76f7f
commit da0826ad3b
2 changed files with 129 additions and 31 deletions

View File

@ -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,7 +341,7 @@ 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
@ -323,6 +351,29 @@ class HeapTypeObjectPtr(PyObjectPtr):
# 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)

View File

@ -39,7 +39,7 @@
Summary: Version 3 of the Python programming language aka Python 3000
Name: python3
Version: %{pybasever}.2
Release: 2%{?dist}
Release: 3%{?dist}
License: Python
Group: Development/Languages
Source: http://python.org/ftp/python/%{version}/Python-%{version}.tar.bz2
@ -74,7 +74,7 @@ Source3: macros.pybytecompile
#
# 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
Source4: python-gdb.py
# Systemtap tapset to make it easier to use the systemtap static probes
@ -695,6 +695,11 @@ rm -fr %{buildroot}
%changelog
* Thu Mar 25 2010 David Malcolm <dmalcolm@redhat.com> - 3.1.2-3
- 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@redhat.com> - 3.1.2-2
- refresh gdb hooks to v3 (reworking how they are packaged)