/* Установить контрольную точку для интересующей функции */
Breakpoint 2 at 0x8057913: file whizprog.c, line 3140.
(gdb) cont
По достижении второй контрольной точки программа готова завершиться неудачей, и вы можете пошагово ее пройти, делая все необходимое для отслеживания проблемы.
Вместо использования фиксированной константы ('++count == 1426
') можно использовать глобальную переменную, которая устанавливается отладчиком в любое нужное вам значение. Это дает возможность избежать перекомпилирования программы
Для gawk
мы пошли на один шаг дальше и внесли возможность отладочной ловушки в язык, так что функция ловушки могла быть вызвана из программы awk
. При компилировании для отладки доступна специальная ничего не делающая функция stopme()
. Эта функция, в свою очередь, вызывает функцию С с тем же названием. Это позволяет нам поместить вызовы stopme()
в завершающуюся неудачей программу awk
непосредственно перед сбойным участком. Например, если gawk
выдает ошибочные результаты для программы awk
в 1200-й вводимой записи, мы можем добавить в программу awk
строку, подобную этой:
NR == 1198 { stopme() } # Остановиться для отладки, когда число записей == 1198
/* ...оставшаяся часть программы как ранее... */
Затем из GDB мы можем установить контрольную точку на функции С stopme()
и запустить программу awk
. Когда контрольная точка срабатывает, мы можем затем установить контрольные точки на другие части gawk
, где, как мы ожидаем, находится действительная проблема.
Методика функции-ловушки полезна сама по себе. Однако, возможность переместить ее на уровень приложения умножает ее полезность, и она сохранила нам бесчисленное число часов отладки при отслеживании непонятных проблем.
15.5. Отладочные инструменты
Помимо GDB и различных ловушек в исходном коде, которые вы используете для общей отладки, имеется ряд полезных пакетов, которые могут помочь обнаружить определенные разновидности проблем. Поскольку управление динамической памятью является в крупномасштабных программах такой трудной задачей, многие инструменты фокусируются на этой области, часто действуя в качестве замещающих malloc()
и free()
элементов
Имеются коммерческие инструменты, которые делают множество (или все) из тех вещей, что и описываемые нами программы, но не все они доступны для GNU/Linux, а многие довольно дороги. Все пакеты, обсуждающиеся в данном разделе, являются свободно доступными.
15.5.1. Библиотека dbug
— усовершенствованный printf()
Первым пакетом, который мы исследуем, является библиотека dbug
. Она основана на идее условно компилируемого отладочного кода, которую мы представили ранее в данной главе, но идет намного дальше, предоставляя сравнительно сложную трассировку времени исполнения и условный вывод отладки. Она реализует многие из описанных нами советов, избавляя вас от хлопот по собственной их реализации.
Библиотека dbug
, написанная Фредом Фишем (Fred Fish) в начале 1980-х, была с тех пор несколько усовершенствована. Теперь она явным образом является общим достоянием, поэтому ее можно использовать без всяких проблем как в свободном, так и частном программном обеспечении. Она доступна через архив FTP Фреда Фиша[175] как в виде сжатого файла tar, так и в виде архива ZIP. Документация хорошо резюмирует dbug
:
dbug
является примером внутреннего отладчика. Поскольку она требует внутренней поддержки программы и ее использование не зависит от каких бы то ни было особых возможностей среды исполнения, она всегда доступна и будет выполняться в любом окружении, в котором будет выполняться сама программа. Вдобавок, поскольку это законченный пакет с особым интерфейсом пользователя, все программы, которые ее используют, будут иметь сходные возможности отладки. Это резко контрастирует с другими формами внутренней поддержки, где у каждого разработчика своя собственная, обычно менее квалифицированная, форма внутреннего отладчика...
Пакет dbug
лишь незначительно снижает скорость выполнения программ, обычно значительно менее 10%, и немного увеличивает их размеры, обычно от 10 до 20%. Определив особый идентификатор препроцессора С, можно снизить оба этих показателя до нуля без необходимости изменений в исходном коде.
Следующий список является кратким изложением возможностей пакета dbug
. Каждую возможность можно отдельно включать или отключать во время запуска программы, указав соответствующие аргументы командной строки.
• Трассировка исполнения, отображающая уровень потока управления полуграфическим способом с использованием отступов, обозначающих глубину вложения
• Вывод значений всех или любого набора ключевых внутренних переменных.
• Ограничение действий определенным набором указанных функций.
• Ограничение трассировки функций указанной глубиной вложения.
• Пометку каждой выводимой строки названием исходного файла и номером строки.
• Пометку каждой выводимой строки названием текущего процесса.
• Сохранение в стеке или восстановление состояния отладки для обеспечения исполнения со встроенными значениями по умолчанию для отладки.
• Перенаправление потока вывода отладки в стандартный вывод (stdout
) или указанный файл. По умолчанию поток вывода направляется в стандартную ошибку (stderr
). Механизм перенаправления полностью независим от обычного перенаправления командной строки, чтобы избежать конфликтов вывода.
Пакет dbug
требует от вас использования определенного порядка при написании своего кода. В частности, нужно использовать его макросы при возвращении из функции или вызове setjmp()
и longjmp()
. Нужно добавлять один вызов макроса в качестве первого исполняемого оператора каждой функции и вызвать несколько дополнительных макросов из main()
. Наконец, нужно добавить отладочную опцию командной строки, по соглашению, это -#
, которая редко используется в качестве действительной опции, если вообще используется. В обмен на дополнительную работу вы получаете все только что очерченные преимущества. Давайте взглянем на пример в руководстве:
1 #include <stdio.h>
2 #include "dbug.h"
3
4 int
5 main(argc, argv)
6 int argc;
7 char *argv[];
8 {
9 register int result, ix;
10 extern int factorial(), atoi();
11
12 DBUG_ENTER("main");
13 DBUG_PROCESS(argv[0]);
14 DBUG_PUSH_ENV("DBUG");
15 for (ix = 1; ix < argc && argv[ix][0] == '-'; ix++) {
16 switch (argv[ix][1]) {
17 case '#':
18 DBUG_PUSH(&(argv[ix][2]));
19 break;
20 }
21 }
22 for (; ix < argc; ix++) {
23 DBUG_PRINT("args", ("argv[%d] = %s", ix, argv[ix]));
24 result = factorial(atoi(argv(ixj));
25 printf("%d\n", result);
26 fflush(stdout);