В этом коротком туториале мы рассмотрим базовые приёмы работы с GDB, а также посмотрим как можно (и нужно) подготавливать файлы к отладке для GDB.
GDB — переносимый отладчик проекта GNU, который работает на многих UNIX-подобных системах и умеет производить отладку многих языков программирования, включая Си, C++, Free Pascal, FreeBASIC, Ada, Фортран, Python3, Swift, NASM и Rust.
GDB
Почему именно GDB? Всё легко, он уже установлен на многих UNIX-подобных системах, лёгок в использовании и поддерживает много языков. Работа с ним оказывается очень лёгкой, а также его можно подключить к VSCode и другим редакторам кода (Включая Vim, NeoVim (ясное дело), Emacs, Atom и далее)
Подготовка файлов
Для примера мы возьмём файлы .cpp и будем проходиться по ним вдоль и поперёк.
Для того чтобы нам пройтись по такому файлу нам нужно скомпилировать его с помощью G++ с использованием флага -g (это действительно важно, без этого флага, программа не будет корректно работать в GDB).
g++ -g file_name.cpp -o output_name
gdb output_name
Python-файл вы можете продебажить с помощью этой команды:
gdb -ex r --args python program_name.py <arguments>
Для Java вы просто можете использовать jdb, который уже идёт в комплекте c JDK.
Также, если вам не хочется компилировать всё ручками, вы можете просто использовать сайт OnlineGDB, там просто нужно вставить код и нажать debug, а затем внизу откроется консоль, где вы сможете писать команды.
Использование GDB
Как только мы зашли в GDB нам выводится следующее сообщение:
GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from 3_Hero's_Inventory.cpp...done.
Последняя строка говорит о том, нормально ли запустился файл.
Теперь нужно посмотреть, где в нашем файле точка вхождения (строка, откуда наша программа начинает свою работу), в случае cpp это метод main()
. Находим номер этой строки c помощью команды list
и пишем её порядковый номер с буквой b
(также можно просто указать имя функции b main
тоже работает):
(gdb) list
1 #include <iostream>
2 #include <string>
3
4 using namespace std;
5
6 int main(int argc, char *argv[])
7 {
8 // Hero's Inventory - скрипт, где мы имитируем инвентарь игрока
9
10 const int MAX_ITEMS = 10; // Задаём константу, максимум по инвентарю игрока
(gdb) b 6
Breakpoint 1 at 0xcb5: file ./3_Hero's_Inventory.cpp, line 6.
Далее запускаем программу с помощью комманды r
:
(gdb) r
Starting program: /home/username77177/gitprojects/learning/cpp/build_folder/3_Hero's_Inventory.cpp
Breakpoint 1, main (argc=1, argv=0x7fffffffdd18) at ./3_Hero's_Inventory.cpp:7
7 {
Также вы можете включить TUI, с помощью комбинации клавиш <Ctrl-x a>
Для того, чтобы посмотреть на какой мы сейчас строке, нужно написать f
:
(gdb) f
#0 main (argc=1, argv=0x7fffffffdd18) at ./3_Hero's_Inventory.cpp:14
14 items[itemnum++] = "Sword";
Для того, чтобы сделать шаг, нужно нажать n
(от слова next):
(gdb) n
10 const int MAX_ITEMS = 10; // Задаём константу, максимум по инвентарю игрока
Как мы видим GDB сразу пропускает пустые строки (или строки с комментариями) и переходит к следующей строке.
Предположим, что у нас есть функция, при нажатии n
наш отладчик быстро пройдет функцию, не заходя в неё, чтобы зайти в функцию нужно сделать "шаг внутрь" (step-in) или просто клавиша s
:
(gdb) s
11 string items[MAX_ITEMS]; // Создаём массив из строк c 10 элементами
(В примере нет функции, однако шаг step-in все равно будет работать и с обычными инициализациями, условиями и циклами)
Чтобы узнать какие переменные (локальные) сейчас инициализированны в программе нужно написать комманду info locals
:
(gdb) info locals
MAX_ITEMS = 10
items = {"", "", "", "", "", "", "", "", "", ""}
itemnum = 0
game = 247
Чтобы вывести только одну переменную, нужно написать print имя_переменной
:
(gdb) print MAX_ITEMS
$1 = 10
Мы можем также изменить переменную с помощью set
:
(gdb) set x = 77177
(gdb) print x
$1 = 77177
Мы можем также следить за переменными с помощью watch
:
watch x
Также, если нужно можно посмотреть что в данный момент находится в регистрах (info registers
):
(gdb) info registers
rax 0x7fffffffdc00 140737488346112
rbx 0xffffffffffffffff -1
rcx 0xa0 160
rdx 0x7fffffffdd28 140737488346408
rsi 0x7fffffffdd18 140737488346392
rdi 0x7fffffffdbf0 140737488346096
rbp 0x7fffffffdc30 0x7fffffffdc30
rsp 0x7fffffffdab0 0x7fffffffdab0
r8 0x7ffff782fd80 140737345944960
r9 0x0 0
r10 0x6 6
r11 0x7ffff7b77020 140737349382176
r12 0x7fffffffdc10 140737488346128
r13 0x7fffffffdd10 140737488346384
r14 0x0 0
r15 0x0 0
rip 0x555555554cfe 0x555555554cfe <main(int, char**)+100>
eflags 0x286 [ PF SF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
Чтобы посмотреть какие в данный момент есть breakpoints (точки останова) нужно написать info breakpoints
:
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000555555554cb5 in main(int, char**)
at ./3_Hero's_Inventory.cpp:6
breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555554cfe in main(int, char**)
at ./3_Hero's_Inventory.cpp:14
Чтобы удалить точку останова del breakpoint_num
:
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000555555554cb5 in main(int, char**)
at ./3_Hero's_Inventory.cpp:6
breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555554cfe in main(int, char**)
at ./3_Hero's_Inventory.cpp:14
(gdb) del 1
(gdb) info breakpoints
Num Type Disp Enb Address What
2 breakpoint keep y 0x0000555555554cfe in main(int, char**)
at ./3_Hero's_Inventory.cpp:14
Чтобы прыгнуть к следующей точке останова нужно нажать c
:
(gdb) r
Starting program: /home/username77177/gitprojects/learning/cpp/build_folder/3_Hero's_Inventory.cpp
Breakpoint 3, main (argc=1, argv=0x7fffffffdd18) at ./3_Hero's_Inventory.cpp:7
7 {
(gdb) c
Continuing.
Breakpoint 2, main (argc=1, argv=0x7fffffffdd18) at ./3_Hero's_Inventory.cpp:14
14 items[itemnum++] = "Sword";
Мы можем вызывать функции из программы (локальные) с помощью call
:
(gdb) call MyFunction()
Чтобы продолжить выполнение функции и остановить программу когда она (функция) завершится нужно написать finish
или fin
:
(gdb) fin
Стоит уточнить, что нельзя использовать finish
в главном методе.
Чтобы завершить выполнение программы, нужно написать kill
:
(gdb) kill
Kill the program being debugged? (y or n) y
Также можно написать help
в любой момент и получить краткую справку, как пользоваться отладчиком
(gdb) help
List of classes of commands:
aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands
Type "help" followed by a class name for a list of commands in that class.
Type "help all" for the list of all commands.
Type "help" followed by command name for full documentation.
Type "apropos word" to search for commands related to "word".
Command name abbreviations are allowed if unambiguous.
myxo
Ставить breakpoint можно не только на номера строк, но и на символы функций. Например `b main`. Команда start поставит временную бряку на main и запустит программу.
И ещё finish не «быстро завершает выполнение функции», а продолжает её выполнение и останавливает программу когда она завершится.
Username77177 Автор
Дополнил, спасибо