В этой статье приведено несколько практических советов по отладке консольных приложений Linux. Рассматривается вывод отладочных сообщений, некоторые опции компилятора gcc для управления отладочной информацией, для перехвата ошибок наподобие утечки памяти и доступа мимо буфера и другие полезные возможности.
[printf, stdio]
Текстовый вывод с помощью printf вероятно наиболее часто используемый метод отладки инженерами, кто предпочитает не использовать отладчик. Однако этот метод не лишен недостатков: интенсивный вывод на печать может значительно повлиять на тайминги программы, в результате могут маскироваться старые баги и появляться новые. Также нужно всегда помнить о том, что в релизе программы необходимо удалить лишний отладочный вывод.
[Вывод в лог]
Сообщение лога может выглядеть почти так же, как сообщение, выводимое printf.
Часто большой проект содержит свою собственную систему лога. Это позволяет реализовать различные уровни сообщений лога, используемые в разных местах кода, с возможностью конфигурирования уровня вывода в лог на этапе сборки, или с помощью опций запуска.
Реализация функции лога не очень сложный процесс. Обычно создается функция наподобие следующей:
int log_printf(enum log_level lvl, const char *fmt, ...);
Внутри этой функции должна быть логика, проверяющая уровень вывода сообщения lvl по сравнению с текущим уровнем, установленным конфигурацией. Вот пример уровней вывода в лог:
LOG_ERROR. Верхний, самый важный вывод уровень вывода сообщения, сигнализирующий об ошибке. В любой момент, когда происходит ошибка, её сообщение должен видеть пользователь в STDERR в некоторой описательной информацией.
LOG_WARNING. Вывод предупреждения, которое может означать возможную потенциальную ошибку. Как правило, предупреждение также должно быть напечатано в STDERR. Иногда такая информация может быть полезной, однако не стоит перегружать пользователя лишними сообщениями.
LOG_DEBUG. Этот вид сообщений может помочь разработчику понять, что происходит. Такой уровень лога обычно не должен быть выпущен в релизе приложения.
Это установленные на скорую руку правила, не жесткие, которые очень зависят от того, что делает ваше приложение, насколько оно большое, как взаимодействует с другими системами, и так далее. Общий смысл, однако, заключается в том, что ведение журнала и особенно хорошие сообщения об ошибках могут сэкономить время. Прежде всего, написание кода займет немного больше времени, но это хорошая привычка, и когда проблемы появляются, это может помочь вам немедленно диагностировать проблему без каких-либо дополнительных усилий по отладке.
[Отладка с помощью GDB]
Проект GNU предоставляет утилиту отладки командной строки, которая называется GDB. Это отладчик, который может быть очень полезен в разработке, поскольку помогает увидеть, что происходит внутри программы. Он может быть даже полезен в поиске причин ошибок отказа доступа к памяти (segmentation fault).
Чтобы начать использовать GDB, ваше компилируемое приложение должно установить флаги поддержки информации отладки. Эти флаги обеспечивают добавление в двоичный код специальной информации, наподобие закладок по всему скомпилированному коду, которые связывают машинные инструкции с файлом исходного кода на языке C и его строками. Для добавления информации отладки просто добавьте "-g" в свою переменную компиляции CFLAGS.
Чтобы запустить программу под управлением отладчика gdb, просто запустите 'gdb '. В качестве примера ниже на скриншоте показана простая программа на языке C в виде файла "a.c".
1 #include < stdio.h> 2 3 int a = 4; 4 5 int function(void) 6 { 7 static int b = 2; 8 9 return a++ + b; 10 } 11 12 void main(void) 13 { 14 int i; 15 16 for (i = 0; i < 5; i++) { 17 int c; 18 19 c = function()); 20 21 printf("%d\n", c); 22 } 23 }
Скомпилируйте этот код с опцией -g и запустите его с gdb, чтобы получить приглашение (gdb).
$ gcc -g a.c
$ gdb a.out
GNU gdb (Ubuntu 7.11.1-0ubuntu1-16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later < http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
< http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
< http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb)
Ниже показана вставка точек останова (breakpoint), установка переменных и выполнение шагов по строкам программы. Здесь устанавливаются точки останова на строках 9, 17, и запуск программы. Программа остановится на строке 19 (потому что строка 17 это только декларация переменной, так что выполнение кода здесь не останавливается). Затем проверяется значение переменной 'i', которое оказывается равным 0.
(gdb) b 9
Breakpoint 1 at 0x400521: file a.c, line 9. (gdb) b 17
Breakpoint 2 at 0x40054b: file a.c, line 17. (gdb) run
Starting program: /tmp/a.out
Breakpoint 2, main () at a.c:19
19 c = function();
(gdb) print i
$1 = 0
Отсюда происходит переход к следующей точке останова, которая находится внутри вызова функции, изучаются значения, содержащиеся в переменных 'a' и 'b', и мы видим, что они содержат 4 и 2 соответственно.
(gdb) next
Breakpoint 1, function () at a.c:9
9 return a++ + b;
(gdb) print a
$2 = 4
(gdb) print b
$3 = 2
Это всего лишь небольшой пример того, как использовать GDB. Он очень мощный, и с ним можно делать гораздо больше вещей. Впрочем, отлаживать подобное в командной строке немного громоздко. GDB может либо использоваться в интерактивном режиме (как было продемонстрировано), или его можно использовать в качестве просто бэкенда для гораздо более удобного интерфейса.
[Отладка с помощью VisialGDB]
VisualGDB это коммерческое ПО, add-on для Microsoft Visual Studio. Находка для программистов, которые разрабатывают в среде Visual Studio, к которой привыкли, но также нуждаются в гибкости для нацеливания на любые платформы Linux.
В основном для использования VisualGDB понадобятся 3 отдельные машины.
1. Visual Studio host machine - на ней вы разрабатываете и отлаживаете свое приложение. 2. Cross-compilation machine - здесь VisualGDB подготовит компиляцию вашего приложения. Здесь должен быть установлен соответствующий целевой Yocto SDK. 3. Target machine - здесь запускается ваше приложение, и на ней GDB запускается в фоновом режиме (backend).
Когда все это будет установлено, вы сможете устанавливать точки останова в различных файлах кликами мыши, и отлаживать Linux-приложение почти так же, как и приложение Windows.
[Отладка с помощью Valgrind]
Фактически Valgrind это набор инструментов, но Memcheck в нем самый важный.
Эта утилита полезна для определения утечек памяти (memory leaks) или доступа к памяти вне определенных границ (out-of-bounds accesses), самые распространенные проблемы программирования на C/C++. Чтобы воспользоваться утилитой, скомпилируйте вашу программу с опцией -g, как было описано выше в секции "Отладка с помощью GDB", затем просто запустите вашу программу со следующим:
valgrind --leak-check=yes
Ваша программа запустится в несколько раз медленнее, чем обычно; это потому, что она работает в виртуальном окружении, где происходит глубокий анализ работы с памятью.
$ valgrind --leak-check=yes ./a.out
==26954== Memcheck, a memory error detector
==26954- Copyright (C) 2002-2015, and GNU GPL 'd, by Julian Seward et al.
==26954== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==26954== Command: ./a.out
==26954==
==26954 Invalid write of size 1
==26954== at 0x400548: return_without_free (a.c:10)
==26954== by 0x400556: main (a.c:17)
==26954== Address 0x5204429 is 1 bytes after a block of size 1,000 alloc'd
==26954== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26954== by 0x40052E: return_without_free (a.c:6)
==26954== by 0x400556: main (a.c:17)
==26954==
==26954==
==26954== HEAP SUMMARY:
==26954== in use at exit: 1,000 bytes in 1 blocks
==26954== total heap usage: 1 allocs, 0 frees, 1,000 bytes allocated
==26954==
==26954== 1,000 bytes in 1 blocks are definitely lost in loss record 1 of 1
==26954== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26954== by 0x40052E: return_without_free (a.c:6)
==26954== by 0x400556: main (a.c:17)
==26954==
==26954== LEAK SUMMARY:
==26954== definitely lost: 1,000 bytes in 1 blocks
==26954== indirectly lost: 0 bytes in O blocks
==26954== possibly lost: 0 bytes in O blocks
==26954== still reachable: 0 bytes in O blocks
==26954== suppressed: 0 bytes in 0 blocks
==26954==
==26954== For counts of detected and suppressed errors, rerun with: -v
==26954== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
В зависимости от сложности и структуры вашей программы, Valgrind сможет или н сможет выдать полезную информацию о существующих проблемах в коде. Не все проблемы доступа мимо массивов могут быть перехвачены, однако если вы найдете утечку памяти или ошибку сегмента, утилита Valgrind может с минимальными затратами усилий показать места потенциальных ошибок.
Отладка ядра Linux требует другого подхода к отладке вашего приложения [5].
[Поиск багов с использованием опций компилятора]
Существует множество диагностических опций компилятора, которые помогают разобратся с различными ошибками наподобие попыток доступа вне массива, утечек памяти и т. п. Подробнее см. [4].
[Ссылки]
1. How to debug your Linux Application site:bytesnap.com. 2. Debugging Applications site:access.redhat.com. 3. Developing C and C++ applications in RHEL site:access.redhat.com. 4. Опции GCC для поддержки отладки. 5. How to Survive Embedded Linux site:bytesnap.com. |