Как получить более подробный обратный ход

Я пытаюсь распечатать backtrace, когда моя программа на С++ завершена. Функция backtrace печати функции выглядит следующим образом:

   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?

Ответ 1

Да - передать флаг -rdynamic в компоновщик. Это приведет к тому, что компоновщик выйдет в таблицах ссылок как имя всех статических функций в вашем коде, а не только экспортированных.

Цена, которую вы платите, - это немного более длительное время запуска вашей программы. Для небольших и средних программ вы не заметите этого. Что вы получаете, так это то, что backtrace() может дать вам имя всех статических функций в вашей обратной трассировке.

Однако - BEWARE: есть несколько ошибок, о которых вам нужно знать:

  • backtrace_symbols выделяет память из malloc. Если вы попали в SIGSEGV из-за коррупции в malloc arena (довольно часто), вы тут же будете иметь двойную ошибку и никогда не увидите свою обратную трассировку.

  • В зависимости от платформы, на которой выполняется эта операция (например, x86), имя адреса/функции точной функции, в которой вы разбились, будет заменено на место в стеке с обратным адресом обработчика сигнала. Вам нужно получить правильный EIP от разбитой функции из параметров обработчика сигналов для этих платформ.

  • syslog не является безопасной функцией асинхронного сигнала. Это может занять внутреннюю блокировку, и если эта блокировка будет предпринята, когда произошел сбой (поскольку вы столкнулись в середине другого вызова syslog), у вас есть мертвая блокировка

Если вы хотите узнать все детали gory, ознакомьтесь с этим видео, которое я расскажу об этом в OLS: http://free-electrons.com/pub/video/2008/ols/ols2008-gilad-ben-yossef-fault-handlers.ogg

Ответ 2

Загрузите адреса addr2line, и он покажет вам имя файла, номер строки и имя функции.

Ответ 3

  • Создать канал
  • вилка()
  • Сделать дочерний процесс execute addr2line
  • В родительском процессе конвертируйте адреса, возвращенные из backtrace() в шестнадцатеричные
  • Введите шестнадцатеричные адреса в трубку
  • Считать вывод из addr2line и распечатать/записать его

Поскольку вы все это делаете из обработчика сигналов, не забудьте использовать функциональные возможности, которые не являются безопасными для асинхронных сигналов. Вы можете увидеть список функций POSIX, поддерживающих асинхронный сигнал, здесь.

Ответ 4

Если вам хорошо, когда вы выполняете правильные обратные трассировки при запуске valgrind, это может быть для вас вариант:

VALGRIND_PRINTF_BACKTRACE (формат,...):

Он предоставит вам обратную трассировку для всех функций, включая статические.

Ответ 5

Лучший вариант, который я нашел, - это libbacktrace от Ian Lance Taylor:

https://github.com/ianlancetaylor/libbacktrace

backtrace_symbols() выполняет печать только экспортированных символов и не может быть менее переносимым, так как для него требуется GNU libc.

addr2line хорош, так как включает имена файлов и номера строк. Но он не работает, как только загрузчик выполняет перемещение. В настоящее время как ASLR, если он распространен, он будет терпеть неудачу очень часто.

Только libunwind не позволяет печатать имена файлов и номера строк. Для этого нужно проанализировать информацию об отладке DWARF внутри двоичного файла ELF. Это можно сделать с помощью libdwarf.

Ответ 6

Если вам нужна очень подробная backtrace, вы должны использовать ptrace (2), чтобы проследить процесс, в котором вы хотите получить обратную трассировку.

Вы сможете увидеть все функции, используемые вашим процессом, но вам понадобятся некоторые базовые знания asm

Ответ 7

Если вы не хотите использовать "сигнал другого процесса, который запускает 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(), как уже указывал кто-то.