在ARM cortex m3中调用堆栈展开

时间:2012-07-04 09:08:45

标签: c arm stack-trace cortex-m3 iar

我想创建一个调试工具,它可以帮助我更好地调试我的应用程序。 我正在工作(没有操作系统)。在Atmel的SAM3上使用IAR嵌入式工作台。

我有一个看门狗定时器,在超时的情况下调用特定的IRQ(在发布时将替换为软件复位)。 在IRQ处理程序中,我想打印出(UART)堆栈跟踪,其中发生了Watchdog超时的确切位置。

我查看了网络,但没有找到该功能的任何实现。

任何人都知道如何处理这类事情?

编辑:好的,我设法从堆栈中获取返回地址,因此我确切知道WDT超时发生的位置。 展开整个堆栈并不简单,因为每个函数都会将不同数量的局部变量推入堆栈。

我最终得到的代码就是这个(对于其他人来说,可能会发现它很有用)

void WDT_IrqHandler( void )
{
    uint32_t * WDT_Address;
    Wdt *pWdt = WDT ;
    volatile uint32_t dummy ;
    WDT_Address = (uint32_t *) __get_MSP() + 16 ;
    LogFatal ("Watchdog Timer timeout,The Return Address is %#X", *WDT_Address);
    /* Clear status bit to acknowledge interrupt */
    dummy = pWdt->WDT_SR ;

}

5 个答案:

答案 0 :(得分:4)

ARM定义了一对部分.ARM.exidx和.ARM.extbl,它们包含足够的信息来展开堆栈而不需要调试符号。这些部分用于异常处理,但您也可以使用它们来执行回溯。添加-funwind-tables以强制GCC包含这些部分。

答案 1 :(得分:3)

要使用ARM执行此操作,您需要告诉编译器生成堆栈帧。例如,使用gcc,选中-mapcs-frame选项。它可能不是你需要的,但这将是一个开始。

如果你没有这个,那么“展开”堆栈几乎是不可能的,因为根据参数和局部变量,每个函数都需要精确的堆栈使用。

如果您正在寻找一些示例代码,您可以检查Linux内核源代码中的dump_stack(),并找回为ARM执行的相关代码段。

答案 2 :(得分:2)

我听到了我的名字吗? :)

您可能需要一点内联汇编。只需弄清楚堆栈帧的格式,以及哪个寄存器保存普通的 1 堆栈指针,并将相关值传输到C变量中,您可以从这些变量格式化字符串以输出到UART。

它不应该太棘手,但当然(相当低级)你需要注意细节。

1 与“非例外”一样;实际上,不确定ARM是否有普通代码和异常的堆栈。

答案 3 :(得分:1)

遵循执行应该非常简单。不是以编程方式在你的... ...

我们从ARM ARM了解到,在Cortex-M3上它推动了xPSR, 堆栈上的ReturnAddress,LR(R14),R12,R3,R2,R1和R0。破坏lr,以便它可以检测中断返回,然后调用向量表中列出的入口点。如果你在asm中实现你的isr来控制堆栈,你可以有一个简单的循环来禁用中断源(关闭wdt,无论如何,这需要一些时间)然后进入一个循环来转储一部分的堆。

从该转储中,您将看到lr / return地址,被中断的函数/指令,从程序的反汇编中,您可以看到编译器为每个函数放置在堆栈上的内容,在每个函数中减去舞台并尽可能远地回到你打印的堆栈内容。

你也可以在ram中复制堆栈并在以后解析它而不是在isr中执行此类操作(复制仍然需要花费太多时间,但是比等待uart更少侵入)。

如果你所追求的是被中断的指令的地址,这是最简单的任务,只需从堆栈中读取它,它将在一个已知的位置,然后打印出来。

答案 4 :(得分:0)

您的看门狗定时器可以在任何时刻触发,即使堆栈中没有足够的信息来展开(例如,堆栈空间已经分配给寄存器溢出,但寄存器尚未复制)。

对于正确优化的代码,您需要调试信息,句点。您可以通过监视程序计时器执行的操作是寄存器和堆栈转储,其格式的机器可读性足以允许转换为gdb的核心转储。