Fault Trace模块常见问题

1. 如何判断我的程序是不是HardFault了?

A:在没有使用看门狗的情况下,当系统产生HardFault之后,会在中断处理函数中执行死循环,此时包括Bluetooth LE在内的所有模块都无法正常工作,表现出“死了”的效果。此时可以使用J-Link连上芯片来判断是否产生了HardFault。确保硬件连接后,打开J-Link Commander软件,输入connect并回车,然后在提示Please specify device / core时输入cortex-m4并回车,在提示Please specify target interface时输入s并回车,在提示Specify target interface speed [kHz]输入4000并回车。此时J-Link Commander会尝试连接芯片,成功后会提示Cortex-M4 identified,如下图所示:

Img

此时输入h并回车,从IPSR的值可以看到目前所在的中断:

Img

中断号003代表目前处于HardFaultMemManage中,属于HardFault的范畴。

如果使用了看门狗,则很有可能因为看门狗复位导致第一现场丢失,此时就需要借助Fault Trace模块的异常记录功能来判断。在出现Hard Fault后,Fault Trace模块会自动将异常信息存入NVDS中。模块的导入方法以及如何读取存储的错误信息请参考《GR5xx Fault Trace Module应用说明》“使用Fault Trace Module”章节。

2. 为什么我的程序会HardFault?

A:HardFault产生的原因非常多样化,没有一个通用答案。不过可以借助Cortex-M4核心的异常机制并结合一些关键线索进行分析。在使用了Fault Trace模块的cortex-backtrace组件的情况下,HardFault时会自动反推调用栈信息。下面是使用了cortex-backtrace组件后HardFault时的信息输出:

