Глава 14. Отладка
Содержание
CPython поставляется со встроенным отладчиком, pdb
, для отладки приложений Python. Этот
отладчик pdb
исключителен при отладке крушений внутри некого приложения Python, а также для
написания тестов и инспектирования локальных переменных.
Когда же дело касается CPython, однако, вам требуется второй отладчик, тот, который понимает C.
В этой главе вы изучите как:
-
Подключать отладчик к своему интерпретатору CPython
-
Применять этот отладчик чтобы заглянуть вовнутрь выполнения процесса CPython
Существует два типа отладчиков: консольный и наглядный. Консольные отладчики
(такие как pdb
) снабжают вас неким приглашением на ввод команд и собственные команды для изучения переменных
и стека. Наглядные отладчики это приложения с графическим интерфейсом, которые
представляют данные в координатной сетке.
В этой главе рассматриваются следующие отладчики:
Отладчик | Тип | Платформа |
---|---|---|
LLDB |
консольный |
macOS |
GDB |
консольный |
Linux |
Отладчик Visual Studio |
наглядный |
Windows |
Отладчик CLion |
наглядный |
Windows, macOS, Linux |
Отладчик VS Code |
наглядный |
Windows, macOS, Linux |
В C, если некое приложение пытается считывать или записывать в некую область памяти, в которую она не должна этого делать, тогда возбуждается ошибка сегментации. Эта ошибка прекращает такой исполняемый процесс немедленно чтобы остановить его от осуществления каких бы то ни разрушений в прочих приложениях. Ошибка сегментация также может происходить когда вы пытаетесь считывать из памяти, которая не содержит никаких данных или при некой неверной ссылке.
Если CPython вызывает ошибку сегментации, тогда вы получите очень мало сведений о том что произошло:
[1] 63476 segmentation fault ./python portscanner.py
CPython поставляется со встроенным обработчиком отказов. Если вы запускаете CPython с -X faulthandler
или -X dev
, тогда вместо выдачи на печать системного сообщения ошибки сегментации, имеющийся обработчик
ошибок выведет на печать значение исполнявшегося потока и трассировку стека самого Python где произошла эта ошибка:
Fatal Python error: Segmentation fault
Thread 0x0000000119021dc0 (most recent call first):
File "/cpython/Lib/threading.py", line 1039 in _wait_for_tstate_lock
File "/cpython/Lib/threading.py", line 1023 in join
File "/cpython/portscanner.py", line 26 in main
File "/cpython/portscanner.py", line 32 in <module>
[1] 63540 segmentation fault ./python -X dev portscanner.py
Эта функциальная возможность также полезна при разработке и тестированию расширения C CPython.
Для получения существенных сведений из своего отладчика, вам следует компилировать необходимые символы отладки в CPython. Без таких символов, тот стек отладки, которй пребывает внутри сеанса отладки не будет содержать верных названий функций, имён переменных или названий файлов.
Следуйте тем же самым шагам, которые вы предпринимали в разделе Компиляция CPython в Windows из Главы 3, Компиляция CPython, при этом обеспечьте компиляцию в конфигурации отладки для получения символов отладки:
> build.bat -p x64 -c Debug
Помните, чот отладочная конфигурация производит исполняемый python_d.exe
, а потому убедитесь что вы
применяете для отладки именно этот исполняемый файл.
Соответствующие шаги из Главы 3, Компиляция CPython предписывают запуск сценария
./configure
с флагом --with-pydebug
. Если вы не включили этот
флаг, тогда вернитесь обратно и снова запустите ./configure
с первонаяальными параметрами и этим
флагом --with-pydebug
. Это произведёт правильный исполняемый файл и символы для отладки.
Отладчик LLDB поставляется с инструментами разработки Xcode, поэтому вы должны иметь их уже установленными.
Запустите LLDB и загрузите скомпилированный исполняемый файл CPython в качестве его цели:
$ lldb ./python.exe
(lldb) target create "./python.exe"
Current executable set to './python.exe' (x86_64).
Теперь вы будете обладать приглашением на ввод, в котором вы можете вводить некоторые команды для отладки.
Для создания точки прерывания воспользуйтесь командой break set
с необходимым файлом (относительно
вашего корня) и значением номера строки:
lldb) break set --file Objects/floatobject.c --line 532
Breakpoint 1: where = python.exe`float_richcompare + 2276 at
floatobject.c:532:26, address = 0x000000010006a974
Замечание | |
---|---|
Существует также условное обозначение для настройки точек прерывания: |
Вы можете добавлять множество точек прерывания при помощи команды break set
. Для просмотра
перечня утановленных в данный момент точек прерывания воспользуйтесь командой break list
:
(lldb) break list
Current breakpoints:
1: file = 'Objects/floatobject.c', line = 532, exact_match = 0, locations = 1
1.1: where = python.exe`float_richcompare + 2276 at floatobject.c:532:26,
address = python.exe[...], unresolved, hit count = 0
Для запуска CPython воспользуйтесь командой process launch --
параметрами командной строки которые вы
обычно применяете с Python.
Для запуска Python с некой строкой, например, python -c "print(1)"
воспользуйтесь следующей
командой:
(lldb) process launch -- -c "print(1)"
Для запуска Python с неким сценарием применяйте такую команду:
(lldb) process launch -- my_script.py
Если вы уже обладаете запущенным интерпретатором CPython, тогда вы можете подключаться к нему.
Изнутри своего сеанса LLDB исполните process attach --pid
с соответствующим идентификатором
процесса:
(lldb) process attach --pid 123
Вы можете получить идентификатор процесса из монитора активности или воспользовавшись os.getpid()
в Python.
Все настроенные до или после этой точки точки прерывания остановят этот процесс.
Чтобы посмотреть как обрабатываются точки прерывания, установите некую точку прерывания в функции
float_richcompare()
Objects/floatobject.c
.
Затем запустите этот процесс и сравните два плавающих значения при помощи своего оператора почти равенства, который вы разрабатывали в предыдущих главах:
(lldb) process launch -- -c "1.0~=1.1"
Process 64421 launched: '/cpython/python.exe' (x86_64)
Process 64421 stopped
* thread #1, queue = '...', stop reason = breakpoint 1.1
frame #0: 0x000000010006a974 python.exe`float_richcompare(v=1.0,
w=1.1, op=6) at floatobject.c:532:26
529 break;
530 case Py_AlE: {
531 double diff = fabs(i - j);
-> 532 const double rel_tol = 1e-9;
533 const double abs_tol = 0.1;
534 r = (((diff <= fabs(rel_tol * j)) ||
Target 0: (python.exe) stopped.
LLDB выдаст вам приглашение на ввод снова. Вы можете просмотреть локальные переменные при помощи команды
v
:
(lldb) v
(PyObject *) v = 0x000000010111b370 1.0
(PyObject *) w = 0x000000010111b340 1.1
(int) op = 6
(double) i = 1
(double) j = 1.1000000000000001
(int) r = 0
(double) diff = 0.10000000000000009
(const double) rel_tol = 2.1256294105914498E-314
(const double) abs_tol = 0
Вы можете вычислять выражение C при помощи команды expr
с любой допустимой командой C.
Могут применяться любые переменные из области действия. Например, чтобы вызвать fabs(rel_tol)
и
привести его к double
выполните такую команду:
(lldb) expr (double)fabs(rel_tol)
(double) $1 = 2.1256294105914498E-314
Это выдаст на печать получаемые в результате переменные и назначать их некому идентификатору ($1
).
Вы можете повторно применять этот идентификатор в качестве временной переменной.
Вы можете также пожелать изучать экземпляры PyObject
:
(lldb) expr v->ob_type->tp_name
(const char *) $6 = 0x000000010034fc26 "float"
Для отслеживания хода исполнения с этой точки прерывания применяйте команду bt
:
(lldb) bt
* thread #1, queue = '...', stop reason = breakpoint 1.1
* frame #0: ...
python.exe`float_richcompare(...) at floatobject.c:532:26
frame #1: ...
python.exe`do_richcompare(...) at object.c:796:15
frame #2: ...
python.exe`PyObject_RichCompare(...) at object.c:846:21
frame #3: ...
python.exe`cmp_outcome(...) at ceval.c:4998:16
Чтобы вмешиваться, применяйте команду step
или s
.
Чтобы перешагнуть или продолжить со следующего предложения, воспользуйтесь командой next
или
n
.
Для продолжения исполнения употребляйте команду continue
или
c
.
Для выхода из сеанса пользуйтесь командой quit
или q
.
Смотри также | |
---|---|
LLDB Documentation Tutorial содержит наиболее исчерпывающий список команд. |
LLDB поддерживает расширения, написанные на Python. Имеется некое расширение открытого исходного кода,
cpython_lldb
, которая выводит на печать дополнительные сведения в вашем сеансе LLDB для естественных объектов
CPython/
Для его установки запустите такие команды:
$ mkdir -p ~/.lldb
$ cd ~/.lldb && git clone https://github.com/malor/cpython-lldb
$ echo "command script import ~/.lldb/cpython-lldb/cpython_lldb.py" \
>> ~/.lldbinit
$ chmod +x ~/.lldbinit
Теперь, всякий раз когда вы наблюдаете переменные в LLDB, вы также видите некоторые дополнительные сведения справа, такие как численное
значение для целых чисел и чисел с плавающей запятой или текст для строк Unicode. Внутри консоли LLDB у вас также имеется некая дополнительная
команда, py-bt
, которая выводит на печать значение отслеживания стека для кадров Python.
GDB это отладчик, обычно применяемый для приложений C/C++, написанных для платформ Linux. Он также очень популярен в команде разработчиков CPython.
При компиляции CPython он вырабатывает некий сценарий, python-gdb.py
. Не исполняйте этот сценарий
напрямую. Вместо этого GDB обнаружит и исполнит его автоматически после настройки.
Для настройки этого этапа отредактируйте файл .gdbinit
внутри своего домашнего пути
(~/.gdbinit
) и добавьте приводимую ниже строку, в которой
/path/to/checkout
это путь к проверке git cpython
:
add-auto-load-safe-path /path/to/checkout
Для запуска GDB запустите его с аргументом, указывающим на ваш скомпилированный исполняемый файл CPython:
$ gdb ./python
GDB загрузит все символы для скомпилированного исполняемого файла и предоставит вам приглашение командной строки. GDB обладает набором встроенных команд, а пакет расширений CPython некоторыми дополнительными командами.
Для установки точки прерывания воспользуйтесь командой b <file>:<line>
относительно
пути этого исполняемого файла:
(gdb) b Objects/floatobject.c:532
Breakpoint 1 at 0x10006a974: file Objects/floatobject.c, line 532.
Вы можете устнаовить столько точек прерывания, сколько пожелаете.
Для запуска конкретного процесса применяйте команду run
с идущими далее аргументами для запуска
интерпретатора Python.
Например, для запуска с некой строкой воспользуйтесь такой строкой:
(gdb) run -c "print(1)"
Чтобы запустить Python со сценарием употребляйте такую команду:
(gdb) run my_script.py
Когда вы уже имеете запущенным интерпретатор CPython, вы можете присоединиться к нему.
Изнутри сеанса GDB запустите команду attach
с идентификатором нужного процесса:
(gdb) attach 123
Вы можете получить значение идентификатора процесса из монитора активности или воспользовавшись
os.getpid()
в Python.
Все установленные до и после этой точки будут останавливать этот процесс.
Когда GDB попадает в эту точку прерывания, вы можете пользоваться командой print
или
p
для вывода на печать переменной:
(gdb) p *(PyLongObject*)v
$1 = {ob_base = {ob_base = {ob_refcnt = 8, ob_type = ...}, ob_size = 1}, ob_digit = {42}}
Чтобы вмешиваться, применяйте команду step
или s
.
Чтобы перешагнуть или продолжить со следующего предложения, воспользуйтесь командой next
или
n
.
Расширение python-gdb
загрузит некоторый дополнительный набор команд в имеющуюся консоль GDB:
Команда | Назначение |
---|---|
|
Отыскивает переменную Python и выводит её на печать |
|
Выводит её на печать отслеживание стека Python |
|
Выводит на печать результат |
|
Спускаемся вниз на один кадр Python |
|
Поднимаемсяна один кадр Python вверх |
|
Выводит её на печать исходный код Python для текущего кадра |
Microsoft Visual Studio поступает в пакете с визуальным отладчиком. Этот отладчик является мощным и поддерживает визуализатор стека кадра, список отслеживаемого, а также возможность вычисления выражений.
Для его применения откройте Visual Studio и соответствующий файл решения
PCBuild/pcbuild.sln
.
Для добавления новой точки прерывания, проследуйте в тот файл, в который вы желаете в своём окне решения, затем кликните в соответствующем внутреннем поле слева от номера вашей строки.
Это добавит красный кружок для указания того что вы установили в этой строке точку прерывания:
Когда вы наводите курсор на красный кружок, появляется зубец Кликните по этому зубцу для настройки условных точек прерываний. Добавьте одно или более условных выражений, которые должны быть вычислены до попадания в эту точку прерывания:
Из верхнего меню выберите Debug/Start Debugger
или нажмите F5
.
Visual Studio запустит новую среду исполнения Pythob и REPL.
Когда вы попадаете в некую точку прерывания вы можете пройти вперёд по предложениям применяя кнопки навигации или следующих горячих клавиш:
-
Для вмешательства:
F11
-
Для продвижения:
F10
-
Чтобы покинуть:
Shift + F11
Снизу вы увидите стек вызова. Вы можете выбирать в этом стеке кадры для изменения навигации и инспектирования переменных в прочих кадрах:
В своём редакторе кода вы можете выделить любую переменную или выражение чтобы увидеть их значение. Вы также можете кликнуть правой
кнопкой мыши и выбрать Add Watch
. Это добавляет соответствующую
переменную в окно просмотра, в котором вы сможете быстро увидеть значения переменных, которые требуются вам для отладки:
IDE CLion оборудован мощным визуальным отладчиком. В macOS он работает с LLDB и с GDB в macOS, Windows и Linux.
Для настройки этого отладчика пройдите в Preferences
и
выберите Build, Execution, Deployment/Toolchains
:
Имеется блок выбора для целевого отладчика. Выберите подходящие параметры для своей операционной системы:
-
macOS: Снабжён LLDB
-
Windows или Linux: Снабжаются GDB
Из CLion 2020.2 вы можете компилировать и отлаживать все основывающиеся на makefile проекты, в том числе и CPython.
Для запуска отладки выполните шаги из раздела Монтаж JetBrains CLion
Главы 2, Настройка вашей среды разработки. После выполнения этих шагов вам придётся
Make Application
цели. Выберите
Run/Debug
из верхнего меню чтобы запустить необходимый процесс и
приступить к отладке.
В качестве альтернативы вы можете подключить необходимый отладчик для исполнения процесса CPython.
Для подключения к исполняющему процесс CPython отладчику CLion выберите
Run/Attach to Process
.
Всплывёт список запущенных процессов. Отыщите процесс Python, к которому вы желаете присоединиться и выберите
Attach
. Начнётся сеанс отладки.
Важно | |
---|---|
Когда у вас имеется установленным подключаемый модуль Python, это покажет в самом верху процесс Python. Не выбирайте его!. Он использует отладчик Python, не отладчик C: Вместо этого прокрутите далее вниз до списка Native и отыщите правильный процесс Python. |
Для создания некой точки прерывания, переместитесь в необходимый файл и строку, которая вам нужна, затем кликните по внутреннему полю между номером строки и самому коду. Возникнет красный кружок для указания установки точки прерывания:
Кликните правой кнопкой для подключения условий:
Чтобы просматривать и управлять всеми текущими точками прерывания переместитесь из самого верхнего меню к
Run/View Breakpoints
:
Теперь вы можете включать и отключать точки прерывания, а также отключать их при попадании в иную точку прерывания.
При попадании точки прерывания CLion установит панель отладки. Внутри это панели отладки имеется стек вызова, отображающий где была достигнута точка прерывания. Вы можете выбирать иные кадры в своём стеке вызова для переключения между ними.
Далее в стеке вызовов имеются локальные переменные. Вы можете расширить свойства указателей и тип структур, а также будут отражены значения простых типов:
Внутри прерывания вы можете вычислять выражения для получения дополнительных сведений о локальных переменных. Вы можете отыскать окно
Evaliate
в
Run/Debugging Actions/Evaluate Expression
или иконку закладки в
окне отладки.
Внутри окна Evaluation вы можете набрать выражение и CLion наберёт далее названия свойств и типы:
Вы также можете преобразовывать выражения, что полезно для приведения PyObject*
в реальный тип, например
в PyFloatObject*
:
В этой главе вы рассмотрели как настраивать отладчик во всех основных операционных системах. Хотя изначальная установка и затратна по времени, вознаграждение великолепно. Наличие возможности установки точек прерывания и изучение переменных и памяти для исполнения процесса CPython придаст вам супермощность.
Вы можете применять эти навыки для расширения CPython, оптимизации имеющихся частей базового кода или отслеживания вниз противных ошибок.