在应用程序中处理信号时,我可以在调试器中正确地看到回溯信息。但是,回溯系统调用未能正确显示堆栈帧。gdb 存储堆栈帧的方式与回溯系统调用转储它们的方式是否有区别?
首先,因为C标准中没有任何调用堆栈的保证。(可以想象一些C编译器进行整个程序分析并避免使用堆栈,例如如果递归不可能发生;我不知道有这样的C编译器)。请参见这个C FAQ问题以获取奇怪的C实现。
因为编译器有时可能会执行一些优化,例如内联某些调用(甚至是未标记inline
的函数,特别是在使用-flto
传递给gcc
以请求链接时优化时),或者有时会发出尾调用(而GCC两者都可以执行)。您可以禁用优化,但这样会损失很多性能。优化编译器将仅将某些变量放入寄存器中,并重复使用某些堆栈槽来存储多个变量。
-fomit-frame-pointer
编译,那么没有它就无法获取帧信息。libbacktrace
,也可以使用GNU glibc中的backtrace(3)函数;当使用GCC编译时,还可以使用可用的return address builtins。但是所有这些工具可能无法在优化代码上工作。struct
中生成的),要么避免过度优化,例如只使用gcc -g -O1
进行编译。backtrace
不是一个系统调用(列在syscalls(2)中),而是一个glibc
特定的库函数。
请非常仔细地阅读signal(7)和sigreturn(2)。有很少量的(异步信号安全)函数可以可靠地从信号处理程序中调用(直接或间接),backtrace
(或printf
)不在其中。实际上,一个便携式的信号处理程序通常只需要设置一些volatile sigatomic_t
标志,您应该在其他地方测试-或调用siglongjmp(3)。
调试器使用由gcc编译器在使用-g
选项时放入二进制文件中的一组额外数据。这些数据不被回溯调用所使用,只有基本的链接器信息被使用。这意味着例如任何静态数据都无法通过回溯查看,但可以通过gdb查看,这也导致各种优化破坏了回溯,而gdb通过明确的知识来解决这个问题。
请记住,gdb是特定于某种语言和编译器的,而回溯则更具可移植性。
请参阅backtrace的man页面http://linux.die.net/man/3/backtrace:
省略帧指针(如gcc(1)的任何非零优化级别所暗示的那样)可能会导致这些假设被违反。
如果回溯调用想要使用此信息,则必须强制您始终使用调试符号进行编译,并且会有更大的开销和许多其他问题。