我希望我的异常处理程序和调试函数能够打印调用堆栈回溯,基本上就像glibc中的backtrace()库函数一样。不幸的是,我的C库(Newlib)没有提供这样的调用。
我得到了类似这样的东西:
#include <unwind.h> // GCC's internal unwinder, part of libgcc
_Unwind_Reason_Code trace_fcn(_Unwind_Context *ctx, void *d)
{
int *depth = (int*)d;
printf("\t#%d: program counter at %08x\n", *depth, _Unwind_GetIP(ctx));
(*depth)++;
return _URC_NO_REASON;
}
void print_backtrace_here()
{
int depth = 0;
_Unwind_Backtrace(&trace_fcn, &depth);
}
这基本上是有效的,但产生的跟踪并不总是完整的。例如,如果我这样做了
int func3() { print_backtrace_here(); return 0; }
int func2() { return func3(); }
int func1() { return func2(); }
int main() { return func1(); }
回溯只显示func3()和main()。(这是obv。一个玩具示例,但我检查了反汇编,并确认这些函数在这里都是完整的,没有优化或内联。)
更新:我在旧的ARM7系统上尝试了这个回溯代码,但是使用了相同的(或者至少是尽可能等效的)编译器选项和链接器脚本,它输出了一个正确的、完整的回溯(即func1和func2没有丢失),实际上它甚至回溯到引导初始化代码中。因此,问题大概不在于链接器脚本或编译器选项。(此外,通过反汇编确认在此ARM7测试中也未使用帧指针)。
代码是使用-fomit- frame -pointer编译的,但是我的平台(裸机ARM Cortex M3)定义了一个不使用帧指针的ABI。(这个系统的前一个版本在ARM7上使用了旧的APCS ABI,带有强制堆栈帧和帧指针,以及一个像here这样的回溯,它工作得很好)。
整个系统是用-fexception编译的,这确保了_Unwind使用的必要元数据包含在ELF文件中。(我认为_Unwind是为异常处理而设计的)。
所以,我的问题是:在使用GCC的嵌入式系统中,是否有一种“标准”的、公认的方法来获得可靠的回溯?
如果有必要,我不介意摆弄链接器脚本和crt0代码,但我不想给工具链本身带来任何机会。
谢谢!
发布于 2011-08-05 03:07:27
为此,您需要在某些目标中使用-funwind-tables
或-fasynchronous-unwind-tables
,这是_Unwind_Backtrace
正常工作所必需的!
发布于 2010-08-04 00:57:58
由于ARM平台不使用帧指针,因此您永远不会确切地知道栈帧有多大,并且不能简单地将栈转出到R14中的单个返回值之外。
在调查没有调试符号的崩溃时,我们只需转储整个堆栈,并查找与指令范围内的每一项最接近的符号。它确实会产生大量的误报,但对于调查崩溃仍然非常有用。
如果您运行的是纯ELF可执行文件,则可以将调试符号从发布的可执行文件中分离出来。然后,gdb可以帮助您找出标准unix核心转储中发生的事情。
发布于 2010-08-04 00:40:25
gcc确实做了返回优化。在func1()和func2()中,它不调用func2()/func3(),而是跳转到func2()/func3(),因此func3()可以立即返回到main()。
在您的示例中,func1()和func2()不需要设置堆栈帧,但是如果它们需要设置堆栈帧(例如,对于局部变量),如果函数调用是最后一条指令,则gcc仍然可以进行优化-它然后在跳转到func3()之前清理堆栈。
请看一下生成的汇编代码以了解它。
编辑/更新:
要验证这就是原因,请在函数调用后执行一些无法由编译器重新排序的操作(例如,使用返回值)。或者尝试使用-O0进行编译。
https://stackoverflow.com/questions/3398664
复制相似问题