有没有办法从Linux发行版二进制转储堆栈跟踪线号?

当我的c ++ linux应用程序崩溃时,我有要求堆栈跟踪。 我成功地使用backtrace()和backtrace_symbols()来做到这一点。 现在,另外我想获得崩溃的行号。 它是如何做到的?

Solutions Collecting From Web of "有没有办法从Linux发行版二进制转储堆栈跟踪线号?"

我从中获得帮助

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等)。 这样,如果你的程序不幸崩溃,而你没有用调试器运行它,你可以得到一个堆栈跟踪,并知道故障发生的地方。 这种技术也可以用来了解你的程序在哪里停止响应的情况下循环…

下面的代码为trace中的每个地址运行外部程序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 

只有当程序已经被编译了调试信息时(例如使用gcc -Wall -g或使用g++ -Wall -g )。 没有-g可执行文件不包含任何源代码行信息。 如果使用gcc你可以编译优化和调试信息(例如g++ -Wall -g -O2 ),但是有时候线路位置会是“令人惊讶的”。

-Wall标志要求GCC显示所有警告。 这是非常有用的(因此我建议使用它),但与-g或调试信息无关。

至于如何提取行号,最简单的方法是fork一个gdb进程。 或者,您可以获得调试信息( DWARF格式)并解析它,也许使用ELF工具链中的 libdwarf 。 我不知道这是值得的麻烦…

为了得到回溯,你可以简单地运行你的程序,通过gdb或许作为gdb --args yourprogram itsarguments


附加物

你也可以在最近的GCC中使用libbacktrace ,它是为解决你的问题而设计的(它是“解释”当前可执行文件的DWARF格式,你可以用g++ -O -g编译)。

Saqlain指出,addr2line可以用来获取行号。

如果图书馆是首选,请查看LPT套件 。 如何安装说明在这里 。 LPT依赖于bfd库。