如何在C中手动迭代堆栈帧?

时间:2014-11-10 09:54:20

标签: c linux gcc gdb backtrace

在处理应用程序中的信号时,我可以正确地看到调试器中的回溯。但是回溯系统调用没有正确显示堆栈帧.gdb存储堆栈帧的方式和回溯系统调用如何转储它们有区别吗?

3 个答案:

答案 0 :(得分:4)

您无法移植遍历C99C11中的堆栈帧。

首先,因为C标准中不保证任何call stack。 (可以想象一些C编译器进行整个程序分析并避免堆栈,如果它没用,例如,如果不能进行递归;我不知道这样的C编译器)。参见例如this C FAQ question用于奇怪的C实现。

然后,因为编译器有时会做一些optimizations,例如inline一些电话(甚至是未标记为inline的函数,特别是在要求link-time optimizations -flto传递给gcc时),或者有时会发出tail-calls (和GCC可以同时执行)。您可以禁用优化,但随后会失去很多性能。优化编译器只会将一些变量放在寄存器中,并为多个变量重用一些堆栈槽。

最后,在某些体系结构(例如32位x86)上,某些代码(特别是某些库代码,例如libc内部)可能会使用-fomit-frame-pointer进行编译,然后无法获取没有它的框架信息。

您可以在libbacktrace内使用Ian Taylor旁边的GCC;您还可以使用backtrace(3)中的GNU glibc函数;您甚至可以在使用return address builtins进行编译时使用GCC。但是所有这些工具可能无法用于优化代码。

实际上,如果你真的需要一些回溯,那么你自己实现(我在我过时的MELT系统中实现了这一点,生成了C ++代码,通过在一些本地struct打包本地人) ,或避免过多优化,例如仅通过gcc -g -O1进行编译。

请注意backtrace system call(在syscalls(2)中列出),而是glibc特定的库函数。

仔细阅读signal(7)sigreturn(2)。很少(异步信号安全)函数可以直接或间接地从信号处理程序中调用,而backtrace(或printf他们。在实践中,便携式信号处理程序通常应该只设置一些volatile sigatomic_t标志,你应该在别处测试 - 或者调用siglongjmp(3)

答案 1 :(得分:0)

当您使用-g选项时,调试器使用gcc编译器将另外一组额外数据放入二进制文件中。回溯调用不使用此数据,仅使用基本链接器信息。这意味着,例如,回溯无法查看任何静态数据,但是通过gdb,这也会导致各种优化打破回溯,gdb通过显式知识解决这些问题。

请记住,gdb特定于某种语言和编译器,而backtrace则更具可移植性。

请参阅backtrace http://linux.die.net/man/3/backtrace的手册页:

  

省略帧指针(如任何gcc(1)的非零优化级别所暗示的那样)可能导致违反这些假设。

如果回溯调用想要使用此信息,则必须强制您始终使用调试符号进行编译,并且会产生更大的开销和许多其他问题。

答案 2 :(得分:0)

问题可能是, 当回溯被驱逐时,你的堆栈可能已经损坏了。

只需在函数中获取局部变量的地址即可。计算堆栈大小。从中添加它 局部变量地址并在加法和位置变量地址之间打印;;

你打印了你的堆栈:)