简体   繁体   English

在C中调用堆栈回溯

[英]Call stack backtrace in C

I am trying to get call stack backtrace at my assert/exception handler. 我正在尝试在我的断言/异常处理程序中获取调用堆栈回溯。 Can't include "execinfo.h" therefore can't use int backtrace(void **buffer, int size); 不能include "execinfo.h"因此不能使用int backtrace(void **buffer, int size); . Also, tried to use __builtin_return_address() but acording to : http://codingrelic.geekhold.com/2009/05/pre-mortem-backtracing.html 另外,尝试使用__builtin_return_address()但根据: http : //codingrelic.geekhold.com/2009/05/pre-mortem-backtracing.html

... on some architectures, including my beloved MIPS, only __builtin_return_address(0) works.MIPS has no frame pointer, making it difficult to walk back up the stack. 在某些架构上,包括我心爱的MIPS,仅__builtin_return_address(0)起作用.MIPS没有帧指针,因此很难向后移动堆栈。 Frame 0 can use the return address register directly. 帧0可以直接使用返回地址寄存器。

How can I reproduce full call stack backtrace? 如何重现完整的调用堆栈回溯?

I have successfully used the method described here , to get a call trace from stack on MIPS32. 我已经成功地使用了这里描述的方法,以从MIPS32的堆栈中获取调用跟踪。

You can then print out the call stack: 然后,您可以打印出调用堆栈:

void *retaddrs[16];
int n, i;

n = get_call_stack_no_fp (retaddrs, 16);

printf ("CALL STACK: ");
for (i = 0; i < n; i++) {
    printf ("0x%08X ", (uintptr_t)retaddrs[i]);
}
printf ("\r\n");

... and if you have the ELF file, then use the addr2line to convert the return addresses to function names: ...,如果您有ELF文件,则使用addr2line将返回地址转换为函数名称:

addr2line -a -f -p -e xxxxxxx.elf addr addr ...

There are of course many gotchas, when using a method like this, including interrupts and exception handlers or results of code optimization. 当使用这样的方法时,当然会有很多陷阱,包括中断和异常处理程序或代码优化的结果。 But nevertheless, it might be helpful sometimes. 但是尽管如此,有时它还是有帮助的。

I have successfully used the method suggested by @Erki A and described here . 我已成功使用@Erki A建议的方法,并在此处进行了介绍。 Here is a short summary of the method: 这是该方法的简短摘要:

The problem: 问题:
get a call stack without a frame pointer. 获取没有框架指针的调用堆栈。 Solution main idea: conclude from the assembly code what the debugger understood from debug info. 解决方案的主要思想:从汇编代码中得出调试器从调试信息中了解的内容。 The information we need: 1. Where the return address is kept. 我们需要的信息: 1.寄信人地址保留在何处。 2. What amount the stack pointer is decremented. 2.堆栈指针递减多少。

To reproduce the whole stack trace one need to: 要重现整个堆栈跟踪,需要:

 1. Get the current $sp and $ra 
 2. Scan towards the beginning of the function and look for "addui 
    sp,sp,spofft" command (spofft<0) 
 3. Reprodece prev. $sp (sp- spofft) 
 4. Scan forward and look for "sw r31,raofft(sp)" 
 5. Prev. return address stored at [sp+ raofft] 

Above I described one iteration. 上面我描述了一个迭代。 You stop when the $ra is 0. How to get the first $ra? 您在$ ra为0时停止。如何获得第一个$ ra?

 __builtin_return_address(0) 

How to get the first $sp? 如何获得第一个$ sp?

 register unsigned sp asm("29");
 asm("" : "=r" (sp));

***Since most of my files compiled with micro-mips optimisation I had to deal with micro-mips-ISA. ***由于我的大多数文件都是使用micro-mips优化进行编译的,因此我不得不处理micro-mips-ISA。 A lot of issues arose when I tried to analyze code that compiled with microMips optimization(remember that the goal at each step is to reproduce prev. ra and prev. sp): It makes things a bit more complicated: 当我尝试分析使用microMips优化编译的代码时,出现了很多问题(请记住,每个步骤的目标都是复制prev。ra和prev。sp):这使事情变得更加复杂:

 1. ra ($31) register contain unaligned return address.
    You may find more information at Linked questions.
    The unaligned ra helps you understand that you run over different 
    ISA(micro-mips-isa) 
 2. There are functions that do not move the sp. You can find more           
    information [here][3].
    (If a "leaf" function only modifies the temporary registers and returns 
    to a return statement in its caller's code, then there is no need for 
    $ra to be changed, and there is no need for a stack frame for that 
    function.)
 3. Functions that do not store the ra 
 4. MicroMips instructions can be both - 16bit and 32bit: run over the 
    commnds using unsinged short*.
 5. There are functions that perform "addiu sp, sp, spofft" more than once
 6. micro-mips-isa has couple variations for the same command
    for example: addiu,addiusp.

I have decided to ignore part of the issues and that is why it works for 95% of the cases. 我决定忽略部分问题,这就是为什么它在95%的情况下都有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM