From: Илья Аввакумов <http://fpc.by.ru>
Newsgroups: http://fpc.by.ru
Date: Mon, 9 Dec 2003 14:31:37 +0000 (UTC)
Subject: Пример использования отладчика GNU GDB
Откровенно говоря, программа GNU GDB довольно многофункциональная.
Пошаговая отладка -- лишь одна из ее возможностей. В этой статье я
попытался описать те лишь команды GDB, которые позволяют проводить
удобную пошаговую отладку программ, написанных на Free Pascal.
Чтобы программу можно было отлаживать, она должна быть откомпилирована
с ключом -g.
Поскольку GDB ориентирован не на Pascal, а на C и C++, то
использование GDB для отладки Pascal программ иногда сопряжено с
неудобствами.
Приведу список подводных камней, обнаруженных мною и разработчиками
Free Pascal (перечисленных в user's manual).
1. Отладочная информация в Free Pascal генерируется в верхнем
регистре. Поэтому имена всех переменных, процедур, функций при
использовании GDB должны указываться БОЛЬШИМИ БУКВАМИ.
2. GDB не воспринимает тип extended (ведь в C такого типа нет).
Обойти эту неприятность можно, если, например, включить в код
такие строки
...
type
{$IFDEF DEBUG}
dbl = double;
{$ELSE}
dbl = extended;
{$ENDIF}
...
var x : dbl;
...
3. К элементам многомерных массивов нужно обращаться в C-шной манере,
а именно, команда
(gdb) print A[1,1]
выдаст первую строку массива A. Для просмотра требуемого элемента
следует писать
(gdb) print A[1][1]
4. GDB не воспринимает множества.
5. Есть трудности с поддержкой объектов (см. user's manual за
подробностями).
6. Есть трудности с глобально переопределенными функциями. (за
подробностями см. user's manual).
7. При отладке процедур, функций, расположенных в разных файлах,
часто возникает несоответствие -- смещение строк. Та строка,
которую GDB показывает текущей, таковой не является, а текущая
расположена строк эдак на двадцать выше. Это приводит к большим
неудобствам при пошаговой отладке. Я для себя сделал из этого
такую мораль -- хоть GDB и позволяет отлаживать процедуры,
описанные в разных файлах, но лучше этой возможностью не
пользоваться, а на время отладки все вызываемые процедуры, работа
которых вас заинтересует, помещать в одном файле.
Все примеры отлаживались с использованием GNU GDB 5.0.
Запуск отладчика GDB
--------------------
gdb [опции] [имя_файла | ID процесса]
После запуска видим "nice GDB logo" (если это почему-то раздражает, то
опция -q позволяет не выводить это введение с информацией об авторских
правах и прочая). В следующей строке приглашение
(gdb)
ждет ввода команды.
Ниже приводится краткий перечень команд GDB.
Краткую справку о любой команде можно получить, введя
(gdb) help [имя_команды, можно краткое]
Если при запуске GDB имя исполняемого файла не было указано (что
следовало бы делать), то указать его можно командой file.
Команда file
(gdb) file <имя исполняемого файла, который подлежит отладке>
Для того, чтобы пролистать содержимое исходника, используйте команду
list (сокращенно l; большая часть наиболее полезных команд имеют
сокращения). При этом подразумевается, что исходник расположен в том
же каталоге, что и исполняемый файл. Как правило, так оно и есть.
Команда list (сокращенно l)
Пролистывает 10 строк вниз, начиная с текущей. Для пролистывания вверх
следует набрать
(gdb) list -
Команда run (сокращенно r)
Запускает отлаживаемую программу под GDB. Если требуется, то после
команды можно указать список аргументов программы. Также допускается
перенаправление потоков ввода и вывода в другие файлы, например
(gdb) run > outfile
Если никаких точек останова не определено, то программа выполняется
тихо, при этом нам сообщается:
(gdb) run
Starting program: test
Program exited normally.
(gdb)
Если же отладчик встречает точку останова, он выдает ее номер, адрес и
дополнительную информацию -- текущую строку, имя процедуры, и т.п.
(gdb) r
Breakpoint 1, main () at test.pp:3
Current language: auto; currently pascal
3 x := x + 1;
(gdb)
И ожидает ввода команды.
Остановка отладки программы
---------------------------
Команда kill (k). Следует запрос
(gdb) kill
Kill the program being debugged? (y or n) y
(gdb)
Здесь введено y (то есть "да"), и отладка программы прекращается.
Командой run ее можно начать заново, при этом все точки останова
(breakpoints), точки просмотра (watchpoints) и точки отлова
(catchpoints) сохраняются.
Выход из отладчика
------------------
Команда quit (q).
(gdb) q
[avva@localhost home]$
Точки останова
--------------
Информацию о командах этого раздела можно получить, введя
(gdb) help breakpoints
Точки останова -- такие, когда GDB приостанавливает выполнение
программы. Как уже упоминалось, имеется 3 типа точек останова:
1. Breakpoints -- точка останова как таковая. Остановка происходит,
когда выполнение доходит до определенной строки, адреса или
процедуры/функции.
2. Watchpoints -- точка просмотра. Выполнение программы
приостанавливается, если программа обратилась к определенной
переменной -- либо считала ее значение, либо изменила его.
3. Catchpoints -- точка отлова. Приостановка происходит при
определенном событии (например, получение сигнала). Я не буду
касаться точек останова этого типа.
Определение точек останова
Breakpoint
Команда break
(gdb) break [аргумент]
или, сокращенно
(gdb) b [аргумент]
определяет точку останова. В качестве аргумента может выступать
* номер строки. Остановка произойдет при достижении строки программы
с этим номером. То, что написано в самой строке, выполняться не
будет. Например
(gdb) b 394 Breakpoint 1 at 0x805a650: file maeq.pas, line 394.
* имя процедуры (функции). Отладчик зайдет в эту процедуру и
остановит выполнение программы. NB!! Имя процедуры (функции)
должно быть указано БОЛЬШИМИ БУКВАМИ. Приведу пример:
(gdb) b CALC Breakpoint 2 at 0x7657c7a: file maeq.pas, line 26.
* если вызвать команду break без аргументов, то точка останова
поставится на текущей строке.
* также можно явно указывать адрес точки останова (перед адресом
надо поставить знак *). Приведу лишь пример для полноты описания:
(gdb) b *0x805a650 Breakpoint 3 at 0x805a650: file maeq.pas, line 394.
Допускается использование нескольких точек останова на одной строке
(функции, адресе).
Watchpoint
Существуют различные виды точек просмотра, и задаются они различными
командами:
* команда watch (сокращенно wa)
(gdb) wa <переменная>
Выполнение программы приостанавливается всякий раз, когда значение
указанной переменной изменяется.
NB!! Имя переменной должно быть указано БОЛЬШИМИ БУКВАМИ.
* команда rwatch (сокращенно rw)
(gdb) rw <переменная>
Выполнение приостанавливается всякий раз, когда программа
считывает значение указанной переменной.
NB!! Имя переменной должно быть указано БОЛЬШИМИ БУКВАМИ.
* команда awatch (сокращенно aw)
(gdb) aw <переменная>
Выполнение приостанавливается всякий раз, когда программа
обращается к указанной переменной, как для считывания, так и для
записи.
NB!! Имя переменной должно быть указано БОЛЬШИМИ БУКВАМИ.
Замечу от себя, что команды rwatch и awatch у меня почему-то
капризничают -- часто не устанавливают точки просмотра на переменную.
Зато команда watch работала всегда.
Управление точками останова
Информацию о всех установленных точках останова можно вывести командой
info.
Команда info имеет много возможностей, но в данном случае
воспользуемся лишь следующим ее форматом:
(gdb) info breakpoints
или, кратко
(gdb) i b
Выводится подробная информация о всех точках останова (как
breakpoints, так и watchpoints), включающая - номер - breakpoint или
watchpoint - активность - сколько раз программа натыкалась на эту
точку - иные характеристики, значение которых мне не совсем понятно
Если какая-то точка останова не нужна, то ее можно сделать неактивной
с помощью команды disable:
(gdb) disable breakpoint <номер этой точки>
Обратно, деактивированная точка останова активируется командой enable:
(gdb) enable breakpoint <номер этой точки>
Статус точки останова -- активна она или нет, легко обозреть той же
командой info.
Если же точка останова не требуется вообще, то она может быть удалена
насовсем.
(gdb) delete breakpoint [номер точки]
а короче
(gdb) d b [номер точки]
Ввод этой команды без аргумента удалит ВСЕ точки останова.
Информацию о командах этого раздела можно получить, введя
(gdb) help running
Команда continue (c)
(gdb) с [аргумент]
Продолжает выполнение остановленной программы. Выполнение будет
происходить, пока снова не встретится точка останова. В качестве
аргумента может использоваться целое число N. Это укажет отладчику
проигнорировать (N-1) точку останова (выполнение остановится на N-ой).
Команда step (s)
(gdb) s [аргумент]
Аналог действия клавиши F7 (Trace into) в IDE. Происходит выполнение
программы до тех пор, пока не будет достигнута следующая строка ее
кода. При указании аргумента -- целого числа N, отладчик выполняет
команду step N раз (если не останавливает выполнение из-за точек
останова или по иным причинам).
Команда next (n)
(gdb) n [аргумент]
Аналог действия клавиши F8 (Step over) в IDE. В отличие от step вызов
процедуры считается единой инструкцией (не заходит в вызываемые
процедуры, функции). Аргумент N работает так же, как и для step.
Команда finish (fin)
(gdb) fin
Выполняет программу до момента выхода из текущей процедуры (функции).
Если функция возвращает значение, то это значение выводится тоже.
Команда until (u)
Производит выполнение программы до тех пор, пока не будет достигнута
строка с номером, большим текущего. Команду until удобно применять при
отладке циклов. Остановка произойдет также, если программа при
выполнении цикла выйдет из текущей процедуры, функции.
Команда stepi (si)
(gdb) si [аргумент]
Действие подобно step, но выполняется не строка, а ровно одна
инструкция в этой строке программы. Аргумент N нужен, если требуется
выполнить N инструкций.
Команда nexti (ni)
(gdb) ni [аргумент]
Аналогична stepi, но вызовы процедур трактуются как одна инструкция.
Управление состоянием (просмотр, изменение) переменных при отладке
Информацию о командах этого раздела можно получить, введя
(gdb) help data
Команда print (p)
(gdb) print <выражение>
Вывод текущего значения переменной (выражения). При использовании
команды print имя переменной можно писать в смешанном регистре, то
есть в этом случае использование больших букв обязательным не
является.
Часто требуется отслеживать значения нескольких переменных. Чтобы не
утруждать себя многократным вводом команды print, используйте команду
display.
Команда display
(gdb) display [аргумент]
В качестве аргумента обычно указывают переменную или выражение. При
этом указанная переменная (выражение) занесется в дисплей, то есть
станет выводиться при каждой остановке программы (при попадании на
точку останова, при пошаговом выполнении командами step и next, etc).
Если вызвать display без аргументов, то GDB выдаст значения всех
переменных (выражений), занесенных в дисплей.
Управление списком этих переменных осуществляется аналогично точ- кам
останова. А именно, команда info display
(gdb) info display
выдаст все переменные, занесенные в дисплей. Любая переменная в списке
дисплея может быть дезактивирована
(gdb) disable display <номер переменной в списке дисплея>
или активирована заново
(gdb) enable display <номер переменной в списке дисплея>
Удаление переменной из списка дисплея производится командой delete или
командой undisplay. Так, команда
(gdb) delete display [номер переменной в списке дисплея]
делает то же, что и
(gdb) undisplay [номер переменной в списке дисплея]
Опять-таки, если не указать номер переменной, то очистится весь список
отображаемых переменных.
Изменение значения переменной
И последнее. Изменение значения переменной на другое можно, например,
произвести с помощью команд set или print.
(gdb) set <оператор присваивания>
(gdb) print <оператор присваивания>
Например,
(gdb) whatis x
TYPE = WORD
(gdb) p x
$1 = 1
(gdb) set x:=2
(gdb)
При использовании set присваивание происходит "тихо". То же самое
можно сделать, но с помощью команда print.
Например,
(gdb) p x
$2 = 2
(gdb) p x:=x-2
$3 = 0
(gdb)
При этом, как видно, выводится новое значение переменной.