Вы можете пользоваться этим руководством в свое удовольствие, чтобы прочитать о GDB все. Однако, достаточно небольшого количества команд, чтобы начать пользоваться отладчиком. Эта глава иллюстрирует эти команды.
В этом примере сеанса мы выделяем ввод пользователя так: ввод, чтобы его было проще отличить от находящегося рядом вывода программы.
В одной из предварительных версий программы GNU m4
(настраиваемый
макропроцессор), была допущена следующая ошибка: иногда, при замене
строк, определяющих кавычки, со значений по умолчанию, команды,
использовавшиеся для поиска одного макроопределения внутри другого,
прекращали работать. В следующем коротком сеансе m4
мы
определим макрос foo
, который раскрывается в 0000
; затем мы
используем встроенную процедуру m4
defn
, чтобы определить
точно такой же макрос bar
. Однако, если мы изменим открывающую кавычку
на <QUOTE>
, а закрывающую на <UNQUOTE>
, та же самая процедура не
сможет определить новый синоним baz
:
$ cd gnu/m4 $ ./m4 define(foo,0000) foo 0000 define(bar,defn(`foo')) bar 0000 changequote(<QUOTE>,<UNQUOTE>) define(baz,defn(<QUOTE>foo<UNQUOTE>)) baz C-d m4: End of input: 0: fatal error: EOF in string(2)
Попытаемся с помощью GDB понять, что же происходит.
$ gdb m4 GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 5.0, Copyright 1999 Free Software Foundation, Inc... (gdb)
GDB читает только минимум символьных данных, достаточный для того, чтобы знать, где в случае необходимости искать остальные; в результате первое приглашение появляется очень быстро. Теперь мы велим GDB использовать меньшую чем обычно ширину экрана, чтобы примеры умещались на страницах этого руководства.
(gdb) set width 70
Нам необходимо увидеть, как работает встроенная процедура m4
changequote
. Посмотрев исходный текст, мы знаем, что
соответствующей подпрограммой является m4_changequote
, так что мы
устанавливаем на ней точку останова с помощью команды GDB break
.
(gdb) break m4_changequote Breakpoint 1 at 0x62f4: file builtin.c, line 879.
Используя команду run
, мы запускаем m4
под управлением
GDB; до тех пор, пока управление не достигло подпрограммы
m4_changequote
, программа выполняется как обычно:
(gdb) run Starting program: /work/Editorial/gdb/gnu/m4/m4 define(foo,0000) foo 0000
Чтобы активировать точку останова, мы вызываем changequote
.
GDB приостанавливает выполнение m4
, выводя на экран
информацию о том месте, где он остановился.
changequote(<QUOTE>,<UNQUOTE>) Breakpoint 1, m4_changequote (argc=3, argv=0x33c70) at builtin.c:879 879 if (bad_argc(TOKEN_DATA_TEXT(argv[0]),argc,1,3))
Теперь мы используем команду n
(next
), чтобы продолжить
выполнение до следующей строки текущей функции.
(gdb) n 882 set_quotes((argc >= 2) ? TOKEN_DATA_TEXT(argv[1])\ : nil,
set_quotes
выглядит как нужная нам подпрограмма. Мы можем войти в
нее, используя команду s
(step
) вместо next
.
step
переходит на следующую исполняемую строку любой
подпрограммы, так что она входит в set_quotes
.
(gdb) s set_quotes (lq=0x34c78 "<QUOTE>", rq=0x34c88 "<UNQUOTE>") at input.c:530 530 if (lquote != def_lquote)
Отображение, показывающее подпрограмму (и ее параметры), где m4
сейчас приостановлен, называется отображением кадра стека. Оно
показывает обзор стека. Мы можем использовать команду
backtrace
(которую можно сокращать как bt
), чтобы посмотреть, в
каком месте стека мы находимся: команда backtrace
выводит
кадр стека для каждой активной подпрограммы.
(gdb) bt #0 set_quotes (lq=0x34c78 "<QUOTE>", rq=0x34c88 "<UNQUOTE>") at input.c:530 #1 0x6344 in m4_changequote (argc=3, argv=0x33c70) at builtin.c:882 #2 0x8174 in expand_macro (sym=0x33320) at macro.c:242 #3 0x7a88 in expand_token (obs=0x0, t=209696, td=0xf7fffa30) at macro.c:71 #4 0x79dc in expand_input () at macro.c:40 #5 0x2930 in main (argc=0, argv=0xf7fffb20) at m4.c:195
Пройдем по шагам еще несколько строк, чтобы понять, что же происходит.
Первые два раза мы можем использовать `s'; следующие два раза
мы пользуемся командой n
, чтобы избежать
вхождения в подпрограмму xstrdup
.
(gdb) s 0x3b5c 532 if (rquote != def_rquote) (gdb) s 0x3b80 535 lquote = (lq == nil || *lq == '\0') ? \ def_lquote : xstrdup(lq); (gdb) n 536 rquote = (rq == nil || *rq == '\0') ? def_rquote\ : xstrdup(rq); (gdb) n 538 len_lquote = strlen(rquote);
Последняя выведенная строка выглядит несколько странно; мы можем
исследовать переменные lquote
и rquote
, чтобы понять,
действительно ли это определенные нами новые левая и правая кавычки.
Мы используем команду p
(print
), чтобы посмотреть их значения.
(gdb) p lquote $1 = 0x35d40 "<QUOTE>" (gdb) p rquote $2 = 0x35d50 "<UNQUOTE>"
Действительно, lquote
и rquote
являются новыми кавычками. Для
просмотра контекста, мы можем вывести десять строк исходного текста,
окружающих текущую строку, используя команду l
(list
).
(gdb) l 533 xfree(rquote); 534 535 lquote = (lq == nil || *lq == '\0') ? def_lquote\ : xstrdup (lq); 536 rquote = (rq == nil || *rq == '\0') ? def_rquote\ : xstrdup (rq); 537 538 len_lquote = strlen(rquote); 539 len_rquote = strlen(lquote); 540 } 541 542 void
Давайте пройдем еще две строки, которые устанавливают len_lquote
и
len_rquote
, и затем исследуем значения этих переменных.
(gdb) n 539 len_rquote = strlen(lquote); (gdb) n 540 } (gdb) p len_lquote $3 = 9 (gdb) p len_rquote $4 = 7
Это, очевидно, неправильно, так как len_lquote
и len_rquote
обозначают длины переменных lquote
и rquote
соответственно.
Мы можем присвоить им лучшие значения, используя
команду p
, так как она может выводить значение любого выражения, а
выражение может содержать вызовы подпрограмм и присваивания.
(gdb) p len_lquote=strlen(lquote) $5 = 7 (gdb) p len_rquote=strlen(rquote) $6 = 9
Достаточно ли этого, чтобы решить проблему использования новых кавычек
во встроенной процедуре m4
defn
? Мы можем
продолжить выполнение m4
командой c
(continue
),
и затем попробовать пример, первоначально вызывавший ошибку:
(gdb) c Continuing. define(baz,defn(<QUOTE>foo<UNQUOTE>)) baz 0000
Получилось! Теперь новые кавычки работают так же хорошо, как и стандартные.
Кажется, проблема заключалась лишь в двух опечатках, приводивших к
неправильному определению длин. Мы позволим m4
выйти, подавая ему
на вход EOF:
C-d Program exited normally.(3)
Сообщение `Program exited normally.'
исходит от GDB; оно
показывает, что m4
закончила выполнение. Мы можем завершить наш
сеанс работы с GDB командой quit
.
(gdb) quit