Шрифт:
Интервал:
Закладка:
Чуть позже мы кратко обсудим и другие средства, lint и splint. Как и компилятор, они анализируют код и сообщают о фрагментах кода, которые могут быть некорректными.
Оснащение средствами контроля
Оснащение средствами контроля — это вставка в программу кода для сбора дополнительной информации о поведении программы во время ее выполнения. Очень популярна вставка вызовов функции printf для вывода значений переменных на разных стадиях выполнения программы. Вы можете с пользой для себя добавить несколько вызовов printf, но должны знать о том, что этот процесс повлечет за собой дополнительные редактирование и компиляцию при любом изменении программы и, конечно, вам придется удалить код, когда ошибки будут исправлены.
Здесь могут помочь два метода оснащения средствами контроля. Первый использует препроцессор языка С для выборочного включения кода средств контроля так, что вам нужно только перекомпилировать программу для вставки или удаления отладочного кода. Сделать это можно очень просто, с помощью конструкций, подобных приведенным далее:
#ifdef DEBUG
printf("variable x has value = %dn", x);
#endif
Вы можете компилировать программу с флагом компилятора -DDEBUG для определения символического имени DEBUG и включения дополнительного кода и без этого флага — для удаления отладочного кода. Можно создать и более сложный вариант использования пронумерованных отладочных макросов:
#define BASIC_DEBUG 1
#define EXTRA_DEBUG 2
#define SUPER_DEBUG 4
#if (DEBUG & EXTRA_DEBUG)
printf...
#endif
В этом случае вы всегда должны определять макрос DEBUG, но можете настраивать объем отладочной информации или уровень детализации. Флаг компилятора -DDEBUG=5 в нашем примере активизирует макросы BASIC_DEBUG и SUPER_DEBUG, но не EXTRA_DEBUG. Флаг DDEBUG=0 отключит всю отладочную информацию. С другой стороны, вставка следующих строк устранит необходимость задания в командной строке DEBUG, если отладки не требуется.
#ifndef DEBUG
#define DEBUG 0
#endif
Несколько макросов, определенных препроцессором С, могут предоставить отладочную информацию. Эти макросы раскрываются для предоставления сведений о текущей компиляции (табл. 10.1).
Обратите внимание на то, что приведенные символические имена начинаются и заканчиваются двумя символами подчеркивания. Это стандартное правило для символических имен препроцессора, и вы должны аккуратно выбирать идентификаторы, чтобы избежать конфликтов. Термин "текущие" в предыдущих описаниях указывает на момент выполнения препроцессорной обработки, т.е. время и дата запуска компилятора и обработки файла.
Таблица 10.1
Макрос Описание __LINE__ Десятичная константа, предоставляющая номер текущей строки __FILE__ Строка, предоставляющая имя текущего файла __DATE__ Строка в форме "ммм дд гггг", текущая дата __TIME__ Строка в форме "чч:мм:сс", текущее времяВыполните упражнение 10.1.
Упражнение 10.1. Отладочная информацияДалее приведена программа cinfo.c, которая выводит дату и время компиляции, если включен режим отладки.
#include <stdio.h>
# include <stdlib.h>
int main() {
#ifdef DEBUG
printf("Compiled: " __DATE__ " at " __TIME__ "n");
printf("This is line %d of file %sn", __LINE__, __FILE__);
#endif
printf("hello worldn");
exit(0);
}
Когда вы откомпилируете эту программу с включенным режимом отладки (используя флаг -DDEBUG), то увидите следующие сведения о компиляции:
$ cc -о cinfo -DDEBUG cinfo.c
$ ./cinfo
Compiled: Jun 30 2007 at 22:58:43
This is line 8 of file cinfo.c
hello world
$
Как это работает
Препроцессор С, часть компилятора, отслеживает текущую строку и текущий файл во время компиляции. Он подставляет текущие (времени компиляции) значения этих переменных везде, где обнаруживает символические имена __LINE__ и __FILE__. Дата и время компиляции становятся доступными аналогичным образом.
Поскольку __DATE__ и __TIME__ — строки, вы можете объединить их в функции printf с помощью строк формата, т.к. в языке С ANSI смежные строки воспринимаются как одна.
Отладка без перекомпиляцииПрежде чем двигаться дальше, стоит отметить, что существует способ применения функции printf, позволяющий отлаживать программу без применения метода #ifdef DEBUG, требующего перекомпиляции программы перед ее использованием.
Метод заключается во вставке глобальной переменной как флага отладки, разрешении опции -d в командной строке, которая дает возможность пользователю включить отладку даже после того, как программа была введена в эксплуатацию, и включении функции мониторинга процесса отладки. Теперь можно вкраплять в код программы строки, подобные следующим:
if (debug) {
sprintf(msg, ...)
write_debug(msg)
}
Записывать вывод отладки следует в стандартный поток ошибок stderr или, если это не годится из-за характера программы, используйте возможности мониторинга, предоставляемые функцией syslog.
Если вы вставляете в программу подобную трассировку для решения проблем, возникающих на этапе разработки, просто оставьте этот код в программе. Если вы будете чуть внимательнее, чем всегда, такой подход не вызовет никаких проблем. Выигрыш проявится, когда программа будет введена в эксплуатацию; если пользователи обнаружат проблему, они смогут выполнить программу в режиме отладки и диагностировать ошибки для вас. Вместо известия о том, что программа выдает сообщение о нарушении сегментации, они смогут написать, что конкретно делает программа в ходе выполнения, а не только описать свои действия. Разница может оказаться огромной.
У этого метода есть явный недостаток: программа становится больше, чем должна быть. В большинстве случаев это, скорее, мнимая проблема, чем реальная. Программа может стать на 20–30% больше, но чаще всего это не оказывает никакого существенного влияния на ее производительность. Снижение производительности наступает при увеличении размера на несколько порядков, а не на небольшую величину.
Контролируемое выполнение
Вернемся к примеру программы. У вас есть ошибка. Вы можете изменить программу, вставив в нее дополнительный код для вывода значений переменных по мере выполнения программы, или применить отладчик для контроля над выполнением программы и просмотра ее состояния в ходе выполнения.
В коммерческих UNIX-системах есть ряд отладчиков, набор которых зависит от поставщика системы. Наиболее распространенные — adb, sdb, idebug и dbx. Более сложные отладчики позволяют просматривать с некоторой степенью детализации состояние программы на уровне исходного кода. Именно к таким относится отладчик GNU, gdb, который может применяться в системах Linux и многих вариантах UNIX. Существуют и внешние интерфейсы (или программы-клиенты) для gdb, делающие его более удобным для пользователя; к таким программам относятся xxgdb, KDbg и ddd. Некоторые IDE, например, те, с которыми вы познакомились в главе 9, также предоставляют средства отладки или внешний интерфейс для gdb. У редактора Emacs даже есть средство (gdb-mode), позволяющее запускать gdb в вашей программе, устанавливать точки останова и построчно просматривать выполнение исходного кода.
Для подготовки программы к отладке необходимо откомпилировать ее с одной или несколькими специальными опциями. Эти опции заставляют компилятор вставлять в программу дополнительную отладочную информацию. Она включает в себя идентификаторы и номера строк — сведения, которые отладчик может использовать, чтобы показать пользователю, до какого места в исходном программном коде дошло выполнение.
Флаг -g — один из обычно применяемых при компиляции программы с последующей отладкой. Вы должны указывать его при компиляции всех исходных файлов, которые нуждаются в отладке, а также для компоновщика, чтобы могли применяться специальные версии стандартной библиотеки С, обеспечивающие поддержку режима отладки в библиотечных функциях. Программа компилятора передаст флаг компоновщику автоматически. Отладка может применяться и с библиотеками, не откомпилированными для этой цели, но с меньшей гибкостью.
Отладочная информация может увеличить исполняемый файл во много (до десяти) раз. Несмотря на увеличение размера исполняемого файла (он займет больше места на диске), объем памяти, необходимый для выполнения программы, практически остается тем же самым. Перед вводом программы в эксплуатацию неплохо удалить из нее отладочную информацию, но только после того, как программа полностью отлажена.
- Linux - Алексей Стахнов - Программное обеспечение
- Разработка приложений в среде Linux. Второе издание - Майкл Джонсон - Программное обеспечение
- Искусство программирования для Unix - Эрик Реймонд - Программное обеспечение
- Fedora 8 Руководство пользователя - Денис Колисниченко - Программное обеспечение
- Linux Mint и его Cinnamon. Очерки применителя - Алексей Федорчук - Программное обеспечение
- Недокументированные и малоизвестные возможности Windows XP - Роман Клименко - Программное обеспечение
- ELASTIX – общайтесь свободно - Владислав Юров - Программное обеспечение
- Изучаем Windows Vista. Начали! - Дмитрий Донцов - Программное обеспечение
- Windows Vista - Сергей Вавилов - Программное обеспечение
- Операционная система UNIX - Андрей Робачевский - Программное обеспечение