diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f956e16 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016-2017 Armink (armink.ztl@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..06f74e9 --- /dev/null +++ b/README.md @@ -0,0 +1,181 @@ +# CmBacktrace: ARM Cortex-M 系列 MCU 错误追踪库 + +[![GitHub release](https://img.shields.io/github/release/armink/CmBacktrace.svg)](https://github.com/armink/CmBacktrace/releases/latest) [![GitHub commits](https://img.shields.io/github/commits-since/armink/CmBacktrace/0.2.0.svg)](https://github.com/armink/CmBacktrace/0.2.0...master) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/armink/CmBacktrace/master/LICENSE) + +## 0、CmBacktrace 是什么 + +[CmBacktrace](https://github.com/armink/CmBacktrace) (Cortex Microcontroller Backtrace)是一款针对 ARM Cortex-M 系列 MCU 的错误代码自动追踪、定位,错误原因自动分析的开源库。主要特性如下: + +- 支持的错误包括: + - 断言(assert) + - 故障(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault) +- 故障原因 **自动诊断** :可在故障发生时,自动分析出故障的原因,定位发生故障的代码位置,而无需再手动分析繁杂的故障寄存器; +- 输出错误现场的 **函数调用栈**(需配合 addr2line 工具进行精确定位),还原发生错误时的现场信息,定位问题代码位置、逻辑更加快捷、精准。也可以在正常状态下使用该库,获取当前的函数调用栈; +- 支持 裸机 及以下操作系统平台: + - [RT-Thread](http://www.rt-thread.org/) + - UCOS + - FreeRTOS +- 根据错误现场状态,输出对应的 线程栈 或 C 主栈; +- 故障诊断信息支持多国语言(目前:简体中文、英文); +- 适配 Cortex-M0/M3/M4/M7 MCU; +- 支持 IAR、KEIL、GCC 编译器; + +## 1、为什么选择 CmBacktrace + +**入门新人** :对于从 C51 、MSP430 等简单单片机转而使用更加复杂的 ARM 新人来说,时不时出现的 "hard falut" 死机会让新人瞬间懵掉。定位错误的方法也往往是连接上仿真器,一步步 F10/F11 单步,定位到具体的错误代码,再去猜测、排除、推敲错误原因,这种过程十分痛苦。 + +**熟练老手** :慢慢的大家知道可以通过故障寄存器信息来定位故障原因及故障代码地址,虽然这样能解决一小部分问题,但是重复的、繁琐的分析过程也会耽误很多时间。而且对于一些复杂问题,只依靠代码地址是无法解决的,必须得还原错误现场的函数调用逻辑关系。虽然连接仿真器可以查看到的函数调用栈,但故障状态下是无法显示的,所以还是得一步步 F10/F11 单步去定位错误代码的位置。另外,还有两种场景, + +- 1、很多产品真机调试时必须断开仿真器 +- 2、问题确实存在,但是极难被重现 + +所以定位这类问题就显得难上加难。 + +**使用本库** :上述所有问题都迎刃而解,可以将错误信息输出到控制台上,还可以将错误信息使用 [EasyFlash](https://github.com/armink/EasyFlash) 的 Log 功能保存至 Flash 中,设备死机后重启依然能够读取上次的错误信息。CmBacktrace 输出的信息包括函数调用栈、故障诊断结果、堆栈、故障寄存器及产品固件信息,极大的提升了错误定位的效率及准确性。 + +俗话说,工欲善其事,必先利其器。所以有时候做事效率低的原因也许是,你会用的工具种类太少。 + +**合作、贡献** :开源软件的发展离不开大家的支持,欢迎大家多提建议,也希望更多的人一起参与进来,共同提高 。如果觉得这个开源项目很赞,可以点击 [项目主页](https://github.com/armink/CmBacktrace) **([Github](https://github.com/armink/CmBacktrace)|[OSChina](http://git.oschina.net/armink/CmBacktrace)|[Coding](https://coding.net/u/armink/p/CmBacktrace/git))** 右上角的 **Star** ,同时把它推荐给更多有需要的朋友。 + +## 2、CmBacktrace 如何使用 + +### 2.1 演示 + +该演示分如下几个步骤: + +- 1、制造除零异常([IAR 工程,点击查看源码](https://github.com/armink/CmBacktrace/tree/master/demos/non_os/stm32f10x/app/src)) +- 2、查看错误诊断信息 +- 3、查看函数调用栈基本信息 +- 4、通过命令行工具进入项目工程存放可执行文件的路径 +- 5、使用 addr2line 命令,查看函数调用栈详细信息,并定位错误代码 + +[![cm_backtrace_demo](https://raw.githubusercontent.com/armink/CmBacktrace/master/docs/zh/images/cm_backtrace_demo.gif)](https://github.com/armink/CmBacktrace) + +### 2.2 Demo + +|目录|平台|链接| +|:--|:--:|:--:| +| `\demos\non_os\stm32f10x` |裸机 STM32 Cortex-M3|[点击查看](https://github.com/armink/CmBacktrace/tree/master/demos/non_os/stm32f10x)| +| `\demos\os\rtthread\stm32f4xx`|RT-Thread STM32 Cortex-M4|[点击查看](https://github.com/armink/CmBacktrace/tree/master/demos/os/rtthread/stm32f4xx)| +| `\demos\os\ucosii\stm32f10x` |UCOSII STM32 Cortex-M3|[点击查看](https://github.com/armink/CmBacktrace/tree/master/demos/os/ucosii/stm32f10x)| +| `\demos\os\freertos\stm32f10x` |FreeRTOS STM32 Cortex-M3|[点击查看](https://github.com/armink/CmBacktrace/tree/master/demos/os/freertos/stm32f10x)| + + + +### 2.3 移植说明 + +#### 2.3.1 准备工作 + +- 1、查看 `\demos` 目录下有没有合适自己的 Demo ,如有类似,则建议在其基础上修改 +- 2、明确操作系统/裸机平台及 CPU 平台 +- 3、将 `\src` 下的全部源文件添加至产品工程中,并保证源码目录被添加至头文件路径 +- 4、cmb_fault.s 汇编文件([点击查看](https://github.com/armink/CmBacktrace/tree/master/cm_backtrace/fault_handler))可以选择性添加至工程,添加后需要把项目原有的 `HardFault_Handler` 注释掉 +- 5、把 `cm_backtrace_init` 函数放在项目初始化地方执行 +- 6、将 `cm_backtrace_assert` 放在项目的断言函数中执行,具体使用方法参照下面的 API 说明 +- 7、如果第 4 步骤没有将 cmb_fault.s 汇编文件启用,则需要将 `cm_backtrace_fault` 放到故障处理函数(例如: `HardFault_Handler` )中执行,具体使用方法参照下面的 API 说明 + +#### 2.3.2 配置说明 + +配置文件名: `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_TYPE|CPU平台|M0/M3/M4/M7| +|CMB_USING_DUMP_STACK_INFO|是否使用 Dump 堆栈的功能|使用则定义该宏| +|CMB_PRINT_LANGUAGE|输出信息时的语言|CHINESE/ENGLISH| + +> 注意:以上部分配置的内容可以在 `cmb_def.h` 中选择,更多灵活的配置请阅读源码 + +### 2.4 API 说明 + +#### 2.4.1 库初始化 + +```C +void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver) +``` + +|参数 |描述| +|:----- |:----| +|firmware_name |固件名称,需与编译器生成的固件名称对应| +|hardware_ver |固件对应的硬件版本号| +|software_ver |固件的软件版本号| + +> **注意** :以上入参将会在断言或故障时输出,主要起了追溯的作用 + +#### 2.4.2 获取函数调用栈 + +```C +size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp) +``` + +|参数 |描述| +|:----- |:----| +|buffer |存储函数调用栈的缓冲区| +|size |缓冲区大小| +|sp |待获取的堆栈指针| + +示例: + +```C +/* 建立深度为 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[i]); +} +``` + +#### 2.4.3 追踪断言错误信息 + +```C +void cm_backtrace_assert(uint32_t sp) +``` + +|参数 |描述| +|:----- |:----| +|sp |断言环境时的堆栈指针| + +> **注意** :入参 SP 尽量在断言函数内部获取,而且尽可能靠近断言函数开始的位置。当在断言函数的子函数中(例如:在 RT-Thread 的断言钩子方法中)使用时,由于函数嵌套会存在寄存器入栈的操作,此时再获取 SP 将发生变化,就需要人为调整(加减固定的偏差值)入参值,所以作为新手 **不建议在断言的子函数** 中使用该函数。 + +#### 2.4.4 追踪故障错误信息 + +```C +void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp) +``` + +|参数 |描述| +|:----- |:----| +|fault_handler_lr |故障处理函数环境下的 LR 寄存器值| +|fault_handler_sp |故障处理函数环境下的 SP 寄存器值| + +该函数可以在故障处理函数(例如: `HardFault_Handler`)中调用。另外,库本身提供了 `HardFault` 处理的汇编文件([点击查看](https://github.com/armink/CmBacktrace/tree/master/cm_backtrace/fault_handler),需根据自己编译器进行选择),会在故障时自动调用 `cm_backtrace_fault` 方法。所以移植时,最简单的方式就是直接使用该汇编文件。 + +### 2.5 常见问题 + +#### 2.5.1 编译出错,提示需要 C99 支持 + +[点击查看教程:一步开启 Keil/IAR/GCC 的 C99 支持](https://github.com/armink/CmBacktrace/blob/master/docs/zh/enable_c99_for_keil_iar_gcc.md) + +#### 2.5.2 如何查看到函数调用栈中函数的具体名称及代码行号 + +[点击查看教程:如何使用 addr2line 工具获取函数调用栈详细信息](https://github.com/armink/CmBacktrace/blob/master/docs/zh/how_to_use_addr2line_for_call_stack.md) + +#### 2.5.3 故障处理函数:HardFault_Handler 重复定义 + +在使用了本库提供的 cmb_fault.s 汇编文件时,因为该汇编文件内部已经定义了 HardFault_Handler ,所以如果项目中还有其他地方定义了该函数,则会提示 HardFault_Handler 被重复定义的错误。此时有两种解决方法: + +- 1、注释/删除其他文件中定义的 `HardFault_Handler` 函数,仅保留 cmb_fault.s 中的; +- 2、将 cmb_fault.s 移除工程,手动添加 `cm_backtrace_fault` 函数至现有的故障处理函数,但需要注意的是,务必 **保证该函数数入参的准备性** ,否则可能会导致故障诊断功能及堆栈打印功能无法正常运行。所以如果是新手,不推荐第二种解决方法。 + +### 2.6 许可 + +采用 MIT 开源协议,细节请阅读项目中的 LICENSE 文件内容。 diff --git a/SConscript b/SConscript new file mode 100644 index 0000000..d384e7b --- /dev/null +++ b/SConscript @@ -0,0 +1,9 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +path = [cwd] + +group = DefineGroup('cm_backtrace', src, depend = [''], CPPPATH = path) + +Return('group') diff --git a/cm_backtrace.c b/cm_backtrace.c new file mode 100644 index 0000000..544676c --- /dev/null +++ b/cm_backtrace.c @@ -0,0 +1,693 @@ +/* + * This file is part of the CmBacktrace Library. + * + * Copyright (c) 2016-2017, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Initialize function and other general function. + * Created on: 2016-12-15 + */ + +#include +#include +#include +#include + +/* include or export for supported __get_MSP, __get_PSP function */ +#if defined(__CC_ARM) + static __inline __asm uint32_t __get_MSP(void) { + mrs r0, msp + bx lr + } + static __inline __asm uint32_t __get_PSP(void) { + mrs r0, psp + bx lr + } +#elif defined(__ICCARM__) + #include +#elif defined(__GNUC__) + __attribute__( ( always_inline ) ) static inline uint32_t __get_MSP(void) { + register uint32_t result; + __asm volatile ("MRS %0, msp\n" : "=r" (result) ); + return(result); + } + __attribute__( ( always_inline ) ) static inline uint32_t __get_PSP(void) { + register uint32_t result; + __asm volatile ("MRS %0, psp\n" : "=r" (result) ); + return(result); + } +#else + #error "not supported compiler" +#endif + +#if defined(__CC_ARM) + #define SECTION_START(_name_) _name_##$$Base + #define SECTION_END(_name_) _name_##$$Limit + #define IMAGE_SECTION_START(_name_) Image$$##_name_##$$Base + #define IMAGE_SECTION_END(_name_) Image$$##_name_##$$Limit + #define CSTACK_BLOCK_START(_name_) SECTION_START(_name_) + #define CSTACK_BLOCK_END(_name_) SECTION_END(_name_) + #define CODE_SECTION_START(_name_) IMAGE_SECTION_START(_name_) + #define CODE_SECTION_END(_name_) IMAGE_SECTION_END(_name_) + + extern const int CSTACK_BLOCK_START(CMB_CSTACK_BLOCK_NAME); + extern const int CSTACK_BLOCK_END(CMB_CSTACK_BLOCK_NAME); + extern const int CODE_SECTION_START(CMB_CODE_SECTION_NAME); + extern const int CODE_SECTION_END(CMB_CODE_SECTION_NAME); +#elif defined(__ICCARM__) + #pragma section=CMB_CSTACK_BLOCK_NAME + #pragma section=CMB_CODE_SECTION_NAME +#elif defined(__GNUC__) + extern const int CMB_CSTACK_BLOCK_START; + extern const int CMB_CSTACK_BLOCK_END; + extern const int CMB_CODE_SECTION_START; + extern const int CMB_CODE_SECTION_END; +#else + #error "not supported compiler" +#endif + +enum { + PRINT_FIRMWARE_INFO, + PRINT_ASSERT_ON_THREAD, + PRINT_ASSERT_ON_HANDLER, + PRINT_THREAD_STACK_INFO, + PRINT_MAIN_STACK_INFO, + PRINT_CALL_STACK_INFO, + PRINT_CALL_STACK_ERR, + PRINT_FAULT_ON_THREAD, + PRINT_FAULT_ON_HANDLER, + PRINT_REGS_TITLE, + PRINT_HFSR_VECTBL, + PRINT_MFSR_IACCVIOL, + PRINT_MFSR_DACCVIOL, + PRINT_MFSR_MUNSTKERR, + PRINT_MFSR_MSTKERR, + PRINT_MFSR_MLSPERR, + PRINT_BFSR_IBUSERR, + PRINT_BFSR_PRECISERR, + PRINT_BFSR_IMPREISERR, + PRINT_BFSR_UNSTKERR, + PRINT_BFSR_STKERR, + PRINT_BFSR_LSPERR, + PRINT_UFSR_UNDEFINSTR, + PRINT_UFSR_INVSTATE, + PRINT_UFSR_INVPC, + PRINT_UFSR_NOCP, + PRINT_UFSR_UNALIGNED, + PRINT_UFSR_DIVBYZERO, + PRINT_DFSR_HALTED, + PRINT_DFSR_BKPT, + PRINT_DFSR_DWTTRAP, + PRINT_DFSR_VCATCH, + PRINT_DFSR_EXTERNAL, + PRINT_MMAR, + PRINT_BFAR, +}; + +static const char *print_info[] = { +#if (CMB_PRINT_LANGUAGE == CMB_PRINT_LANGUAGE_ENGLISH) + [PRINT_FIRMWARE_INFO] = "Firmware name: %s, hardware version: %s, software version: %s", + [PRINT_ASSERT_ON_THREAD] = "Assert on thread %s", + [PRINT_ASSERT_ON_HANDLER] = "Assert on interrupt or bare metal(no OS) environment", + [PRINT_THREAD_STACK_INFO] = "===== Thread stack information =====", + [PRINT_MAIN_STACK_INFO] = "====== Main stack information ======", + [PRINT_CALL_STACK_INFO] = "Show more call stack info by run: addr2line -e %s%s -a -f %.*s", + [PRINT_CALL_STACK_ERR] = "Dump call stack has an error", + [PRINT_FAULT_ON_THREAD] = "Fault on thread %s", + [PRINT_FAULT_ON_HANDLER] = "Fault on interrupt or bare metal(no OS) environment", + [PRINT_REGS_TITLE] = "=================== Registers information ====================", + [PRINT_HFSR_VECTBL] = "Hard fault is caused by failed vector fetch", + [PRINT_MFSR_IACCVIOL] = "Memory management fault is caused by instruction access violation", + [PRINT_MFSR_DACCVIOL] = "Memory management fault is caused by data access violation", + [PRINT_MFSR_MUNSTKERR] = "Memory management fault is caused by unstacking error", + [PRINT_MFSR_MSTKERR] = "Memory management fault is caused by stacking error", + [PRINT_MFSR_MLSPERR] = "Memory management fault is caused by floating-point lazy state preservation", + [PRINT_BFSR_IBUSERR] = "Bus fault is caused by instruction access violation", + [PRINT_BFSR_PRECISERR] = "Bus fault is caused by precise data access violation", + [PRINT_BFSR_IMPREISERR] = "Bus fault is caused by imprecise data access violation", + [PRINT_BFSR_UNSTKERR] = "Bus fault is caused by unstacking error", + [PRINT_BFSR_STKERR] = "Bus fault is caused by stacking error", + [PRINT_BFSR_LSPERR] = "Bus fault is caused by floating-point lazy state preservation", + [PRINT_UFSR_UNDEFINSTR] = "Usage fault is caused by attempts to execute an undefined instruction", + [PRINT_UFSR_INVSTATE] = "Usage fault is caused by attempts to switch to an invalid state (e.g., ARM)", + [PRINT_UFSR_INVPC] = "Usage fault is caused by attempts to do an exception with a bad value in the EXC_RETURN number", + [PRINT_UFSR_NOCP] = "Usage fault is caused by attempts to execute a coprocessor instruction", + [PRINT_UFSR_UNALIGNED] = "Usage fault is caused by indicates that an unaligned access fault has taken place", + [PRINT_UFSR_DIVBYZERO] = "Usage fault is caused by Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set)", + [PRINT_DFSR_HALTED] = "Debug fault is caused by halt requested in NVIC", + [PRINT_DFSR_BKPT] = "Debug fault is caused by BKPT instruction executed", + [PRINT_DFSR_DWTTRAP] = "Debug fault is caused by DWT match occurred", + [PRINT_DFSR_VCATCH] = "Debug fault is caused by Vector fetch occurred", + [PRINT_DFSR_EXTERNAL] = "Debug fault is caused by EDBGRQ signal asserted", + [PRINT_MMAR] = "The memory management fault occurred address is %08x", + [PRINT_BFAR] = "The bus fault occurred address is %08x", +#elif (CMB_PRINT_LANGUAGE == CMB_PRINT_LANUUAGE_CHINESE) + [PRINT_FIRMWARE_INFO] = "�̼����ƣ�%s��Ӳ���汾�ţ�%s�������汾�ţ�%s", + [PRINT_ASSERT_ON_THREAD] = "���߳�(%s)�з�������", + [PRINT_ASSERT_ON_HANDLER] = "���жϻ���������·�������", + [PRINT_THREAD_STACK_INFO] = "=========== �̶߳�ջ��Ϣ ===========", + [PRINT_MAIN_STACK_INFO] = "============ ����ջ��Ϣ ============", + [PRINT_CALL_STACK_INFO] = "�鿴���ຯ������ջ��Ϣ�������У�addr2line -e %s%s -a -f %.*s", + [PRINT_CALL_STACK_ERR] = "��ȡ��������ջʧ��", + [PRINT_FAULT_ON_THREAD] = "���߳�(%s)�з��������쳣", + [PRINT_FAULT_ON_HANDLER] = "���жϻ���������·��������쳣", + [PRINT_REGS_TITLE] = "========================= �Ĵ�����Ϣ =========================", + [PRINT_HFSR_VECTBL] = "����Ӳ����ԭ��ȡ�ж�����ʱ����", + [PRINT_MFSR_IACCVIOL] = "�����洢����������ԭ����ͼ�Ӳ��������ʵ�����ȡָ��", + [PRINT_MFSR_DACCVIOL] = "�����洢����������ԭ����ͼ�Ӳ��������ʵ��������д����", + [PRINT_MFSR_MUNSTKERR] = "�����洢����������ԭ�򣺳�ջʱ��ͼ���ʲ�������������", + [PRINT_MFSR_MSTKERR] = "�����洢����������ԭ����ջʱ��ͼ���ʲ�������������", + [PRINT_MFSR_MLSPERR] = "�����洢����������ԭ�򣺶��Ա��渡��״̬ʱ��������", + [PRINT_BFSR_IBUSERR] = "�������ߴ���ԭ��ָ�����ߴ���", + [PRINT_BFSR_PRECISERR] = "�������ߴ���ԭ�򣺾�ȷ���������ߴ���", + [PRINT_BFSR_IMPREISERR] = "�������ߴ���ԭ�򣺲���ȷ���������ߴ���", + [PRINT_BFSR_UNSTKERR] = "�������ߴ���ԭ�򣺳�ջʱ��������", + [PRINT_BFSR_STKERR] = "�������ߴ���ԭ����ջʱ��������", + [PRINT_BFSR_LSPERR] = "�������ߴ���ԭ�򣺶��Ա��渡��״̬ʱ��������", + [PRINT_UFSR_UNDEFINSTR] = "�����÷�����ԭ����ͼִ��δ����ָ��", + [PRINT_UFSR_INVSTATE] = "�����÷�����ԭ����ͼ�л��� ARM ״̬", + [PRINT_UFSR_INVPC] = "�����÷�����ԭ����Ч���쳣������", + [PRINT_UFSR_NOCP] = "�����÷�����ԭ����ͼִ��Э������ָ��", + [PRINT_UFSR_UNALIGNED] = "�����÷�����ԭ����ͼִ�зǶ������", + [PRINT_UFSR_DIVBYZERO] = "�����÷�����ԭ����ͼִ�г� 0 ����", + [PRINT_DFSR_HALTED] = "�������Դ���ԭ��NVIC ͣ������", + [PRINT_DFSR_BKPT] = "�������Դ���ԭ��ִ�� BKPT ָ��", + [PRINT_DFSR_DWTTRAP] = "�������Դ���ԭ�����ݼ���ƥ��", + [PRINT_DFSR_VCATCH] = "�������Դ���ԭ�򣺷�����������", + [PRINT_DFSR_EXTERNAL] = "�������Դ���ԭ���ⲿ��������", + [PRINT_MMAR] = "�����洢����������ĵ�ַ��%08x", + [PRINT_BFAR] = "�������ߴ���ĵ�ַ��%08x", +#else + #error "CMB_PRINT_LANGUAGE defined error in 'cmb_cfg.h'" +#endif +}; + +static char fw_name[CMB_NAME_MAX] = {0}; +static char hw_ver[CMB_NAME_MAX] = {0}; +static char sw_ver[CMB_NAME_MAX] = {0}; +static uint32_t main_stack_start_addr = 0; +static size_t main_stack_size = 0; +static uint32_t code_start_addr = 0; +static size_t code_size = 0; +static bool init_ok = false; +static char call_stack_info[CMB_CALL_STACK_MAX_DEPTH * (8 + 1)] = { 0 }; +static bool on_fault = false; +static struct cmb_hard_fault_regs regs; + +#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) +static bool statck_has_fpu_regs = false; +#endif + +#ifdef CMB_USING_OS_PLATFORM +static bool on_thread_before_fault = false; +#endif + +/** + * library initialize + */ +void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver) { + strncpy(fw_name, firmware_name, CMB_NAME_MAX); + strncpy(hw_ver, hardware_ver, CMB_NAME_MAX); + strncpy(sw_ver, software_ver, CMB_NAME_MAX); + +#if defined(__CC_ARM) + main_stack_start_addr = (uint32_t)&CSTACK_BLOCK_START(CMB_CSTACK_BLOCK_NAME); + main_stack_size = (uint32_t)&CSTACK_BLOCK_END(CMB_CSTACK_BLOCK_NAME) - main_stack_start_addr; + code_start_addr = (uint32_t)&CODE_SECTION_START(CMB_CODE_SECTION_NAME); + code_size = (uint32_t)&CODE_SECTION_END(CMB_CODE_SECTION_NAME) - code_start_addr; +#elif defined(__ICCARM__) + main_stack_start_addr = (uint32_t)__section_begin(CMB_CSTACK_BLOCK_NAME); + main_stack_size = (uint32_t)__section_end(CMB_CSTACK_BLOCK_NAME) - main_stack_start_addr; + code_start_addr = (uint32_t)__section_begin(CMB_CODE_SECTION_NAME); + code_size = (uint32_t)__section_end(CMB_CODE_SECTION_NAME) - code_start_addr; +#elif defined(__GNUC__) + main_stack_start_addr = (uint32_t)(&CMB_CSTACK_BLOCK_START); + main_stack_size = (uint32_t)(&CMB_CSTACK_BLOCK_END) - main_stack_start_addr; + code_start_addr = (uint32_t)(&CMB_CODE_SECTION_START); + code_size = (uint32_t)(&CMB_CODE_SECTION_END) - code_start_addr; +#else + #error "not supported compiler" +#endif + + init_ok = true; +} + +/** + * print firmware information, such as: firmware name, hardware version, software version + */ +void cm_backtrace_firmware_info(void) { + cmb_println(print_info[PRINT_FIRMWARE_INFO], fw_name, hw_ver, sw_ver); +} + +#ifdef CMB_USING_OS_PLATFORM +/** + * Get current thread stack information + * + * @param sp stack current pointer + * @param start_addr stack start address + * @param size stack size + */ +static void get_cur_thread_stack_info(uint32_t sp, uint32_t *start_addr, size_t *size) { + CMB_ASSERT(start_addr); + CMB_ASSERT(size); + +#if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) + *start_addr = (uint32_t) rt_thread_self()->stack_addr; + *size = rt_thread_self()->stack_size; +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII) + extern OS_TCB *OSTCBCur; + + *start_addr = (uint32_t) OSTCBCur->OSTCBStkBottom; + *size = OSTCBCur->OSTCBStkSize * sizeof(OS_STK); +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII) + #error "not implemented, I hope you can do this" + //TODO ��ʵ�� +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS) + #error "not implemented, I hope you can do this" + //TODO ��ʵ�� +#endif +} + +/** + * Get current thread name + */ +static const char *get_cur_thread_name(void) { +#if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) + return rt_thread_self()->name; +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII) + extern OS_TCB *OSTCBCur; + +#if OS_TASK_NAME_SIZE > 0 || OS_TASK_NAME_EN > 0 + return (const char *)OSTCBCur->OSTCBTaskName; +#else + return NULL; +#endif /* OS_TASK_NAME_SIZE > 0 || OS_TASK_NAME_EN > 0 */ + +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII) + #error "not implemented, I hope you can do this" + //TODO ��ʵ�� +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS) + #error "not implemented, I hope you can do this" + //TODO ��ʵ�� +#endif +} + +#ifdef CMB_USING_DUMP_STACK_INFO +/** + * dump current thread stack information + */ +static void dump_cur_thread_stack(uint32_t stack_start_addr, size_t stack_size, uint32_t *stack_pointer) { + cmb_println(print_info[PRINT_THREAD_STACK_INFO]); + for (; (uint32_t) stack_pointer < stack_start_addr + stack_size; stack_pointer++) { + cmb_println(" addr: %08x data: %08x", stack_pointer, *stack_pointer); + } + cmb_println("===================================="); +} +#endif /* CMB_USING_DUMP_STACK_INFO */ +#endif /* CMB_USING_OS_PLATFORM */ + +#ifdef CMB_USING_DUMP_STACK_INFO +/** + * dump current main stack information + */ +static void dump_main_stack(uint32_t stack_start_addr, size_t stack_size, uint32_t *stack_pointer) { + cmb_println(print_info[PRINT_MAIN_STACK_INFO]); + for (; (uint32_t) stack_pointer < stack_start_addr + stack_size; stack_pointer++) { + cmb_println(" addr: %08x data: %08x", stack_pointer, *stack_pointer); + } + cmb_println("===================================="); +} +#endif /* CMB_USING_DUMP_STACK_INFO */ + +/** + * backtrace function call stack + * + * @param buffer call stack buffer + * @param size buffer size + * @param sp stack pointer + * + * @return depth + */ +size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp) { + uint32_t stack_start_addr = main_stack_start_addr, pc; + size_t depth = 0, stack_size = main_stack_size; + bool regs_saved_lr_is_valid = false; + + if (on_fault) { + /* first depth is PC */ + buffer[depth++] = regs.saved.pc; + /* second depth is from LR, so need decrease a word to PC */ + pc = regs.saved.lr - sizeof(size_t); + if ((pc >= code_start_addr) && (pc <= code_start_addr + code_size) && (depth < CMB_CALL_STACK_MAX_DEPTH) + && (depth < size)) { + buffer[depth++] = pc; + regs_saved_lr_is_valid = true; + } + +#ifdef CMB_USING_OS_PLATFORM + /* program is running on thread before fault */ + if (on_thread_before_fault) { + get_cur_thread_stack_info(sp, &stack_start_addr, &stack_size); + } + } else { + /* OS environment */ + if (__get_SP() == __get_PSP()) { + get_cur_thread_stack_info(sp, &stack_start_addr, &stack_size); + } +#endif /* CMB_USING_OS_PLATFORM */ + + } + + /* copy called function address */ + for (; sp < stack_start_addr + stack_size; sp += sizeof(size_t)) { + /* the *sp value may be LR, so need decrease a word to PC */ + pc = *((uint32_t *) sp) - sizeof(size_t); + if ((pc >= code_start_addr) && (pc <= code_start_addr + code_size) && (depth < CMB_CALL_STACK_MAX_DEPTH) + && (depth < size)) { + /* the second depth function may be already saved, so need ignore repeat */ + if ((depth == 2) && regs_saved_lr_is_valid && (pc == buffer[1])) { + continue; + } + buffer[depth++] = pc; + } + } + + return depth; +} + +/** + * dump function call stack + * + * @param sp stack pointer + */ +static void print_call_stack(uint32_t sp) { + size_t i, cur_depth = 0; + uint32_t call_stack_buf[CMB_CALL_STACK_MAX_DEPTH] = {0}; + + cur_depth = cm_backtrace_call_stack(call_stack_buf, CMB_CALL_STACK_MAX_DEPTH, sp); + + for (i = 0; i < cur_depth; i++) { + sprintf(call_stack_info + i * (8 + 1), "%08lx", call_stack_buf[i]); + call_stack_info[i * (8 + 1) + 8] = ' '; + } + + if (cur_depth) { + cmb_println(print_info[PRINT_CALL_STACK_INFO], fw_name, CMB_ELF_FILE_EXTENSION_NAME, cur_depth * (8 + 1), + call_stack_info); + } else { + cmb_println(print_info[PRINT_CALL_STACK_ERR]); + } +} + +/** + * backtrace for assert + * + * @param sp the stack pointer when on assert occurred + */ +void cm_backtrace_assert(uint32_t sp) { + CMB_ASSERT(init_ok); + +#ifdef CMB_USING_OS_PLATFORM + uint32_t cur_stack_pointer = __get_SP(); +#endif + + cmb_println(""); + cm_backtrace_firmware_info(); + +#ifdef CMB_USING_OS_PLATFORM + /* OS environment */ + if (cur_stack_pointer == __get_MSP()) { + cmb_println(print_info[PRINT_ASSERT_ON_HANDLER]); + +#ifdef CMB_USING_DUMP_STACK_INFO + dump_main_stack(main_stack_start_addr, main_stack_size, (uint32_t *) sp); +#endif /* CMB_USING_DUMP_STACK_INFO */ + + } else if (cur_stack_pointer == __get_PSP()) { + cmb_println(print_info[PRINT_ASSERT_ON_THREAD], get_cur_thread_name()); + +#ifdef CMB_USING_DUMP_STACK_INFO + uint32_t stack_start_addr; + size_t stack_size; + get_cur_thread_stack_info(sp, &stack_start_addr, &stack_size); + dump_cur_thread_stack(stack_start_addr, stack_size, (uint32_t *) sp); +#endif /* CMB_USING_DUMP_STACK_INFO */ + + } + +#else + + /* bare metal(no OS) environment */ +#ifdef CMB_USING_DUMP_STACK_INFO + dump_main_stack(main_stack_start_addr, main_stack_size, (uint32_t *) sp); +#endif /* CMB_USING_DUMP_STACK_INFO */ + +#endif /* CMB_USING_OS_PLATFORM */ + + print_call_stack(sp); +} + +#if (CMB_CPU_PLATFORM_TYPE != CMB_CPU_ARM_CORTEX_M0) +/** + * fault diagnosis then print cause of fault + */ +static void fault_diagnosis(void) { + if (regs.hfsr.bits.VECTBL) { + cmb_println(print_info[PRINT_HFSR_VECTBL]); + } + if (regs.hfsr.bits.FORCED) { + /* Memory Management Fault */ + if (regs.mfsr.value) { + if (regs.mfsr.bits.IACCVIOL) { + cmb_println(print_info[PRINT_MFSR_IACCVIOL]); + } + if (regs.mfsr.bits.DACCVIOL) { + cmb_println(print_info[PRINT_MFSR_DACCVIOL]); + } + if (regs.mfsr.bits.MUNSTKERR) { + cmb_println(print_info[PRINT_MFSR_MUNSTKERR]); + } + if (regs.mfsr.bits.MSTKERR) { + cmb_println(print_info[PRINT_MFSR_MSTKERR]); + } + +#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) + if (regs.mfsr.bits.MLSPERR) { + cmb_println(print_info[PRINT_MFSR_MLSPERR]); + } +#endif + + if (regs.mfsr.bits.MMARVALID) { + if (regs.mfsr.bits.IACCVIOL || regs.mfsr.bits.DACCVIOL) { + cmb_println(print_info[PRINT_MMAR], regs.mmar); + } + } + } + /* Bus Fault */ + if (regs.bfsr.value) { + if (regs.bfsr.bits.IBUSERR) { + cmb_println(print_info[PRINT_BFSR_IBUSERR]); + } + if (regs.bfsr.bits.PRECISERR) { + cmb_println(print_info[PRINT_BFSR_PRECISERR]); + } + if (regs.bfsr.bits.IMPREISERR) { + cmb_println(print_info[PRINT_BFSR_IMPREISERR]); + } + if (regs.bfsr.bits.UNSTKERR) { + cmb_println(print_info[PRINT_BFSR_UNSTKERR]); + } + if (regs.bfsr.bits.STKERR) { + cmb_println(print_info[PRINT_BFSR_STKERR]); + } + +#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) + if (regs.bfsr.bits.LSPERR) { + cmb_println(print_info[PRINT_BFSR_LSPERR]); + } +#endif + + if (regs.bfsr.bits.BFARVALID) { + if (regs.bfsr.bits.PRECISERR) { + cmb_println(print_info[PRINT_BFAR], regs.bfar); + } + } + + } + /* Usage Fault */ + if (regs.ufsr.value) { + if (regs.ufsr.bits.UNDEFINSTR) { + cmb_println(print_info[PRINT_UFSR_UNDEFINSTR]); + } + if (regs.ufsr.bits.INVSTATE) { + cmb_println(print_info[PRINT_UFSR_INVSTATE]); + } + if (regs.ufsr.bits.INVPC) { + cmb_println(print_info[PRINT_UFSR_INVPC]); + } + if (regs.ufsr.bits.NOCP) { + cmb_println(print_info[PRINT_UFSR_NOCP]); + } + if (regs.ufsr.bits.UNALIGNED) { + cmb_println(print_info[PRINT_UFSR_UNALIGNED]); + } + if (regs.ufsr.bits.DIVBYZERO) { + cmb_println(print_info[PRINT_UFSR_DIVBYZERO]); + } + } + } + /* Debug Fault */ + if (regs.hfsr.bits.DEBUGEVT) { + if (regs.dfsr.value) { + if (regs.dfsr.bits.HALTED) { + cmb_println(print_info[PRINT_DFSR_HALTED]); + } + if (regs.dfsr.bits.BKPT) { + cmb_println(print_info[PRINT_DFSR_BKPT]); + } + if (regs.dfsr.bits.DWTTRAP) { + cmb_println(print_info[PRINT_DFSR_DWTTRAP]); + } + if (regs.dfsr.bits.VCATCH) { + cmb_println(print_info[PRINT_DFSR_VCATCH]); + } + if (regs.dfsr.bits.EXTERNAL) { + cmb_println(print_info[PRINT_DFSR_EXTERNAL]); + } + } + } +} +#endif /* (CMB_CPU_PLATFORM_TYPE != CMB_CPU_ARM_CORTEX_M0) */ + +#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) +static uint32_t statck_del_fpu_regs(uint32_t fault_handler_lr, uint32_t sp) { + statck_has_fpu_regs = (fault_handler_lr & (1UL << 4)) == 0 ? true : false; + + /* the stack has S0~S15 and FPSCR registers when statck_has_fpu_regs is true, double word align */ + return statck_has_fpu_regs == true ? sp + sizeof(size_t) * 18 : sp; +} +#endif + +/** + * backtrace for fault + * @note only call once + * + * @param fault_handler_lr the LR register value on fault handler + * @param fault_handler_sp the stack pointer on fault handler + */ +void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp) { + uint32_t stack_pointer = fault_handler_sp, saved_regs_addr = stack_pointer; + const char *regs_name[] = { "R0 ", "R1 ", "R2 ", "R3 ", "R12", "LR ", "PC ", "PSR" }; + +#ifdef CMB_USING_DUMP_STACK_INFO + uint32_t stack_start_addr = main_stack_start_addr; + size_t stack_size = main_stack_size; +#endif + + CMB_ASSERT(init_ok); + /* only call once */ + CMB_ASSERT(!on_fault); + + on_fault = true; + + cmb_println(""); + cm_backtrace_firmware_info(); + +#ifdef CMB_USING_OS_PLATFORM + on_thread_before_fault = fault_handler_lr & (1UL << 2); + /* check which stack was used before (MSP or PSP) */ + if (on_thread_before_fault) { + cmb_println(print_info[PRINT_FAULT_ON_THREAD], get_cur_thread_name() != NULL ? get_cur_thread_name() : "NO_NAME"); + saved_regs_addr = stack_pointer = __get_PSP(); + +#ifdef CMB_USING_DUMP_STACK_INFO + get_cur_thread_stack_info(stack_pointer, &stack_start_addr, &stack_size); +#endif /* CMB_USING_DUMP_STACK_INFO */ + + } else { + cmb_println(print_info[PRINT_FAULT_ON_HANDLER]); + } +#else + /* bare metal(no OS) environment */ + cmb_println(print_info[PRINT_FAULT_ON_HANDLER]); +#endif /* CMB_USING_OS_PLATFORM */ + + /* delete saved R0~R3, R12, LR,PC,xPSR registers space */ + stack_pointer += sizeof(size_t) * 8; + +#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) + stack_pointer = statck_del_fpu_regs(fault_handler_lr, stack_pointer); +#endif /* (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) */ + + /* dump stack information */ +#ifdef CMB_USING_DUMP_STACK_INFO +#ifdef CMB_USING_OS_PLATFORM + if (on_thread_before_fault) { + dump_cur_thread_stack(stack_start_addr, stack_size, (uint32_t *) stack_pointer); + } else { + dump_main_stack(stack_start_addr, stack_size, (uint32_t *) stack_pointer); + } +#else + /* bare metal(no OS) environment */ + dump_main_stack(stack_start_addr, stack_size, (uint32_t *) stack_pointer); +#endif /* CMB_USING_OS_PLATFORM */ +#endif /* CMB_USING_DUMP_STACK_INFO */ + + /* dump register */ + cmb_println(print_info[PRINT_REGS_TITLE]); + + regs.saved.r0 = ((uint32_t *)saved_regs_addr)[0]; // Register R0 + regs.saved.r1 = ((uint32_t *)saved_regs_addr)[1]; // Register R1 + regs.saved.r2 = ((uint32_t *)saved_regs_addr)[2]; // Register R2 + regs.saved.r3 = ((uint32_t *)saved_regs_addr)[3]; // Register R3 + regs.saved.r12 = ((uint32_t *)saved_regs_addr)[4]; // Register R12 + regs.saved.lr = ((uint32_t *)saved_regs_addr)[5]; // Link register LR + regs.saved.pc = ((uint32_t *)saved_regs_addr)[6]; // Program counter PC + regs.saved.psr.value = ((uint32_t *)saved_regs_addr)[7]; // Program status word PSR + + + cmb_println(" %s: %08x %s: %08x %s: %08x %s: %08x", regs_name[0], regs.saved.r0, + regs_name[1], regs.saved.r1, + regs_name[2], regs.saved.r2, + regs_name[3], regs.saved.r3); + cmb_println(" %s: %08x %s: %08x %s: %08x %s: %08x", regs_name[4], regs.saved.r12, + regs_name[5], regs.saved.lr, + regs_name[6], regs.saved.pc, + regs_name[7], regs.saved.psr.value); + cmb_println("=============================================================="); + + /* the Cortex-M0 is not support fault diagnosis */ +#if (CMB_CPU_PLATFORM_TYPE != CMB_CPU_ARM_CORTEX_M0) + regs.syshndctrl.value = CMB_SYSHND_CTRL; // System Handler Control and State Register + regs.mfsr.value = CMB_NVIC_MFSR; // Memory Fault Status Register + regs.mmar = CMB_NVIC_MMAR; // Memory Management Fault Address Register + regs.bfsr.value = CMB_NVIC_BFSR; // Bus Fault Status Register + regs.bfar = CMB_NVIC_BFAR; // Bus Fault Manage Address Register + regs.ufsr.value = CMB_NVIC_UFSR; // Usage Fault Status Register + regs.hfsr.value = CMB_NVIC_HFSR; // Hard Fault Status Register + regs.dfsr.value = CMB_NVIC_DFSR; // Debug Fault Status Register + regs.afsr = CMB_NVIC_AFSR; // Auxiliary Fault Status Register + + fault_diagnosis(); +#endif + + print_call_stack(stack_pointer); +} diff --git a/cm_backtrace.h b/cm_backtrace.h new file mode 100644 index 0000000..9fc9a61 --- /dev/null +++ b/cm_backtrace.h @@ -0,0 +1,40 @@ +/* + * This file is part of the CmBacktrace Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is an head file for this library. You can see all be called functions. + * Created on: 2016-12-15 + */ + +#ifndef _CORTEXM_BACKTRACE_H_ +#define _CORTEXM_BACKTRACE_H_ + +#include "cmb_def.h" + +void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver); +void cm_backtrace_firmware_info(void); +size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp); +void cm_backtrace_assert(uint32_t sp); +void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp); + +#endif /* _CORTEXM_BACKTRACE_H_ */ diff --git a/cmb_cfg.h b/cmb_cfg.h new file mode 100644 index 0000000..26e04f6 --- /dev/null +++ b/cmb_cfg.h @@ -0,0 +1,63 @@ +/* + * This file is part of the CmBacktrace Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the configure head file for this library. + * Created on: 2016-12-15 + */ + +#ifndef _CMB_CFG_H_ +#define _CMB_CFG_H_ + +/* print line, must config by user */ +#include +#define cmb_println(...) rt_kprintf(__VA_ARGS__);rt_kprintf("\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 +/* cpu platform type, must config by user */ +#if defined(PKG_CMBACKTRACE_PLATFORM_M0_M0PLUS) + #define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M0 +#elif defined(PKG_CMBACKTRACE_PLATFORM_M3) + #define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M3 +#elif defined(PKG_CMBACKTRACE_PLATFORM_M4) + #define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M4 +#elif defined(PKG_CMBACKTRACE_PLATFORM_M7) + #define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M7 +#else + #error "You must select a CPU platform on menuconfig" +#endif /* PKG_CMBACKTRACE_PLATFORM_M0_M0PLUS */ +/* enable dump stack information */ +#if defined(PKG_CMBACKTRACE_DUMP_STACK) + #define CMB_USING_DUMP_STACK_INFO +#endif +/* language of print information */ +#if defined(PKG_CMBACKTRACE_PRINT_ENGLISH) + #define CMB_PRINT_LANGUAGE CMB_PRINT_LANGUAGE_ENGLISH +#if defined(PKG_CMBACKTRACE_PRINT_CHINESE) + #define CMB_PRINT_LANGUAGE CMB_PRINT_LANUUAGE_CHINESE +#endif /* PKG_CMBACKTRACE_PRINT_ENGLISH */ +#endif /* _CMB_CFG_H_ */ diff --git a/cmb_def.h b/cmb_def.h new file mode 100644 index 0000000..b4d0db8 --- /dev/null +++ b/cmb_def.h @@ -0,0 +1,335 @@ +/* + * This file is part of the CmBacktrace Library. + * + * Copyright (c) 2016-2017, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the macro definition head file for this library. + * Created on: 2016-12-15 + */ + +#ifndef _CMB_DEF_H_ +#define _CMB_DEF_H_ + +#include +#include +#include + +/* library software version number */ +#define CMB_SW_VERSION "0.2.0" + +#define CMB_CPU_ARM_CORTEX_M0 0 +#define CMB_CPU_ARM_CORTEX_M3 1 +#define CMB_CPU_ARM_CORTEX_M4 2 +#define CMB_CPU_ARM_CORTEX_M7 3 + +#define CMB_OS_PLATFORM_RTT 0 +#define CMB_OS_PLATFORM_UCOSII 1 +#define CMB_OS_PLATFORM_UCOSIII 2 +#define CMB_OS_PLATFORM_FREERTOS 3 + +#define CMB_PRINT_LANGUAGE_ENGLISH 0 +#define CMB_PRINT_LANUUAGE_CHINESE 1 + +/* name max length, default size: 32 */ +#ifndef CMB_NAME_MAX +#define CMB_NAME_MAX 32 +#endif + +/* print information language, default is English */ +#ifndef CMB_PRINT_LANGUAGE +#define CMB_PRINT_LANGUAGE CMB_PRINT_LANGUAGE_ENGLISH +#endif + + +#if defined(__CC_ARM) + /* C stack block name, default is STACK */ + #ifndef CMB_CSTACK_BLOCK_NAME + #define CMB_CSTACK_BLOCK_NAME STACK + #endif + /* code section name, default is ER_IROM1 */ + #ifndef CMB_CODE_SECTION_NAME + #define CMB_CODE_SECTION_NAME ER_IROM1 + #endif +#elif defined(__ICCARM__) + /* C stack block name, default is 'CSTACK' */ + #ifndef CMB_CSTACK_BLOCK_NAME + #define CMB_CSTACK_BLOCK_NAME "CSTACK" + #endif + /* code section name, default is '.text' */ + #ifndef CMB_CODE_SECTION_NAME + #define CMB_CODE_SECTION_NAME ".text" + #endif +#elif defined(__GNUC__) + /* C stack block start address, defined on linker script file, default is _sstack */ + #ifndef CMB_CSTACK_BLOCK_START + #define CMB_CSTACK_BLOCK_START _sstack + #endif + /* C stack block end address, defined on linker script file, default is _estack */ + #ifndef CMB_CSTACK_BLOCK_END + #define CMB_CSTACK_BLOCK_END _estack + #endif + /* code section start address, defined on linker script file, default is _stext */ + #ifndef CMB_CODE_SECTION_START + #define CMB_CODE_SECTION_START _stext + #endif + /* code section end address, defined on linker script file, default is _etext */ + #ifndef CMB_CODE_SECTION_END + #define CMB_CODE_SECTION_END _etext + #endif +#else + #error "not supported compiler" +#endif + +/* supported function call stack max depth, default is 16 */ +#ifndef CMB_CALL_STACK_MAX_DEPTH +#define CMB_CALL_STACK_MAX_DEPTH 16 +#endif + +/* system handler control and state register */ +#ifndef CMB_SYSHND_CTRL +#define CMB_SYSHND_CTRL (*(volatile unsigned int*) (0xE000ED24u)) +#endif + +/* memory management fault status register */ +#ifndef CMB_NVIC_MFSR +#define CMB_NVIC_MFSR (*(volatile unsigned char*) (0xE000ED28u)) +#endif + +/* bus fault status register */ +#ifndef CMB_NVIC_BFSR +#define CMB_NVIC_BFSR (*(volatile unsigned char*) (0xE000ED29u)) +#endif + +/* usage fault status register */ +#ifndef CMB_NVIC_UFSR +#define CMB_NVIC_UFSR (*(volatile unsigned short*)(0xE000ED2Au)) +#endif + +/* hard fault status register */ +#ifndef CMB_NVIC_HFSR +#define CMB_NVIC_HFSR (*(volatile unsigned int*) (0xE000ED2Cu)) +#endif + +/* debug fault status register */ +#ifndef CMB_NVIC_DFSR +#define CMB_NVIC_DFSR (*(volatile unsigned short*)(0xE000ED30u)) +#endif + +/* memory management fault address register */ +#ifndef CMB_NVIC_MMAR +#define CMB_NVIC_MMAR (*(volatile unsigned int*) (0xE000ED34u)) +#endif + +/* bus fault manage address register */ +#ifndef CMB_NVIC_BFAR +#define CMB_NVIC_BFAR (*(volatile unsigned int*) (0xE000ED38u)) +#endif + +/* auxiliary fault status register */ +#ifndef CMB_NVIC_AFSR +#define CMB_NVIC_AFSR (*(volatile unsigned short*)(0xE000ED3Cu)) +#endif + +/** + * Cortex-M fault registers + */ +struct cmb_hard_fault_regs{ + struct { + unsigned int r0; // Register R0 + unsigned int r1; // Register R1 + unsigned int r2; // Register R2 + unsigned int r3; // Register R3 + unsigned int r12; // Register R12 + unsigned int lr; // Link register + unsigned int pc; // Program counter + union { + unsigned int value; + struct { + unsigned int IPSR : 8; // Interrupt Program Status register (IPSR) + unsigned int EPSR : 19; // Execution Program Status register (EPSR) + unsigned int APSR : 5; // Application Program Status register (APSR) + } bits; + } psr; // Program status register. + } saved; + + union { + unsigned int value; + struct { + unsigned int MEMFAULTACT : 1; // Read as 1 if memory management fault is active + unsigned int BUSFAULTACT : 1; // Read as 1 if bus fault exception is active + unsigned int UnusedBits1 : 1; + unsigned int USGFAULTACT : 1; // Read as 1 if usage fault exception is active + unsigned int UnusedBits2 : 3; + unsigned int SVCALLACT : 1; // Read as 1 if SVC exception is active + unsigned int MONITORACT : 1; // Read as 1 if debug monitor exception is active + unsigned int UnusedBits3 : 1; + unsigned int PENDSVACT : 1; // Read as 1 if PendSV exception is active + unsigned int SYSTICKACT : 1; // Read as 1 if SYSTICK exception is active + unsigned int USGFAULTPENDED : 1; // Usage fault pended; usage fault started but was replaced by a higher-priority exception + unsigned int MEMFAULTPENDED : 1; // Memory management fault pended; memory management fault started but was replaced by a higher-priority exception + unsigned int BUSFAULTPENDED : 1; // Bus fault pended; bus fault handler was started but was replaced by a higher-priority exception + unsigned int SVCALLPENDED : 1; // SVC pended; SVC was started but was replaced by a higher-priority exception + unsigned int MEMFAULTENA : 1; // Memory management fault handler enable + unsigned int BUSFAULTENA : 1; // Bus fault handler enable + unsigned int USGFAULTENA : 1; // Usage fault handler enable + } bits; + } syshndctrl; // System Handler Control and State Register (0xE000ED24) + + union { + unsigned char value; + struct { + unsigned char IACCVIOL : 1; // Instruction access violation + unsigned char DACCVIOL : 1; // Data access violation + unsigned char UnusedBits : 1; + unsigned char MUNSTKERR : 1; // Unstacking error + unsigned char MSTKERR : 1; // Stacking error + unsigned char MLSPERR : 1; // Floating-point lazy state preservation (M4/M7) + unsigned char UnusedBits2 : 1; + unsigned char MMARVALID : 1; // Indicates the MMAR is valid + } bits; + } mfsr; // Memory Management Fault Status Register (0xE000ED28) + unsigned int mmar; // Memory Management Fault Address Register (0xE000ED34) + + union { + unsigned char value; + struct { + unsigned char IBUSERR : 1; // Instruction access violation + unsigned char PRECISERR : 1; // Precise data access violation + unsigned char IMPREISERR : 1; // Imprecise data access violation + unsigned char UNSTKERR : 1; // Unstacking error + unsigned char STKERR : 1; // Stacking error + unsigned char LSPERR : 1; // Floating-point lazy state preservation (M4/M7) + unsigned char UnusedBits : 1; + unsigned char BFARVALID : 1; // Indicates BFAR is valid + } bits; + } bfsr; // Bus Fault Status Register (0xE000ED29) + unsigned int bfar; // Bus Fault Manage Address Register (0xE000ED38) + + union { + unsigned short value; + struct { + unsigned short UNDEFINSTR : 1; // Attempts to execute an undefined instruction + unsigned short INVSTATE : 1; // Attempts to switch to an invalid state (e.g., ARM) + unsigned short INVPC : 1; // Attempts to do an exception with a bad value in the EXC_RETURN number + unsigned short NOCP : 1; // Attempts to execute a coprocessor instruction + unsigned short UnusedBits : 4; + unsigned short UNALIGNED : 1; // Indicates that an unaligned access fault has taken place + unsigned short DIVBYZERO : 1; // Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set) + } bits; + } ufsr; // Usage Fault Status Register (0xE000ED2A) + + union { + unsigned int value; + struct { + unsigned int UnusedBits : 1; + unsigned int VECTBL : 1; // Indicates hard fault is caused by failed vector fetch + unsigned int UnusedBits2 : 28; + unsigned int FORCED : 1; // Indicates hard fault is taken because of bus fault/memory management fault/usage fault + unsigned int DEBUGEVT : 1; // Indicates hard fault is triggered by debug event + } bits; + } hfsr; // Hard Fault Status Register (0xE000ED2C) + + union { + unsigned int value; + struct { + unsigned int HALTED : 1; // Halt requested in NVIC + unsigned int BKPT : 1; // BKPT instruction executed + unsigned int DWTTRAP : 1; // DWT match occurred + unsigned int VCATCH : 1; // Vector fetch occurred + unsigned int EXTERNAL : 1; // EDBGRQ signal asserted + } bits; + } dfsr; // Debug Fault Status Register (0xE000ED30) + + unsigned int afsr; // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional) +}; + +/* assert for developer. */ +#define CMB_ASSERT(EXPR) \ +if (!(EXPR)) \ +{ \ + cmb_println("(%s) has assert failed at %s.", #EXPR, __FUNCTION__); \ + while (1); \ +} + +/* ELF(Executable and Linking Format) file extension name for each compiler */ +#if defined(__CC_ARM) + #define CMB_ELF_FILE_EXTENSION_NAME ".axf" +#elif defined(__ICCARM__) + #define CMB_ELF_FILE_EXTENSION_NAME ".out" +#elif defined(__GNUC__) + #define CMB_ELF_FILE_EXTENSION_NAME ".elf" +#else + #error "not supported compiler" +#endif + +#ifndef cmb_println + #error "cmb_println isn't defined in 'cmb_cfg.h'" +#endif + +#ifndef CMB_CPU_PLATFORM_TYPE + #error "CMB_CPU_PLATFORM_TYPE isn't defined in 'cmb_cfg.h'" +#endif + +#if __STDC_VERSION__ < 199901L + #error "not supported compiler, must be C99 or higher. try to add '-std=c99' to compile parameters" +#endif + +#if (defined(CMB_USING_BARE_METAL_PLATFORM) && defined(CMB_USING_OS_PLATFORM)) + #error "CMB_USING_BARE_METAL_PLATFORM and CMB_USING_OS_PLATFORM only one of them can be used" +#elif defined(CMB_USING_OS_PLATFORM) + #if !defined(CMB_OS_PLATFORM_TYPE) + #error "CMB_OS_PLATFORM_TYPE isn't defined in 'cmb_cfg.h'" + #endif /* !defined(CMB_OS_PLATFORM_TYPE) */ + #if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) + #include + #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII) + #include + #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII) + #include + //TODO ������ + #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS) + #error "not implemented, I hope you can do this" + //TODO ��ʵ�� + #else + #error "not supported OS type" + #endif /* (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) */ +#endif /* (defined(CMB_USING_BARE_METAL_PLATFORM) && defined(CMB_USING_OS_PLATFORM)) */ + +/* include or export for supported, __get_SP function */ +#if defined(__CC_ARM) + static __inline __asm uint32_t __get_SP(void) { + mov r0, sp + bx lr + } +#elif defined(__ICCARM__) + #include +#elif defined(__GNUC__) + __attribute__( ( always_inline ) ) static inline uint32_t __get_SP(void) { + register uint32_t result; + __asm volatile ("MOV %0, sp\n" : "=r" (result) ); + return(result); + } +#else + #error "not supported compiler" +#endif + +#endif /* _CMB_DEF_H_ */