У меня есть дамп ядра процесса Python, который я пытаюсь проверить. В частности, меня интересует ценность конкретного экземпляра Decimal
.
Я загрузил расширения Python в gdb, в частности, я использую файл libpython.py распространяется с Python 2.7. И это обеспечивает некоторую степень проверки состояния языка Python изнутри gdb. Примечательно, что проверка основана на воссоздании «прокси-объекта» для объектов из подчиненного процесса, и эти прокси-объекты создаются путем копирования __dict__
проверяемого объекта.
К сожалению, экземпляры класса с __slots__
не имеют __dict__
, поэтому .get_attr_dict()
возвращает None, и поэтому атрибуты экземпляра Decimal
кажутся не поддающимися проверке без некоторых усилий.
Кроме того, поскольку это дамп ядра, а не активный процесс, я не могу просто вызвать PyObject_GenericGetAttr
. Кажется, мой единственный вариант - вручную перемещаться по структурам в gdb, пока я не получу значение.
Как можно осуществить такую ручную проверку?
Начните с некоторого значения для проверки. В этом примере мы рассмотрим self._int
. Начните с получения адреса self
и сохранения его в удобной переменной:
(gdb) py-locals
self = <Decimal at remote 0x7f2d6cbc0280>
[...]
(gdb) set $self = (PyObject*)0x7f2d6cbc0280
Слотовые атрибуты реализованы как дескрипторы, а дескриптор атрибута _int
хранится в словаре типа (в данном случае Decimal
). К счастью, libpython уже печатает словари Python, поэтому нам не нужно вручную интерпретировать структуру словаря, а можно просто прочитать адрес дескриптора из вывода и сохранить его в другой удобной переменной:
(gdb) p $self->ob_type->tp_dict
$5 = {[...] '_int': <member_descriptor at remote 0x7f2d8e527320>, [...]}
(gdb) set $int = (PyMemberDescrObject *)0x7f2d8e527320
Слотовые атрибуты хранятся с некоторым смещением от указателя объекта ($self
), и мы можем получить это смещение следующим образом:
(gdb) p *$int->d_member
$9 = {name = 0x7f2d931d3774 "_int", type = 16, offset = 24, flags = 0, doc = 0x0}
Чтобы интерпретировать значение, мы также должны принять во внимание тип. Реализация PyMember_GetOne
показывает, как значение должно быть приведено в соответствии с типом, и мы можем видеть в structmember.h, что тип 16 сверху соответствует T_OBJECT_EX
. Теперь мы добавляем offset
из дескриптора в $self
, приводим его соответствующим образом и печатаем результат:
(gdb) p *(PyObject**)((char *)$self+24)
$14 = '600'
И вот оно: self._int
есть '600'
.