gdb

Ошибки, к сожалению, встречаются в любой программе, каким бы крутым профессионалом её разработчик ни был. Поэтому, нравится это вам или нет, пользоваться отладчиком вам всё равно придётся. Жизнь заставит. И чем больше времени вы сейчас потратите на изучение работы с ним, тем больше времени это вам сэкономит в дальнейшем.

Мы рассмотрим отладчик GDB, входящий в комплект программ GNU.

Для того, чтобы им пользоваться, нужно сначала скомпилировать программу так, чтобы её двоичный файл содержал отладочную информацию. Эта информация включает в себя, в частности, описание соответствий между адресами исполняемого кода и строками в исходном коде.

Такая компиляция достигается путём добавления флага -g к команде на компиляцию. Например, если бы мы собирали программу kalkul без применения Makefile, мы бы дали такую команду:

g++ main.cpp problem.cpp -o kalkul -g

Если же мы пользуемся командой make, то надо поставить опцию CFLAGS=-g. Тогда все команды на компиляцию, содержащиеся в Make-файле, автоматически получат флаг -g.

Давайте возьмём программу, которую мы создали из файлов main.cpp, problem.cpp и problem.h (мы тогда называли этот каталог проекта kalkulcpp). У нас Makefile уже сформирован. Воспользуемся им.

Очистим пакет от результатов предыдущей сборки.

make clean

Соберём программу снова, но уже с включением отладочной информации.

make CFLAGS=-g

Запустим отладчик GDB, загрузив в него нашу программу для отладки. (Если помните, исполняемая программа у нас находилась в каталоге src.)

gdb ./src/kalkul

Чтобы запустить программу внутри отладчика,даётся команда run.

run

Чтобы посмотреть исходный код, даётся команда list.

list

Если дать эту команду без параметров, то она первые девять строк исходного кода главного файла (то есть такого, в котором имеется функция main). Чтобы просматривать файл дальше, надо снова набирать list. Чтобы посмотреть конкретные строки, надо указать два параметра: с какой строки начинать просмотр, и с какой строки заканчивать.

list 12,15

Чтобы просмотреть другие файлы проекта, надо перед номерами строк указать название нужного файла и отделить его от номеров строк двоеточием.

list problem.cpp:20,29

Поставим точку останова на строке номер 21. Точка останова – это метка, указывающая, что программа, дойдя до этого места, должна остановиться.

list problem.cpp:20,27

break 21

Посмотреть, где вы поставили точки останова, можно с помощью команды info breakpoints.

info breakpoints

(При желании можно вместо номера строки указать название функции,тогда программа остановится перед входом в функцию.)

Запустим программу.

run

Введём первое число 5 и знак математического действия « + ». Программа дойдёт до точки останова и остановится, выведя нам строку, у которой эта точка расположена.

Нам, конечно, интересно знать,в каком именно месте мы остановились, и что программа уже успела выполнить. Даём команду backtrace.

backtrace

Отладчик выдаёт нам следующую информацию:

#0 CProblem::Calculate (this=0x804b008) at problem.cpp:21

#1 0x08048e00 in CProblem::Solve (this=0x804b008) at problem.cpp:93

#2 0x08048efc in main () at main.cpp:15

Это означается, что мы находимся внутри выполняющейся функции Calculate, являющейся функцией-членом класса CProblem. Она была вызвана из функции Solve того же класса, а та, в свою очередь, из функции main. Таким образом, команда backtrace показывает весь стек вызываемых функций от начала программы до текущего места.

Посмотрим, чему же равно на этом этапе значение переменной Numeral.

print Numeral

И нам сразу выводится число 5, которое мы и вводили в программу. (Значение, введённое нами с клавиатуры, присвоилось именно этой переменной.)

Если мы вместо print будем пользоваться командой display, то величина этой переменной будет показываться каждый раз, когда программа останавливается, без специального указания.

display Numeral

Добавим ещё одну точку останова на строке 25 файла problem.cpp.

break problem.cpp:25

Продолжим выполнение программы.

