В этом коротком туториале мы рассмотрим базовые приёмы работы с 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.