如何获得更详细的回溯
当我的 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 输出?
I am trying to print a backtrace when my C++ program terminated. Function printing backtrace is like below;
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);
}
It gives an output like ;
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]
Is there a way to get more detailed backtrace with function names and lines, like gdb outputs?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
是 - 将 -rdynamic 标志传递给链接器。它将导致链接器将代码中所有非静态函数的名称放入链接表中,而不仅仅是导出的函数。
您付出的代价是程序的启动时间稍微长一些。对于中小型程序,您不会注意到它。您得到的是 backtrace() 能够为您提供回溯中所有非静态函数的名称。
但是 - 请注意:您需要注意几个问题:
backtrace_symbols 从 malloc 分配内存。如果您由于 malloc arena 损坏(很常见)而进入 SIGSEGV,您将在这里出现双重错误,并且永远不会看到您的回溯。
根据运行的平台(例如 x86),崩溃的确切函数的地址/函数名称将在堆栈上替换为信号处理程序的返回地址。您需要从这些平台的信号处理程序参数中获取崩溃函数的正确 EIP。
syslog 不是异步信号安全函数。它可能会在内部获取锁,如果在崩溃发生时获取该锁(因为您在另一次调用 syslog 的过程中崩溃),那么您就会遇到死锁
如果您想了解所有血淋淋的细节,请观看我的这段视频在 OLS 上发表演讲:http://free-electrons.com/pub/video/2008/ols/ols2008-gilad-ben-yossef-fault-handlers.ogg
Yes - pass the -rdynamic flag to the linker. It will cause the linker to put in the link tables the name of all the none static functions in your code, not just the exported ones.
The price you pay is a very slightly longer startup time of your program. For small to medium programs you wont notice it. What you get is that backtrace() is able to give you the name of all the none static functions in your back trace.
However - BEWARE: there are several gotchas you need to be aware of:
backtrace_symbols allocates memory from malloc. If you got into a SIGSEGV due to malloc arena corruption (quite common) you will double fault here and never see your back trace.
Depending on the platform this runs on (e.g. x86), the address/function name of the exact function where you crashed will be replaced in place on the stack with the return address of the signal handler. You need to get the right EIP of the crashed function from the signal handler parameters for those platforms.
syslog is not an async signal safe function. It might take a lock internally and if that lock is taken when the crash occurred (because you crashed in the middle of another call to syslog) you have a dead lock
If you want to learn all the gory details, check out this video of me giving a talk about it at OLS: http://free-electrons.com/pub/video/2008/ols/ols2008-gilad-ben-yossef-fault-handlers.ogg
将地址输入 addr2line ,它将显示文件名、行号、和函数名称。
Feed the addresses to addr2line and it will show you the file name, line number, and function name.
如果您愿意在运行 valgrind 时仅获得正确的回溯,那么这可能是您的一个选择:
VALGRIND_PRINTF_BACKTRACE(format, ...):
它将为您提供所有函数的回溯,包括静态函数。
If you're fine with only getting proper backtraces when running through valgrind, then this might be an option for you:
VALGRIND_PRINTF_BACKTRACE(format, ...):
It will give you the backtrace for all functions, including static ones.
我发现更好的选择是 Ian Lance Taylor 的 libbacktrace:
https://github.com/ianlancetaylor/libbacktrace
backtrace_symbols() 只打印导出的符号,并且由于它需要 GNU libc,因此可移植性不能降低。
addr2line 很好,因为它包含文件名和行号。但一旦加载器执行重定位,它就会失败。现在ASLR已经很普遍了,所以经常会失败。
libunwind 本身不允许打印文件名和行号。为此,需要解析 ELF 二进制文件中的 DWARF 调试信息。不过,这可以使用 libdwarf 来完成。但是当 libbacktrace 免费为您提供所需的一切时,为什么还要麻烦呢?
The better option I have found is libbacktrace by Ian Lance Taylor:
https://github.com/ianlancetaylor/libbacktrace
backtrace_symbols() does prints only exported symbols and could not be less portable as it requires the GNU libc.
addr2line is nice as it includes file names and line numbers. But it fails as soon as the loader performs relocations. Nowadays as ASLR is common, it will fail very often.
libunwind alone will not allow one to print file names and line numbers. To do this, one needs to parse DWARF debugging information inside the ELF binary file. This can be done using libdwarf, though. But why bother when libbacktrace gives you everything required for free?
因为您正在执行所有这些操作从信号处理程序中,确保不使用非异步信号安全的功能。您可以在此处查看异步信号安全 POSIX 函数的列表。
Since you're doing all this from a signal handler, make sure to not use functionality which is not async-signal-safe. You can see a list of async-signal-safe POSIX functions here.
如果您不想采用“向在您上运行 gdb 的不同进程发出信号”的方法(我认为 gby 正在提倡这种方法),您还可以稍微更改代码以在崩溃日志文件上调用 open() ,然后调用 backtrace_symbols_fd( ) 和 open() 返回的 fd - 根据 glibc 手册,这两个函数都是异步信号安全的。当然,您仍然需要 -rdynamic。另外,据我所知,有时您仍然需要在 backtrace*() 函数无法解码的某些地址上运行 addr2line 。
另请注意 fork() 不是异步信号安全的: http:// Article.gmane.org/gmane.linux.man/1893/match=fork+async,至少在 Linux 上不是。正如有人已经指出的那样,syslog() 也不是。
If you don't want to take the "signal a different process that runs gdb on you" approach, which I think gby is advocating, you can also slightly alter your code to call open() on a crash log file and then backtrace_symbols_fd() with the fd returned by open() - both functions are async signal safe according to the glibc manual. You'll need still -rdynamic, of course. Also, from what I've seen, you still sometimes need to run addr2line on some addresses that the backtrace*() functions won't be able to decode.
Also note fork() is not async signal safe: http://article.gmane.org/gmane.linux.man/1893/match=fork+async, at least not on Linux. Neither is syslog(), as somebody already pointed out.
如果您想要非常详细的回溯,您应该使用 ptrace(2) 来跟踪您想要回溯的进程。
您将能够看到您的进程使用的所有函数,但您需要一些基本的汇编知识
If ou want a very detailled backtrace, you should use ptrace(2) to trace the process you want the backtrace.
You will be able to see all functions your process used but you need some basic asm knowledge