Linux 如何获得更详细的回溯
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5945775/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get more detailed backtrace
提问by korhan
I am trying to print a backtrace when my C++ program terminated. Function printing backtrace is like below;
当我的 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);
}
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?
有没有办法使用函数名称和行(如 gdb 输出)获得更详细的回溯?
回答by Intrepidd
If ou want a very detailled backtrace, you should use ptrace(2) to trace the process you want the backtrace.
如果你想要一个非常详细的回溯,你应该使用 ptrace(2) 来跟踪你想要回溯的过程。
You will be able to see all functions your process used but you need some basic asm knowledge
您将能够看到您的流程使用的所有功能,但您需要一些基本的 asm 知识
回答by gby
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.
是 - 将 -rdynamic 标志传递给链接器。它会导致链接器将代码中所有非静态函数的名称放入链接表,而不仅仅是导出的函数。
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.
您付出的代价是程序的启动时间稍长一些。对于中小型程序,您不会注意到它。您得到的是 backtrace() 能够为您提供回溯中所有非静态函数的名称。
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
backtrace_symbols 从 malloc 分配内存。如果您由于 malloc arena 损坏(很常见)而进入 SIGSEGV,您将在这里出现双重错误并且永远不会看到您的回溯。
根据它运行的平台(例如 x86),您崩溃的确切函数的地址/函数名称将在堆栈中替换为信号处理程序的返回地址。您需要从这些平台的信号处理程序参数中获取崩溃函数的正确 EIP。
syslog 不是异步信号安全功能。它可能会在内部使用一个锁,如果在崩溃发生时使用了该锁(因为您在另一个对 syslog 的调用过程中崩溃了),那么您就有了一个死锁
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
如果您想了解所有血腥细节,请查看我在 OLS 上的视频:http: //free-electrons.com/pub/video/2008/ols/ols2008-gilad-ben-yossef-故障处理程序.ogg
回答by janneb
- Create a pipe
- fork()
- Make child process execute addr2line
- In parent process, convert the addresses returned from backtrace() to hexadecimal
- Write the hex addresses to the pipe
- Read back the output from addr2line and print/log it
- 创建管道
- 叉子()
- 使子进程执行 addr2line
- 在父进程中,将 backtrace() 返回的地址转换为十六进制
- 将十六进制地址写入管道
- 读回 addr2line 的输出并打印/记录它
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.
由于您是通过信号处理程序执行所有这些操作,因此请确保不要使用非异步信号安全的功能。您可以在此处查看异步信号安全 POSIX 函数的列表。
回答by dsvensson
If you're fine with only getting proper backtraces when running through valgrind, then this might be an option for you:
如果您在通过 valgrind 运行时只获得适当的回溯很好,那么这可能是您的一个选择:
VALGRIND_PRINTF_BACKTRACE(format, ...):
VALGRIND_PRINTF_BACKTRACE(格式,...):
It will give you the backtrace for all functions, including static ones.
它将为您提供所有功能的回溯,包括静态功能。
回答by Nemo
回答by Erwan Legrand
The better option I have found is libbacktrace by Ian Lance Taylor:
我发现更好的选择是 Ian Lance Taylor 的 libbacktrace:
https://github.com/ianlancetaylor/libbacktrace
https://github.com/ianlancetaylor/libbacktrace
backtrace_symbols() does prints only exported symbols and could not be less portable as it requires the GNU libc.
backtrace_symbols() 只打印导出的符号,并且因为它需要 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.
addr2line 很好,因为它包含文件名和行号。但是一旦加载器执行重定位,它就会失败。现在ASLR很常见,它会经常失败。
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?
单独的 libunwind 不允许打印文件名和行号。为此,需要解析 ELF 二进制文件中的 DWARF 调试信息。不过,这可以使用 libdwarf 来完成。但是,当 libbacktrace 免费为您提供所需的一切时,何必费心呢?
回答by fencekicker
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.
如果您不想采用“在您身上运行 gdb 的不同进程的信号”方法,我认为 gby 正在提倡这种方法,您还可以稍微更改代码以在崩溃日志文件上调用 open() 然后 backtrace_symbols_fd( ) 与由 open() 返回的 fd - 根据 glibc 手册,这两个函数都是异步信号安全的。当然,您仍然需要 -rdynamic。此外,根据我所见,您有时仍然需要在 backtrace*() 函数无法解码的某些地址上运行 addr2line。
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.
还要注意 fork() 不是异步信号安全的:http: //article.gmane.org/gmane.linux.man/1893/match=fork+async,至少不是在 Linux 上。正如有人已经指出的那样,syslog() 也不是。