Я использую ГБД, чтобы понять, как CPython выполняет исходный файл test.py
, и я хочу остановить CPython, когда он начнет выполнение интересующего меня кода операции.
ОПЕРАЦИОННЫЕ СИСТЕМЫ: Ubuntu 18.04.2 LTS
Отладчик: GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
Первая проблема - многие собственные файлы CPython .py
выполняются до того, как дойдет очередь до моего test.py
, так что я не могу просто ломиться в _PyEval_EvalFrameDefault
- их много, поэтому я должен отличать свой файл от других.
Вторая проблема — я не могу установить условие типа «когда имя файла равно test.py», потому что имя файла не является простой строкой C
, это объект Unicode CPython, поэтому стандартные строковые функции ГБД нельзя использовать для сравнение.
В этот момент я делаю следующий трюк для прерывания выполнения на нужной строке test.py
исходника:
Например, у меня есть исходный файл:
x = ['a', 'b', 'c']
# I want to set the breakpoint at this line.
for e in x:
print(e)
Я добавляю в код двоичный оператор сдвига влево:
x = ['a', 'b', 'c']
# Added for breakpoint
a = 12
b = 2 << a
for e in x:
print(e)
А затем отследите выполнение кода операции BINARY_LSHIFT
в файле Python/ceval.c
с помощью этой команды ГБД:
break ceval.c:1327
Я выбрал код операции BINARY_LSHIFT
из-за его редкого использования в коде. Таким образом, я могу быстро добраться до нужной части .py
файла - это происходит один раз во всех остальных .py
модулях, выполняемых до моего test.py
.
Я смотрю более простой способ сделать то же самое, поэтому вопросы:
test.py
начнет выполняться? Я должен упомянуть, какое имя файла test.py
появляется на разных этапах: парсинг, компиляция, выполнение. Таким образом, также будет хорошо иметь возможность прервать выполнение CPython на любом этапе.test.py
, где я хочу разбить? Это легко для файлов .c
, но не для файлов .py
.Моя идея состояла бы в том, чтобы использовать C-расширение, чтобы сделать возможной установку C-брейкпоинтов в python-скрипте (аналогично pdb.set_trace()
или breakpoint()
начиная с Python3.7), который я буду называть cbreakpoint
.
Рассмотрим следующий python-скрипт:
#example.py
from cbreakpoint import cbreakpoint
cbreakpoint(breakpoint_id=1)
print("hello")
cbreakpoint(breakpoint_id=2)
Его можно использовать в gdb следующим образом:
>>> gdb --args python example.py
[gdb] b cbreakpoint
[gdb] run
Теперь отладчик остановится на cbreakpoint(breakpoint_id=1)
и cbreakpoint(breakpoint_id=2)
.
Вот доказательство концепции, написанное на Cython, чтобы избежать необходимости в шаблонном коде:
#cbreakpoint.pyx
cdef extern from *:
"""
long long last_breakpoint_id = -1;
void cbreakpoint(long long breakpoint_id){
last_breakpoint_id = breakpoint_id;
}
"""
void c_cbreakpoint "cbreakpoint"(long long breakpoint_id)
def cbreakpoint(breakpoint_id = 0):
c_cbreakpoint(breakpoint_id)
который можно построить на месте с помощью:
cythonize -i cbreakpoint.pyx
Если Cython не установлен, я загрузил версию, которая не зависит от Cython (слишком много кода для этого поста) на гитхаб.
Также можно условно разорвать, учитывая breakpoint_id
, то есть:
>>> gdb --args python example.py
[gdb] break src/cbreakpoint.c:595 if breakpoint_id == 2
[gdb] run
сломается только после того, как hello
будет напечатано - в cbreakpoint
с id=2
(в то время как cbreakpoint
с id=1
будет пропущено). В зависимости от версии Cython строка может меняться, но ее можно узнать, когда gdb остановится на cbreakpoint
.
Он также сделал бы что-то подобное без каких-либо дополнительных модулей:
breakpoint
или import pdb; pdb.set_trace()
вместо cbreakpoint
gdb --args python example.py
+ бегpdb
прерывает программу, нажмите Ctrl+C
, чтобы прервать ее в gdb.gdb
.gdb
, а затем в pdb
(т.е. c+enter
дважды).Небольшая проблема заключается в том, что после этого точки останова могут сработать в pdb
, поэтому первый метод немного более надежен.
Я пытаюсь проверить первый метод, но используя простое расширение C, потому что я вообще не понимаю Китон. Спасибо за идеи.
@MiniMax Нет, «Программа получила сигнал SIGINT, Interrupt» - это именно то, что вам нужно - после этого программа прерывается. Вам нужно ввести c+enter (для continue
) для запуска gdb, а затем кроме тогоc+enter
для запуска pdb.
@MiniMax вы можете установить cbreakpoint
по предоставленной ссылке github или переписать его самостоятельно как расширение C - однако для решения с cythonize
это не более чем установка cython, сохранение данного кода как cbreakpoint.pyx и вызов cythonize - в результате чего можно использовать, когда это необходимо.
Второй способ не работает. Я сделал шаги № 1,2 и получил командную строку
pdb
, нажалCtrl+C
и получил сообщение Программа получила сигнал SIGINT, Interrupt.. Программа Python, которая находилась в отладке, была полностью прерванаCtrl+C
, поэтому после этого отлаживать нечего.