I have a core dump of a Python process which I'm trying to inspect. In particular, I'm interested in the value of a particular Decimal
instance.
I've loaded the Python extensions to gdb, specifically I'm using the libpython.py distributed with Python 2.7 . And this provides some degree of inspection into the Python language state from within gdb. Notably, the inspection is based on recreating a "proxy object" for objects from the inferior process, and those proxy objects are created by copying the __dict__
of the object being inspected.
Unfortunately, instances of a class with __slots__
have no __dict__
, and so .get_attr_dict()
returns None, and thus the attributes of the Decimal
instance don't seem to be inspectable without some effort.
Furthermore, since this is a core dump and not a live process, I can't simply call PyObject_GenericGetAttr
. It seems my only option is to traverse the structures manually within gdb until I get at the value.
How might such a manual inspection be accomplished?
Begin with some value to inspect. For this example, we'll look at self._int
. Begin by getting the address of self
, and saving it to a convenience variable:
(gdb) py-locals
self = <Decimal at remote 0x7f2d6cbc0280>
[...]
(gdb) set $self = (PyObject*)0x7f2d6cbc0280
Slotted attributes are implemented as descriptors , and the descriptor for the _int
attribute is stored in the type's (in this case, Decimal
) dictionary. Fortunately libpython already prints python dicts, so we don't need to manually interpret the dictionary structure but can just read the address of the descriptor from the output and save it to another convenience variable:
(gdb) p $self->ob_type->tp_dict
$5 = {[...] '_int': <member_descriptor at remote 0x7f2d8e527320>, [...]}
(gdb) set $int = (PyMemberDescrObject *)0x7f2d8e527320
Slotted attributes are stored at some offset from the object pointer ( $self
), and we can get that offset like this:
(gdb) p *$int->d_member
$9 = {name = 0x7f2d931d3774 "_int", type = 16, offset = 24, flags = 0, doc = 0x0}
To interpret the value we must also take note of the type. The implementation of PyMember_GetOne
shows how the value should be cast according to the type, and we can see in structmember.h that type 16 from above corresponds to T_OBJECT_EX
. Now we add the offset
from the descriptor to $self
, cast it appropriately, and print the result:
(gdb) p *(PyObject**)((char *)$self+24)
$14 = '600'
And there you have it: self._int
is '600'
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.