Оптимальный способ установки точки останова в исходном коде Python при отладке CPython с помощью GDB

Я использую ГБД, чтобы понять, как 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.

Я смотрю более простой способ сделать то же самое, поэтому вопросы:

  1. Могу ли я поймать момент, когда test.py начнет выполняться? Я должен упомянуть, какое имя файла test.py появляется на разных этапах: парсинг, компиляция, выполнение. Таким образом, также будет хорошо иметь возможность прервать выполнение CPython на любом этапе.
  2. Могу ли я указать строку test.py, где я хочу разбить? Это легко для файлов .c, но не для файлов .py.
Библиотека для работы с мороженым
Библиотека для работы с мороженым
Лично я попрощался с операторами print() в python. Без шуток.
2
0
323
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Моя идея состояла бы в том, чтобы использовать 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.


Он также сделал бы что-то подобное без каких-либо дополнительных модулей:

  1. добавьте breakpoint или import pdb; pdb.set_trace() вместо cbreakpoint
  2. gdb --args python example.py + бег
  3. Когда pdb прерывает программу, нажмите Ctrl+C, чтобы прервать ее в gdb.
  4. Активируйте точки останова в gdb.
  5. продолжайте в gdb, а затем в pdb (т.е. c+enterдважды).

Небольшая проблема заключается в том, что после этого точки останова могут сработать в pdb, поэтому первый метод немного более надежен.

Второй способ не работает. Я сделал шаги № 1,2 и получил командную строку pdb, нажал Ctrl+C и получил сообщение Программа получила сигнал SIGINT, Interrupt.. Программа Python, которая находилась в отладке, была полностью прервана Ctrl+C, поэтому после этого отлаживать нечего.

MiniMax 06.08.2019 22:13

Я пытаюсь проверить первый метод, но используя простое расширение C, потому что я вообще не понимаю Китон. Спасибо за идеи.

MiniMax 06.08.2019 22:20

@MiniMax Нет, «Программа получила сигнал SIGINT, Interrupt» - это именно то, что вам нужно - после этого программа прерывается. Вам нужно ввести c+enter (для continue) для запуска gdb, а затем кроме тогоc+enter для запуска pdb.

ead 07.08.2019 00:34

@MiniMax вы можете установить cbreakpoint по предоставленной ссылке github или переписать его самостоятельно как расширение C - однако для решения с cythonize это не более чем установка cython, сохранение данного кода как cbreakpoint.pyx и вызов cythonize - в результате чего можно использовать, когда это необходимо.

ead 07.08.2019 00:38

Другие вопросы по теме