CmBacktrace故障排查全攻略
目录
1.下载Cmbacktrace
2.解压压缩包
3.新建工程
1.开启时钟
2.选择系统时钟源
3.使能串口
4.使能rtos
5.配置时钟树
6.配置项目信息
7.配置外设文件然后新建工程
4.串口重定向
5.复制Cmbacktrace的源码
6.keil中添加组和头文件路径
7.文件配置
1.配置中断文件
2.初始化库
3.cmb_cfg.h
4.tasks.c
5.FreeRTOS.h
8.addr2line
1.概念
2.如何获取addr2line工具
3.如何使用
9.测试
10.测试结果
11.打印信息解读
一、基础信息:故障发生的 “场景定位”
二、线程栈信息:初步排查 “栈溢出”
三、寄存器信息:故障发生时的 “现场快照”
四、故障原因:直接定位 “根因”
五、关键指令:将 “二进制地址” 转为 “代码行”
指令参数解读:
六、后续排查步骤(必做!)
总结
1.下载Cmbacktrace
https://gitee.com/Armink/CmBacktrace
2.解压压缩包
3.新建工程
1.开启时钟
2.选择系统时钟源
3.使能串口
4.使能rtos
5.配置时钟树
6.配置项目信息
7.配置外设文件然后新建工程
4.串口重定向
记得开启微库
uart.c中添加
#include "usart.h"/* USER CODE BEGIN 0 */
int fputc(int ch,FILE *f)
{HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,HAL_MAX_DELAY);return ch;
}
/* USER CODE END 0 */
5.复制Cmbacktrace的源码
新建一个文件夹
6.keil中添加组和头文件路径
7.文件配置
1.配置中断文件
在使用了cmbacetrace库提供的cmb_fault.s汇编文件时,因为该汇编文件内部已经定义了HardFault_Handler,所以如果项目中还有其他地方定义了该函数,则会提示 HardFault_Handler被重复定义的错误。
注释HardFault函数
2.初始化库
调用cm_bcktrace_init函数进行库初始化
3.cmb_cfg.h
4.tasks.c
因为 FreeRTOS的TCB中没有StackSize 信息,所以修改了其源码,在FreeRTOS/tasks.c 中增加
了uxSizeOfStack 字段,以及 vTaskStackAddr()、vTaskStackSize()、vTaskName()函数。
5.FreeRTOS.h
在xSTATIC_TCB中添加
8.addr2line
1.概念
addr2line(它是标准的GNU Binutils 中的一部分)是一个可以将指令的地址和可执行映像转换成文
件名、函数名和源代码行数的工具。
2.如何获取addr2line工具
-
安装MinGW(网上教程很多,自行搜索),安装后在其安装目录的bin文件夹里会包含 addr2line.exe,此时只用保证环境变量path中包含该路径即可;
-
(XP平台除外):cmbacetrace库的tools文件夹中已存放addr2line.exe,可以将其直接拷贝至C:\Windows 下,或者将CmBacktrace仓库的tools文件夹路径添加至到环境变量path中,这样都能保证命令行工具能正常使用 addr2line命令。
3.如何使用
使用addr2line -- help可以看到如下介绍:
常用的参数:
-
-e:指定可执行映像名称
-
-a:显示函数地址
-
-f:显示函数名称
addr2line -e CmBacktraceDemo.axf -afpic 08002092 08001102
在使用前先配置系统环境变量
将这个路径添加到path中
其次是保证我们初始化函数cm_backtrace_init的第一个参数名要与keil-Output-Name of Executable一致
然后就可以在终端中打开使用了
9.测试
10.测试结果
11.打印信息解读
一、基础信息:故障发生的 “场景定位”
test start
Firmware name: CmBacktrace, hardware version: V1.0.0, software version: V1.1.0
Fault on thread defaultTask
-
核心结论:故障发生在 FreeRTOS 的
defaultTask
任务中(这是 FreeRTOS 默认创建的任务,若未修改任务名,通常是用户业务逻辑的 “主任务”)。 -
补充说明:固件 / 软硬版本信息用于标识当前运行的程序版本,排除 “版本错配导致故障” 的可能(若你确认当前版本是预期版本,则可忽略此点)。
二、线程栈信息:初步排查 “栈溢出”
===== Thread stack information =====addr: 20001020 data: a5a5a5a5addr: 20001024 data: a5a5a5a5
====================================
-
关键解读:
-
栈地址
20001020
/20001024
是 SRAM 地址(ARM Cortex-M 系列中,0x20000000
开头通常是内部 SRAM),符合栈在 SRAM 中的存储逻辑。 -
栈数据
a5a5a5a5
是 栈初始化填充值(多数 RTOS / 编译器会用0xA5
填充未使用的栈空间,标记 “未被覆盖的栈区域”)。
-
-
结论:当前打印的这 2 个栈地址未被异常覆盖(仍为初始值),暂时排除 “栈溢出导致故障” 的直接可能(但不代表栈完全无问题,仅说明这两个地址的栈空间正常)。
三、寄存器信息:故障发生时的 “现场快照”
寄存器是 CPU 执行指令时的 “临时数据区”,这部分信息是定位故障的核心依据:
=================== Registers information ====================R0 : 00000000 R1 : 0000000a R2 : 0000000a R3 : 200000acR12: a5a5a5a5 LR : 08001103 PC : 08002092 PSR: 61000000
==============================================================
四、故障原因:直接定位 “根因”
Usage fault is caused by Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set)
-
核心结论:故障类型是 Usage Fault(用法错误),具体原因是 “除零操作”(divide by zero)。
-
补充说明:
DIV_0_TRP
是 Cortex-M 内核SCB->CCR
寄存器中的 “除零检测使能位”—— 只有当该位开启时,CPU 才会把 “除零操作” 触发为 Usage Fault(否则除零结果会是无穷大或异常值,但不会触发故障)。你当前的配置是正确的,这能精准定位除零问题。
五、关键指令:将 “二进制地址” 转为 “代码行”
Show more call stack info by run: addr2line -e CmBacktrace.axf -afpiC 08002092 08001102
这是 CmBacktrace 给出的 最关键排查指令,作用是把前面的二进制地址(PC/LR)转换成对应的 源代码文件名和行号,直接定位到 “哪行代码出了除零错误”。
指令参数解读:
六、后续排查步骤(必做!)
1.执行 addr2line 指令,定位代码行:
-
打开编译环境的 “命令行终端”(如 STM32CubeIDE 的 Terminal、Keil 的 μVision Command Prompt)。
-
切换到 AXF 文件所在目录(通常在
Debug
或Release
文件夹下)。
-
复制并执行指令(注意想要的 AXF 文件名和路径):
2.检查定位到的代码行,修复除零问题:
-
找到
main.c:123
这类代码行,查看是否有除法操作(如/
或%
)。 -
示例错误代码:
int a = 10;
int b = 0;
int c = a / b; // 这里会触发除零错误,对应 R0=b=0,R1=a=10
修复方案:在除法操作前增加 “除数非零判断”,例如:
int c;
if (b != 0) {c = a / b;
} else {// 处理除数为零的异常(如打印日志、设置默认值)printf("Error: divisor is zero!\n");c = 0;
}
总结
当前故障的核心是:FreeRTOS 的 defaultTask 任务中,某行代码执行了 “除零操作”(如 10/0),触发了 Usage Fault。通过 addr2line
指令定位到具体代码行后,增加 “除数非零判断” 即可修复。