当 Cortex-M3 出现硬故障时如何保留堆栈跟踪?

Posted

技术标签:

【中文标题】当 Cortex-M3 出现硬故障时如何保留堆栈跟踪?【英文标题】:How to retain a stacktrace when Cortex-M3 gone in hardfault? 【发布时间】:2015-09-24 03:55:48 【问题描述】:

使用以下设置:

基于 Cortex-M3 的 µC gcc-arm cross toolchain 使用 C 和 C++ FreeRtos 7.5.3 日食月神 使用 JLinkGDBServer 分离 Jlink Code Confidence FreeRtos debug plugin

使用 JLinkGDBServer 和 eclipse 作为调试前端,我在单步执行代码时总是有一个很好的堆栈跟踪。使用 Code Confidence freertos 工具(eclipse 插件)时,我还看到了当前未运行的所有线程的堆栈跟踪(没有该插件,我只看到活动线程的堆栈跟踪)。到目前为止一切顺利。

但是现在,当我的应用程序遇到硬故障时,堆栈跟踪就会丢失。 好吧,我知道如何找出导致硬故障的代码地址的技术(如here 所示)。 但与完整的堆栈跟踪相比,这是非常糟糕的信息。

好的,有时当陷入硬故障时,无法保留堆栈跟踪,例如当堆栈被错误代码破坏时。但是如果堆栈是健康的,我认为获得堆栈跟踪是可能的(不是吗?)。

我认为在硬故障中丢失堆栈跟踪的原因是堆栈指针会被 Cortex-M3 架构自动从 PSP 切换到 MSP。现在的一个想法是,(可能)将 MSP 设置为之前的 PSP 值(并且可能需要做一些额外的堆栈准备?)。

关于如何在发生硬故障时保留堆栈跟踪的任何建议或其他方法?

编辑 2015-07-07,添加更多细节。

我使用这段代码来挑起一个硬故障:

__attribute__((optimize("O0"))) static void checkHardfault() 
    volatile uint32_t* varAtOddAddress = (uint32_t*)-1;
    (*varAtOddAddress)++;

当进入checkHardfault() 时,我的堆栈跟踪看起来像这样:

gdb-> backtrace
#0  checkHardfault () at Main.cxx:179
#1  0x100360f6 in GetOneEvent () at Main.cxx:185
#2  0x1003604e in executeMainLoop () at Main.cxx:121
#3  0x1001783a in vMainTask (pvParameters=0x0) at Main.cxx:408
#4  0x00000000 in ?? ()

当遇到硬故障((*varAtOddAddress)++;)并发现自己在 HardFault_Handler() 内时,堆栈跟踪是:

gdb-> backtrace
#0  HardFault_Handler () at Hardfault.c:312
#1  <signal handler called>
#2  0x10015f36 in prvPortStartFirstTask () at freertos/portable/GCC/ARM_CM3/port.c:224
#3  0x10015fd6 in xPortStartScheduler () at freertos/portable/GCC/ARM_CM3/port.c:301
Backtrace stopped: previous frame inner to this frame (corrupt stack?)

【问题讨论】:

我可以给你一个STM32的解决方案,它是基于Cortex的。 查看我在类似问题上给出的几个答案,here 和 here(这个更详细一点,因为它指的是您问题范围之外的特定问题) . 如果内存服务正常,那么 PC 和 LR 在中断发生之前将最后两个函数的地址存储在调用堆栈中,R0 到 R3 存储传递给这些函数的参数。跨度> 您建议的解决方案看起来与freertos.org 中描述的相同(正如我在问题中提到的那样)。它只是给PC 留下了一个提示,这最终导致了硬故障(我现在从你的评论中了解到LR 内还有一个调用级别),但它不会提供堆栈跟踪。 @Joe 你能在这方面取得进展吗?事实证明,我也有类似的情况 【参考方案1】:

让调试器向您提供硬故障之前状态的详细信息的最快方法是将处理器返回到硬故障之前的状态。

在调试器中,编写一个脚本,从各种硬件寄存器中获取信息,并将 PC、LR、R0-R14 恢复到导致硬故障之前的状态,然后执行堆栈转储。

当然,当您因为从损坏的堆栈中弹出东西或踩到内存中的东西而最终遇到硬故障时,这并不总是有用的。您通常倾向于破坏一堆重要的寄存器,返回到内存中的某个疯狂点,然后执行那里的任何内容。在真正的问题发生后,您最终可能会在数千(数百万?)个周期内出现硬故障。

【讨论】:

【参考方案2】:

考虑使用以下 gdb 宏来恢复寄存器内容:

define hfstack
    set $frame_ptr = (unsigned *)$sp
    if $lr & 0x10
        set $sp = $frame_ptr + (8 * 4)
    else
        set $sp = $frame_ptr + (26 * 4)
    end
    set $lr = $frame_ptr[5]
    set $pc = $frame_ptr[6]
    bt
end

document hfstack
set the correct stack context after a hard fault on Cortex M
end

【讨论】:

以上是关于当 Cortex-M3 出现硬故障时如何保留堆栈跟踪?的主要内容,如果未能解决你的问题,请参考以下文章

LPC11xx Cortex-M0 FreeRTOS 硬故障

Arm Cortex-M4 LDRD 指令导致硬故障

关于 Cortex-M3 的双堆栈机制

Cortex-M3/M4/M7 故障异常分析

fmodf() 是不是会导致 stm32 出现硬故障?

尝试使用 vTaskList() 列出所有任务时出现硬故障