如何获得更详细的回溯

时间:2011-05-10 05:53:30

标签: c linux

当我的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 个答案:

答案 0 :(得分:19)

是 - 将-rdynamic标志传递给链接器。它将使链接器在链接表中输出代码中所有非静态函数的名称,而不仅仅是导出的函数。

您支付的价格是您的计划启动时间略长。对于中小型课程,你不会注意到它。你得到的是backtrace()能够给你后面跟踪中所有非静态函数的名称。

但是 - 请注意:您需要注意几个陷阱:

  1. backtrace_symbols从malloc分配内存。如果你因为malloc竞技场腐败(非常常见)而进入SIGSEGV,你会在这里加倍过错而永远不会看到你的背影。

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

  3. syslog不是异步信号安全功能。它可能需要内部锁定,如果在发生崩溃时采取了锁定(因为你在另一次调用syslog的过程中崩溃了)你就有了死锁

  4. 如果您想了解所有血腥细节,请查看我在OLS上发表演讲的视频:http://free-electrons.com/pub/video/2008/ols/ols2008-gilad-ben-yossef-fault-handlers.ogg

答案 1 :(得分:4)

将地址输入addr2line,它会显示文件名,行号和功能名称。

答案 2 :(得分:3)

如果您在通过valgrind运行时只获得适当的回溯,那么这可能是您的选择:

VALGRIND_PRINTF_BACKTRACE(格式,...):

它将为您提供所有功能的回溯,包括静态功能。

答案 3 :(得分:2)

  1. 创建管道
  2. fork()的
  3. 让子进程执行addr2line
  4. 在父进程中,将backtrace()返回的地址转换为十六进制
  5. 将十六进制地址写入管道
  6. 回读addr2line的输出并打印/记录
  7. 由于您是从信号处理程序执行所有操作,因此请确保不要使用非异步信号安全的功能。您可以看到一个async-signal-safe POSIX函数列表here

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

我找到的更好的选择是Ian Lance Taylor的libbacktrace:

https://github.com/ianlancetaylor/libbacktrace

backtrace_symbols()只打印导出的符号,因为它需要GNU libc,所以不能轻易移植。

addr2line很不错,因为它包含文件名和行号。但是只要加载程序执行重定位,它就会失败。如今,由于ASLR很常见,它会经常失败。

单独的libunwind将不允许用户打印文件名和行号。为此,需要解析ELF二进制文件中的DWARF调试信息。但是,这可以使用libdwarf来完成。但是,当libbacktrace为您提供免费所需的一切时,为什么还要费心呢?

答案 6 :(得分:0)

如果你不想接受&#34;信号,那就是在你身上运行gdb的另一个过程&#34;我认为gby提倡的方法,你也可以稍微改变你的代码,在崩溃日志文件上调用open(),然后用open()返回的fd调用backtrace_symbols_fd() - 根据glibc,这两个函数都是异步信号安全的手册。当然,你还需要 - 动态。另外,根据我所看到的情况,您有时仍需要在某些地址上运行addr2line,这些地址是回溯*()函数无法解码的。

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