查看原文
其他

【收藏】get这些技巧,HardFault_Handler排查只需要几分钟

bug菌 最后一个bug 2022-07-15


1、聊一聊

    今天跟大家推荐的这首歌曲挺有意思的,特别是副歌部分记得加入歌单。

    这篇文章主要是跟大家介绍几种比较使用的方法,用于排除stm32硬件fault,其实bug菌不太喜欢讲一些针对某款固定芯片的特有技术,这里仅仅只是以stm32为例讲解一下如何去处理此类问题,其他芯片效仿即可。

2、仿真定位排查

1

提出问题

    对于使用stm32有一段时间的小伙伴,应该都认识HardFault_Handler了吧,可能大部分都是在程序挂掉了以后才知道有这个中断服务函数,所以大家一般就把它与程序死机困在了一块,原因是里面写了一个死循环。



    其实所谓的异常中断都是通知用户来完成响应操作,在开发阶段一旦有异常触发大部分都会在此处stop,然后观察系统的各状态寄存器等来排查异常触发点,而在实际的产品阶段此处应该是系统软件发生异常的最后补救点

    不过很多小伙伴都没有去修改官方的中断服务例程,当异常发生一脸懵逼不知道该如何下手排查问题,于是便拿jlink一步一步的仿真调试来查看程序在哪个点开始进入异常中断,如果程序不大可能还好排查;如果程序稍微大一点,花个几天时间应该大有人在,这就大大影响了开发的效率。

    同时一些产品被封起来根本无法使用仿真器,而且有些fault可能非常难以复现,所以必须得使用一种自动检测的手段来排查问题。


2

理论依据

    既然bug菌要在这里跟大家介绍一些实用的方法,就一定得有一些理论基础,如果大家对下面的这些知识不熟悉还得好好补充一下:(这里以Cortex-M3为例)

需要补充的知识点:
  • Cortex-M3相关寄存器的作用;

  • Cortex-M3的双堆栈机制;

  • 从用户模式到中断服务例程寄存器的入栈和出栈机制等;

  •  fault 分类以及各自诱因等。

    以上这几点知识大家都可以在<Cortex-M3权威指南>上找到,并且非常的通俗易懂,bug菌就不在这里重复"造轮子"了,如果有些小伙伴平时使用的并不是stm32,所需补充的知识其实大同小异,找到对应芯片内核参考手册,然而根据如上的几个方面进行分析即可。


3

排查原理

   对于常规的仿真器调试一旦程序进入HardFault_Handler,那么程序便卡在了中断服务程序中的死循环中,不太熟悉内核的小伙伴一定希望"如果仿真器能够有程序倒退功能该多好呀",也就说大部分的硬件异常只需找到主程序的进入点基本上就能定位具体的fault原因。

    可惜的是目前仿真器并没有此类功能,程序是一直往下执行的,对于开发人员倒是可以通过编程让程序回到入口点,不过处理相对比较麻烦,不过我们可以通过程序运行的各个状态推导出之前的程序的运行状态。

    然而异常中断本质上和普通的定时器中断等等并无差别,那么在中断触发前必然是要保存现场,运行完中断服务函数以后需要恢复现场,然后继续运行之前的程序,同样当触发HardFault同样需要保存现场,那么完全可以根据系统所保存的现场信息推导出进入异常的入口点。

    那么所有的问题都归结到触发中断系统是如何保存中断现场,要回答这个问题大家得看看上面所提到的几点知识。那么下面作者就以两个开源项目中该部分的处理为大家简单介绍一下如何排查fault。


3、RTT中的处理

    RTT系统中对HardFault_Handler进行了比较详细的处理,基本上可以把这块参考过来,下面bug菌画了个流程图方便大家阅读:(如下图所示)


代码概要分析:
1、代码碎片1

    上面代码实现的是流程图左半部分,很多小伙伴发现r0~r3、r12,lr,pc等等几个寄存器并没有入栈,其实这几个寄存器是硬件上自动压入堆栈中了,不需要我们手动压入。

    为什么需要判断MSP和PSP呢 ? 这个问题大家可以参考<Cortex-M3权威指南>里面的双堆栈机制,一般在RTOS中任务中使用的PSP,而中断中使用的是MSP,但我们进入中断服务函数以后其堆栈指针变成了MSP,为了能够获得任务状态下产生的异常,我们需要找到之前的PSP然后获得其自动入栈的寄存器数据来进行分析,自动入栈的PC和LR都是我们用来定位异常前程序位置的重要寄存器。特别是LR是调用子程序时存储返回地址,从而可以定位发生异常的位置。

2、代码碎片2


    

    上图是调用的异常处理函数,其中参数来自r0寄存器的传递(可以查找ARM的函数调用传参形式),那么这个结构体指针参数应该是与入栈寄存器是一一对应的(如下图所示),这样我们便可以通过该指针获得相应的寄存器数据并打印出来,这样对于一些不能使用仿真器的场合是再好用不过了。



3、代码碎片3

    该部分的处理就是所画流程图的右侧部分实现,其中hard_fault_track函数中主要就是根据具体的每种fault类型寄存器分析fault的原因(其中每个寄存器中的每个位代表什么故障原因都在权威指南中有详细说明)。


    

    好了,那么RTT中对HardFault_Handler的处理Bug菌就讲到这里,其输出的相关信息,通过把源程序仿真查看汇编与C的映射栏进行定位异常前的代码位置,进而进一步分析代码。如下图Keil中的汇编与C映射窗口,可以通过直接查找Code地址来定位C代码。


4、开源故障诊断-"CmBacktrace"

    cmbacktrace是amink开源的一个ARM Cortex-M 系列 MCU 错误追踪库,其可以支持stm32不同系列的内核fault分析,同时也支持不同的RTOS分析,比如RTT,FreeRTOS,Ucos。

      github地址 : https://github.com/armink/CmBacktrace


   

    通过包含如上几个文件即可加入到对应的项目工程用于分析故障,其具体的实现思路是与RTT类似的,这里就不具体分析了。

    不过相对功能比较丰富,比如输出错误现场的函数调用栈,也可以在正常状态下使用该库,获取当前的函数调用栈,从而可以更加详细的了解程序运行情况,大家可以参考学习顺便可以了解一下Cortex内核的相关知识,作者也简单的跑了一下例程,其运行结果如下:


   

5、最后小结

    对于硬件异常故障的排查bug菌就介绍这么多了,出现硬件异常问题大部分的小伙伴都是由于编码不规范、代码的容错机制不够强大导致的,比如数组越界,调用空指针,堆栈溢出等等常见问题,所以大家在调试阶段可以把断言用上便于开发。

    好了,这里是公众号:“最后一个bug”,一个为大家打造的技术知识提升基地。同时非常感谢各位小伙伴的支持,我们下期精彩见!

   如果有想加入公众号群聊共同讨论技术的小伙伴可以添加下方bug菌微信

推荐好文  点击蓝色字体即可跳转

C语言数值常量的“那些事”(细节分析)

【C进阶】这种地方别再强制类型转化了,来告诉你个小技巧!

goto关键字你不知道的"那些事"(C语言提升)

【重磅】剖析MCU的IAP升级软件设计(设计思路篇)

【硬壳】C程序里面嵌点"机器码"玩一玩"(小知识揭露大道理)

【典藏】自制小型GUI界面框架(设计思想篇)

【C进阶】听说用 “ 逗号表达式 ” 仅仅为了秀技?

深度剖析"bit序"与"字节序"

【进阶】嵌入式编程技法之"数据驱动编程"

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存