continue

Команда Continue продолжает выполнение программы с текущего адреса. Если бы мы набрали run, программа начала бы выполняться с начала. Поскольку на строке 24 имеется команда cin >> SecondNumeral, то нам придётся ввести второе слагаемое. Введём, например,число 2. После этого программа опять остановится на строке 25 (наша вторая точка останова).

Посмотрим, чему равны значения наших переменных Numeral, SecondNumeral и Operation. Если вы помните, именно такие переменные мы объявляли в классе CProblem.

print Numeral

print Operation

print SecondNumeral

У нас получится 5, « + », 2. Так и должно быть. Но давайте теперь «передумаем» и лучше присвоим переменной SecondNumeral значение 4. Отладчик GDB позволяет прямо во время выполнения программы изменить значение любой переменной.

set SecondNumeral=4

Если не верим, что её значение изменилось,можно проверить.

print SecondNumeral

Теперь мы ожидаетм, что результат будет 9. Давайте выполним программу до конца.

continue

Результат действительно равен 9.

Давайте теперь уберём наши точки останова. Мы, кажется, создали две таких точки. Но это можно проверить.

info breakpoints

Удалим их.

delete 1

delete 2

Унас не должно остаться ни одной точки останова. Проверяем.

info breakpoints

Действительно не осталось ни одной.

Теперь давайте пошагово пройдём всю программу (благо, она у нас небольшая).

Поставим точку останова на десятой строке главного файла.

break main.cpp:10

Запустим программу

run

Дойдя до десятой строчки, она остановится. Теперь проходим её, останавливаясь на каждой строчке, с помощью команды step.

step

Чтобы не набирать каждый раз s-t-e-p, можно просто вводить букву s. Как только программа доходит до команды Problem->SetValues(), она сразу переходит в файл problem.cpp, где находится определение функции-члена CProblem::SetValues() и проходит код этой функции. То же самое, когда она дойдёт до вызова Problem->Solve().

Чтобы при вызове функции, программа не входила в неё, а продолжала дальше выполняться только на текущем уровне стека, вместо step даётся команда next или просто n.

next

Если мы вошли в функцию, но не хотим дальше проходить её по шагам, а хотим, чтобы она отработала и вернула нас на предыдущий уровень стека (то есть, обратно в функцию, вызвавшую её), мы пользуемся командой finish.

finish

Таким образом, можно просмотреть, как выполняется вся программа или любой участок программы. На любом шаге можно проверять значение любой переменной. Чтобы перестать проходить программу по шагам и запустить её до конца, надо дать команду continue.

Дадим короткий список наиболее часто встречающихся команд отладчика GDB. За более подробной информацией вы, конечно, всегда можете обратиться к встроенному описанию программы (info gdb) или руководством по пользованию (man gdb).

backtrace– выводит весь путь к текущей точке останова, то есть названия всех функций, начиная от main(); иными словами, выводит весь стек функций;

break– устанавливает точку останова; параметром может быть номер строки или название функции;

clear– удаляет все точки останова на текущем уровне стека (то есть в текущей функции);

continue– продолжает выполнение программы от текущей точки до конца;

delete– удаляет точку останова или контрольное выражение;

display– добавляет выражение в список выражений, значения которых отображаются каждый раз при остановке программы;

finish– выполняет программу до выхода из текущей функции; отображает возвращаемое значение,если такое имеется;

info breakpoints– выводит список всех имеющихся точек останова;

info watchpoints– выводит список всех имеющихся контрольных выражений;

list– выводит исходный код; в качестве параметра передаются название файла исходного кода, затем, через двоеточие, номер начальной и конечной строки;

next– пошаговое выполнение программы, но, в отличие от команды step, не выполняет пошагово вызываемые функции;

print– выводит значение какого-либо выражения (выражение передаётся в качестве параметра);

run– запускает программу на выполнение;

set– устанавливает новое значение переменной

step– пошаговое выполнение программы;

watch– устанавливает контрольное выражение, программа остановится, как только значение контрольного выражения изменится;