Fault on interrupt or bare metal(no OS) environment
==== Main stack information ====
  addr: 2007fe28    data: 00000000
  addr: 2007fe2c    data: 00207379
  addr: 2007fe30    data: 00000301
  addr: 2007fe34    data: ffffff00
  addr: 2007fe38    data: 00240001
  addr: 2007fe3c    data: 01f40000
  addr: 2007fe40    data: ca3b0100
  addr: 2007fe44    data: 44501f80
  addr: 2007fe48    data: ffff0001
  addr: 2007fe4c    data: ffffffff
  addr: 2007fe50    data: ffffffff
  addr: 2007fe54    data: ffffffff
  addr: 2007fe58    data: ffffffff
  addr: 2007fe5c    data: ffffffff
  addr: 2007fe60    data: ffffffff
  addr: 2007fe64    data: ffffffff
  addr: 2007fe68    data: ffffffff
  addr: 2007fe6c    data: ffffffff
  addr: 2007fe70    data: ffffffff
  addr: 2007fe74    data: ffffffff
  addr: 2007fe78    data: ffffffff
  addr: 2007fe7c    data: ffffffff
  addr: 2007fe80    data: ffffffff
  addr: 2007fe84    data: ffffffff
  addr: 2007fe88    data: ffffffff
  addr: 2007fe8c    data: ffffffff
  addr: 2007fe90    data: ffffffff
  addr: 2007fe94    data: ffffffff
  addr: 2007fe98    data: ffffffff
  addr: 2007fe9c    data: ffffffff
  addr: 2007fea0    data: ffffffff
  addr: 2007fea4    data: ffffffff
  addr: 2007fea8    data: ffffffff
  addr: 2007feac    data: ffffffff
  addr: 2007feb0    data: ffffffff
  addr: 2007feb4    data: ffffffff
  addr: 2007feb8    data: ffffffff
  addr: 2007febc    data: ffffffff
  addr: 2007fec0    data: ffffffff
  addr: 2007fec4    data: ffffffff
  addr: 2007fec8    data: ffffffff
  addr: 2007fecc    data: ffffffff
  addr: 2007fed0    data: ffffffff
  addr: 2007fed4    data: ffffffff
  addr: 2007fed8    data: ffffffff
  addr: 2007fedc    data: ffffffff
  addr: 2007fee0    data: ffffffff
  addr: 2007fee4    data: ffffffff
  addr: 2007fee8    data: 20003384
  addr: 2007feec    data: 00000000
  addr: 2007fef0    data: 2000d46e
  addr: 2007fef4    data: 0020747d
  addr: 2007fef8    data: 00000002
  addr: 2007fefc    data: 00000000
  addr: 2007ff00    data: 00000000
  addr: 2007ff04    data: 00000007
  addr: 2007ff08    data: 00000001
  addr: 2007ff0c    data: 00000000
  addr: 2007ff10    data: 2000341c
  addr: 2007ff14    data: 00000008
  addr: 2007ff18    data: 002073b5
  addr: 2007ff1c    data: 00000008
  addr: 2007ff20    data: 00000e01
  addr: 2007ff24    data: 00209901
  addr: 2007ff28    data: 2000d450
  addr: 2007ff2c    data: 00209891
  addr: 2007ff30    data: 0000004a
  addr: 2007ff34    data: b0000000
  addr: 2007ff38    data: 00000002
  addr: 2007ff3c    data: 200012f4
  addr: 2007ff40    data: 00000e01
  addr: 2007ff44    data: 00000008
  addr: 2007ff48    data: ffffffff
  addr: 2007ff4c    data: 00040ee3
  addr: 2007ff50    data: 00000002
  addr: 2007ff54    data: 20001478
  addr: 2007ff58    data: 00000000
  addr: 2007ff5c    data: 0007e917
  addr: 2007ff60    data: 00000000
  addr: 2007ff64    data: 20001c9c
  addr: 2007ff68    data: 0007e948
  addr: 2007ff6c    data: 0007e948
  addr: 2007ff70    data: ffffffff
  addr: 2007ff74    data: 00040213
  addr: 2007ff78    data: 00000000
  addr: 2007ff7c    data: 002107bc
  addr: 2007ff80    data: 00000000
  addr: 2007ff84    data: 000817d1
  addr: 2007ff88    data: 00000000
  addr: 2007ff8c    data: 000817b1
  addr: 2007ff90    data: 002107bc
  addr: 2007ff94    data: ffffffe9
  addr: 2007ff98    data: 00000000
  addr: 2007ff9c    data: 00000000
  addr: 2007ffa0    data: 00000001
  addr: 2007ffa4    data: 00000003
  addr: 2007ffa8    data: ffffffff
  addr: 2007ffac    data: 0020b8c5
  addr: 2007ffb0    data: 0020b8c4
  addr: 2007ffb4    data: 61000000
  addr: 2007ffb8    data: 20003858
  addr: 2007ffbc    data: 0010747d
  addr: 2007ffc0    data: 00000001
  addr: 2007ffc4    data: 000996cb
  addr: 2007ffc8    data: 4000d000
  addr: 2007ffcc    data: 20003858
  addr: 2007ffd0    data: 20008170
  addr: 2007ffd4    data: 00106d3b
  addr: 2007ffd8    data: 00000001
  addr: 2007ffdc    data: 00000001
  addr: 2007ffe0    data: 00000000
  addr: 2007ffe4    data: 00106423
  addr: 2007ffe8    data: 200037c0
  addr: 2007ffec    data: 0009785d
  addr: 2007fff0    data: 002107bc
  addr: 2007fff4    data: 002107bc
  addr: 2007fff8    data: 00000000
  addr: 2007fffc    data: 0020b8c5
=========
==== Registers information =====
  R0 : 00123456     R1 : deadbeef
  R2 : 00000000     R3 : 00000000
  R12: 00000000     LR : 00077ded
  PC : 00203ca8     PSR: 61000011
=========
Fault reason:
Bus fault: imprecise data access violation
Call stack info : 00203ca8<--00077de9<--00207375<--000002fd<--00207479<--00000003<--002073b1<--00000dfd<--002098fd<--0020988d<--00000dfd<--00040edf<--0004020f<--0020b8c1<--0020b8c1<--

cortex-backtrace主要打印了4个信息:栈数据关键寄存器异常类型调用栈

其中,栈数据可以结合汇编进行场景重现,对于某些由数据错误引起的HardFault调试有帮助(例如除0、空指针等等)。

关键寄存器包括:

  • 用于传参、存储返回值和临时数据的R0-R3寄存器

  • 用于过程间临时传参的**R12 (IP)**寄存器

  • 用于存储返回地址的LR寄存器

  • 用于存储当前运行地址的PC寄存器

  • 用于存储运算标志位、中断号及运行状态的PSR寄存器

