0、CmBacktrace 是什么CmBacktrace (Cortex Microcontroller Backtrace)是一款针对 ARM Cortex-M 系列 MCU 的错误代码自动追踪、定位,错误原因自动分析的开源库。主要特性如下:& s/ k" ?& D3 r$ z- ^! j& n( ^
熟练老手 :慢慢的大家知道可以通过故障寄存器信息来定位故障原因及故障代码地址,虽然这样能解决一小部分问题,但是重复的、繁琐的分析过程也会耽误很多时间。而且对于一些复杂问题,只依靠代码地址是无法解决的,必须得还原错误现场的函数调用逻辑关系。虽然连接仿真器可以查看到的函数调用栈,但故障状态下是无法显示的,所以还是得一步步 F10/F11 单步去定位错误代码的位置。另外,还有两种场景," L1 r+ j+ x S! L1 f
使用本库 :上述所有问题都迎刃而解,可以将错误信息输出到控制台上,还可以将错误信息使用 EasyFlash 的 Log 功能保存至 Flash 中,设备死机后重启依然能够读取上次的错误信息。CmBacktrace 输出的信息包括函数调用栈、故障诊断结果、堆栈、故障寄存器及产品固件信息,极大的提升了错误定位的效率及准确性。5 T5 E0 H# }% [ 俗话说,工欲善其事,必先利其器。所以有时候做事效率低的原因也许是,你会用的工具种类太少。$ C" S; N0 ?) I+ `; P) Q 合作、贡献 :开源软件的发展离不开大家的支持,欢迎大家多提建议,也希望更多的人一起参与进来,共同提高 。如果觉得这个开源项目很赞,可以点击 项目主页 (Github|OSChina|Coding) 右上角的 Star ,同时把它推荐给更多有需要的朋友。& Q& u+ S; ^$ {9 N5 X& B 2、CmBacktrace 如何使用2.1 演示该演示分如下几个步骤:2 b7 U( H- n" I% W9 r N3 Z
![]() 2.2 Demo 目录 平台 链接 \demos\non_os\stm32f10x 裸机 STM32 Cortex-M3 ( v! i" S# H5 ?# n$ v* `, z1 E\demos\os\rtthread\stm32f4xx RT-Thread STM32 Cortex-M4 \demos\os\ucosii\stm32f10x UCOSII STM32 Cortex-M3 9 i( d6 S; n$ b7 B; h\demos\os\freertos\stm32f10x FreeRTOS STM32 Cortex-M3 2.3 移植说明2.3.1 准备工作
配置名称 功能 备注 cmb_println(...) 错误及诊断信息输出 必须配置 : K5 }1 A/ y" W+ n, NCMB_USING_BARE_METAL_PLATFORM 是否使用在裸机平台 使用则定义该宏 ( T* j1 T/ t! a$ lCMB_USING_OS_PLATFORM 是否使用在操作系统平台 操作系统与裸机必须二选一 CMB_OS_PLATFORM_TYPE 操作系统平台 RTT/UCOSII/UCOSIII/FREERTOS c, T2 p1 @* ?$ z* @1 ZCMB_CPU_PLATFORM_TYPE CPU平台 M0/M3/M4/M7 ; r! _! {! ]/ C1 RCMB_USING_DUMP_STACK_INFO 是否使用 Dump 堆栈的功能 使用则定义该宏 CMB_PRINT_LANGUAGE 输出信息时的语言 CHINESE/ENGLISH 注意:以上部分配置的内容可以在 cmb_def.h 中选择,更多灵活的配置请阅读源码2.4 API 说明2.4.1 库初始化void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver)2 l; o; n0 A! s! Z Z4 S# S0 f' W 参数 描述 firmware_name 固件名称,需与编译器生成的固件名称对应 hardware_ver 固件对应的硬件版本号 software_ver 固件的软件版本号 注意 :以上入参将会在断言或故障时输出,主要起了追溯的作用4 r: I) u0 Q. H. L. d1 A2 _2.4.2 获取函数调用栈size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp)$ K1 R6 l* n) z: Z: H- B7 J/ a " \& r$ |! N( Y" \/ W @! y 参数 描述 buffer 存储函数调用栈的缓冲区 . k* |0 \, O" g% p6 O2 s% j5 ksize 缓冲区大小 - W( A7 X3 I' T( \sp 待获取的堆栈指针 示例:) \- h( K4 I! x* G/* 建立深度为 16 的函数调用栈缓冲区,深度大小不应该超过 CMB_CALL_STACK_MAX_DEPTH(默认16) */uint32_t call_stack[16] = {0};size_t i, depth = 0;/* 获取当前环境下的函数调用栈,每个元素将会以 32 位地址形式存储, depth 为函数调用栈实际深度 */depth = cm_backtrace_call_stack(call_stack, sizeof(call_stack), __get_SP());/* 输出当前函数调用栈信息 * 注意:查看函数名称及具体行号时,需要使用 addr2line 工具转换 */for (i = 0; i < depth; i++) { printf("%08x ", call_stack);}/ f, ]; l- q9 z$ P8 F& Q b ) ?3 ?& J- x2 r$ e 2.4.3 追踪断言错误信息void cm_backtrace_assert(uint32_t sp) . I4 H" \' e- X; e& Y) i 参数 描述 4 D3 }0 R- b$ A/ T$ u, F% w- P5 dsp 断言环境时的堆栈指针 注意 :入参 SP 尽量在断言函数内部获取,而且尽可能靠近断言函数开始的位置。当在断言函数的子函数中(例如:在 RT-Thread 的断言钩子方法中)使用时,由于函数嵌套会存在寄存器入栈的操作,此时再获取 SP 将发生变化,就需要人为调整(加减固定的偏差值)入参值,所以作为新手 不建议在断言的子函数 中使用该函数。, ~* l$ D* b; I8 G2 t z0 l2.4.4 追踪故障错误信息void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp) % {/ _8 I) `% q) X1 E" F+ v 参数 描述 fault_handler_lr 故障处理函数环境下的 LR 寄存器值 9 \0 f/ _: `9 ~ q9 J. gfault_handler_sp 故障处理函数环境下的 SP 寄存器值 该函数可以在故障处理函数(例如: HardFault_Handler)中调用。另外,库本身提供了 HardFault 处理的汇编文件(点击查看,需根据自己编译器进行选择),会在故障时自动调用 cm_backtrace_fault 方法。所以移植时,最简单的方式就是直接使用该汇编文件。: p- N2 z G$ L' g; K; D2.5 常见问题2.5.1 编译出错,提示需要 C99 支持点击查看教程:一步开启 Keil/IAR/GCC 的 C99 支持* `6 Y! i" L6 k$ x8 ^, X6 ` 2.5.2 如何查看到函数调用栈中函数的具体名称及代码行号点击查看教程:如何使用 addr2line 工具获取函数调用栈详细信息 2.5.3 故障处理函数:HardFault_Handler 重复定义在使用了本库提供的 cmb_fault.s 汇编文件时,因为该汇编文件内部已经定义了 HardFault_Handler ,所以如果项目中还有其他地方定义了该函数,则会提示 HardFault_Handler 被重复定义的错误。此时有两种解决方法: K- a4 [6 M. L8 I/ }# V$ L& y
|
微信公众号
手机版
, {; U7 y1 P2 U- Q
int main(void)
{( u) r3 W' J& q% u% G- d6 s
#if 05 {& m% j* d# w; E+ o+ p V
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x20000); 9 I% P2 W# G$ L7 u+ L3 p8 q
__enable_irq();
#endif9 L3 t* o. @- C$ ]' Z# @
mainInit();' r7 e0 U0 d3 x$ L
delay_ms(2000);6 h* U. j& i5 J. r" Z( g, o7 j/ L
delay_ms(2000);
OSInit();$ G, B7 j5 [$ S$ p/ S9 U
OSTaskCreate(start_task, (void *) 0,(OS_STK *) &START_TASK_STK[START_STK_SIZE - 1], START_TASK_PRIO);$ G. p5 \8 h: A; S8 ~
OSStart();* `0 b4 m3 T( F7 s. e6 l v) ~! v
}
void start_task(void *pdata)
{
u8 *sta;
DEBUG("PC_process", " enter start_task function !!!");) `; v* e5 n9 j; F* I9 t' g: T, w
OSStatInit(); ( _. N) p8 k) W: w, _
while (1)" d4 P! k0 Y# R
{8 J P- `) ]: }
delay_ms(2000);
DEBUG("PC_process", " sta = %d",*sta);3 d- E% _, G" B; U: O R6 ^9 z2 e4 Z# W
}: h% n* Q4 L! ^/ e. R# p% R
}
5 J1 m! H6 L6 }
为什么用这个工具 定位出来的 代码 位置: OSStart(), 但是错误应该是 这句:DEBUG("PC_process", " sta = %d",*sta); 啊, 这是为什么啊???
下面是定位出来的内容:. L: Q1 e$ q$ z, f2 P
E:\工作\MCU 资料\stm32 资料\cmBacktrack_project_test\cmbackTrace\online_gprs\Objects>addr2line -e charger.axf -a -f 08000352 0800252b 0800428f 080002ff2 L+ V1 ~$ s7 l( p! l# w* U# r
0x08000352. M9 U0 F! T" K8 q7 s* ]
OSStartHighRdy% t& E4 J& w- n! f
E:\工作\MCU 资料\stm32 资料\cmBacktrack_project_test\cmbackTrace\online_gprs/..\src_common\ucosii\port\/os_cpu_a.asm:110& G9 C: ^& r& ?! c. Y8 @% b
0x0800252b' J1 K" J q& i& D( A( v* p# [
OSStart
E:\宸ヤ綔\MCU 璧勬枡\stm32 璧勬枡\cmBacktrack_project_test\cmbackTrace\online_gprs/..\src_common\ucosii\core\/os_core.c:830$ R& u/ |% W8 U; r8 J
0x0800428f
main' w2 F; }* ]1 X) J% T7 z1 O
E:\宸ヤ綔\MCU 璧勬枡\stm32 璧勬枡\cmBacktrack_project_test\cmbackTrace\online_gprs/src\main.c:52) v7 w: s* g+ v N" G& X
0x080002ff$ Z/ N* z3 A) r+ r7 e& v9 f8 \9 J- G
__rt_entry_main
5 B2 o0 b1 Z" j9 Z, U( A y
" h+ e# o( ?3 s; m; P
请问怎样才能正确引用该标签?" G7 k; i G* k. ^: e' L
7 {2 U- s8 @( R* W0 L
启动文件startup_stm32f103xe.s头部已经明确定义了 STACK. {5 k0 l' a$ Z4 q# O& |
Stack_Size EQU 0x1000* q: F l1 N3 ?
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
; g# d$ S* o$ u4 A; D' D5 A
1 F, B. w! F$ Q* Y' c
希望楼主多多分享,赠人玫瑰,手有余香,念念不忘,必有回响;.