Как получить более подробную обратную трассировку
Я пытаюсь напечатать обратную трассировку, когда моя программа на C++ завершена. Функция обратного следа печати как ниже;
void print_backtrace(void){
void *tracePtrs[10];
size_t count;
count = backtrace(tracePtrs, 10);
char** funcNames = backtrace_symbols(tracePtrs, count);
for (int i = 0; i < count; i++)
syslog(LOG_INFO,"%s\n", funcNames[i]);
free(funcNames);
}
Это дает вывод как;
desktop program: Received SIGSEGV signal, last error is : Success
desktop program: ./program() [0x422225]
desktop program: ./program() [0x422371]
desktop program: /lib/libc.so.6(+0x33af0) [0x7f0710f75af0]
desktop program: /lib/libc.so.6(+0x12a08e) [0x7f071106c08e]
desktop program: ./program() [0x428895]
desktop program: /lib/libc.so.6(__libc_start_main+0xfd) [0x7f0710f60c4d]
desktop program: ./program() [0x4082c9]
Есть ли способ получить более детальную обратную трассировку с именами функций и строками, например, с выводами GDB?
7 ответов
Да - передать флаг -rdynamic компоновщику. Это заставит компоновщик выводить в таблицах ссылок имена всех не статических функций в вашем коде, а не только экспортируемых.
Цена, которую вы платите, - это немного более длительное время запуска вашей программы. Для небольших и средних программ вы не заметите это. Вы получаете то, что backtrace() может дать вам имя всех не статических функций в вашей обратной трассировке.
Однако - ВНИМАНИЕ: есть несколько ошибок, о которых нужно знать:
backtrace_symbols выделяет память из malloc. Если вы попали в SIGSEGV из-за повреждения malloc arena (довольно часто), вы здесь дважды ошибетесь и никогда не увидите свой обратный след.
В зависимости от платформы, на которой он работает (например, x86), адрес / имя функции точной функции, в которой произошел сбой, будет заменен на месте в стеке адресом возврата обработчика сигнала. Вам необходимо получить правильное EIP сбойной функции из параметров обработчика сигналов для этих платформ.
Системный журнал не является функцией безопасности асинхронного сигнала. Это может занять внутреннюю блокировку, и если эта блокировка была взята при возникновении сбоя (потому что вы потерпели крах во время другого вызова системного журнала), у вас есть мертвая блокировка
Если вы хотите узнать все кровавые подробности, посмотрите это видео, где я рассказываю об этом на OLS: http://free-electrons.com/pub/video/2008/ols/ols2008-gilad-ben-yossef-fault-handlers.ogg
Если вам хорошо, когда вы проходите через valgrind только правильные трассировки, то это может быть вариант для вас:
VALGRIND_PRINTF_BACKTRACE (формат, ...):
Это даст вам обратную трассировку для всех функций, включая статические.
Лучший вариант, который я нашел, это libbacktrace от Ian Lance Taylor:
https://github.com/ianlancetaylor/libbacktrace
backtrace_symbols () печатает только экспортированные символы и не может быть менее переносимым, поскольку требует GNU libc.
addr2line хорош тем, что содержит имена файлов и номера строк. Но это терпит неудачу, как только загрузчик выполняет перемещения. В настоящее время, поскольку ASLR является распространенным явлением, он очень часто дает сбой.
Одна только libunwind не позволит печатать имена файлов и номера строк. Для этого необходимо проанализировать отладочную информацию DWARF внутри двоичного файла ELF. Это можно сделать с помощью libdwarf. Но зачем беспокоиться, когда libbacktrace дает вам все необходимое бесплатно?
- Создать трубу
- вилка ()
- Заставить дочерний процесс выполнить addr2line
- В родительском процессе конвертируйте адреса, возвращаемые из backtrace() в шестнадцатеричные
- Запишите шестнадцатеричные адреса в трубу
- Прочтите вывод из addr2line и распечатайте / запишите его
Поскольку вы делаете все это из обработчика сигнала, убедитесь, что вы не используете функциональность, которая не безопасна для async-signal. Вы можете увидеть список асинхронно-безопасных POSIX-функций здесь.
Если вы не хотите использовать подход "сигнализировать о другом процессе, который запускает на вас gdb", который, как мне кажется, пропагандирует gby, вы также можете слегка изменить свой код для вызова open() в файле журнала сбоев, а затем backtrace_symbols_fd() с fd, возвращаемым open() - обе функции защищены от асинхронного сигнала согласно руководству glibc. Тебе, конечно, понадобится еще динамичный. Кроме того, из того, что я видел, вам все еще иногда нужно запустить addr2line для некоторых адресов, которые функции backtrace*() не смогут декодировать.
Также обратите внимание, что fork() не является безопасным для асинхронного сигнала: http://article.gmane.org/gmane.linux.man/1893/match=fork+async, по крайней мере, в Linux. Это также не syslog(), как кто-то уже указывал.
Если вам нужна очень детализированная обратная трассировка, вы должны использовать ptrace(2), чтобы отследить процесс, который вам нужен.
Вы сможете увидеть все функции, используемые в вашем процессе, но вам нужны базовые знания asm