Есть ли способ выгрузить трассировку стека с номером строки из двоичного файла релиза Linux?

У меня есть требование сбрасывать следы стека при сбое моего приложения Linux C ++. Мне удалось сделать это с помощью backtrace () и backtrace_symbols (). Теперь, кроме того, я хотел бы получить номера строк сбоя. Как это сделать?

9

Решение

Я получил помощь от

http://www.linuxjournal.com/files/linuxjournal.com/linuxjournal/articles/063/6391/6391l2.html
а также http://www.linuxjournal.com/article/6391?page=0,0 придумать пример кода, который показывает, как вы можете достичь этого.

По сути, речь идет о вставке обратной трассировки стека в обработчик сигналов и в то, чтобы последний перехватывал все «плохие» сигналы, которые может получить ваша программа (SIGSEGV, SIGBUS, SIGILL, SIGFPE и тому подобное). Таким образом, если ваша программа, к сожалению, дает сбой и вы не запускали ее с отладчиком, вы можете получить трассировку стека и узнать, где произошла ошибка. Эту технику также можно использовать, чтобы понять, где зацикливается ваша программа, если она перестает отвечать …

Ниже код запускает внешнюю программу addr2line для каждого адреса в трассировке преобразовать его в имя файла и номер строки.

Исходный код ниже печатает номера строк для всех локальных функций. Если вызывается функция из другой библиотеки, вы можете увидеть пару ??: 0 вместо имен файлов.

#include <stdio.h>
#include <signal.h>
#include <execinfo.h>

void bt_sighandler(int sig, struct sigcontext ctx) {

void *trace[16];
char **messages = (char **)NULL;
int i, trace_size = 0;

if (sig == SIGSEGV)
printf("Got signal %d, faulty address is %p, ""from %p\n", sig, ctx.cr2, ctx.eip);
else
printf("Got signal %d\n", sig);

trace_size = backtrace(trace, 16);
/* overwrite sigaction with caller's address */
trace[1] = (void *)ctx.eip;
messages = backtrace_symbols(trace, trace_size);
/* skip first stack frame (points here) */
printf("[bt] Execution path:\n");
for (i=1; i<trace_size; ++i)
{
printf("[bt] #%d %s\n", i, messages[i]);

char syscom[256];
sprintf(syscom,"addr2line %p -e sighandler", trace[i]); //last parameter is the name of this app
system(syscom);
}

exit(0);
}int func_a(int a, char b) {

char *p = (char *)0xdeadbeef;

a = a + b;
*p = 10;  /* CRASH here!! */

return 2*a;
}int func_b() {

int res, a = 5;

res = 5 + func_a(a, 't');

return res;
}int main() {

/* Install our signal handler */
struct sigaction sa;

sa.sa_handler = (void *)bt_sighandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;

sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
/* ... add any other signal here */

/* Do something */
printf("%d\n", func_b());
}

Этот код должен быть скомпилирован как: gcc sighandler.c -o sighandler -rdynamic

Программа выводит:

Got signal 11, faulty address is 0xdeadbeef, from 0x8048975
[bt] Execution path:
[bt] #1 ./sighandler(func_a+0x1d) [0x8048975]
/home/karl/workspace/stacktrace/sighandler.c:44
[bt] #2 ./sighandler(func_b+0x20) [0x804899f]
/home/karl/workspace/stacktrace/sighandler.c:54
[bt] #3 ./sighandler(main+0x6c) [0x8048a16]
/home/karl/workspace/stacktrace/sighandler.c:74
[bt] #4 /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0x3fdbd6]
??:0
[bt] #5 ./sighandler() [0x8048781]
??:0
13

Другие решения

Это возможно только в том случае, если программа скомпилирована с отладочной информацией (т.е. с gcc -Wall -g или с g++ -Wall -g). Без -g исполняемый файл не содержит никакой информации об исходной строке. И если с помощью gcc вы можете скомпилировать как с оптимизацией & отладочная информация (например, g++ -Wall -g -O2) но иногда расположение линии будет «удивительным».

-Wall флаг просит GCC показать все предупреждения. Это очень полезно (поэтому я рекомендую использовать его), но не связано с -g или отладочная информация.

Что касается того, как извлечь номер строки, самым простым способом было бы раскошелиться gdb процесс. Кроме того, вы можете получить отладочную информацию (в DWARF формат) и разобрать, возможно, используя libdwarf от ELF цепочка для инструментов. Я не уверен, что это стоит того …

Чтобы просто получить обратную трассировку, вы можете просто запустить свою программу через gdb возможно как gdb --args yourprogram itsarguments


добавлений

Вы также можете использовать libbacktrace изнутри недавнего GCC (на самом деле это Обратная связь Яна Тейлора), который предназначен для решения вашей проблемы (это «интерпретация» формата DWARF текущего исполняемого файла, который вы бы скомпилировали g++ -O -g).

3

Как отметил Саклайн, addr2line может использоваться для получения номера строки.

Если библиотека предпочтительнее, взгляните на LPT комплект. Инструкция по установке находится на Вот. LPT опирается на библиотеку bfd.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector