Created
April 9, 2022 14:38
-
-
Save versusvoid/76a9538be85f8a74281f482574250a21 to your computer and use it in GitHub Desktop.
Show slotted attributes and modules when debuging python with gdb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- a/libpython.py 2022-03-23 18:38:36.000000000 +0300 | |
+++ b/libpython.py 2022-04-09 17:34:59.590014916 +0300 | |
@@ -370,10 +370,14 @@ | |
'frozenset' : PySetObjectPtr, | |
'builtin_function_or_method' : PyCFunctionObjectPtr, | |
'method-wrapper': wrapperobject, | |
+ 'module': PyModuleObjectPtr, | |
} | |
if tp_name in name_map: | |
return name_map[tp_name] | |
+ if tp_flags & Py_TPFLAGS_TUPLE_SUBCLASS: | |
+ return PyTupleObjectPtr | |
+ | |
if tp_flags & Py_TPFLAGS_HEAPTYPE: | |
return HeapTypeObjectPtr | |
@@ -381,8 +385,6 @@ | |
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_BYTES_SUBCLASS: | |
return PyBytesObjectPtr | |
if tp_flags & Py_TPFLAGS_UNICODE_SUBCLASS: | |
@@ -437,7 +439,7 @@ | |
return self._rep | |
-def _write_instance_repr(out, visited, name, pyop_attrdict, address): | |
+def _write_instance_repr(out, visited, name, pyop_attrdict, slots, address): | |
'''Shared code for use by all classes: | |
write a representation to file-like object "out"''' | |
out.write('<') | |
@@ -455,20 +457,40 @@ | |
out.write('=') | |
pyop_val.write_repr(out, visited) | |
out.write(')') | |
+ elif slots: | |
+ # Write slotted attributes: | |
+ out.write('(') | |
+ first = True | |
+ for name, val in slots.items(): | |
+ if not first: | |
+ out.write(', ') | |
+ first = False | |
+ out.write(name) | |
+ out.write('=') | |
+ val.write_repr(out, visited) | |
+ out.write(')') | |
+ | |
out.write(' at remote 0x%x>' % address) | |
class InstanceProxy(object): | |
- def __init__(self, cl_name, attrdict, address): | |
+ def __init__(self, cl_name, attrdict, slots, address): | |
self.cl_name = cl_name | |
self.attrdict = attrdict | |
+ self.slots = slots | |
self.address = address | |
def __repr__(self): | |
+ fields = None | |
if isinstance(self.attrdict, dict): | |
+ fields = self.attrdict | |
+ elif isinstance(self.slots, dict): | |
+ fields = self.slots | |
+ | |
+ if fields is not None: | |
kwargs = ', '.join(["%s=%r" % (arg, val) | |
- for arg, val in self.attrdict.items()]) | |
+ for arg, val in fields.items()]) | |
return '<%s(%s) at remote 0x%x>' % (self.cl_name, | |
kwargs, self.address) | |
else: | |
@@ -519,6 +541,40 @@ | |
# Not found, or some kind of error: | |
return None | |
+ def get_slots(self): | |
+ try: | |
+ typeobj = self.type() | |
+ members = typeobj.field('tp_members') | |
+ if not members: | |
+ return None | |
+ | |
+ type_PyMemberDef_ptr = gdb.lookup_type('PyMemberDef').pointer() | |
+ member = members.cast(type_PyMemberDef_ptr) | |
+ PyObjectPtrPtr = PyObjectPtr.get_gdb_type().pointer() | |
+ result = {} | |
+ while member['name']: | |
+ # T_OBJECT_EXT | |
+ if int_from_int(member['type']) != 16: | |
+ member += 1 | |
+ continue | |
+ | |
+ name = member['name'].string() | |
+ offset = int_from_int(member['offset']) | |
+ | |
+ ptr = self._gdbval.cast(_type_char_ptr()) + offset | |
+ ptr = ptr.cast(PyObjectPtrPtr) | |
+ result[name] = PyObjectPtr.from_pyobject_ptr(ptr.dereference()) | |
+ | |
+ member += 1 | |
+ | |
+ return result | |
+ except RuntimeError: | |
+ # Corrupt data somewhere; fail safe | |
+ pass | |
+ | |
+ # Not found, or some kind of error: | |
+ return None | |
+ | |
def proxyval(self, visited): | |
''' | |
Support for classes. | |
@@ -535,11 +591,16 @@ | |
if pyop_attr_dict: | |
attr_dict = pyop_attr_dict.proxyval(visited) | |
else: | |
- attr_dict = {} | |
+ attr_dict = None | |
+ | |
+ slots = self.get_slots() | |
+ if slots: | |
+ slots = {k: v.proxyval(visited) for k, v in slots.items()} | |
+ | |
tp_name = self.safe_tp_name() | |
# Class: | |
- return InstanceProxy(tp_name, attr_dict, long(self._gdbval)) | |
+ return InstanceProxy(tp_name, attr_dict, slots, long(self._gdbval)) | |
def write_repr(self, out, visited): | |
# Guard against infinite loops: | |
@@ -549,8 +610,9 @@ | |
visited.add(self.as_address()) | |
pyop_attrdict = self.get_attr_dict() | |
- _write_instance_repr(out, visited, | |
- self.safe_tp_name(), pyop_attrdict, self.as_address()) | |
+ slots = self.get_slots() | |
+ _write_instance_repr(out, visited, self.safe_tp_name(), | |
+ pyop_attrdict, slots, self.as_address()) | |
class ProxyException(Exception): | |
def __init__(self, tp_name, args): | |
@@ -594,6 +656,47 @@ | |
_typename = 'PyClassObject' | |
+class PyModuleObjectPtr(PyObjectPtr): | |
+ """ | |
+ Class wrapping a gdb.Value that's a PyModuleObject* i.e. a <module> | |
+ instance within the process being debugged. | |
+ """ | |
+ _typename = 'PyModuleObject' | |
+ | |
+ def proxyval(self, visited): | |
+ # Guard against infinite loops: | |
+ if self.as_address() in visited: | |
+ return ProxyAlreadyVisited('(...)') | |
+ visited.add(self.as_address()) | |
+ md_dict = self.pyop_field('md_dict').proxyval(visited) | |
+ return ProxyException(self.safe_tp_name(), md_dict) | |
+ | |
+ def write_repr(self, out, visited): | |
+ # Guard against infinite loops: | |
+ if self.as_address() in visited: | |
+ out.write('(...)') | |
+ return | |
+ visited.add(self.as_address()) | |
+ | |
+ out.write(self.safe_tp_name()) | |
+ out.write('{') | |
+ md_dict = self.pyop_field('md_dict') | |
+ first = True | |
+ for k, v in md_dict.iteritems(): | |
+ name = k.proxyval(visited) | |
+ if name.startswith('__'): | |
+ continue | |
+ | |
+ if not first: | |
+ out.write(', ') | |
+ first = False | |
+ out.write(name) | |
+ out.write('=') | |
+ v.write_repr(out, visited) | |
+ | |
+ out.write('}') | |
+ | |
+ | |
class BuiltInFunctionProxy(object): | |
def __init__(self, ml_name): | |
self.ml_name = ml_name | |
@@ -1144,6 +1247,10 @@ | |
return | |
visited.add(self.as_address()) | |
+ name = self.safe_tp_name() | |
+ if name != 'tuple': | |
+ out.write(name) | |
+ | |
out.write('(') | |
for i in safe_range(int_from_int(self.field('ob_size'))): | |
if i > 0: | |
@@ -1958,3 +2065,17 @@ | |
pyop_value.get_truncated_repr(MAX_OUTPUT_LEN))) | |
PyLocals() | |
+ | |
+# When you want to find specific variable | |
+''' | |
+f = Frame.get_selected_python_frame() | |
+pyop_frame = f.get_pyop() | |
+s = pyop_frame.get_var_by_name('storage')[0] | |
+while s is None: | |
+ move_in_stack(move_up=True) | |
+ f = Frame.get_selected_python_frame() | |
+ pyop_frame = f.get_pyop() | |
+ s = pyop_frame.get_var_by_name('storage')[0] | |
+ | |
+print(s.get_truncated_repr(10000000000)) | |
+''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment