简体   繁体   中英

How to read attributes of a python object with __slots__ in gdb

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM