gdb — штатный отладчик GNU для Linux. Он помогает остановить программу в нужной строке, посмотреть переменные, пройти по стеку и найти место, где поведение разошлось с ожиданием. Освоить его проще, чем кажется: для большинства задач хватает десяти команд. Ниже — рабочий минимум: сборка с символами, точки останова, дампы, потоки и разбор ошибок.
Сборка с отладочной информацией
Без флага -g в бинарнике нет номеров строк и описаний переменных, поэтому отладчик покажет только адреса и имена функций. Оптимизация -O2 дополнительно переставляет код, поэтому переменные могут исчезать.
gcc -g -O0 -Wall -Wextra -o app main.c
gcc -g -Og -fno-omit-frame-pointer -o app main.c
file app
readelf -S app | grep debug
Для отладки используйте -O0 или -Og. Второй вариант сохраняет часть оптимизаций и лучше подходит для программ, которые на нулевой оптимизации работают слишком медленно. Флаг -fno-omit-frame-pointer делает стек читаемым.
Запуск и базовые команды
Программу передают аргументом или через --args, если у неё есть параметры командной строки.
gdb ./app
gdb --args ./app --config test.conf
run
bt
quit
Команда run запускает программу, bt показывает стек вызовов в момент падения. Сокращения работают: r, c, n, s, p. Полные имена удобнее в скриптах, короткие — при ручной работе.
Точки останова
Остановка задаётся по функции, файлу и строке. Условная точка срабатывает только при выполнении выражения — это спасает при отладке циклов.
break main
break parser.c:42
break process if count > 100
info breakpoints
delete 2
tbreak создаёт временную точку, которая удаляется после первого срабатывания. watch останавливает выполнение при изменении переменной, rwatch — при чтении, awatch — при любом доступе. Наблюдение за большими структурами лучше не ставить: отладчик замедляет программу в разы.
Стек и переменные
Когда программа остановлена, bt показывает кадры, frame переключает контекст. В выбранном кадре доступны аргументы и локальные переменные.
bt full
frame 2
info args
info locals
p *ptr
x/16xb buffer
Команда print умеет вызывать функции и печатать структуры: p *node, p arr[3], p strlen(name). Вызов функции меняет состояние программы, поэтому в дампах его лучше избегать. x показывает память: x/16xb — шестнадцать байт в hex, x/s — строку.
Для изменения значения используйте set var count = 10. Это удобно для проверки гипотезы, но не заменяет исправление кода.
Пошаговое выполнение
next выполняет строку целиком, step заходит внутрь вызова, finish доходит до возврата из функции, until выходит из цикла. continue продолжает до следующей точки останова.
next
step
finish
until 57
continue
Если вы заблудились, list показывает исходник вокруг текущей строки, а where повторяет стек. В TUI-режиме (tui enable) исходник и текущая строка видны постоянно.
Дампы памяти
Падение можно разобрать постфактум, если система сохраняет core-файл. Ограничение размера задаётся в оболочке, а путь — через core_pattern или systemd-coredump.
ulimit -c unlimited
cat /proc/sys/kernel/core_pattern
coredumpctl list
coredumpctl gdb 1234
При включённом systemd-coredump файлы лежат не рядом с программой, а в журнале. Команда coredumpctl gdb открывает последний дамп в отладчике с нужными символами. Если дампов нет, проверьте ulimit -c в том процессе, который запускает программу: ограничение наследуется.
Для ручного анализа подойдёт gdb ./app core. Символы должны соответствовать тому бинарнику, который упал, иначе стек будет бессмысленным.
При разборе дампа проверяйте по порядку:
- первый кадр стека и функцию, в которой произошло падение;
- значения аргументов и локальных переменных на момент сбоя;
- указатели: нулевые, освобождённые и вышедшие за границы буфера;
- последние записи в журнале приложения перед аварийным завершением;
- версию бинарника и наличие отладочных символов для библиотек.
Подключение к работающему процессу
Отладчик можно присоединить к уже запущенной программе. Это полезно для демонов и зависших служб.
gdb -p $(pidof app)
gdb -p 1234
bt
detach
Доступ к чужому процессу ограничен: параметр kernel.yama.ptrace_scope при значении 1 разрешает подключаться только к своему потомку или процессу root. Во многих дистрибутивах это значение включено по умолчанию. Менять его на постоянной основе не стоит: это ослабляет защиту. Для разовой отладки используйте sudo или временное изменение параметра.
Потоки и форки
В многопоточной программе полезно увидеть все стеки сразу.
info threads
thread 3
thread apply all bt
set follow-fork-mode child
set detach-on-fork off заставляет отладчик держать и родителя, и потомка. Без этого после fork вы останетесь в одном процессе и потеряете второй.
Автоматизация и условные сценарии
Команды можно записать в ~/.gdbinit или в отдельный файл и подключать через source. Это убирает рутину при повторяющихся запусках.
set pagination off
set print pretty on
break main
commands
silent
bt
continue
end
Блок commands выполняется при каждом попадании в точку. silent подавляет служебные сообщения. Флаг -x script.gdb запускает сценарий без интерактивного ввода, что удобно в CI.
Оптимизированный код и символы
В сборке с -O2 переменные могут отображаться как <optimized out>, а шаги прыгать между строками. Это не ошибка отладчика, а следствие перестановки кода. Помогают сборка с -Og, отключение инлайна и просмотр дизассемблера.
disassemble /m main
info line *0x401136
p $pc
info registers
Для системных библиотек нужны отдельные пакеты символов. В Debian и Ubuntu можно настроить debuginfod, в Fedora он обычно включён. Проверьте DEBUGINFOD_URLS и команду debuginfod-find.
Итог
gdb не требует особой подготовки: соберите программу с -g, поставьте точку останова и посмотрите стек. Для падений используйте coredumpctl, для зависших служб — подключение по PID, для многопоточных ошибок — thread apply all bt. Главное правило — отлаживать ту же сборку, что упала, и не забывать про символы. Освоив эти команды, вы будете тратить на поиск ошибки минуты, а не часы.