通过PC寄存器可以知道异常具体发生在哪个位置。可以通过汇编文件(.s文件或是.asm文件)去查对应地址的汇编指令,也可以使用GCC工具链提供的addr2line工具来获取地址对应到哪个源文件中的哪一行。有时候可以看到PC的值是一个奇数,这是Cortex-M4核心的Thumb模式导致的,此时把地址值-1即是正确的地址。

异常类型是对Cortex-M4核心的**CFSR(Congifurable Fault Status Register)**寄存器的解析。该寄存器是Cortex-M4核心用于指示HardFault产生原因的寄存器。HardFault又被细分为3种类型:Usage FaultBus FaultMemory Management Fault。具体类型以及指示内容的具体含义请参考《Cortex-M4 Devices Generic User Guide》“Configurable Fault Status Register”章节。

调用栈是Cortex Backtrace模块通过对寄存器、运行程序以及栈数据的分析和反推得到的调用关系链。和PC寄存器的值一样,结合汇编文件或者addr2line工具可以对整个调用链条进行定位。

3. 如果不使用Cortex Backtrace组件,能分析上面的内容吗?

A:可以。Cortex Backtrace的主要功能是反推调用栈,如果不使用Cortex Backtrace组件,只使用Fault Trace的异常记录功能,则可以在Hard Fault发生后,使用GRToolbox或GProgrammer读取芯片内存储的异常信息,例如:

HARDFAULT CALLSTACK INFO: R0-00123456 R1-DEADBEEF R2-00000000 R3-00000000 R12-00000000 LR-00077DED PC-00203C00 XPSR-61000011

由于不使用Cortex Backtrace组件,Fault Trace就只会记录关键寄存器的信息。此时同样可以通过异常前PCLR的值结合汇编文件或addr2line来分析。

4. 如果连Fault Trace模块也不使用,能分析HardFault吗?

A:可以。但如果不使用Fault Trace模块,则需要保留Hard Fault现场以获取必要信息。在保留现场的情况下,可以使用J-Link连接芯片进行分析,具体流程为:

  1. 首先通过LR寄存器的值判断异常发生现场使用的栈指针:

| 中断前使用FPU | 中断前未使用FPU — | — | — 中断前在Handler Mode,使用MSP | 0xFFFFFFE1 | 0xFFFFFFF1 中断前在Thread Mode,使用MSP | 0xFFFFFFE9 | 0xFFFFFFF9 中断前在Thread Mode,使用PSP | 0xFFFFFFED | 0xFFFFFFFD

同样以之前的Hard Fault为例,使用J-Link连接上芯片后读取现场:

Img

从图中可得,异常发生之前使用的栈指针为MSP = 0x2007FE08

  1. 在确定栈指针后,从栈指针处读8个word(32Byte)。Cortex-M4在中断发生时的压栈顺序为:xPSR, PC, LR, R12, R3, R2, R1, R0,则读取到的8个word的含义是反过来的。其中第6个word为异常前的PC,第7个word为异常前的LR。通过这两个值就可以反推出异常发生前的位置。此处使用J-Link Commander的mem32指令进行读取:

Img

从图中可得,LR = 0x00077DEDPC = 0x00203E0C

结合汇编文件或使用addr2line工具可以定位到具体的位置,进行分析。

5. 得到的PC指向了一个莫名其妙的地方是怎么回事?

A:有几种可能。第一,可能是调用了非法地址,此时比起PC更需要关心LR的值,即在跳转之前的位置,例如函数指针为空或为非法值时的跳转。第二,可能MSP和PSP搞反了,这样反推得到的PC和LR就是错误的值。第三,对于GR5xx SoC有些代码被固化到ROM中,有些情况下(例如非法参数)可能会导致ROM中的代码产生Hard Fault,此时PC值在0x000000000x000FFFFF之间(不同型号SoC的ROM地址范围不同,具体范围请参考对应型号的Datasheet)。

6. 读取不了栈上的数据怎么办?

  1. 请确保芯片已经被Halt住,即在J-Link连上后需要先输入h让芯片停止运行。

  2. 检查所读取的栈指针和所要读取的长度是否在正确的RAM范围内。

  3. 如果上述步骤都不能解决问题,则有可能是芯片存在硬件异常,请检查供电及各个关键电源信号是否符合要求,或联系FAE获取技术支持。