Памятка по отладке при помощи GDB
19 января 2016
Ранее в заметке Основы использования отладчика WinDbg мы узнали, как можно отлаживать приложения под Windows. Теперь настало время познакомиться с отладчиком gdb, который позволяет делать все то же самое под Linux и другими *nix системами. Благодаря этой заметке вы узнаете, как при помощи gdb ставить брейкпоинты и смотреть значения локальных переменных, анализировать coredump’ы и вот это все.
Для комфортной отладки сначала нужно собрать программу с правильными флагами. Во-первых, необходимо указать флаг -g
. Иначе программа будет собрана без отладочных символов и отлаживать ее придется в ассемблерном коде (что, впрочем, вполне выполнимо для программ, написанных на чистом Си). Во-вторых, рекомендуется отключить оптимизации при помощи флага -O0
, иначе некоторые переменные окажутся, что называется, optimized out, и вы не сможете посмотреть их значения при отладке.
Другими словами, при прочих равных программу лучше собирать как-то так:
Запускаем программу в отладчике:
При необходимости передать программе какие-то аргументы можно так:
Запуск с так называемом Text User Interface, интерфейсом на основе curses (мне лично он не нравится):
Еще можно прицепиться к уже работающему процессу по его id:
Для анализа корок используем команду вроде такой:
Чтобы корки вообще писались, систему нужно правильно настроить:
sudo sysctl -w kernel.core_pattern='/tmp/core_%e.%p'
Плюс в /etc/security/limits.conf пишем что-то вроде:
eax soft core 50000
eax hard core 500000
… а в /etc/sysctl.conf:
Кстати, корки можно создавать «вручную» прямо из gdb при помощи команды:
Для удаленной отладки на сервере выполняем команды:
gdbserver :3003 myprog
На клиенте говорим откуда брать отладочные символы:
… и цепляемся:
Заметьте, что в Ubuntu при попытке прицепиться к своим процессам без sudo вы можете получить ошибку вроде такой:
process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or
try again as the root user.
For more details, see /etc/sysctl.d/10-ptrace.conf
ptrace: Operation not permitted.
Для решения этой проблемы говорим:
… а также правим /etc/sysctl.d/10-ptrace.conf:
Итак, тем или иным образом мы прицепились отладчиком куда надо. Теперь нам доступны следующие команды.
Хэлп:
Для выполнения команды в gdb можно вводить либо всю команду целиком, либо только первые несколько букв. То есть, h
, he
и help
или r
, ru
и run
— это все одни и те же команды. Здесь я преимущественно буду использовать короткие версии, так как это то, что вы скорее всего будете использовать на практике. К тому же, соответствующие длинные версии обычно очевидны.
Начать выполнение программы, если она еще не выполняется:
Продолжить выполнение программы:
Прервать выполнение в любой момент можно нажатием Ctr+C.
Отцепиться от программы, оставшись при этом в отладчике:
Выйти из отладчика (если цеплялись через -p
, процесс продолжит работу):
Просмотр исходного кода программы:
l номер_строки
l откуда,докуда
Если вы отлаживаете программу не на том же сервере, на котором программа была собрана, на него нужно залить исходиники по тому же пути, что использовался на билд сервере, иначе листинг не будет работать.
Когда исходников под рукой нет, можно посмотреть ассемблерный код:
Step — шаг вперед или несколько шагов вперед:
s 3
Next — как step, только без захода внутрь других методов и процедур:
n 3
Until — выполнить программу до указанной строчки:
Продолжить выполнение до возвращения из текущей процедуры:
Показать стэктрейс:
bt
Перемещение между фреймами стака:
f 1
Информация о текущем фрейме:
Показать аргументы в текущем фрейме:
Показать локальные переменные в текущем фрейме:
Ставим бряк:
b my_procedure_name
Список бряков:
Удаление бряка по номеру:
Удаление всех бряков:
Временное включение и выключение бряков:
disable 1
Проигнорировать столько-то итераций:
Условные брейкпоинты ставятся как-то так:
Еще вы можете установить брейкпоинт прямо в исходном коде вашей программы:
do_something();
/* одинаковый синтаксис для GCC и CLang */
__asm__ __volatile__("int3");
do_something_else();
/* ... */
Список нитей:
Переключение на нитку:
Вывести значение переменной:
Также можно кастовать типы:
Обращаться к полям:
p *mystryct.otherfield
Вообще можно использовать gdb как калькулятор:
Можно менять значения переменных в программе:
Есть ограниченная поддержка переменных:
p $i
Можно вызывать процедуры и методы:
p $pi
p free($pi)
Дамп памяти (explore):
x/32uh some_var
x/64dw some_var
В приведенных примерах выводится дамп (1) 16-и байт с выводом в hex, (2) 32-х полуслов, которые выводятся, как числа без знака и (3) 64-х слов, которые выводятся, как числа со знаком.
Еще gdb умеет выполнять «скрипты», что позволяет, к примеру, быстро получить текущий стектрейс процесса и тут же отсоединиться, почти не останавливая его работу:
Описанных выше возможностей gdb вам хватит в 95% случаев. Но не следует думать, что ничего другого gdb не умеет. Мы не рассмотрели вачдоги, форматы вывода в стиле p/x
, DDD, и многое другое. GDB User Manual занимает более 700 страниц, поэтому пересказать его в рамках небольшого поста никак не представляется возможным. Из мануала вы узнаете, как отлаживать процессы, использующие системный вызов fork(), а также многие другие интересные вещи.
Дополнение: Еще вас могут заинтересовать статьи Памятка по использованию отладчика LLDB, Reverse debugging кода на C/C++ с помощью GDB и RR, а также Один простой, но эффективный отладочный прием. Больше об отладке ассемблерного кода с помощью GDB вы узнаете из поста Написание и отладка кода на ассемблере x86/x64 в Linux.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.