栈回溯之CmBacktrace

简介

CmBacktrace (Cortex Microcontroller Backtrace)是一款针对 ARM Cortex-M 系列 MCU 的错误代码自动追踪、定位,错误原因自动分析的开源库。主要特性如下:

  • 支持的错误包括:
    • 断言(assert)
    • 故障(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault)
  • 故障原因 自动诊断 :可在故障发生时,自动分析出故障的原因,定位发生故障的代码位置,而无需再手动分析繁杂的故障寄存器;
  • 输出错误现场的 函数调用栈(需配合 addr2line 工具进行精确定位),还原发生错误时的现场信息,定位问题代码位置、逻辑更加快捷、精准。也可以在正常状态下使用该库,获取当前的函数调用栈;
  • 支持 裸机 及以下操作系统平台:
  • 根据错误现场状态,输出对应的 线程栈 或 C 主栈;
  • 故障诊断信息支持多国语言(目前:简体中文、英文);
  • 适配 Cortex-M0/M3/M4/M7 MCU;
  • 支持 IAR、KEIL、GCC 编译器;



环境

IDEKeil 5.30(Windows 11)
MCUSTM32H750(Cortex M7),Cortex M3/M4 MCU都可以
RTOSRT-Thread
源码https://github.com/armink/CmBacktrace



移植源码

  1. 下载源码 git clone https://github.com/armink/CmBacktrace.git
  2. cm_backtrace文件夹复制至工程,并将根目录所有源文件添加至工程
  3. cm_backtrace/fault_handler/keil下的cmb_fault.s 汇编文件添加至工程,添加后需要把项目原有的 HardFault_Handler 注释掉
  4. cm_backtrace文件夹添加至头文件路径



配置说明

配置文件名: cmb_cfg.h ,针对不同的平台和场景,用户需要自自行手动配置,常用配置如下:

配置名称功能备注
cmb_println(…)错误及诊断信息输出必须配置
CMB_USING_BARE_METAL_PLATFORM是否使用在裸机平台使用则定义该宏
CMB_USING_OS_PLATFORM是否使用在操作系统平台操作系统与裸机必须二选一
CMB_OS_PLATFORM_TYPE操作系统平台RTT/UCOSII/UCOSIII/FREERTOS
CMB_CPU_PLATFORM_TYPECPU平台M0/M3/M4/M7
CMB_USING_DUMP_STACK_INFO是否使用 Dump 堆栈的功能使用则定义该宏
CMB_PRINT_LANGUAGE输出信息时的语言CHINESE/ENGLISH

根据环境,我这里的cmb_cfg.h内容如下:

#ifndef _CMB_CFG_H_
#define _CMB_CFG_H_

#include <stdio.h>

/* print line, must config by user */
#define cmb_println(...)    do{printf(__VA_ARGS__);printf("\r\n");}while (0)
     
/* e.g., printf(__VA_ARGS__);printf("\r\n")  or  SEGGER_RTT_printf(0, __VA_ARGS__);SEGGER_RTT_WriteString(0, "\r\n")  */
/* enable bare metal(no OS) platform */
/* #define CMB_USING_BARE_METAL_PLATFORM */
/* enable OS platform */
#define CMB_USING_OS_PLATFORM 
/* OS platform type, must config when CMB_USING_OS_PLATFORM is enable */
#define CMB_OS_PLATFORM_TYPE  CMB_OS_PLATFORM_RTT         /* or CMB_OS_PLATFORM_UCOSII or CMB_OS_PLATFORM_UCOSIII or CMB_OS_PLATFORM_FREERTOS or CMB_OS_PLATFORM_RTX5 */
/* cpu platform type, must config by user */
#define CMB_CPU_PLATFORM_TYPE    CMB_CPU_ARM_CORTEX_M7      /* CMB_CPU_ARM_CORTEX_M0 or CMB_CPU_ARM_CORTEX_M3 or CMB_CPU_ARM_CORTEX_M4 or CMB_CPU_ARM_CORTEX_M7 or CMB_CPU_ARM_CORTEX_M33 */
/* enable dump stack information */
#define CMB_USING_DUMP_STACK_INFO
/* language of print information */
#define CMB_PRINT_LANGUAGE     CMB_PRINT_LANGUAGE_CHINESE_UTF8        /*CMB_PRINT_LANGUAGE_ENGLISH(default) or CMB_PRINT_LANGUAGE_CHINESE or CMB_PRINT_LANGUAGE_CHINESE_UTF8 */
#endif /* _CMB_CFG_H_ */



实例测试

初始化

在工程最开始调用初始化函数,三个参数依次为固件名称,硬件版本,软件版本

固件名称最好和Options(魔术棒)> Output > Name of Executable一致,方便后续使用addr2line 工具

cm_backtrace_init("STM32H750VB-RTT", "V1.0.0", "V0.0.1");



测试源码

调用关系main() > test2() > test1() > fault_test()

void fault_test() 
{
	volatile int *SCB1 = (volatile int *) 0xFEEEEEEE;
	*SCB1 |= 0x10;

    printf("SCB1: %d\r\n", *SCB1);
}

