четверг, 19 августа 2010 г.

Разделение отладочной информации и исполняемого файла в GDB

В GDB существует способ разделения отладочной информации и исполняемого файла.
Это актуально если программа не желает поставлять отладочную информацию вместе с бинарником.
Имеется возможность postmortem-отладки таких бинарников с последующей загрузкой креш-дампа и анализа как обычных с отладочной инфой.
Рассмотрим пример:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void bar()
{
   const char* crash_dir="/var/crash/";
   chdir(crash_dir);
   //По хорошему нужно написать функцию для определения имени краш-дампа
   //по анализу /proc/sys/kernel/core_pattern
   fprintf(stderr,"Created core dump %score.%d\n",crash_dir,getpid());
   abort();
}

void foo()
{
   int stack_variable=-1;
   bar();   
}

int main(int argc,char** argv)
{
   foo();
   return 0;
}

собираем наш пример:
gcc test.c -o test -g -O0

копируем отладочную информацию в отдельный файл test.debug:
objcopy --only-keep-debug test test.debug

удаляем отладочную информацию из основного файла:
strip -g test

добавляем ссылку на отладочную информацию в релиз файл:
objcopy --add-gnu-debuglink=test.debug test

В данном примере используется способ разделения отладочной информации с использованием gnu.debuglink. В результирующем файле будет ссылка на файл с отладочной информацией. Для того чтобы посмотреть что находится в секции файла .gnu_debuglink, можно воспользоваться коммандой:
objdump -s --section=.gnu_debuglink ./test
Мы увидим примерно следующее:
Contents of section .gnu_debuglink:
 0000 74657374 2e646562 75670008 e179394b  test.debug...y9K

запрашиваемая секция состоит из 3х частей:
1) имя файла с отладочной инфой к которому прилинкован бинарник - zero-terminated строка
2) затем выравнивание от 0 до 3х нулевых байт до следующих 4х байт
3) 4 байта crc от {полного содержимого файла с отладочной информацией}, которую gdb использует для проверки подргужаемого файла

Запустим наше приложение:
ulimit -c unlimited && ./test
Created core dump /var/crash/core.13708
Аварийный останов (core dumped)

С помощью команды ulimit мы указали размер допустимого креш дампа, приложение может также использовать setrlimit для указания данного лимита программно.

Проверка подргузки отладочных символов:
gdb --quiet --core=/var/crash/core.13708 ./test

Using host libthread_db library "/lib/i686/libthread_db.so.1".

Reading symbols from /lib/i686/libc.so.6...done.
Loaded symbols for /lib/i686/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `./test'.
Program terminated with signal 6, Aborted.
#0  0xffffe410 in __kernel_vsyscall ()

При отладке с использованием gdb указавается обычный бинарник который получает пользователь, только gdb 'магическим' способом находит отладочную инофрмацию в прилинкованном файле. Проверим, как выглядит стек упавшей программы через bactrace full:
(gdb) backtrace full
#0  0xffffe410 in __kernel_vsyscall ()
No symbol table info available.
#1  0xb7e69c00 in raise () from /lib/i686/libc.so.6
No symbol table info available.
#2  0xb7e6b668 in abort () from /lib/i686/libc.so.6
No symbol table info available.
#3  0x08048477 in bar () at test.c:11
        crash_dir = 0x8048570 "/var/crash/"
#4  0x08048489 in foo () at test.c:17
        stack_variable = -1
#5  0x080484a1 in main () at test.c:22
No locals.
(gdb)
Мы видим все что было в стеке до падения. Правда libc у меня собрана без отладочной информации поэтому никаких переменных в стеке и информации о исходниках в пространстве этой библиотеки мы не увидим.

Имя файла с отладочной информацией может быть любым, но обычно имеет суффикс: .debug
gdb ищет файл с отладочной информацией сначала рядом с бинарником, затем в директории .debug
а затем внутри глобальной директории для отладки по пути с тем же именем из которого запущена программа.

Например, если есть бинарник /tmp/example/test то поиск бинарника с отладочной инфой test.debug будет произведен по путям:
/tmp/example/test.debug
/tmp/example/.debug/test.debug
/usr/lib/debug/tmp/example/test.debug где /usr/lib/debug глобальная директория для отладки

Для того чтобы узнать глобальную директорию для отладки нужно выполнить внутрях gdb:
show debug-file-directory
Для того чтобы сменить её используется команда set var_name = value.

Безусловно данный пример иллюстрирует возможности gdb с лучшей стороны, можно использовать данную технику для проприетарных приложений с закрытыми исходниками а также тестовых билдов где не критична сборка с оптимизацией. Можно отслеживать создание креш-дампов в обработчиках сигналов SIGSEGV или SIGBUS для того чтобы отловить падение программы, и отправить краш-дамп разработчикам. Единственная проблема это размер креш-дампов, но они хорошо жмутся с помощью gzip.

Комментариев нет:

Отправить комментарий