如何根据HardFault中断抛出的寄存器值排查数组越界

Gitee仓库

bash 复制代码
git clone https://gitee.com/banana-peel-x/freedom-learn.git

项目场景:

实习的时候遇到了函数不可重入问题,场景是上位机与MCU通过udp协议通信,控制MCU测试不同的功能项,频率为1s/次。其中测试CAN老化功能时,需要每秒让CAN控制器进入LoopBack模式,自发自收后上传。测试时偶发跑死,串口抛出异常。

根因:是由于在其他的task里会控制can的模式,在udp的钩子函数里(我的task)里也需要切换can的模式,自测没有问题是两者偶尔各自控制一下硬件没有影响,压测会出现同时控制can,出现HardFault。


问题描述

从本科做项目开始,排查Bug主要靠直觉,小代码量绰绰有余。对于代码量很大的程序却很低效。最后靠部门大佬帮忙解决,大佬一通操作直接看起了汇编,近来吃不好睡不香,故捡起来琢磨一下大佬的排查方法。

首先改进启动文件,startup_stm32f103xb.s:通常出现HardFault异常后会进入HardFault_Handler中断,这里弃用HardFault_Handler函数,改用hard_fault_handler_c函数。
常规

c 复制代码
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler          [WEAK]
                B       .
                ENDP

改进

c 复制代码
HardFault_Handler\
                PROC
                MOVS    r0, #4
                MOV     r1, lr
                TST     r0, r1
                BEQ     stacking_used_MSP
                MRS     r0, psp
                B       get_LR_and_branch
stacking_used_MSP
                MRS     r0, msp
get_LR_and_branch
                MOV     r1, lr
                IMPORT  hard_fault_handler_c
                BL       hard_fault_handler_c  
                ENDP

在hard_fault_handler_c函数中通过Uart抛出寄存器的值

c 复制代码
void hard_fault_handler_c(unsigned int * hardfault_args, unsigned lr_value)
{
  unsigned int stacked_r0;
  unsigned int stacked_r1;
  unsigned int stacked_r2;
  unsigned int stacked_r3;
  unsigned int stacked_r12;
  unsigned int stacked_lr;
  unsigned int stacked_pc;
  unsigned int stacked_psr;
  stacked_r0 = ((unsigned long) hardfault_args[0]);
  stacked_r1 = ((unsigned long) hardfault_args[1]);
  stacked_r2 = ((unsigned long) hardfault_args[2]);
  stacked_r3 = ((unsigned long) hardfault_args[3]);
  stacked_r12 = ((unsigned long) hardfault_args[4]);
  stacked_lr = ((unsigned long) hardfault_args[5]);
  stacked_pc = ((unsigned long) hardfault_args[6]);
  stacked_psr = ((unsigned long) hardfault_args[7]);

  printf ("[Hard fault handler]\r\n");
  printf ("R0 = %x\r\n", stacked_r0);
  printf ("R1 = %x\r\n", stacked_r1);
  printf ("R2 = %x\r\n", stacked_r2);
  printf ("R3 = %x\r\n", stacked_r3);
  printf ("R12 = %x\r\n", stacked_r12);
  printf ("Stacked LR = %x\r\n", stacked_lr);
  printf ("Stacked PC = %x\r\n", stacked_pc);
  printf ("Stacked PSR = %x\r\n", stacked_psr);
  printf ("Current LR = %x\r\n", lr_value);

  while(1); // endless loop
}

复制代码
发生异常后,可以通过串口抛出的值快速定位问题,KEIL在不破坏现场的情况下进入Debug模式。
当中断或者异常发生时,内核会将xPSR、PC、LR、R12、R3、R2、R1、R0这些寄存器按照顺序依次存入堆栈,这些寄存器中的值是中断/异常发生前一刻的值。
    故抛出的PC寄存器存的就是被中断打断的指令的下一条指令地址,所以我们同个定位PC寄存器中地址所指向的代码,就可以精准定位或定位在问题代码附近。

原因分析:

根据Stacked PC = 8002e8c,在汇编中查找抛出异常前代码运行位置,可以看出StackFlow_50ms函数中数组越界。



相关推荐
智商偏低2 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen3 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
望获linux3 小时前
【实时Linux实战系列】CPU 隔离与屏蔽技术
java·linux·运维·服务器·操作系统·开源软件·嵌入式软件
森焱森5 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白5 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D6 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术8 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt9 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘9 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang9 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c