void test1()
{
	printf("test 1 start\r\n");
	fault_test();
	printf("test 1 end\r\n");
}

void test2()
{
	printf("test 2 start\r\n");
	test1();
	printf("test 2 end\r\n");
}

int main(void)
{
	//......
	test2();
	//......
}



日志

由日志可知设备挂掉的原因为非对齐访问

固件名称:STM32H750VB-RTT,硬件版本号:V1.0.0,软件版本号:V0.0.1
在线程(main)中发生错误异常
=========== 线程堆栈信息 ===========
  addr: 20001040    data: deadbeef
  addr: 20001044    data: 2000104c
  addr: 20001048    data: 20001054
  addr: 2000104c    data: 0800a9e3
  addr: 20001050    data: 200010cc
  addr: 20001054    data: 08008e9f
  addr: 20001058    data: 23232323
  addr: 2000105c    data: 23232323
  addr: 20001060    data: 23232323
  addr: 20001064    data: 23232323
  addr: 20001068    data: 23232323
  addr: 2000106c    data: 23232323
  addr: 20001070    data: 23232323
  addr: 20001074    data: 23232323
  addr: 20001078    data: 23232323
  addr: 2000107c    data: 23232323
  addr: 20001080    data: 23232323
  addr: 20001084    data: 23232323
  addr: 20001088    data: 23232323
  addr: 2000108c    data: 00000000
  addr: 20001090    data: deadbeef
  addr: 20001094    data: deadbeef
  addr: 20001098    data: deadbeef
  addr: 2000109c    data: deadbeef
  addr: 200010a0    data: deadbeef
  addr: 200010a4    data: deadbeef
  addr: 200010a8    data: deadbeef
  addr: 200010ac    data: deadbeef
  addr: 200010b0    data: 200010c4
  addr: 200010b4    data: 08009247
  addr: 200010b8    data: deadbeef
  addr: 200010bc    data: deadbeef
  addr: 200010c0    data: 200010cc
  addr: 200010c4    data: 08008f81
  addr: 200010c8    data: deadbeef
  addr: 200010cc    data: 08008155
====================================
========================= 寄存器信息 =========================
  R0 : feeeeeee  R1 : f0000000  R2 : 00000000  R3 : 08008a0a
  R12: 0000c000  LR : 0800a9a7  PC : 08008a76  PSR: 61000000
==============================================================
发生用法错误,原因:企图执行非对齐访问
查看更多函数调用栈信息,请运行:addr2line -e STM32H750VB-RTT.axf -afpiC 08008a76 0800a9a6 0800a9e2 08008e9e 08009246 08008f80



调用addr2line

在生成编译结果的目录右键打开终端,运行日志提示的addr2line的命令

没有addr2line的可以在tools文件夹获取,因为我装有MinGW (Minimalist GNU for Windows),所以直接运行:

结果可以很清晰的看到函数调用过程和出错的地方

addr2line -e STM32H750VB-RTT.axf -afpiC 08008a82 0800a9b2 0800a9ee 08008eaa 08009252 08008f8c

