Я запускаю python 3.6.6-debug (устанавливается через pyenv) и скопировал соответствующий libpython.py из cpython/Tools/gdb/libpython.py
в ~/.config/gdb
(с отмеченным тегом v3.6.6).
В моем .gdbinit у меня есть:
source ~/.config/gdb/libpython.py
Для простых процессов я могу без проблем использовать py-list
, py-bt
и т. Д., Но программа, которую я сейчас тестирую под py.test, выдает мне эту ошибку для любой вспомогательной команды python gdb:
(gdb) py-list
Python Exception <class 'RuntimeError'> Type does not have a target.:
Error occurred in Python command: Type does not have a target.
Что означает эта ошибка и как ее исправить?
Обновление
Я покопался в libpython.py, чтобы увидеть, как именно py-list
/ py-bt
делает свое дело, а затем вручную запустил соответствующие команды gdb python изнутри gdb, чтобы воспроизвести проблему и точно определить, где в libpython.py проблема происходит После отладки, приведенной ниже, я смог получить более подробную трассировку в gdb:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "~/git/cpython/Tools/gdb/libpython.py", line 916, in filename
File "~/git/cpython/Tools/gdb/libpython.py", line 1158, in proxyval
RuntimeError: Type does not have a target.
Проблема возникает в строке libpython.py 1158 , то есть
fields = gdb.lookup_type('PyUnicodeObject').target().fields()
Это проясняет ситуацию: libpython.py получает объект Type для PyUnicodeObject и затем пытается вызвать для него метод target
, , но объект Type для PyUnicodeObject не имеет цели . Согласно документации gdb :
- Функция: Type.target ()
Возвращает новый объект gdb.Type, который представляет целевой тип этого типа.
Для типа указателя целевым типом является тип указательного объекта. Для типа массива (имеется в виду C-подобные массивы) тип назначения - это тип элементов массива. Для типа функции или метода целевой тип является типом возвращаемого значения. Для сложного типа целевой тип является типом элементов. Для typedef целевой тип является псевдонимом.
Если тип не имеет цели, этот метод вызовет исключение.
Это определенно похоже на ошибку, хотя я не могу найти упоминания об этой проблеме нигде в Интернете, в системе отслеживания ошибок python или в истории изменений python. Я собираюсь открыть вопрос на трекере Python и посмотреть, что говорят сопровождающие (если кто-то не сталкивался с этим раньше и не отправил ответ).
Как я отлаживал
Настройка ptrace
для разрешения отладки без sudo
$ sudo sh -c 'echo 0 > /proc/sys/kernel/yama/ptrace_scope
Определить родительский процесс Python зависшей (многопроцессорной) программы
$ pstree -p -s 22391
systemd(1)───tmux(31719)───bash(5161)───py.test(22391)─┬─py.test(22478)
├─py.test(24577)
├─py.test(24578)
├─python3.6(25427)
├─python3.6(25545)
├─python3.6(25546)
├─python3.6(25547)
├─python3.6(27376)───{python3.6}(27393)
├─python3.6(30563)───{python3.6}(30580)
├─{py.test}(27368)
├─{py.test}(30562)
├─{py.test}(629)
└─{py.test}(630)
(я просто догадался выше, использование pid любого из запущенных процессов python для моей программы работало бы)
Присоединить к родительскому процессу
$ gdb -p 22391
Определите самый последний фрейм выполнения Python и переключитесь на него
(gdb) bt 10
#0 0x00007fec7309a5d3 in select () at ../sysdeps/unix/syscall-template.S:84
#1 0x00007fec738692aa in pysleep (secs=50000000) at ./Modules/timemodule.c:1417
#2 0x00007fec738671a3 in time_sleep (self=0x7fec71a00458, obj=0x7fec6cf728b0) at ./Modules/timemodule.c:235
#3 0x00007fec7368513e in _PyCFunction_FastCallDict (func_obj=0x7fec719ff5f8, args=0x7fec406fac08, nargs=1, kwargs=0x0) at Objects/methodobject.c:209
#4 0x00007fec73685535 in _PyCFunction_FastCallKeywords (func=0x7fec719ff5f8, stack=0x7fec406fac08, nargs=1, kwnames=0x0) at Objects/methodobject.c:294
#5 0x00007fec7379ab0d in call_function (pp_stack=0x7ffc37032440, oparg=1, kwnames=0x0) at Python/ceval.c:4830
#6 0x00007fec737927ca in _PyEval_EvalFrameDefault (f=0x7fec406faa58, throwflag=0) at Python/ceval.c:3328
===> #7 0x00007fec7377eb3b in PyEval_EvalFrameEx (f=0x7fec406faa58, throwflag=0) at Python/ceval.c:754
#8 0x00007fec7363a208 in gen_send_ex (gen=0x7fec3d0b88d8, arg=0x0, exc=0, closing=0) at Objects/genobject.c:189
#9 0x00007fec7363bca6 in gen_iternext (gen=0x7fec3d0b88d8) at Objects/genobject.c:563
(More stack frames follow...)
(gdb) frame 7
#7 0x00007fec7377eb3b in PyEval_EvalFrameEx (f=0x7fec406faa58, throwflag=0) at Python/ceval.c:754
754 Python/ceval.c: No such file or directory.
Добавьте исходные каталоги Python и используйте tui enable
, чтобы получить некоторый контекст
(gdb) dir ~/git/cpython
Source directories searched: /home/calid/git/cpython:$cdir:$cwd
(gdb) tui enable
Запустить интерактивный интерпретатор Python в gdb и вручную ввести строки libpython, чтобы получить текущий скрипт / номер строки на python
(gdb) pi
>>> gdbframe = gdb.selected_frame()
>>> f = gdbframe.read_var('f')
>>> pyframe = PyFrameObjectPtr.from_pyobject_ptr(f)
>>> pyframe.filename()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "~/git/cpython/Tools/gdb/libpython.py", line 916, in filename
File "~/git/cpython/Tools/gdb/libpython.py", line 1158, in proxyval
RuntimeError: Type does not have a target.
Это воспроизвело исключение, которое я видел с py-list
и py-bt
, но на этот раз я также получил очень полезную трассировку.
Включить трассировки стека GDB Python по умолчанию
set python print-stack full
После всего, что я наткнулся на документацию для варианта выше. Установка, которая включает печать трассировки стека по умолчанию и избавила бы от необходимости делать всю эту ручную отладку ... Поэтому, оглядываясь назад, я выполнил lot дополнительной работы, которую мне не нужно было выполнять :) ( хотя я многому научился в процессе).
Я добавил это в свой gdbinit на будущее.
Ресурсы