如何获得更详细的回溯

当我的C ++程序终止时,我试图打印一个回溯。 function打印回溯如下;

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输出?

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

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

但是 – 请注意:您需要注意几个问题:

  1. backtrace_symbols从malloc分配内存。 如果由于malloc竞技场腐败(相当常见)而陷入SIGSEGV,那么在这里你会加倍错误,从来没有看到你的背影。

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

  3. syslog不是一个异步信号安全功能。 它可能会在内部锁定,如果在发生崩溃时执行此锁定(因为您在另一个对syslog的调用中崩溃了),则会出现死锁

如果你想了解所有血腥的细节,看看我的这个视频在OLS的谈话: http : //free-electrons.com/pub/video/2008/ols/ols2008-gilad-ben-yossef-故障handlers.ogg

将地址提交给addr2line ,它将显示文件名,行号和函数名称。

  1. 创建一个管道
  2. 叉子()
  3. 使子进程执行addr2line
  4. 在父进程中,将从backtrace()返回的地址转换为十六进制
  5. 将十六进制地址写入管道
  6. 回读addr2line的输出并打印/记录

由于您是从信号处理程序完成所有这些工作,请确保不要使用不是异步信号安全的功能。 你可以在这里看到一个安全异步信号的POSIX函数列表。

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

VALGRIND_PRINTF_BACKTRACE(格式,…):

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

我发现的更好的选择是Ian Lance Taylor的libbacktrace:

https://github.com/ianlancetaylor/libbacktrace

backtrace_symbols()确实只打印导出的符号,因为它需要GNU libc,所以不会轻便。

addr2line很好,因为它包含文件名和行号。 但是只要加载器执行重定位,它就会失败。 如今如同ASLR一样常常会失败。

libunwind本身不允许打印文件名和行号。 为此,需要在ELF二进制文件中解析DWARF调试信息。 这可以使用libdwarf完成。

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

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

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

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