0x08008a76: fault_test at D:\RT-ThreadStudio\workspace\STM32H750VB-RTT\MDK-ARM/../Core/Src/main.c:21
0x0800a9a6: test1 at D:\RT-ThreadStudio\workspace\STM32H750VB-RTT\MDK-ARM/../Core/Src/main.c:30
0x0800a9e2: test2 at D:\RT-ThreadStudio\workspace\STM32H750VB-RTT\MDK-ARM/../Core/Src/main.c:37
0x08008e9e: main at D:\RT-ThreadStudio\workspace\STM32H750VB-RTT\MDK-ARM/../Core/Src/main.c:83
0x08009246: rt_components_init at D:\RT-ThreadStudio\workspace\STM32H750VB-RTT\MDK-ARM/..\RT-Thread\/src\components.c:127
0x08008f80: main_thread_entry at D:\RT-ThreadStudio\workspace\STM32H750VB-RTT\MDK-ARM/..\RT-Thread\/src\components.c:195
  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 以下是 VxWorks 中获取回溯信息的源码示例: ```c void stackTraceTaskPrint (int tid) { REG_SET regSet; WIND_TCB *pTcb = taskIdVerify (tid); if (pTcb == NULL) return; if (taskRegsGet (tid, &regSet) != OK) return; printf ("Stack trace for task %#x (%s):\n", (int) pTcb, pTcb->name); stackTracePrint ((char *) pTcb->name, tid, (char *) regSet.spReg, (char *) regSet.pc); } ``` 此函数获取给定任务 ID 的任务控制块,并通过 taskRegsGet 函数获取该任务的寄存器值,然后调用 stackTracePrint 函数打印该任务的回溯信息。 ```c void stackTracePrint (char *name, int tid, char *sp, char *pc) { int count; char **list; list = (char **) malloc (MAX_TRACE_DEPTH * sizeof (char *)); if (list == NULL) return; count = stackTrace (tid, sp, pc, list, MAX_TRACE_DEPTH); printf ("Stack trace for task %#x (%s):\n", tid, name); stackTraceDepthPrint (list, count); free (list); } ``` 此函数分配堆内存以存储回溯信息,并调用 stackTrace 函数获取回溯信息。然后它将回溯信息作为参数传递给 stackTraceDepthPrint 函数,以打印回溯信息。 ```c int stackTrace (int tid, char *sp, char *pc, char **pList, int maxDepth) { int i = 0; int depth = 0; FUNCPTR funcPtr; char *nextPc; char **list = pList; while (i < maxDepth) { if (taskIdVerify (tid) == NULL) break; if (i == 0) { if (pc == 0) break; funcPtr = (FUNCPTR) pc; } else { if (nextPc == 0) break; funcPtr = (FUNCPTR) (nextPc - CALL_INSTR_SIZE); } if (funcPtr == NULL) break; *list++ = (char *) funcPtr; depth++; i++; if (taskRegsStackProbe (tid, (char *) funcPtr, &nextPc) != OK) break; } return (depth); } ``` 此函数根据给定的任务 ID、堆指针和程序计数器获取回溯信息。它使用 taskRegsStackProbe 函数来确定每个函数调用的下一个程序计数器值,并将每个函数的地址存储在列表中。如果达到最大深度或无法确定下一个程序计数器,则停止回溯。 ```c void stackTraceDepthPrint (char **list, int depth) { int i; for (i = 0; i < depth; i++) printf ("%s\n", symFindByValue ((int) list[i], N_TEXT, (char **) NULL, (char **) NULL)); } ``` 此函数用于打印回溯列表中的每个函数地址对应的符号名称。它使用 symFindByValue 函数查找每个地址对应的符号名称,并将其打印到控制台上。 ### 回答2: vxWorks是一款实时操作系统,它的回溯功能可以帮助开发人员定位程序的异常或错误。回溯是指在程序发生异常或错误时,通过分析程序运行时的调用来定位出错的源码位置。 在vxWorks中,回溯功能是通过函数backtrace()实现的。backtrace()函数会返回当前函数调用的信息,包括每个函数的返回地址和参数值。使用该函数可以获取当前的信息,然后通过解析信息,可以找到调用中每个函数的源码位置。 具体的回溯源码如下: 1. 定义一个结构体存储函数调用信息: ```c #define MAX_STACK_FRAMES 50 typedef struct { void *returnAddr; void *args[MAX_ARGS]; } StackFrame; ``` 2. 定义回溯函数backtrace(),该函数会获取当前函数调用信息: ```c void backtrace(StackFrame *frames, int maxFrames) { int frameCount = 0; // 使用汇编指令获取帧信息 // 将返回地址和参数值存储到StackFrame结构体中 while (frameCount < maxFrames && frameCount < MAX_STACK_FRAMES) { frames[frameCount].returnAddr = __builtin_return_address(frameCount); frameCount++; } } ``` 3. 在需要进行回溯的地方,调用backtrace()函数,并解析信息: ```c StackFrame frames[MAX_STACK_FRAMES]; backtrace(frames, MAX_STACK_FRAMES); for (int i = 0; i < MAX_STACK_FRAMES; i++) { void *returnAddr = frames[i].returnAddr; // 解析返回地址和参数值,找到对应的源码位置 // 打印出错函数、源码文件名和行号等信息 } ``` 通过以上的源码实现,我们可以获取程序运行时的调用信息,从而快速定位程序的异常或错误源码位置。这对于调试程序非常有帮助,可以提高开发效率。 ### 回答3: vxWorks是一个实时操作系统,它提供了回溯功能,可以帮助开发人员快速定位程序中的问题。 在vxWorks中,回溯源码的实现是基于任务控制块(Task Control Block, TCB)和堆溢出检测技术。 首先,每个任务在TCB中都有一个指向堆顶部的指针,通过这个指针可以访问任务的堆。当任务运行时,它的函数调用和局部变量会存储在堆中。当任务出现问题时,可以通过访问任务的堆来获取回溯信息。 在vxWorks中,堆溢出检测技术可以帮助检测任务是否发生了溢出。当任务的堆溢出时,会触发一个硬件中断。当这个中断发生时,系统会保存当前的寄存器状态,并调用回溯函数。 回溯函数是vxWorks中回溯功能的核心。它会逐级遍历任务的调用,获取每一级函数的返回地址和参数,并将这些信息打印输出。这样,开发人员就可以根据回溯信息来定位程序中的问题。 回溯函数的源码在vxWorks的源代码中可以找到,一般位于目录"vxworks\kernel\lib"中。回溯函数会使用汇编语言和C语言混合编程实现,通过访问任务的堆来获取回溯信息,然后将这些信息格式化输出。 总之,vxWorks提供了回溯源码,开发人员可以通过它来定位程序中的问题。回溯的实现依赖于任务的堆和堆溢出检测技术,通过遍历任务的调用获取回溯信息,并将其打印输出。这样,开发人员就可以根据回溯信息来进行程序调试和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值