2011-05-10 97 views
18

我想在我的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输出?

+0

您是否安装了调试libc?如果您在命令行上将-g传递给GCC,IIRC Linux将为此使用带有调试符号的libc。 – 2011-05-10 05:58:56

+0

为什么不使用gdb,我可以问一下吗?此外,GNU libc手册的[Backtraces部分](http://www.gnu.org/s/hello/manual/libc/Backtraces.html)看起来很有用。 – 2011-05-10 06:53:55

回答

0

如果你想要一个非常详细的回溯,你应该使用ptrace(2)跟踪你想要的回溯过程。

你将能够看到您的使用过程中的所有功能,但你需要一些基本的知识汇编

18

是 - 通过-rdynamic标志链接。这将导致链接器在链接表中输出代码中所有非静态函数的名称,而不仅仅是导出的名称。

您支付的价格是您的程序启动时间稍长一点。对于小到中等程序你不会注意到它。你得到的是backtrace()能够给你所有后面跟踪中没有静态函数的名字。

但是当心 - :有你需要知道的几个陷阱:

  1. backtrace_symbols通过malloc分配内存。如果由于malloc竞技场腐败(相当常见)而陷入SIGSEGV,那么您会在这里加倍错误,并且从未看到您的后退痕迹。

  2. 根据运行的平台(例如x86),崩溃的确切函数的地址/函数名将被替换为栈中的信号处理程序的返回地址。您需要从这些平台的信号处理程序参数中获取崩溃函数的正确EIP。

  3. syslog不是异步信号安全功能。发生碰撞时,它可能会在内部采取了锁,如果锁被采取了(因为你在另一个呼叫日志中部坠毁)如果您想了解所有的血淋淋的细节,你有一个死锁

在OLS检查出我的这段视频给人讲了:http://free-electrons.com/pub/video/2008/ols/ols2008-gilad-ben-yossef-fault-handlers.ogg

2
  1. 创建一条管道
  2. 叉()
  3. 使子进程中执行addr2line
  4. 在PA租过程中,从转换回溯返回的地址()为十六进制
  5. 写的十六进制地址管道
  6. 读回从addr2line和打印输出/记录它

既然你做这一切从信号处理程序中,请确保不要使用不是异步信号安全的功能。您可以看到异步信号安全的POSIX函数列表here

2

如果你的罚款通过Valgrind的运行时才会得到适当的回溯,那么这可能是一个选择:

VALGRIND_PRINTF_BACKTRACE(格式,...):

它会给你回溯所有功能,包括静态功能。

4

将地址输入到addr2line,它会显示文件名,行号和函数名称。

1

更好的选择,我发现是libbacktrace由伊恩·兰斯·泰勒:

https://github.com/ianlancetaylor/libbacktrace

backtrace_symbols()不会只打印导出符号和它需要GNU库不能少便携。

addr2line不错,因为它包含文件名和行号。但是,只要加载器执行重定位,它就会失败。现在ASLR很常见,它会经常失败。

libunwind本身不允许打印文件名和行号。为此,需要在ELF二进制文件内解析DWARF调试信息。不过,这可以使用libdwarf来完成。但是当libbacktrace为你提供免费所需的一切时,为什么还要麻烦呢?

0

如果您不想采用“发出运行gdb的其他进程的信号”方法,我认为gby提倡的方法,您还可以稍微更改代码以在崩溃日志文件中调用open()然后用open()返回的fd返回backtrace_symbols_fd() - 根据glibc手册,这两个函数都是异步信号安全的。当然,你仍然需要动态的。另外,从我所看到的情况来看,您仍然有时需要在backtrace *()函数无法解码的某些地址上运行addr2line。

另请注意fork()不是异步信号安全:http://article.gmane.org/gmane.linux.man/1893/match=fork+async,至少在Linux上不是。正如有人已经指出的那样,syslog()也不是。

相关问题