你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

CmBacktrace: ARM Cortex-M 系列 MCU 错误追踪库

[复制链接]
zoomdy 发布时间:2017-8-22 11:42
0、CmBacktrace 是什么CmBacktrace (Cortex Microcontroller Backtrace)是一款针对 ARM Cortex-M 系列 MCU 的错误代码自动追踪、定位,错误原因自动分析的开源库。主要特性如下:& s/ k" ?& D3 r$ z- ^! j& n( ^
  • 支持的错误包括:
    & u4 k& `  ?$ g9 @
    • 断言(assert)
    • 故障(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault)
      : r) j( D3 I* i  J" Z0 q+ X' F$ M1 f
  • 故障原因 自动诊断 :可在故障发生时,自动分析出故障的原因,定位发生故障的代码位置,而无需再手动分析繁杂的故障寄存器;
  • 输出错误现场的 函数调用栈(需配合 addr2line 工具进行精确定位),还原发生错误时的现场信息,定位问题代码位置、逻辑更加快捷、精准。也可以在正常状态下使用该库,获取当前的函数调用栈;
  • 支持 裸机 及以下操作系统平台:
    4 q+ z& C  V9 c: M. ^
    • RT-Thread
    • UCOS
    • FreeRTOS(需修改源码)  R  ?; L0 q, q
  • 根据错误现场状态,输出对应的 线程栈 或 C 主栈;
  • 故障诊断信息支持多国语言(目前:简体中文、英文);
  • 适配 Cortex-M0/M3/M4/M7 MCU;
  • 支持 IAR、KEIL、GCC 编译器;, J' f4 ^' ], |( m3 R0 A
1、为什么选择 CmBacktrace入门新人 :对于从 C51 、MSP430 等简单单片机转而使用更加复杂的 ARM  新人来说,时不时出现的 "hard falut" 死机会让新人瞬间懵掉。定位错误的方法也往往是连接上仿真器,一步步 F10/F11 单步,定位到具体的错误代码,再去猜测、排除、推敲错误原因,这种过程十分痛苦。" N; Y9 N; m' U2 V) J! b
熟练老手 :慢慢的大家知道可以通过故障寄存器信息来定位故障原因及故障代码地址,虽然这样能解决一小部分问题,但是重复的、繁琐的分析过程也会耽误很多时间。而且对于一些复杂问题,只依靠代码地址是无法解决的,必须得还原错误现场的函数调用逻辑关系。虽然连接仿真器可以查看到的函数调用栈,但故障状态下是无法显示的,所以还是得一步步 F10/F11 单步去定位错误代码的位置。另外,还有两种场景," L1 r+ j+ x  S! L1 f
  • 1、很多产品真机调试时必须断开仿真器
  • 2、问题确实存在,但是极难被重现
    6 q7 Y" _  w( o/ U* ?' Z6 C
所以定位这类问题就显得难上加难。' @3 ?( x% ?* r9 M& ^2 J% t# A
使用本库 :上述所有问题都迎刃而解,可以将错误信息输出到控制台上,还可以将错误信息使用 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
  • 1、制造除零异常(IAR 工程,点击查看源码)
  • 2、查看错误诊断信息
  • 3、查看函数调用栈基本信息
  • 4、通过命令行工具进入项目工程存放可执行文件的路径
  • 5、使用 addr2line 命令,查看函数调用栈详细信息,并定位错误代码3 E. j0 H2 d5 f8 m

: S. b( l- _2 U; E6 u. k2.2 Demo
目录
平台
链接

! f8 Y# k" e  `1 B: o
\demos\non_os\stm32f10x
裸机 STM32 Cortex-M3
( v! i" S# H5 ?# n$ v* `, z1 E
\demos\os\rtthread\stm32f4xx
RT-Thread STM32 Cortex-M4

& O: ^1 g& V2 u9 s0 q
\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 准备工作
  • 1、查看 \demos 目录下有没有合适自己的 Demo ,如有类似,则建议在其基础上修改
  • 2、明确操作系统/裸机平台及 CPU 平台
  • 3、将 \src 下的全部源文件添加至产品工程中,并保证源码目录被添加至头文件路径
  • 4、cmb_fault.s 汇编文件(点击查看)可以选择性添加至工程,添加后需要把项目原有的 HardFault_Handler 注释掉
  • 5、把 cm_backtrace_init 函数放在项目初始化地方执行
  • 6、将 cm_backtrace_assert 放在项目的断言函数中执行,具体使用方法参照下面的 API 说明
  • 7、如果第 4 步骤没有将 cmb_fault.s 汇编文件启用,则需要将 cm_backtrace_fault 放到故障处理函数(例如: HardFault_Handler )中执行,具体使用方法参照下面的 API 说明
    5 C* z; T9 [3 f: S2 O9 e5 Z8 f
2.3.2 配置说明配置文件名: cmb_cfg.h ,针对不同的平台和场景,用户需要自自行手动配置,常用配置如下:
4 G0 k. X6 d% g- q( m' S; U
配置名称
功能
备注

- Z0 R+ C) ]4 l5 {( P
cmb_println(...)
错误及诊断信息输出
必须配置
: K5 }1 A/ y" W+ n, N
CMB_USING_BARE_METAL_PLATFORM
是否使用在裸机平台
使用则定义该宏
( T* j1 T/ t! a$ l
CMB_USING_OS_PLATFORM
是否使用在操作系统平台
操作系统与裸机必须二选一

5 t3 k  ~! d! o- @+ G* m
CMB_OS_PLATFORM_TYPE
操作系统平台
RTT/UCOSII/UCOSIII/FREERTOS
  c, T2 p1 @* ?$ z* @1 Z
CMB_CPU_PLATFORM_TYPE
CPU平台
M0/M3/M4/M7
; r! _! {! ]/ C1 R
CMB_USING_DUMP_STACK_INFO
是否使用 Dump 堆栈的功能
使用则定义该宏

  f9 x/ n" E6 i" {) t9 f# G6 C
CMB_PRINT_LANGUAGE
输出信息时的语言
CHINESE/ENGLISH
注意:以上部分配置的内容可以在 cmb_def.h 中选择,更多灵活的配置请阅读源码
- c( y8 c6 x% [
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

  X) k% x! L$ M0 y- w, I" d( L( c
参数
描述

) s- l- f5 Z- c3 i& f: a* M
firmware_name
固件名称,需与编译器生成的固件名称对应

2 t8 {* ]! X, H/ V& w8 R, V
hardware_ver
固件对应的硬件版本号

* B: s$ q( s; }, I6 z
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
参数
描述

0 i3 Y1 O$ F( }4 ^4 H
buffer
存储函数调用栈的缓冲区
. k* |0 \, O" g% p6 O2 s% j5 k
size
缓冲区大小
- 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)
! w2 I8 q+ f- }6 ^  m4 K0 g. I4 H" \' e- X; e& Y) i
参数
描述
4 D3 }0 R- b$ A/ T$ u, F% w- P5 d
sp
断言环境时的堆栈指针
注意 :入参 SP 尽量在断言函数内部获取,而且尽可能靠近断言函数开始的位置。当在断言函数的子函数中(例如:在 RT-Thread 的断言钩子方法中)使用时,由于函数嵌套会存在寄存器入栈的操作,此时再获取 SP 将发生变化,就需要人为调整(加减固定的偏差值)入参值,所以作为新手 不建议在断言的子函数 中使用该函数。, ~* l$ D* b; I8 G2 t  z0 l
2.4.4 追踪故障错误信息void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp)
" {- b# Q5 k. S9 t( T$ U% {/ _8 I) `% q) X1 E" F+ v
参数
描述

) C$ t( M7 |6 y7 O0 x' y
fault_handler_lr
故障处理函数环境下的 LR 寄存器值
9 \0 f/ _: `9 ~  q9 J. g
fault_handler_sp
故障处理函数环境下的 SP 寄存器值
该函数可以在故障处理函数(例如: HardFault_Handler)中调用。另外,库本身提供了 HardFault 处理的汇编文件(点击查看,需根据自己编译器进行选择),会在故障时自动调用 cm_backtrace_fault 方法。所以移植时,最简单的方式就是直接使用该汇编文件。: p- N2 z  G$ L' g; K; D
2.5 常见问题2.5.1 编译出错,提示需要 C99 支持点击查看教程:一步开启 Keil/IAR/GCC 的 C99 支持* `6 Y! i" L6 k$ x8 ^, X6 `
2.5.2 如何查看到函数调用栈中函数的具体名称及代码行号点击查看教程:如何使用 addr2line 工具获取函数调用栈详细信息
6 K" `, r7 M% }3 V- M2.5.3 故障处理函数:HardFault_Handler 重复定义在使用了本库提供的 cmb_fault.s 汇编文件时,因为该汇编文件内部已经定义了 HardFault_Handler ,所以如果项目中还有其他地方定义了该函数,则会提示 HardFault_Handler 被重复定义的错误。此时有两种解决方法:  K- a4 [6 M. L8 I/ }# V$ L& y
  • 1、注释/删除其他文件中定义的 HardFault_Handler 函数,仅保留 cmb_fault.s 中的;
  • 2、将 cmb_fault.s 移除工程,手动添加 cm_backtrace_fault 函数至现有的故障处理函数,但需要注意的是,务必 保证该函数数入参的准备性 ,否则可能会导致故障诊断功能及堆栈打印功能无法正常运行。所以如果是新手,不推荐第二种解决方法。) }6 q" Q  p4 D5 N. [
