Отладка с помощью GDB - 1. Пример сеанса GDB

[Содержание]   [Назад]   [Пред]   [Вверх]   [След]   [Вперед]  


1. Пример сеанса GDB

Вы можете пользоваться этим руководством в свое удовольствие, чтобы прочитать о 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


[Содержание]   [Назад]   [Пред]   [Вверх]   [След]   [Вперед]