2.6 许可采用 MIT 开源协议,细节请阅读项目中的 LICENSE 文件内容。. `5 H& U5 Q7 X+ P6 i7 ?

$ a7 r1 v, @3 b6 p
赞 收藏 3 评论10 发布时间:2017-8-22 11:42

举报

10个回答
zero99 回答时间:2017-8-22 14:11:40
谢谢分享,先收藏了
hjl2832 回答时间:2017-9-4 09:58:27
这个不错,谢谢分享。。。。。。。。。。
andey 回答时间:2017-9-4 13:12:12
提示: 作者被禁止或删除 内容自动屏蔽
xiang90721 回答时间:2017-9-16 22:42:24
对于硬件错误,查看堆栈寄存器的值能找到确定点的位置么?我现在通过使用这个,系统为rt-thread,使用addr2line命令定位在rt_hw_interrupt_enable关闭系统中断函数上,这就让我费解了,所以怀疑正确性
Jone888 回答时间:2018-5-8 11:34:19
本帖最后由 dawdawd 于 2018-5-8 11:38 编辑 1 R* I% a5 A8 F1 ]9 r, ~0 Y
, {; U7 y1 P2 U- Q
int main(void)
2 d: ?# i; `! o{( 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();
  C! ?2 I9 R" U) j% \#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);
0 U' E' N* x- x0 a) c* F% B    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
}
. L; c9 _4 E* D+ [
' m4 E, _1 m- q* I3 xvoid start_task(void *pdata)
$ h5 D. N! u8 S& x{
. z3 A$ q0 I: ]! z3 R) f$ H6 C    u8 *sta;
6 j  M' m, L+ S    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);
# J( x! `0 _+ `: w( L+ |9 V4 p% v        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
}
& Y% `) w! g# ?6 `2 p
8 X7 z+ g# t- j, v  U5 J1 m! H6 L6 }
为什么用这个工具 定位出来的 代码 位置: OSStart(),  但是错误应该是 这句:DEBUG("PC_process", " sta = %d",*sta); 啊, 这是为什么啊???
* X0 |; t1 q0 Y$ V8 P. q+ R' a下面是定位出来的内容:. 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
1 ]/ \+ {" y2 @2 E) }: i* CE:\宸ヤ綔\MCU 璧勬枡\stm32 璧勬枡\cmBacktrack_project_test\cmbackTrace\online_gprs/..\src_common\ucosii\core\/os_core.c:830$ R& u/ |% W8 U; r8 J
0x0800428f
# Z  R8 [5 N. ]; ]" Gmain' 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
( |! c/ |/ y8 h
! @: V0 I$ t% m2 I5 B2 o0 b1 Z" j9 Z, U( A  y
" h+ e# o( ?3 s; m; P

# z$ D3 \0 }: A4 y
御猫 回答时间:2019-1-30 11:48:35
mark
waiman 回答时间:2020-6-20 15:06:43
你好,我在MDK 5.29 编译器AC6.13.1下。初始化时提示无法定义标签 STACK5 p( a$ G% L& g6 \4 O# E
请问怎样才能正确引用该标签?" 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 ?

7 o+ Y. r3 p8 {3 G' X( G                AREA    STACK, NOINIT, READWRITE, ALIGN=3
2 H% h4 Y9 ~4 yStack_Mem       SPACE   Stack_Size
/ w* |+ r8 O( R+ e5 {__initial_sp
4 P0 p* I  r6 W+ w8 G& \; g# d$ S* o$ u4 A; D' D5 A
1 F, B. w! F$ Q* Y' c
andey 回答时间:2020-6-20 17:28:02
提示: 作者被禁止或删除 内容自动屏蔽
desk1983 回答时间:2020-6-29 09:19:48
非常好的资料,对初学者很有帮助;1 e& b, @7 Z7 U: H( ]
希望楼主多多分享,赠人玫瑰,手有余香,念念不忘,必有回响;.
溪悦 回答时间:2020-6-30 09:58:42
赞赞赞
' H; x! p4 w+ t7 _) u

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版