├── LICENSE ├── Languages ├── README.md ├── en-US │ └── cmb_en_US.h └── zh-CN │ ├── cmb_zh_CN.h │ └── cmb_zh_CN_UTF8.h ├── README.md ├── README_ZH.md ├── SConscript ├── cm_backtrace.c ├── cm_backtrace.h ├── cmb_cfg.h ├── cmb_def.h ├── cmb_flash_log.c └── cmb_port.c /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2019 Armink (armink.ztl@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Languages/README.md: -------------------------------------------------------------------------------- 1 | # CmBacktrace: ARM Cortex-M 系列 MCU 错误追踪库 2 | 3 | ## 多语言支持 4 | 5 | | Language | Location (or type) | Language tag | 6 | |----------------------|----------------------------|--------------| 7 | | English | United States | en-US | 8 | | Chinese (Simplified) | People's Republic of China | zh-CN | -------------------------------------------------------------------------------- /Languages/en-US/cmb_en_US.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the CmBacktrace Library. 3 | * 4 | * Copyright (c) 2020, Armink, 5 | * Chenxuan, 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * 'Software'), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject to 13 | * the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | * 26 | * NOTE: DO NOT include this file on the header file. 27 | * Encoding: UTF-8 28 | * Created on: 2020-09-06 29 | */ 30 | 31 | [PRINT_MAIN_STACK_CFG_ERROR] = "ERROR: Unable to get the main stack information, please check the configuration of the main stack", 32 | [PRINT_FIRMWARE_INFO] = "Firmware name: %s, hardware version: %s, software version: %s", 33 | [PRINT_ASSERT_ON_THREAD] = "Assert on thread %s", 34 | [PRINT_ASSERT_ON_HANDLER] = "Assert on interrupt or bare metal(no OS) environment", 35 | [PRINT_THREAD_STACK_INFO] = "===== Thread stack information =====", 36 | [PRINT_MAIN_STACK_INFO] = "====== Main stack information ======", 37 | [PRINT_THREAD_STACK_OVERFLOW] = "Error: Thread stack(%08x) was overflow", 38 | [PRINT_MAIN_STACK_OVERFLOW] = "Error: Main stack(%08x) was overflow", 39 | [PRINT_CALL_STACK_INFO] = "Show more call stack info by run: addr2line -e %s%s -afpiC %.*s", 40 | [PRINT_CALL_STACK_ERR] = "Dump call stack has an error", 41 | [PRINT_FAULT_ON_THREAD] = "Fault on thread %s", 42 | [PRINT_FAULT_ON_HANDLER] = "Fault on interrupt or bare metal(no OS) environment", 43 | [PRINT_REGS_TITLE] = "=================== Registers information ====================", 44 | [PRINT_HFSR_VECTBL] = "Hard fault is caused by failed vector fetch", 45 | [PRINT_MFSR_IACCVIOL] = "Memory management fault is caused by instruction access violation", 46 | [PRINT_MFSR_DACCVIOL] = "Memory management fault is caused by data access violation", 47 | [PRINT_MFSR_MUNSTKERR] = "Memory management fault is caused by unstacking error", 48 | [PRINT_MFSR_MSTKERR] = "Memory management fault is caused by stacking error", 49 | [PRINT_MFSR_MLSPERR] = "Memory management fault is caused by floating-point lazy state preservation", 50 | [PRINT_BFSR_IBUSERR] = "Bus fault is caused by instruction access violation", 51 | [PRINT_BFSR_PRECISERR] = "Bus fault is caused by precise data access violation", 52 | [PRINT_BFSR_IMPREISERR] = "Bus fault is caused by imprecise data access violation", 53 | [PRINT_BFSR_UNSTKERR] = "Bus fault is caused by unstacking error", 54 | [PRINT_BFSR_STKERR] = "Bus fault is caused by stacking error", 55 | [PRINT_BFSR_LSPERR] = "Bus fault is caused by floating-point lazy state preservation", 56 | [PRINT_UFSR_UNDEFINSTR] = "Usage fault is caused by attempts to execute an undefined instruction", 57 | [PRINT_UFSR_INVSTATE] = "Usage fault is caused by attempts to switch to an invalid state (e.g., ARM)", 58 | [PRINT_UFSR_INVPC] = "Usage fault is caused by attempts to do an exception with a bad value in the EXC_RETURN number", 59 | [PRINT_UFSR_NOCP] = "Usage fault is caused by attempts to execute a coprocessor instruction", 60 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 61 | [PRINT_UFSR_STKOF] = "Usage fault is caused by indicates that a stack overflow (hardware check) has taken place", 62 | #endif 63 | [PRINT_UFSR_UNALIGNED] = "Usage fault is caused by indicates that an unaligned access fault has taken place", 64 | [PRINT_UFSR_DIVBYZERO0] = "Usage fault is caused by Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set)", 65 | [PRINT_DFSR_HALTED] = "Debug fault is caused by halt requested in NVIC", 66 | [PRINT_DFSR_BKPT] = "Debug fault is caused by BKPT instruction executed", 67 | [PRINT_DFSR_DWTTRAP] = "Debug fault is caused by DWT match occurred", 68 | [PRINT_DFSR_VCATCH] = "Debug fault is caused by Vector fetch occurred", 69 | [PRINT_DFSR_EXTERNAL] = "Debug fault is caused by EDBGRQ signal asserted", 70 | [PRINT_MMAR] = "The memory management fault occurred address is %08x", 71 | [PRINT_BFAR] = "The bus fault occurred address is %08x", 72 | -------------------------------------------------------------------------------- /Languages/zh-CN/cmb_zh_CN.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/CmBacktrace/15d00d6fc772f77fa851301a0e87d9b86777c48e/Languages/zh-CN/cmb_zh_CN.h -------------------------------------------------------------------------------- /Languages/zh-CN/cmb_zh_CN_UTF8.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the CmBacktrace Library. 3 | * 4 | * Copyright (c) 2020, Armink, 5 | * Chenxuan, 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * 'Software'), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject to 13 | * the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | * 26 | * NOTE: DO NOT include this file on the header file. 27 | * Encoding: UTF-8 28 | * Created on: 2020-09-06 29 | */ 30 | 31 | [PRINT_MAIN_STACK_CFG_ERROR] = "错误:无法获取主栈信息,请检查主栈的相关配置", 32 | [PRINT_FIRMWARE_INFO] = "固件名称:%s,硬件版本号:%s,软件版本号:%s", 33 | [PRINT_ASSERT_ON_THREAD] = "在线程(%s)中发生断言", 34 | [PRINT_ASSERT_ON_HANDLER] = "在中断或裸机环境下发生断言", 35 | [PRINT_THREAD_STACK_INFO] = "=========== 线程堆栈信息 ===========", 36 | [PRINT_MAIN_STACK_INFO] = "============ 主堆栈信息 ============", 37 | [PRINT_THREAD_STACK_OVERFLOW] = "错误:线程栈(%08x)发生溢出", 38 | [PRINT_MAIN_STACK_OVERFLOW] = "错误:主栈(%08x)发生溢出", 39 | [PRINT_CALL_STACK_INFO] = "查看更多函数调用栈信息,请运行:addr2line -e %s%s -afpiC %.*s", 40 | [PRINT_CALL_STACK_ERR] = "获取函数调用栈失败", 41 | [PRINT_FAULT_ON_THREAD] = "在线程(%s)中发生错误异常", 42 | [PRINT_FAULT_ON_HANDLER] = "在中断或裸机环境下发生错误异常", 43 | [PRINT_REGS_TITLE] = "========================= 寄存器信息 =========================", 44 | [PRINT_HFSR_VECTBL] = "发生硬错误,原因:取中断向量时出错", 45 | [PRINT_MFSR_IACCVIOL] = "发生存储器管理错误,原因:企图从不允许访问的区域取指令", 46 | [PRINT_MFSR_DACCVIOL] = "发生存储器管理错误,原因:企图从不允许访问的区域读、写数据", 47 | [PRINT_MFSR_MUNSTKERR] = "发生存储器管理错误,原因:出栈时企图访问不被允许的区域", 48 | [PRINT_MFSR_MSTKERR] = "发生存储器管理错误,原因:入栈时企图访问不被允许的区域", 49 | [PRINT_MFSR_MLSPERR] = "发生存储器管理错误,原因:惰性保存浮点状态时发生错误", 50 | [PRINT_BFSR_IBUSERR] = "发生总线错误,原因:指令总线错误", 51 | [PRINT_BFSR_PRECISERR] = "发生总线错误,原因:精确的数据总线错误", 52 | [PRINT_BFSR_IMPREISERR] = "发生总线错误,原因:不精确的数据总线错误", 53 | [PRINT_BFSR_UNSTKERR] = "发生总线错误,原因:出栈时发生错误", 54 | [PRINT_BFSR_STKERR] = "发生总线错误,原因:入栈时发生错误", 55 | [PRINT_BFSR_LSPERR] = "发生总线错误,原因:惰性保存浮点状态时发生错误", 56 | [PRINT_UFSR_UNDEFINSTR] = "发生用法错误,原因:企图执行未定义指令", 57 | [PRINT_UFSR_INVSTATE] = "发生用法错误,原因:试图切换到 ARM 状态", 58 | [PRINT_UFSR_INVPC] = "发生用法错误,原因:无效的异常返回码", 59 | [PRINT_UFSR_NOCP] = "发生用法错误,原因:企图执行协处理器指令", 60 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 61 | [PRINT_UFSR_STKOF] = "发生用法错误,原因:硬件检测到栈溢出", 62 | #endif 63 | [PRINT_UFSR_UNALIGNED] = "发生用法错误,原因:企图执行非对齐访问", 64 | [PRINT_UFSR_DIVBYZERO0] = "发生用法错误,原因:企图执行除 0 操作", 65 | [PRINT_DFSR_HALTED] = "发生调试错误,原因:NVIC 停机请求", 66 | [PRINT_DFSR_BKPT] = "发生调试错误,原因:执行 BKPT 指令", 67 | [PRINT_DFSR_DWTTRAP] = "发生调试错误,原因:数据监测点匹配", 68 | [PRINT_DFSR_VCATCH] = "发生调试错误,原因:发生向量捕获", 69 | [PRINT_DFSR_EXTERNAL] = "发生调试错误,原因:外部调试请求", 70 | [PRINT_MMAR] = "发生存储器管理错误的地址:%08x", 71 | [PRINT_BFAR] = "发生总线错误的地址:%08x", 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CmBacktrace: ARM Cortex-M series MCU error tracking library 2 | 3 | [中文页](README_ZH.md) | English 4 | 5 | [![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/1.4.0.svg)](https://github.com/armink/CmBacktrace/compare/1.0.0...master) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/armink/CmBacktrace/master/LICENSE) 6 | 7 | ## 0、What is CmBacktrace 8 | 9 | [CmBacktrace](https://github.com/armink/CmBacktrace) (Cortex Microcontroller Backtrace) is an open source library that automatically tracks and locates error codes for ARM Cortex-M series MCUs, and automatically analyzes the causes of errors. The main features are as follows: 10 | 11 | - Supported errors include: 12 | - Assert 13 | - Fault (Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault) 14 | - Failure reason **Automatic diagnosis**: When a failure occurs, the cause of the failure can be automatically analyzed, and the code location of the failure can be located, without the need to manually analyze the complicated fault registers; 15 | -Output the **function call stack** of the error site (need to cooperate with the addr2line tool for precise positioning), restore the field information when the error occurred, and locate the problem code location and logic more quickly and accurately. You can also use the library under normal conditions to get the current function call stack; 16 | - Support bare metal and the following operating system platforms: 17 | - [RT-Thread](http://www.rt-thread.org/) 18 | - UCOS 19 | - FreeRTOS (source code needs to be modified) 20 | - According to the error scene status, output the corresponding thread stack or C main stack; 21 | - The fault diagnosis information supports multiple languages ​​(currently: Simplified Chinese, English); 22 | - Adapt to Cortex-M0/M3/M4/M7 MCU; 23 | - Support IAR, KEIL, GCC compiler; 24 | 25 | ## 1. Why choose CmBacktrace 26 | 27 | **Beginner Newcomer**: For newcomers who switch from simple microcontrollers such as C51 and MSP430 to more complex ARM, the chance of "hard falut" death from time to time makes newcomers instantly stunned. The method of locating errors is often to connect to the emulator, step by step F10/F11, locate the specific error code, and then guess, eliminate, and deliberate on the cause of the error. This process is very painful. 28 | 29 | **Skilled veteran**: Slowly everyone knows that the fault cause and fault code address can be located through the fault register information. Although this can solve a small part of the problem, the repeated and tedious analysis process will also delay a lot of time. Moreover, for some complex problems, it is impossible to solve only by the code address, and the function call logic relationship of the wrong scene must be restored. Although the function call stack can be viewed by connecting to the emulator, it cannot be displayed in the fault state, so it is necessary to step by step F10/F11 to locate the error code. In addition, there are two scenarios, 30 | 31 | 1. The emulator must be disconnected when debugging many products 32 | 2. The problem does exist, but it is extremely difficult to reproduce 33 | 34 | So locating such problems is even more difficult. 35 | 36 | **Use this library**: All the above problems can be solved easily. The error message can be output to the console, and the error message can also be saved using the Log function of [EasyFlash](https://github.com/armink/EasyFlash) In Flash, the last error message can still be read after the device crashes and restarts. The information output by CmBacktrace includes function call stack, fault diagnosis result, stack, fault register and product firmware information, which greatly improves the efficiency and accuracy of error location. 37 | 38 | As the saying goes, a worker must first sharpen his tools if he wants to do his job well. So sometimes the reason for the low efficiency may be that you will use too few types of tools. 39 | 40 | **Cooperation, Contribution**: The development of open source software is inseparable from everyone's support. You are welcome to make more suggestions and hope that more people will join in and improve together. If you think this open source project is great, you can click [Project Homepage](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** in the upper right corner, At the same time recommend it to more friends in need. 41 | 42 | ## 2. How to use CmBacktrace 43 | 44 | ### 2.1 Demo 45 | 46 | The demonstration is divided into the following steps: 47 | 48 | 1. Manufacturing division by zero exception ([IAR project, click to view source code](https://github.com/armink/CmBacktrace/tree/master/demos/non_os/stm32f10x/app/src)) 49 | 2. View error diagnosis information 50 | 3. View the basic information of the function call stack 51 | 4. Enter the path where the executable file is stored in the project project through the command line tool 52 | 5. Use the addr2line command to view the function call stack details and locate the error code 53 | 54 | [![cm_backtrace_demo](https://raw.githubusercontent.com/armink/CmBacktrace/master/docs/zh/images/cm_backtrace_demo.gif)](https://github.com/armink/CmBacktrace) 55 | 56 | ### 2.2 Demo 57 | 58 | |Catalog|Platform|Link| 59 | |:--|:--:|:--:| 60 | | `\demos\non_os\stm32f10x` |bare metal STM32 Cortex-M3|[click to view](https://github.com/armink/CmBacktrace/tree/master/demos/non_os/stm32f10x)| 61 | | `\demos\os\rtthread\stm32f4xx`|RT-Thread STM32 Cortex-M4|[Click to view](https://github.com/armink/CmBacktrace/tree/master/demos/os/rtthread/stm32f4xx)| 62 | | `\demos\os\ucosii\stm32f10x` |UCOSII STM32 Cortex-M3|[Click to view](https://github.com/armink/CmBacktrace/tree/master/demos/os/ucosii/stm32f10x)| 63 | | `\demos\os\freertos\stm32f10x` |FreeRTOS STM32 Cortex-M3|[Click to view](https://github.com/armink/CmBacktrace/tree/master/demos/os/freertos/stm32f10x)| 64 | 65 | 66 | 67 | ### 2.3 Porting instructions 68 | 69 | #### 2.3.1 Preparation 70 | 71 | 1. Check whether there is a suitable Demo in the `\demos` directory, if there is a similar, it is recommended to modify it on the basis of it 72 | 2. Identify the operating system/bare metal platform and CPU platform 73 | 3. Add all source files under `\src` to the product project, and ensure that the source code directory is added to the header file path 74 | 4. The cmb_fault.s assembly file ([click to view](https://github.com/armink/CmBacktrace/tree/master/cm_backtrace/fault_handler)) can be optionally added to the project. After adding, you need to add the original project `HardFault_Handler` comment out 75 | 5. Put the `cm_backtrace_init` function in the project initialization place for execution 76 | 6. Put `cm_backtrace_assert` in the assertion function of the project for execution. For specific usage, please refer to the API description below 77 | 7. If the cmb_fault.s assembly file is not enabled in step 4, you need to put `cm_backtrace_fault` in the fault handling function (for example: `HardFault_Handler`) for execution. For details, refer to the API description below 78 | 79 | #### 2.3.2 Configuration Instructions 80 | 81 | Configuration file name: `cmb_cfg.h`, users need to manually configure themselves for different platforms and scenarios. Common configurations are as follows: 82 | 83 | | Configuration name | Function | Remarks | 84 | |:--|:--|:--| 85 | |cmb_println(...)|Error and diagnostic information output|Must be configured| 86 | |CMB_USING_BARE_METAL_PLATFORM|Whether it is used on a bare metal platform|Define this macro if it is used| 87 | |CMB_USING_OS_PLATFORM|Whether it is used on the operating system platform|Operating system and bare metal must choose one of two| 88 | |CMB_OS_PLATFORM_TYPE|Operating System Platform|RTT/UCOSII/UCOSIII/FREERTOS| 89 | |CMB_CPU_PLATFORM_TYPE|CPU platform|M0/M3/M4/M7| 90 | |CMB_USING_DUMP_STACK_INFO|Whether to use Dump stack function|Use to define this macro| 91 | |CMB_PRINT_LANGUAGE|Language when outputting information|CHINESE/ENGLISH| 92 | 93 | > Note: The content of the above configuration can be selected in `cmb_def.h`, please read the source code for more flexible configuration 94 | 95 | ### 2.4 API description 96 | 97 | #### 2.4.1 Library initialization 98 | 99 | ```C 100 | void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver) 101 | ``` 102 | 103 | |Parameter |Description| 104 | |:----- |:----| 105 | |firmware_name |firmware name, which must correspond to the firmware name generated by the compiler| 106 | |hardware_ver |The hardware version number corresponding to the firmware| 107 | |software_ver |Software version number of the firmware| 108 | 109 | > **Note**: The above input parameters will be output in case of assertion or failure, mainly for retrospective purposes 110 | 111 | #### 2.4.2 Get function call stack 112 | 113 | ```C 114 | size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp) 115 | ``` 116 | 117 | |Parameter |Description| 118 | |:----- |:----| 119 | |buffer |Buffer for storing function call stack| 120 | |size |Buffer size| 121 | |sp |Stack pointer to be obtained| 122 | 123 | Example: 124 | 125 | ```C 126 | /* Create a function call stack buffer with a depth of 16, and the depth should not exceed CMB_CALL_STACK_MAX_DEPTH (default 16) */ 127 | uint32_t call_stack[16] = {0}; 128 | size_t i, depth = 0; 129 | /* Get the function call stack in the current environment, each element will be stored as a 32-bit address, and depth is the actual depth of the function call stack */ 130 | depth = cm_backtrace_call_stack(call_stack, sizeof(call_stack), cmb_get_sp()); 131 | /* Output current function call stack information 132 | * Note: When viewing the function name and specific line number, you need to use the addr2line tool to convert 133 | */ 134 | for (i = 0; i **Note**: The input parameter SP should be obtained inside the assertion function as much as possible, and as close as possible to the beginning of the assertion function. When used in the sub-function of the assertion function (for example: in the assertion hook method of RT-Thread), due to the nesting of the function, there will be the operation of registering the stack, and the SP will be changed at this time, and manual adjustment is required ( Add and subtract a fixed deviation value) into the parameter value, so as a novice, it is not recommended to use this function in the sub-function of the assertion. 150 | 151 | #### 2.4.4 Tracking fault information 152 | 153 | ```C 154 | void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp) 155 | ``` 156 | 157 | |Parameter |Description| 158 | |:----- |:----| 159 | |fault_handler_lr |LR register value under fault handling function environment| 160 | |fault_handler_sp |SP register value under fault handling function environment| 161 | 162 | This function can be called in the fault handling function (for example: `HardFault_Handler`). In addition, the library itself provides assembly files processed by `HardFault` ([click to view](https://github.com/armink/CmBacktrace/tree/master/cm_backtrace/fault_handler), you need to choose according to your own compiler), The `cm_backtrace_fault` method is automatically called in the event of a fault. So when transplanting, the easiest way is to use the assembly file directly. 163 | 164 | ### 2.5 FAQ 165 | 166 | #### 2.5.1 Compilation error, suggesting that C99 support is required 167 | 168 | [Click to view the tutorial: Open Keil/IAR/GCC C99 support in one step](https://github.com/armink/CmBacktrace/blob/master/docs/zh/enable_c99_for_keil_iar_gcc.md) 169 | 170 | #### 2.5.2 How to view the specific name and code line number of the function in the function call stack 171 | 172 | [Click to view the tutorial: How to use the addr2line tool to get the function call stack details](https://github.com/armink/CmBacktrace/blob/master/docs/zh/how_to_use_addr2line_for_call_stack.md) 173 | 174 | #### 2.5.3 Fault handling function: HardFault_Handler repeated definition 175 | 176 | When the cmb_fault.s assembly file provided by this library is used, because HardFault_Handler is already defined in the assembly file, if this function is defined elsewhere in the project, an error that HardFault_Handler is repeatedly defined will be prompted. There are two solutions at this time: 177 | 178 | 1. Comment/delete the `HardFault_Handler` function defined in other files, and only keep the cmb_fault.s; 179 | 2. Remove cmb_fault.s from the project, and manually add the `cm_backtrace_fault` function to the existing fault handling function, but it should be noted that you must ** ensure the readiness of the function to enter the parameters**, otherwise it may cause The fault diagnosis function and stack printing function cannot operate normally. So if you are a novice, the second solution is not recommended. 180 | 181 | #### 2.5.4 Prompt that the main stack information cannot be obtained during initialization 182 | 183 | The default main stack configuration is defined in `cmd_def.h`, roughly as follows: 184 | 185 | ```c 186 | 187 | #if defined(__CC_ARM) 188 | /* C stack block name,default is STACK */ 189 | #ifndef CMB_CSTACK_BLOCK_NAME 190 | #define CMB_CSTACK_BLOCK_NAME STACK 191 | #endif 192 | ... 193 | #elif defined(__ICCARM__) 194 | /* C stack block name, default is'CSTACK' */ 195 | #ifndef CMB_CSTACK_BLOCK_NAME 196 | #define CMB_CSTACK_BLOCK_NAME "CSTACK" 197 | #endif 198 | ... 199 | #elif defined(__GNUC__) 200 | /* C stack block start address, defined on linker script file, default is _sstack */ 201 | #ifndef CMB_CSTACK_BLOCK_START 202 | #define CMB_CSTACK_BLOCK_START _sstack 203 | #endif 204 | /* C stack block end address, defined on linker script file, default is _estack */ 205 | #ifndef CMB_CSTACK_BLOCK_END 206 | #define CMB_CSTACK_BLOCK_END _estack 207 | #endif 208 | ... 209 | #else 210 | ``` 211 | 212 | For example, under the Keil-MDK compiler, `STACK` will be selected as the name of the main stack block by default, but under some special platforms, the name of the main stack block of the project may not be called `STACK`, causing CmBacktrace to fail to obtain the correct main stack Information, so there will be the above error message during initialization. 213 | 214 | There are generally two ways to solve this problem 215 | 216 | 1. Redefine the main stack information in `cmb_cfg.h`. At this time, CmBacktrace will give priority to using the configuration information in `cmb_cfg.h`; 217 | 2. Modify the project configuration. If it is Keil-MDK, change the name of the main stack to the default `STACK` at the beginning of the startup file. This problem rarely occurs with other compilers. 218 | 219 | ### 2.6 License 220 | 221 | The MIT open source agreement is adopted. For details, please read the contents of the LICENSE file in the project. -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | # CmBacktrace: ARM Cortex-M 系列 MCU 错误追踪库 2 | 3 | 中文页 | [English](README.md) 4 | 5 | [![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/1.4.0.svg)](https://github.com/armink/CmBacktrace/compare/1.0.0...master) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/armink/CmBacktrace/master/LICENSE) 6 | 7 | ## 0、CmBacktrace 是什么 8 | 9 | [CmBacktrace](https://github.com/armink/CmBacktrace) (Cortex Microcontroller Backtrace)是一款针对 ARM Cortex-M 系列 MCU 的错误代码自动追踪、定位,错误原因自动分析的开源库。主要特性如下: 10 | 11 | - 支持的错误包括: 12 | - 断言(assert) 13 | - 故障(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault) 14 | - 故障原因 **自动诊断** :可在故障发生时,自动分析出故障的原因,定位发生故障的代码位置,而无需再手动分析繁杂的故障寄存器; 15 | - 输出错误现场的 **函数调用栈**(需配合 addr2line 工具进行精确定位),还原发生错误时的现场信息,定位问题代码位置、逻辑更加快捷、精准。也可以在正常状态下使用该库,获取当前的函数调用栈; 16 | - 支持 裸机 及以下操作系统平台: 17 | - [RT-Thread](http://www.rt-thread.org/) 18 | - UCOS 19 | - FreeRTOS(需修改源码) 20 | - 根据错误现场状态,输出对应的 线程栈 或 C 主栈; 21 | - 故障诊断信息支持多国语言(目前:简体中文、英文); 22 | - 适配 Cortex-M0/M3/M4/M7 MCU; 23 | - 支持 IAR、KEIL、GCC 编译器; 24 | 25 | ## 1、为什么选择 CmBacktrace 26 | 27 | **入门新人** :对于从 C51 、MSP430 等简单单片机转而使用更加复杂的 ARM 新人来说,时不时出现的 "hard falut" 死机会让新人瞬间懵掉。定位错误的方法也往往是连接上仿真器,一步步 F10/F11 单步,定位到具体的错误代码,再去猜测、排除、推敲错误原因,这种过程十分痛苦。 28 | 29 | **熟练老手** :慢慢的大家知道可以通过故障寄存器信息来定位故障原因及故障代码地址,虽然这样能解决一小部分问题,但是重复的、繁琐的分析过程也会耽误很多时间。而且对于一些复杂问题,只依靠代码地址是无法解决的,必须得还原错误现场的函数调用逻辑关系。虽然连接仿真器可以查看到的函数调用栈,但故障状态下是无法显示的,所以还是得一步步 F10/F11 单步去定位错误代码的位置。另外,还有两种场景, 30 | 31 | - 1、很多产品真机调试时必须断开仿真器 32 | - 2、问题确实存在,但是极难被重现 33 | 34 | 所以定位这类问题就显得难上加难。 35 | 36 | **使用本库** :上述所有问题都迎刃而解,可以将错误信息输出到控制台上,还可以将错误信息使用 [EasyFlash](https://github.com/armink/EasyFlash) 的 Log 功能保存至 Flash 中,设备死机后重启依然能够读取上次的错误信息。CmBacktrace 输出的信息包括函数调用栈、故障诊断结果、堆栈、故障寄存器及产品固件信息,极大的提升了错误定位的效率及准确性。 37 | 38 | 俗话说,工欲善其事,必先利其器。所以有时候做事效率低的原因也许是,你会用的工具种类太少。 39 | 40 | **合作、贡献** :开源软件的发展离不开大家的支持,欢迎大家多提建议,也希望更多的人一起参与进来,共同提高 。如果觉得这个开源项目很赞,可以点击 [项目主页](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** ,同时把它推荐给更多有需要的朋友。 41 | 42 | ## 2、CmBacktrace 如何使用 43 | 44 | ### 2.1 演示 45 | 46 | 该演示分如下几个步骤: 47 | 48 | - 1、制造除零异常([IAR 工程,点击查看源码](https://github.com/armink/CmBacktrace/tree/master/demos/non_os/stm32f10x/app/src)) 49 | - 2、查看错误诊断信息 50 | - 3、查看函数调用栈基本信息 51 | - 4、通过命令行工具进入项目工程存放可执行文件的路径 52 | - 5、使用 addr2line 命令,查看函数调用栈详细信息,并定位错误代码 53 | 54 | [![cm_backtrace_demo](https://raw.githubusercontent.com/armink/CmBacktrace/master/docs/zh/images/cm_backtrace_demo.gif)](https://github.com/armink/CmBacktrace) 55 | 56 | ### 2.2 Demo 57 | 58 | |目录|平台|链接| 59 | |:--|:--:|:--:| 60 | | `\demos\non_os\stm32f10x` |裸机 STM32 Cortex-M3|[点击查看](https://github.com/armink/CmBacktrace/tree/master/demos/non_os/stm32f10x)| 61 | | `\demos\os\rtthread\stm32f4xx`|RT-Thread STM32 Cortex-M4|[点击查看](https://github.com/armink/CmBacktrace/tree/master/demos/os/rtthread/stm32f4xx)| 62 | | `\demos\os\ucosii\stm32f10x` |UCOSII STM32 Cortex-M3|[点击查看](https://github.com/armink/CmBacktrace/tree/master/demos/os/ucosii/stm32f10x)| 63 | | `\demos\os\freertos\stm32f10x` |FreeRTOS STM32 Cortex-M3|[点击查看](https://github.com/armink/CmBacktrace/tree/master/demos/os/freertos/stm32f10x)| 64 | 65 | 66 | 67 | ### 2.3 移植说明 68 | 69 | #### 2.3.1 准备工作 70 | 71 | - 1、查看 `\demos` 目录下有没有合适自己的 Demo ,如有类似,则建议在其基础上修改 72 | - 2、明确操作系统/裸机平台及 CPU 平台 73 | - 3、将 `\src` 下的全部源文件添加至产品工程中,并保证源码目录被添加至头文件路径 74 | - 4、cmb_fault.s 汇编文件([点击查看](https://github.com/armink/CmBacktrace/tree/master/cm_backtrace/fault_handler))可以选择性添加至工程,添加后需要把项目原有的 `HardFault_Handler` 注释掉 75 | - 5、把 `cm_backtrace_init` 函数放在项目初始化地方执行 76 | - 6、将 `cm_backtrace_assert` 放在项目的断言函数中执行,具体使用方法参照下面的 API 说明 77 | - 7、如果第 4 步骤没有将 cmb_fault.s 汇编文件启用,则需要将 `cm_backtrace_fault` 放到故障处理函数(例如: `HardFault_Handler` )中执行,具体使用方法参照下面的 API 说明 78 | 79 | #### 2.3.2 配置说明 80 | 81 | 配置文件名: `cmb_cfg.h` ,针对不同的平台和场景,用户需要自自行手动配置,常用配置如下: 82 | 83 | | 配置名称 |功能|备注| 84 | |:--|:--|:--| 85 | |cmb_println(...)|错误及诊断信息输出|必须配置| 86 | |CMB_USING_BARE_METAL_PLATFORM|是否使用在裸机平台|使用则定义该宏| 87 | |CMB_USING_OS_PLATFORM|是否使用在操作系统平台|操作系统与裸机必须二选一| 88 | |CMB_OS_PLATFORM_TYPE|操作系统平台|RTT/UCOSII/UCOSIII/FREERTOS| 89 | |CMB_CPU_PLATFORM_TYPE|CPU平台|M0/M3/M4/M7| 90 | |CMB_USING_DUMP_STACK_INFO|是否使用 Dump 堆栈的功能|使用则定义该宏| 91 | |CMB_PRINT_LANGUAGE|输出信息时的语言|CHINESE/ENGLISH| 92 | 93 | > 注意:以上部分配置的内容可以在 `cmb_def.h` 中选择,更多灵活的配置请阅读源码 94 | 95 | ### 2.4 API 说明 96 | 97 | #### 2.4.1 库初始化 98 | 99 | ```C 100 | void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver) 101 | ``` 102 | 103 | |参数 |描述| 104 | |:----- |:----| 105 | |firmware_name |固件名称,需与编译器生成的固件名称对应| 106 | |hardware_ver |固件对应的硬件版本号| 107 | |software_ver |固件的软件版本号| 108 | 109 | > **注意** :以上入参将会在断言或故障时输出,主要起了追溯的作用 110 | 111 | #### 2.4.2 获取函数调用栈 112 | 113 | ```C 114 | size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp) 115 | ``` 116 | 117 | |参数 |描述| 118 | |:----- |:----| 119 | |buffer |存储函数调用栈的缓冲区| 120 | |size |缓冲区大小| 121 | |sp |待获取的堆栈指针| 122 | 123 | 示例: 124 | 125 | ```C 126 | /* 建立深度为 16 的函数调用栈缓冲区,深度大小不应该超过 CMB_CALL_STACK_MAX_DEPTH(默认16) */ 127 | uint32_t call_stack[16] = {0}; 128 | size_t i, depth = 0; 129 | /* 获取当前环境下的函数调用栈,每个元素将会以 32 位地址形式存储, depth 为函数调用栈实际深度 */ 130 | depth = cm_backtrace_call_stack(call_stack, sizeof(call_stack), cmb_get_sp()); 131 | /* 输出当前函数调用栈信息 132 | * 注意:查看函数名称及具体行号时,需要使用 addr2line 工具转换 133 | */ 134 | for (i = 0; i < depth; i++) { 135 | printf("%08x ", call_stack[i]); 136 | } 137 | ``` 138 | 139 | #### 2.4.3 追踪断言错误信息 140 | 141 | ```C 142 | void cm_backtrace_assert(uint32_t sp) 143 | ``` 144 | 145 | |参数 |描述| 146 | |:----- |:----| 147 | |sp |断言环境时的堆栈指针| 148 | 149 | > **注意** :入参 SP 尽量在断言函数内部获取,而且尽可能靠近断言函数开始的位置。当在断言函数的子函数中(例如:在 RT-Thread 的断言钩子方法中)使用时,由于函数嵌套会存在寄存器入栈的操作,此时再获取 SP 将发生变化,就需要人为调整(加减固定的偏差值)入参值,所以作为新手 **不建议在断言的子函数** 中使用该函数。 150 | 151 | #### 2.4.4 追踪故障错误信息 152 | 153 | ```C 154 | void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp) 155 | ``` 156 | 157 | |参数 |描述| 158 | |:----- |:----| 159 | |fault_handler_lr |故障处理函数环境下的 LR 寄存器值| 160 | |fault_handler_sp |故障处理函数环境下的 SP 寄存器值| 161 | 162 | 该函数可以在故障处理函数(例如: `HardFault_Handler`)中调用。另外,库本身提供了 `HardFault` 处理的汇编文件([点击查看](https://github.com/armink/CmBacktrace/tree/master/cm_backtrace/fault_handler),需根据自己编译器进行选择),会在故障时自动调用 `cm_backtrace_fault` 方法。所以移植时,最简单的方式就是直接使用该汇编文件。 163 | 164 | ### 2.5 常见问题 165 | 166 | #### 2.5.1 编译出错,提示需要 C99 支持 167 | 168 | [点击查看教程:一步开启 Keil/IAR/GCC 的 C99 支持](https://github.com/armink/CmBacktrace/blob/master/docs/zh/enable_c99_for_keil_iar_gcc.md) 169 | 170 | #### 2.5.2 如何查看到函数调用栈中函数的具体名称及代码行号 171 | 172 | [点击查看教程:如何使用 addr2line 工具获取函数调用栈详细信息](https://github.com/armink/CmBacktrace/blob/master/docs/zh/how_to_use_addr2line_for_call_stack.md) 173 | 174 | #### 2.5.3 故障处理函数:HardFault_Handler 重复定义 175 | 176 | 在使用了本库提供的 cmb_fault.s 汇编文件时,因为该汇编文件内部已经定义了 HardFault_Handler ,所以如果项目中还有其他地方定义了该函数,则会提示 HardFault_Handler 被重复定义的错误。此时有两种解决方法: 177 | 178 | - 1、注释/删除其他文件中定义的 `HardFault_Handler` 函数,仅保留 cmb_fault.s 中的; 179 | - 2、将 cmb_fault.s 移除工程,手动添加 `cm_backtrace_fault` 函数至现有的故障处理函数,但需要注意的是,务必 **保证该函数数入参的准备性** ,否则可能会导致故障诊断功能及堆栈打印功能无法正常运行。所以如果是新手,不推荐第二种解决方法。 180 | 181 | #### 2.5.4 初始化时提示无法获取主栈(main stack)信息 182 | 183 | 在 `cmd_def.h` 中有定义默认的主栈配置,大致如下: 184 | 185 | ```c 186 | 187 | #if defined(__CC_ARM) 188 | /* C stack block name, default is STACK */ 189 | #ifndef CMB_CSTACK_BLOCK_NAME 190 | #define CMB_CSTACK_BLOCK_NAME STACK 191 | #endif 192 | ... 193 | #elif defined(__ICCARM__) 194 | /* C stack block name, default is 'CSTACK' */ 195 | #ifndef CMB_CSTACK_BLOCK_NAME 196 | #define CMB_CSTACK_BLOCK_NAME "CSTACK" 197 | #endif 198 | ... 199 | #elif defined(__GNUC__) 200 | /* C stack block start address, defined on linker script file, default is _sstack */ 201 | #ifndef CMB_CSTACK_BLOCK_START 202 | #define CMB_CSTACK_BLOCK_START _sstack 203 | #endif 204 | /* C stack block end address, defined on linker script file, default is _estack */ 205 | #ifndef CMB_CSTACK_BLOCK_END 206 | #define CMB_CSTACK_BLOCK_END _estack 207 | #endif 208 | ... 209 | #else 210 | ``` 211 | 212 | 比如在 Keil-MDK 编译器下会默认选择 `STACK` 作为主栈 block 的名称,但在一些特殊平台下,项目的主栈 block 名称可能不叫 `STACK`,导致 CmBacktrace 无法获取到正确的主栈信息,所以在初始化时会有如上的错误提示信息。 213 | 214 | 解决这个问题一般有两个思路 215 | 216 | - 1、在 `cmb_cfg.h` 中重新定义主栈的信息,此时 CmBacktrace 会优先使用 `cmb_cfg.h` 中的配置信息; 217 | - 2、修改项目配置,如果是 Keil-MDK ,则在启动文件的开头位置,将主栈的名称修改为默认的 `STACK` ,其他编译器一般很少出现该问题。 218 | 219 | 220 | ### 2.6 视频讲解 221 | - https://www.bilibili.com/video/BV1LB4y1Q78a 222 | - https://www.bilibili.com/video/BV1uF411i7Ka 223 | - https://www.bilibili.com/video/BV1rb4y1474Y 224 | 225 | ### 2.7 许可 226 | 227 | 采用 MIT 开源协议,细节请阅读项目中的 LICENSE 文件内容。 228 | -------------------------------------------------------------------------------- /SConscript: -------------------------------------------------------------------------------- 1 | from building import * 2 | import rtconfig 3 | 4 | cwd = GetCurrentDir() 5 | src = Glob('*.c') 6 | path = [cwd] 7 | 8 | # AC5 use option '--c99' 9 | ''' 10 | LOCAL_CCFLAGS = '' 11 | 12 | if rtconfig.CROSS_TOOL == 'gcc': 13 | LOCAL_CCFLAGS += ' -std=c99' 14 | elif rtconfig.CROSS_TOOL == 'keil': 15 | # Using ARM Compiler Version 5 16 | LOCAL_CCFLAGS += ' --c99' 17 | LOCAL_CCFLAGS += ' --no-multibyte-chars' 18 | # Using ARM Compiler Version 6 19 | # pass 20 | 21 | group = DefineGroup('cm_backtrace', src, depend = ['PKG_USING_CMBACKTRACE'], CPPPATH = path, LOCAL_CCFLAGS = LOCAL_CCFLAGS) 22 | ''' 23 | 24 | # 25 | # armclang complier not support option '--c99' 26 | # 27 | group = DefineGroup('cm_backtrace', src, depend = ['PKG_USING_CMBACKTRACE'], CPPPATH = path) 28 | 29 | Return('group') 30 | -------------------------------------------------------------------------------- /cm_backtrace.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the CmBacktrace Library. 3 | * 4 | * Copyright (c) 2016-2019, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: Initialize function and other general function. 26 | * Created on: 2016-12-15 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #if __STDC_VERSION__ < 199901L 35 | #error "must be C99 or higher. try to add '-std=c99' to compile parameters" 36 | #endif 37 | 38 | #if defined(__ARMCC_VERSION) 39 | #define SECTION_START(_name_) _name_##$$Base 40 | #define SECTION_END(_name_) _name_##$$Limit 41 | #define IMAGE_SECTION_START(_name_) Image$$##_name_##$$Base 42 | #define IMAGE_SECTION_END(_name_) Image$$##_name_##$$Limit 43 | #define CSTACK_BLOCK_START(_name_) SECTION_START(_name_) 44 | #define CSTACK_BLOCK_END(_name_) SECTION_END(_name_) 45 | #define CODE_SECTION_START(_name_) IMAGE_SECTION_START(_name_) 46 | #define CODE_SECTION_END(_name_) IMAGE_SECTION_END(_name_) 47 | 48 | extern const int CSTACK_BLOCK_START(CMB_CSTACK_BLOCK_NAME); 49 | extern const int CSTACK_BLOCK_END(CMB_CSTACK_BLOCK_NAME); 50 | extern const int CODE_SECTION_START(CMB_CODE_SECTION_NAME); 51 | extern const int CODE_SECTION_END(CMB_CODE_SECTION_NAME); 52 | #elif defined(__ICCARM__) 53 | #pragma section=CMB_CSTACK_BLOCK_NAME 54 | #pragma section=CMB_CODE_SECTION_NAME 55 | #elif defined(__GNUC__) 56 | extern const int CMB_CSTACK_BLOCK_START; 57 | extern const int CMB_CSTACK_BLOCK_END; 58 | extern const int CMB_CODE_SECTION_START; 59 | extern const int CMB_CODE_SECTION_END; 60 | #else 61 | #error "not supported compiler" 62 | #endif 63 | 64 | enum { 65 | PRINT_MAIN_STACK_CFG_ERROR, 66 | PRINT_FIRMWARE_INFO, 67 | PRINT_ASSERT_ON_THREAD, 68 | PRINT_ASSERT_ON_HANDLER, 69 | PRINT_THREAD_STACK_INFO, 70 | PRINT_MAIN_STACK_INFO, 71 | PRINT_THREAD_STACK_OVERFLOW, 72 | PRINT_MAIN_STACK_OVERFLOW, 73 | PRINT_CALL_STACK_INFO, 74 | PRINT_CALL_STACK_ERR, 75 | PRINT_FAULT_ON_THREAD, 76 | PRINT_FAULT_ON_HANDLER, 77 | PRINT_REGS_TITLE, 78 | PRINT_HFSR_VECTBL, 79 | PRINT_MFSR_IACCVIOL, 80 | PRINT_MFSR_DACCVIOL, 81 | PRINT_MFSR_MUNSTKERR, 82 | PRINT_MFSR_MSTKERR, 83 | PRINT_MFSR_MLSPERR, 84 | PRINT_BFSR_IBUSERR, 85 | PRINT_BFSR_PRECISERR, 86 | PRINT_BFSR_IMPREISERR, 87 | PRINT_BFSR_UNSTKERR, 88 | PRINT_BFSR_STKERR, 89 | PRINT_BFSR_LSPERR, 90 | PRINT_UFSR_UNDEFINSTR, 91 | PRINT_UFSR_INVSTATE, 92 | PRINT_UFSR_INVPC, 93 | PRINT_UFSR_NOCP, 94 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 95 | PRINT_UFSR_STKOF, 96 | #endif 97 | PRINT_UFSR_UNALIGNED, 98 | PRINT_UFSR_DIVBYZERO0, 99 | PRINT_DFSR_HALTED, 100 | PRINT_DFSR_BKPT, 101 | PRINT_DFSR_DWTTRAP, 102 | PRINT_DFSR_VCATCH, 103 | PRINT_DFSR_EXTERNAL, 104 | PRINT_MMAR, 105 | PRINT_BFAR, 106 | }; 107 | 108 | static const char * const print_info[] = { 109 | #if (CMB_PRINT_LANGUAGE == CMB_PRINT_LANGUAGE_ENGLISH) 110 | #include "Languages/en-US/cmb_en_US.h" 111 | #elif (CMB_PRINT_LANGUAGE == CMB_PRINT_LANGUAGE_CHINESE) 112 | #include "Languages/zh-CN/cmb_zh_CN.h" 113 | #elif (CMB_PRINT_LANGUAGE == CMB_PRINT_LANGUAGE_CHINESE_UTF8) 114 | #include "Languages/zh-CN/cmb_zh_CN_UTF8.h" 115 | #else 116 | #error "CMB_PRINT_LANGUAGE defined error in 'cmb_cfg.h'" 117 | #endif 118 | }; 119 | 120 | static char fw_name[CMB_NAME_MAX + 1] = {0}; 121 | static char hw_ver[CMB_NAME_MAX + 1] = {0}; 122 | static char sw_ver[CMB_NAME_MAX + 1] = {0}; 123 | static uint32_t main_stack_start_addr = 0; 124 | static size_t main_stack_size = 0; 125 | static uint32_t code_start_addr = 0; 126 | static size_t code_size = 0; 127 | static bool init_ok = false; 128 | static char call_stack_info[CMB_CALL_STACK_MAX_DEPTH * (8 + 1)] = { 0 }; 129 | static bool on_fault = false; 130 | static bool stack_is_overflow = false; 131 | static struct cmb_hard_fault_regs regs; 132 | 133 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) || \ 134 | (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 135 | static bool statck_has_fpu_regs = false; 136 | #endif 137 | 138 | static bool on_thread_before_fault = false; 139 | 140 | /** 141 | * library initialize 142 | */ 143 | void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver) { 144 | strncpy(fw_name, firmware_name, CMB_NAME_MAX); 145 | strncpy(hw_ver, hardware_ver, CMB_NAME_MAX); 146 | strncpy(sw_ver, software_ver, CMB_NAME_MAX); 147 | 148 | #if defined(__ARMCC_VERSION) 149 | main_stack_start_addr = (uint32_t)&CSTACK_BLOCK_START(CMB_CSTACK_BLOCK_NAME); 150 | main_stack_size = (uint32_t)&CSTACK_BLOCK_END(CMB_CSTACK_BLOCK_NAME) - main_stack_start_addr; 151 | code_start_addr = (uint32_t)&CODE_SECTION_START(CMB_CODE_SECTION_NAME); 152 | code_size = (uint32_t)&CODE_SECTION_END(CMB_CODE_SECTION_NAME) - code_start_addr; 153 | #elif defined(__ICCARM__) 154 | main_stack_start_addr = (uint32_t)__section_begin(CMB_CSTACK_BLOCK_NAME); 155 | main_stack_size = (uint32_t)__section_end(CMB_CSTACK_BLOCK_NAME) - main_stack_start_addr; 156 | code_start_addr = (uint32_t)__section_begin(CMB_CODE_SECTION_NAME); 157 | code_size = (uint32_t)__section_end(CMB_CODE_SECTION_NAME) - code_start_addr; 158 | #elif defined(__GNUC__) 159 | main_stack_start_addr = (uint32_t)(&CMB_CSTACK_BLOCK_START); 160 | main_stack_size = (uint32_t)(&CMB_CSTACK_BLOCK_END) - main_stack_start_addr; 161 | code_start_addr = (uint32_t)(&CMB_CODE_SECTION_START); 162 | code_size = (uint32_t)(&CMB_CODE_SECTION_END) - code_start_addr; 163 | #else 164 | #error "not supported compiler" 165 | #endif 166 | 167 | if (main_stack_size == 0) { 168 | cmb_println(print_info[PRINT_MAIN_STACK_CFG_ERROR]); 169 | return; 170 | } 171 | 172 | init_ok = true; 173 | } 174 | 175 | /** 176 | * print firmware information, such as: firmware name, hardware version, software version 177 | */ 178 | void cm_backtrace_firmware_info(void) { 179 | cmb_println(print_info[PRINT_FIRMWARE_INFO], fw_name, hw_ver, sw_ver); 180 | } 181 | 182 | #ifdef CMB_USING_OS_PLATFORM 183 | /** 184 | * Get current thread stack information 185 | * 186 | * @param sp stack current pointer 187 | * @param start_addr stack start address 188 | * @param size stack size 189 | */ 190 | static void get_cur_thread_stack_info(uint32_t *sp, uint32_t *start_addr, size_t *size) { 191 | CMB_ASSERT(start_addr); 192 | CMB_ASSERT(size); 193 | 194 | #if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) 195 | *start_addr = (uint32_t) rt_thread_self()->stack_addr; 196 | *size = rt_thread_self()->stack_size; 197 | *sp = (uint32_t)rt_thread_self()->sp; 198 | #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII) 199 | extern OS_TCB *OSTCBCur; 200 | 201 | *start_addr = (uint32_t) OSTCBCur->OSTCBStkBottom; 202 | *size = OSTCBCur->OSTCBStkSize * sizeof(OS_STK); 203 | #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII) 204 | extern OS_TCB *OSTCBCurPtr; 205 | 206 | *start_addr = (uint32_t) OSTCBCurPtr->StkBasePtr; 207 | *size = OSTCBCurPtr->StkSize * sizeof(CPU_STK_SIZE); 208 | #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS) 209 | *start_addr = (uint32_t)vTaskStackAddr(); 210 | *size = vTaskStackSize() * sizeof( StackType_t ); 211 | #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTX5) 212 | osRtxThread_t *thread = osRtxInfo.thread.run.curr; 213 | *start_addr = (uint32_t)thread->stack_mem; 214 | *size = thread->stack_size; 215 | #endif 216 | } 217 | 218 | /** 219 | * Get current thread name 220 | */ 221 | static const char *get_cur_thread_name(void) { 222 | #if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) 223 | #if (RT_VER_NUM < 0x50001) 224 | return rt_thread_self()->name; 225 | #else 226 | return rt_thread_self()->parent.name; 227 | #endif 228 | #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII) 229 | extern OS_TCB *OSTCBCur; 230 | 231 | #if OS_TASK_NAME_SIZE > 0 || OS_TASK_NAME_EN > 0 232 | return (const char *)OSTCBCur->OSTCBTaskName; 233 | #else 234 | return NULL; 235 | #endif /* OS_TASK_NAME_SIZE > 0 || OS_TASK_NAME_EN > 0 */ 236 | 237 | #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII) 238 | extern OS_TCB *OSTCBCurPtr; 239 | 240 | return (const char *)OSTCBCurPtr->NamePtr; 241 | #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS) 242 | return vTaskName(); 243 | #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTX5) 244 | osThreadId_t id = osThreadGetId(); 245 | return osThreadGetName(id); 246 | #endif 247 | } 248 | 249 | #endif /* CMB_USING_OS_PLATFORM */ 250 | 251 | #ifdef CMB_USING_DUMP_STACK_INFO 252 | /** 253 | * dump current stack information 254 | */ 255 | static void dump_stack(uint32_t stack_start_addr, size_t stack_size, uint32_t *stack_pointer) { 256 | uint32_t deep = CMB_DUMP_STACK_DEPTH_SIZE; 257 | 258 | if (stack_is_overflow) { 259 | if (on_thread_before_fault) { 260 | cmb_println(print_info[PRINT_THREAD_STACK_OVERFLOW], stack_pointer); 261 | } else { 262 | cmb_println(print_info[PRINT_MAIN_STACK_OVERFLOW], stack_pointer); 263 | } 264 | if ((uint32_t) stack_pointer < stack_start_addr) { 265 | stack_pointer = (uint32_t *) stack_start_addr; 266 | } else if ((uint32_t) stack_pointer > stack_start_addr + stack_size) { 267 | stack_pointer = (uint32_t *) (stack_start_addr + stack_size); 268 | } 269 | } 270 | cmb_println(print_info[PRINT_THREAD_STACK_INFO]); 271 | for (; (uint32_t) stack_pointer < stack_start_addr + stack_size && deep; stack_pointer++, deep--) { 272 | cmb_println(" addr: %08lx data: %08lx", (uint32_t)stack_pointer, *stack_pointer); 273 | } 274 | cmb_println("===================================="); 275 | } 276 | #endif /* CMB_USING_DUMP_STACK_INFO */ 277 | 278 | /* check the disassembly instruction is 'BL' or 'BLX' */ 279 | static bool disassembly_ins_is_bl_blx(uint32_t addr) { 280 | uint16_t ins1 = *((uint16_t *)addr); 281 | uint16_t ins2 = *((uint16_t *)(addr + 2)); 282 | 283 | #define BL_INS_MASK 0xF800 284 | #define BL_INS_HIGH 0xF800 285 | #define BL_INS_LOW 0xF000 286 | #define BLX_INX_MASK 0xFF00 287 | #define BLX_INX 0x4700 288 | 289 | if ((ins2 & BL_INS_MASK) == BL_INS_HIGH && (ins1 & BL_INS_MASK) == BL_INS_LOW) { 290 | return true; 291 | } else if ((ins2 & BLX_INX_MASK) == BLX_INX) { 292 | return true; 293 | } else { 294 | return false; 295 | } 296 | } 297 | 298 | size_t cm_backtrace_call_stack_any(uint32_t *buffer, size_t size, uint32_t sp, uint32_t stack_start_addr, uint32_t stack_size) 299 | { 300 | uint32_t pc; 301 | size_t depth = 0; 302 | /* copy called function address */ 303 | for (; sp < stack_start_addr + stack_size; sp += sizeof(size_t)) { 304 | /* the *sp value may be LR, so need decrease a word to PC */ 305 | pc = *((uint32_t *) sp) - sizeof(size_t); 306 | /* the Cortex-M using thumb instruction, so the pc must be an odd number */ 307 | if (pc % 2 == 0) { 308 | continue; 309 | } 310 | /* fix the PC address in thumb mode */ 311 | pc = *((uint32_t *) sp) - 1; 312 | if ((pc >= code_start_addr + sizeof(size_t)) && (pc <= code_start_addr + code_size) && (depth < CMB_CALL_STACK_MAX_DEPTH) 313 | /* check the the instruction before PC address is 'BL' or 'BLX' */ 314 | && disassembly_ins_is_bl_blx(pc - sizeof(size_t)) && (depth < size)) { 315 | /* the second depth function may be already saved, so need ignore repeat */ 316 | buffer[depth++] = pc; 317 | } 318 | } 319 | 320 | return depth; 321 | } 322 | 323 | /** 324 | * backtrace function call stack 325 | * 326 | * @param buffer call stack buffer 327 | * @param size buffer size 328 | * @param sp stack pointer 329 | * 330 | * @return depth 331 | */ 332 | size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp) { 333 | uint32_t stack_start_addr = main_stack_start_addr, pc; 334 | 335 | #ifdef CMB_USING_OS_PLATFORM 336 | uint32_t tcb_sp; 337 | #endif 338 | size_t depth = 0, stack_size = main_stack_size; 339 | bool regs_saved_lr_is_valid = false; 340 | 341 | if (on_fault) { 342 | if (!stack_is_overflow) { 343 | /* first depth is PC */ 344 | buffer[depth++] = regs.saved.pc; 345 | /* fix the LR address in thumb mode */ 346 | pc = regs.saved.lr - 1; 347 | if ((pc >= code_start_addr) && (pc <= code_start_addr + code_size) && (depth < CMB_CALL_STACK_MAX_DEPTH) 348 | && (depth < size)) { 349 | buffer[depth++] = pc; 350 | regs_saved_lr_is_valid = true; 351 | } 352 | } 353 | 354 | #ifdef CMB_USING_OS_PLATFORM 355 | /* program is running on thread before fault */ 356 | if (on_thread_before_fault) { 357 | get_cur_thread_stack_info(&tcb_sp, &stack_start_addr, &stack_size); 358 | } 359 | } else { 360 | /* OS environment */ 361 | if (cmb_get_sp() == cmb_get_psp()) { 362 | get_cur_thread_stack_info(&tcb_sp, &stack_start_addr, &stack_size); 363 | } 364 | #endif /* CMB_USING_OS_PLATFORM */ 365 | 366 | } 367 | 368 | if (stack_is_overflow) { 369 | sp = stack_start_addr; 370 | } 371 | 372 | /* copy called function address */ 373 | for (; sp < stack_start_addr + stack_size; sp += sizeof(size_t)) { 374 | /* the *sp value may be LR, so need decrease a word to PC */ 375 | pc = *((uint32_t *) sp) - sizeof(size_t); 376 | /* the Cortex-M using thumb instruction, so the pc must be an odd number */ 377 | if (pc % 2 == 0) { 378 | continue; 379 | } 380 | /* fix the PC address in thumb mode */ 381 | pc = *((uint32_t *) sp) - 1; 382 | if ((pc >= code_start_addr + sizeof(size_t)) && (pc <= code_start_addr + code_size) && (depth < CMB_CALL_STACK_MAX_DEPTH) 383 | /* check the the instruction before PC address is 'BL' or 'BLX' */ 384 | && disassembly_ins_is_bl_blx(pc - sizeof(size_t)) && (depth < size)) { 385 | /* the second depth function may be already saved, so need ignore repeat */ 386 | if ((depth == 2) && regs_saved_lr_is_valid && (pc == buffer[1])) { 387 | continue; 388 | } 389 | buffer[depth++] = pc; 390 | } 391 | } 392 | 393 | return depth; 394 | } 395 | 396 | /** 397 | * dump function call stack 398 | * 399 | * @param sp stack pointer 400 | */ 401 | static void print_call_stack(uint32_t sp) { 402 | size_t i, cur_depth = 0; 403 | uint32_t call_stack_buf[CMB_CALL_STACK_MAX_DEPTH] = {0}; 404 | 405 | cur_depth = cm_backtrace_call_stack(call_stack_buf, CMB_CALL_STACK_MAX_DEPTH, sp); 406 | 407 | for (i = 0; i < cur_depth; i++) { 408 | sprintf(call_stack_info + i * (8 + 1), "%08lx", (unsigned long)call_stack_buf[i]); 409 | call_stack_info[i * (8 + 1) + 8] = ' '; 410 | } 411 | 412 | if (cur_depth) { 413 | cmb_println(print_info[PRINT_CALL_STACK_INFO], fw_name, CMB_ELF_FILE_EXTENSION_NAME, cur_depth * (8 + 1), 414 | call_stack_info); 415 | } else { 416 | cmb_println(print_info[PRINT_CALL_STACK_ERR]); 417 | } 418 | } 419 | 420 | /** 421 | * backtrace for assert 422 | * 423 | * @param sp the stack pointer when on assert occurred 424 | */ 425 | void cm_backtrace_assert(uint32_t sp) { 426 | #ifdef CMB_USING_OS_PLATFORM 427 | uint32_t tcb_sp; 428 | uint32_t cur_stack_pointer = cmb_get_sp(); 429 | #endif 430 | 431 | CMB_ASSERT(init_ok); 432 | 433 | cmb_println(" "); 434 | cm_backtrace_firmware_info(); 435 | 436 | #ifdef CMB_USING_OS_PLATFORM 437 | /* OS environment */ 438 | if (cur_stack_pointer == cmb_get_msp()) { 439 | cmb_println(print_info[PRINT_ASSERT_ON_HANDLER]); 440 | 441 | #ifdef CMB_USING_DUMP_STACK_INFO 442 | dump_stack(main_stack_start_addr, main_stack_size, (uint32_t *) sp); 443 | #endif /* CMB_USING_DUMP_STACK_INFO */ 444 | 445 | } else if (cur_stack_pointer == cmb_get_psp()) { 446 | cmb_println(print_info[PRINT_ASSERT_ON_THREAD], get_cur_thread_name()); 447 | 448 | #ifdef CMB_USING_DUMP_STACK_INFO 449 | uint32_t stack_start_addr; 450 | size_t stack_size; 451 | get_cur_thread_stack_info(&tcb_sp, &stack_start_addr, &stack_size); 452 | dump_stack(stack_start_addr, stack_size, (uint32_t *) sp); 453 | #endif /* CMB_USING_DUMP_STACK_INFO */ 454 | 455 | } 456 | 457 | #else 458 | 459 | /* bare metal(no OS) environment */ 460 | #ifdef CMB_USING_DUMP_STACK_INFO 461 | dump_stack(main_stack_start_addr, main_stack_size, (uint32_t *) sp); 462 | #endif /* CMB_USING_DUMP_STACK_INFO */ 463 | 464 | #endif /* CMB_USING_OS_PLATFORM */ 465 | 466 | print_call_stack(sp); 467 | } 468 | 469 | #if (CMB_CPU_PLATFORM_TYPE != CMB_CPU_ARM_CORTEX_M0) 470 | /** 471 | * fault diagnosis then print cause of fault 472 | */ 473 | static void fault_diagnosis(void) { 474 | if (regs.hfsr.bits.VECTBL) { 475 | cmb_println(print_info[PRINT_HFSR_VECTBL]); 476 | } 477 | if (regs.hfsr.bits.FORCED) { 478 | /* Memory Management Fault */ 479 | if (regs.mfsr.value) { 480 | if (regs.mfsr.bits.IACCVIOL) { 481 | cmb_println(print_info[PRINT_MFSR_IACCVIOL]); 482 | } 483 | if (regs.mfsr.bits.DACCVIOL) { 484 | cmb_println(print_info[PRINT_MFSR_DACCVIOL]); 485 | } 486 | if (regs.mfsr.bits.MUNSTKERR) { 487 | cmb_println(print_info[PRINT_MFSR_MUNSTKERR]); 488 | } 489 | if (regs.mfsr.bits.MSTKERR) { 490 | cmb_println(print_info[PRINT_MFSR_MSTKERR]); 491 | } 492 | 493 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) || \ 494 | (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 495 | if (regs.mfsr.bits.MLSPERR) { 496 | cmb_println(print_info[PRINT_MFSR_MLSPERR]); 497 | } 498 | #endif 499 | 500 | if (regs.mfsr.bits.MMARVALID) { 501 | if (regs.mfsr.bits.IACCVIOL || regs.mfsr.bits.DACCVIOL) { 502 | cmb_println(print_info[PRINT_MMAR], regs.mmar); 503 | } 504 | } 505 | } 506 | /* Bus Fault */ 507 | if (regs.bfsr.value) { 508 | if (regs.bfsr.bits.IBUSERR) { 509 | cmb_println(print_info[PRINT_BFSR_IBUSERR]); 510 | } 511 | if (regs.bfsr.bits.PRECISERR) { 512 | cmb_println(print_info[PRINT_BFSR_PRECISERR]); 513 | } 514 | if (regs.bfsr.bits.IMPREISERR) { 515 | cmb_println(print_info[PRINT_BFSR_IMPREISERR]); 516 | } 517 | if (regs.bfsr.bits.UNSTKERR) { 518 | cmb_println(print_info[PRINT_BFSR_UNSTKERR]); 519 | } 520 | if (regs.bfsr.bits.STKERR) { 521 | cmb_println(print_info[PRINT_BFSR_STKERR]); 522 | } 523 | 524 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) || \ 525 | (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 526 | if (regs.bfsr.bits.LSPERR) { 527 | cmb_println(print_info[PRINT_BFSR_LSPERR]); 528 | } 529 | #endif 530 | 531 | if (regs.bfsr.bits.BFARVALID) { 532 | if (regs.bfsr.bits.PRECISERR) { 533 | cmb_println(print_info[PRINT_BFAR], regs.bfar); 534 | } 535 | } 536 | 537 | } 538 | /* Usage Fault */ 539 | if (regs.ufsr.value) { 540 | if (regs.ufsr.bits.UNDEFINSTR) { 541 | cmb_println(print_info[PRINT_UFSR_UNDEFINSTR]); 542 | } 543 | if (regs.ufsr.bits.INVSTATE) { 544 | cmb_println(print_info[PRINT_UFSR_INVSTATE]); 545 | } 546 | if (regs.ufsr.bits.INVPC) { 547 | cmb_println(print_info[PRINT_UFSR_INVPC]); 548 | } 549 | if (regs.ufsr.bits.NOCP) { 550 | cmb_println(print_info[PRINT_UFSR_NOCP]); 551 | } 552 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 553 | if (regs.ufsr.bits.STKOF) { 554 | cmb_println(print_info[PRINT_UFSR_STKOF]); 555 | } 556 | #endif 557 | if (regs.ufsr.bits.UNALIGNED) { 558 | cmb_println(print_info[PRINT_UFSR_UNALIGNED]); 559 | } 560 | if (regs.ufsr.bits.DIVBYZERO0) { 561 | cmb_println(print_info[PRINT_UFSR_DIVBYZERO0]); 562 | } 563 | } 564 | } 565 | /* Debug Fault */ 566 | if (regs.hfsr.bits.DEBUGEVT) { 567 | if (regs.dfsr.value) { 568 | if (regs.dfsr.bits.HALTED) { 569 | cmb_println(print_info[PRINT_DFSR_HALTED]); 570 | } 571 | if (regs.dfsr.bits.BKPT) { 572 | cmb_println(print_info[PRINT_DFSR_BKPT]); 573 | } 574 | if (regs.dfsr.bits.DWTTRAP) { 575 | cmb_println(print_info[PRINT_DFSR_DWTTRAP]); 576 | } 577 | if (regs.dfsr.bits.VCATCH) { 578 | cmb_println(print_info[PRINT_DFSR_VCATCH]); 579 | } 580 | if (regs.dfsr.bits.EXTERNAL) { 581 | cmb_println(print_info[PRINT_DFSR_EXTERNAL]); 582 | } 583 | } 584 | } 585 | } 586 | #endif /* (CMB_CPU_PLATFORM_TYPE != CMB_CPU_ARM_CORTEX_M0) */ 587 | 588 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) || \ 589 | (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 590 | static uint32_t statck_del_fpu_regs(uint32_t fault_handler_lr, uint32_t sp) { 591 | statck_has_fpu_regs = (fault_handler_lr & (1UL << 4)) == 0 ? true : false; 592 | 593 | /* the stack has S0~S15 and FPSCR registers when statck_has_fpu_regs is true, double word align */ 594 | return statck_has_fpu_regs == true ? sp + sizeof(size_t) * 18 : sp; 595 | } 596 | #endif 597 | 598 | /** 599 | * backtrace for fault 600 | * @note only call once 601 | * 602 | * @param fault_handler_lr the LR register value on fault handler 603 | * @param fault_handler_sp the stack pointer on fault handler 604 | */ 605 | void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp) { 606 | uint32_t stack_pointer = fault_handler_sp, saved_regs_addr = stack_pointer, tcb_stack_pointer = 0; 607 | const char *regs_name[] = { "R0 ", "R1 ", "R2 ", "R3 ", "R12", "LR ", "PC ", "PSR" }; 608 | 609 | #ifdef CMB_USING_DUMP_STACK_INFO 610 | uint32_t stack_start_addr = main_stack_start_addr; 611 | size_t stack_size = main_stack_size; 612 | #endif 613 | 614 | CMB_ASSERT(init_ok); 615 | /* only call once */ 616 | CMB_ASSERT(!on_fault); 617 | 618 | on_fault = true; 619 | 620 | cmb_println(" "); 621 | cm_backtrace_firmware_info(); 622 | 623 | #ifdef CMB_USING_OS_PLATFORM 624 | on_thread_before_fault = fault_handler_lr & (1UL << 2); 625 | /* check which stack was used before (MSP or PSP) */ 626 | if (on_thread_before_fault) { 627 | cmb_println(print_info[PRINT_FAULT_ON_THREAD], get_cur_thread_name() != NULL ? get_cur_thread_name() : "NO_NAME"); 628 | saved_regs_addr = stack_pointer = cmb_get_psp(); 629 | 630 | #ifdef CMB_USING_DUMP_STACK_INFO 631 | get_cur_thread_stack_info(&tcb_stack_pointer, &stack_start_addr, &stack_size); 632 | #endif /* CMB_USING_DUMP_STACK_INFO */ 633 | 634 | } else { 635 | cmb_println(print_info[PRINT_FAULT_ON_HANDLER]); 636 | } 637 | #else 638 | /* bare metal(no OS) environment */ 639 | cmb_println(print_info[PRINT_FAULT_ON_HANDLER]); 640 | #endif /* CMB_USING_OS_PLATFORM */ 641 | 642 | /* delete saved R0~R3, R12, LR,PC,xPSR registers space */ 643 | stack_pointer += sizeof(size_t) * 8; 644 | 645 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) || \ 646 | (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 647 | stack_pointer = statck_del_fpu_regs(fault_handler_lr, stack_pointer); 648 | #endif /* (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) */ 649 | 650 | #ifdef CMB_USING_DUMP_STACK_INFO 651 | /* check stack overflow */ 652 | if (stack_pointer < stack_start_addr || stack_pointer > stack_start_addr + stack_size) { 653 | cmb_println("stack_pointer: 0x%08lx, stack_start_addr: 0x%08lx, stack_end_addr: 0x%08lx", (uint32_t)stack_pointer, stack_start_addr, 654 | stack_start_addr + stack_size); 655 | stack_is_overflow = true; 656 | #if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) 657 | if (on_thread_before_fault) { 658 | /* change the stack start adder to TCB->sp when stack is overflow */ 659 | stack_pointer = tcb_stack_pointer; 660 | } 661 | #endif 662 | } 663 | /* dump stack information */ 664 | dump_stack(stack_start_addr, stack_size, (uint32_t *) stack_pointer); 665 | #endif /* CMB_USING_DUMP_STACK_INFO */ 666 | 667 | { 668 | /* dump register */ 669 | cmb_println(print_info[PRINT_REGS_TITLE]); 670 | 671 | regs.saved.r0 = ((uint32_t *)saved_regs_addr)[0]; // Register R0 672 | regs.saved.r1 = ((uint32_t *)saved_regs_addr)[1]; // Register R1 673 | regs.saved.r2 = ((uint32_t *)saved_regs_addr)[2]; // Register R2 674 | regs.saved.r3 = ((uint32_t *)saved_regs_addr)[3]; // Register R3 675 | regs.saved.r12 = ((uint32_t *)saved_regs_addr)[4]; // Register R12 676 | regs.saved.lr = ((uint32_t *)saved_regs_addr)[5]; // Link register LR 677 | regs.saved.pc = ((uint32_t *)saved_regs_addr)[6]; // Program counter PC 678 | regs.saved.psr.value = ((uint32_t *)saved_regs_addr)[7]; // Program status word PSR 679 | 680 | cmb_println(" %s: %08x %s: %08x %s: %08x %s: %08x", regs_name[0], regs.saved.r0, 681 | regs_name[1], regs.saved.r1, 682 | regs_name[2], regs.saved.r2, 683 | regs_name[3], regs.saved.r3); 684 | cmb_println(" %s: %08x %s: %08x %s: %08x %s: %08x", regs_name[4], regs.saved.r12, 685 | regs_name[5], regs.saved.lr, 686 | regs_name[6], regs.saved.pc, 687 | regs_name[7], regs.saved.psr.value); 688 | cmb_println("=============================================================="); 689 | } 690 | 691 | /* the Cortex-M0 is not support fault diagnosis */ 692 | #if (CMB_CPU_PLATFORM_TYPE != CMB_CPU_ARM_CORTEX_M0) 693 | regs.syshndctrl.value = CMB_SYSHND_CTRL; // System Handler Control and State Register 694 | regs.mfsr.value = CMB_NVIC_MFSR; // Memory Fault Status Register 695 | regs.mmar = CMB_NVIC_MMAR; // Memory Management Fault Address Register 696 | regs.bfsr.value = CMB_NVIC_BFSR; // Bus Fault Status Register 697 | regs.bfar = CMB_NVIC_BFAR; // Bus Fault Manage Address Register 698 | regs.ufsr.value = CMB_NVIC_UFSR; // Usage Fault Status Register 699 | regs.hfsr.value = CMB_NVIC_HFSR; // Hard Fault Status Register 700 | regs.dfsr.value = CMB_NVIC_DFSR; // Debug Fault Status Register 701 | regs.afsr = CMB_NVIC_AFSR; // Auxiliary Fault Status Register 702 | 703 | fault_diagnosis(); 704 | #endif 705 | 706 | print_call_stack(stack_pointer); 707 | } 708 | -------------------------------------------------------------------------------- /cm_backtrace.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the CmBacktrace Library. 3 | * 4 | * Copyright (c) 2016, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: It is an head file for this library. You can see all be called functions. 26 | * Created on: 2016-12-15 27 | */ 28 | 29 | #ifndef _CORTEXM_BACKTRACE_H_ 30 | #define _CORTEXM_BACKTRACE_H_ 31 | 32 | #include "cmb_def.h" 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver); 39 | void cm_backtrace_firmware_info(void); 40 | size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp); 41 | void cm_backtrace_assert(uint32_t sp); 42 | void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | 48 | #endif /* _CORTEXM_BACKTRACE_H_ */ 49 | -------------------------------------------------------------------------------- /cmb_cfg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the CmBacktrace Library. 3 | * 4 | * Copyright (c) 2016, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: It is the configure head file for this library. 26 | * Created on: 2016-12-15 27 | */ 28 | 29 | #ifndef _CMB_CFG_H_ 30 | #define _CMB_CFG_H_ 31 | 32 | /* print line, must config by user */ 33 | #include 34 | 35 | #ifndef RT_USING_ULOG 36 | #ifndef CMB_USING_FLASH_LOG_BACKUP 37 | #define cmb_println(...) rt_kprintf(__VA_ARGS__);rt_kprintf("\r\n") 38 | #else 39 | extern void cmb_flash_log_println(const char *fmt, ...); 40 | #define cmb_println(...) rt_kprintf(__VA_ARGS__);rt_kprintf("\r\n");cmb_flash_log_println(__VA_ARGS__) 41 | #endif /* CMB_USING_FLASH_LOG_BACKUP */ 42 | #else 43 | #include 44 | #define CMB_LOG_TAG "cmb" 45 | #define cmb_println(...) ulog_e(CMB_LOG_TAG, __VA_ARGS__);ulog_flush() 46 | #endif /* RT_USING_ULOG */ 47 | 48 | /* enable bare metal(no OS) platform */ 49 | /* #define CMB_USING_BARE_METAL_PLATFORM */ 50 | /* enable OS platform */ 51 | #define CMB_USING_OS_PLATFORM 52 | /* OS platform type, must config when CMB_USING_OS_PLATFORM is enable */ 53 | #define CMB_OS_PLATFORM_TYPE CMB_OS_PLATFORM_RTT 54 | 55 | /* cpu platform type, must config by user */ 56 | #if defined(PKG_CMBACKTRACE_PLATFORM_M0_M0PLUS) 57 | #define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M0 58 | #elif defined(PKG_CMBACKTRACE_PLATFORM_M3) 59 | #define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M3 60 | #elif defined(PKG_CMBACKTRACE_PLATFORM_M4) 61 | #define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M4 62 | #elif defined(PKG_CMBACKTRACE_PLATFORM_M7) 63 | #define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M7 64 | #elif defined(PKG_CMBACKTRACE_PLATFORM_M33) 65 | #define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M33 66 | #else 67 | #error "You must select a CPU platform on menuconfig" 68 | #endif /* PKG_CMBACKTRACE_PLATFORM_M0_M0PLUS */ 69 | 70 | /* enable dump stack information */ 71 | #if defined(PKG_CMBACKTRACE_DUMP_STACK) 72 | #define CMB_USING_DUMP_STACK_INFO 73 | #endif 74 | 75 | /* language of print information */ 76 | #if defined(PKG_CMBACKTRACE_PRINT_ENGLISH) 77 | #define CMB_PRINT_LANGUAGE CMB_PRINT_LANGUAGE_ENGLISH 78 | #elif defined(PKG_CMBACKTRACE_PRINT_CHINESE) 79 | #define CMB_PRINT_LANGUAGE CMB_PRINT_LANGUAGE_CHINESE 80 | #elif defined(PKG_CMBACKTRACE_PRINT_CHINESE_UTF8) 81 | #define CMB_PRINT_LANGUAGE CMB_PRINT_LANGUAGE_CHINESE_UTF8 82 | #endif /* PKG_CMBACKTRACE_PRINT_ENGLISH */ 83 | 84 | #endif /* _CMB_CFG_H_ */ 85 | -------------------------------------------------------------------------------- /cmb_def.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the CmBacktrace Library. 3 | * 4 | * Copyright (c) 2016-2020, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: It is the macro definition head file for this library. 26 | * Created on: 2016-12-15 27 | */ 28 | 29 | #ifndef _CMB_DEF_H_ 30 | #define _CMB_DEF_H_ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | /* library software version number */ 37 | #define CMB_SW_VERSION "1.4.2" 38 | 39 | #define CMB_CPU_ARM_CORTEX_M0 0 40 | #define CMB_CPU_ARM_CORTEX_M3 1 41 | #define CMB_CPU_ARM_CORTEX_M4 2 42 | #define CMB_CPU_ARM_CORTEX_M7 3 43 | #define CMB_CPU_ARM_CORTEX_M33 4 44 | 45 | #define CMB_OS_PLATFORM_RTT 0 46 | #define CMB_OS_PLATFORM_UCOSII 1 47 | #define CMB_OS_PLATFORM_UCOSIII 2 48 | #define CMB_OS_PLATFORM_FREERTOS 3 49 | #define CMB_OS_PLATFORM_RTX5 4 50 | 51 | #define CMB_PRINT_LANGUAGE_ENGLISH 0 52 | #define CMB_PRINT_LANGUAGE_CHINESE 1 53 | #define CMB_PRINT_LANGUAGE_CHINESE_UTF8 2 54 | 55 | /* name max length, default size: 32 */ 56 | #ifndef CMB_NAME_MAX 57 | #define CMB_NAME_MAX 32 58 | #endif 59 | 60 | /* print information language, default is English */ 61 | #ifndef CMB_PRINT_LANGUAGE 62 | #define CMB_PRINT_LANGUAGE CMB_PRINT_LANGUAGE_ENGLISH 63 | #endif 64 | 65 | 66 | #if defined(__ARMCC_VERSION) 67 | /* C stack block name, default is STACK */ 68 | #ifndef CMB_CSTACK_BLOCK_NAME 69 | #define CMB_CSTACK_BLOCK_NAME STACK 70 | #endif 71 | /* code section name, default is ER_IROM1 */ 72 | #ifndef CMB_CODE_SECTION_NAME 73 | #define CMB_CODE_SECTION_NAME ER_IROM1 74 | #endif 75 | #elif defined(__ICCARM__) 76 | /* C stack block name, default is 'CSTACK' */ 77 | #ifndef CMB_CSTACK_BLOCK_NAME 78 | #define CMB_CSTACK_BLOCK_NAME "CSTACK" 79 | #endif 80 | /* code section name, default is '.text' */ 81 | #ifndef CMB_CODE_SECTION_NAME 82 | #define CMB_CODE_SECTION_NAME ".text" 83 | #endif 84 | #elif defined(__GNUC__) 85 | /* C stack block start address, defined on linker script file, default is _sstack */ 86 | #ifndef CMB_CSTACK_BLOCK_START 87 | #define CMB_CSTACK_BLOCK_START _sstack 88 | #endif 89 | /* C stack block end address, defined on linker script file, default is _estack */ 90 | #ifndef CMB_CSTACK_BLOCK_END 91 | #define CMB_CSTACK_BLOCK_END _estack 92 | #endif 93 | /* code section start address, defined on linker script file, default is _stext */ 94 | #ifndef CMB_CODE_SECTION_START 95 | #define CMB_CODE_SECTION_START _stext 96 | #endif 97 | /* code section end address, defined on linker script file, default is _etext */ 98 | #ifndef CMB_CODE_SECTION_END 99 | #define CMB_CODE_SECTION_END _etext 100 | #endif 101 | #else 102 | #error "not supported compiler" 103 | #endif 104 | 105 | /* supported function call stack max depth, default is 32 */ 106 | #ifndef CMB_CALL_STACK_MAX_DEPTH 107 | #define CMB_CALL_STACK_MAX_DEPTH 32 108 | #endif 109 | 110 | /* 111 | * The maximum print depth in case of exception prevents 112 | * too much stack information from printing and insufficient log space 113 | */ 114 | #ifndef CMB_DUMP_STACK_DEPTH_SIZE 115 | #define CMB_DUMP_STACK_DEPTH_SIZE (16) 116 | #endif 117 | 118 | /* system handler control and state register */ 119 | #ifndef CMB_SYSHND_CTRL 120 | #define CMB_SYSHND_CTRL (*(volatile unsigned int*) (0xE000ED24u)) 121 | #endif 122 | 123 | /* memory management fault status register */ 124 | #ifndef CMB_NVIC_MFSR 125 | #define CMB_NVIC_MFSR (*(volatile unsigned char*) (0xE000ED28u)) 126 | #endif 127 | 128 | /* bus fault status register */ 129 | #ifndef CMB_NVIC_BFSR 130 | #define CMB_NVIC_BFSR (*(volatile unsigned char*) (0xE000ED29u)) 131 | #endif 132 | 133 | /* usage fault status register */ 134 | #ifndef CMB_NVIC_UFSR 135 | #define CMB_NVIC_UFSR (*(volatile unsigned short*)(0xE000ED2Au)) 136 | #endif 137 | 138 | /* hard fault status register */ 139 | #ifndef CMB_NVIC_HFSR 140 | #define CMB_NVIC_HFSR (*(volatile unsigned int*) (0xE000ED2Cu)) 141 | #endif 142 | 143 | /* debug fault status register */ 144 | #ifndef CMB_NVIC_DFSR 145 | #define CMB_NVIC_DFSR (*(volatile unsigned short*)(0xE000ED30u)) 146 | #endif 147 | 148 | /* memory management fault address register */ 149 | #ifndef CMB_NVIC_MMAR 150 | #define CMB_NVIC_MMAR (*(volatile unsigned int*) (0xE000ED34u)) 151 | #endif 152 | 153 | /* bus fault manage address register */ 154 | #ifndef CMB_NVIC_BFAR 155 | #define CMB_NVIC_BFAR (*(volatile unsigned int*) (0xE000ED38u)) 156 | #endif 157 | 158 | /* auxiliary fault status register */ 159 | #ifndef CMB_NVIC_AFSR 160 | #define CMB_NVIC_AFSR (*(volatile unsigned short*)(0xE000ED3Cu)) 161 | #endif 162 | 163 | /** 164 | * Cortex-M fault registers 165 | */ 166 | struct cmb_hard_fault_regs{ 167 | struct { 168 | unsigned int r0; // Register R0 169 | unsigned int r1; // Register R1 170 | unsigned int r2; // Register R2 171 | unsigned int r3; // Register R3 172 | unsigned int r12; // Register R12 173 | unsigned int lr; // Link register 174 | unsigned int pc; // Program counter 175 | union { 176 | unsigned int value; 177 | struct { 178 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 179 | unsigned int IPSR : 9; // Interrupt Program Status register (IPSR) 180 | unsigned int EPSR : 18; // Execution Program Status register (EPSR) 181 | unsigned int APSR : 5; // Application Program Status register (APSR) 182 | #else 183 | unsigned int IPSR : 8; // Interrupt Program Status register (IPSR) 184 | unsigned int EPSR : 19; // Execution Program Status register (EPSR) 185 | unsigned int APSR : 5; // Application Program Status register (APSR) 186 | #endif 187 | } bits; 188 | } psr; // Program status register. 189 | } saved; 190 | 191 | union { 192 | unsigned int value; 193 | struct { 194 | unsigned int MEMFAULTACT : 1; // Read as 1 if memory management fault is active 195 | unsigned int BUSFAULTACT : 1; // Read as 1 if bus fault exception is active 196 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 197 | unsigned int HARDFAULTACT : 1; // Read as 1 if hardfault is active 198 | #else 199 | unsigned int UnusedBits1 : 1; 200 | #endif 201 | unsigned int USGFAULTACT : 1; // Read as 1 if usage fault exception is active 202 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 203 | unsigned int SECUREFAULTACT : 1; // Read as 1 if secure fault exception is active 204 | unsigned int NMIACT : 1; // Read as 1 if NMI exception is active 205 | unsigned int UnusedBits2 : 1; 206 | #else 207 | unsigned int UnusedBits2 : 3; 208 | #endif 209 | unsigned int SVCALLACT : 1; // Read as 1 if SVC exception is active 210 | unsigned int MONITORACT : 1; // Read as 1 if debug monitor exception is active 211 | unsigned int UnusedBits3 : 1; 212 | unsigned int PENDSVACT : 1; // Read as 1 if PendSV exception is active 213 | unsigned int SYSTICKACT : 1; // Read as 1 if SYSTICK exception is active 214 | unsigned int USGFAULTPENDED : 1; // Usage fault pended; usage fault started but was replaced by a higher-priority exception 215 | unsigned int MEMFAULTPENDED : 1; // Memory management fault pended; memory management fault started but was replaced by a higher-priority exception 216 | unsigned int BUSFAULTPENDED : 1; // Bus fault pended; bus fault handler was started but was replaced by a higher-priority exception 217 | unsigned int SVCALLPENDED : 1; // SVC pended; SVC was started but was replaced by a higher-priority exception 218 | unsigned int MEMFAULTENA : 1; // Memory management fault handler enable 219 | unsigned int BUSFAULTENA : 1; // Bus fault handler enable 220 | unsigned int USGFAULTENA : 1; // Usage fault handler enable 221 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 222 | unsigned int SECUREFAULTENA : 1; // Secure fault handler enable 223 | unsigned int SECUREFAULTPENDED : 1; // Secure fault pended; Secure fault handler was started but was replaced by a higher-priority exception 224 | unsigned int HARDFAULTPENDED : 1; // Hard fault pended; Hard fault handler was started but was replaced by a higher-priority exception 225 | #else 226 | // None 227 | #endif 228 | } bits; 229 | } syshndctrl; // System Handler Control and State Register (0xE000ED24) 230 | 231 | union { 232 | unsigned char value; 233 | struct { 234 | unsigned char IACCVIOL : 1; // Instruction access violation 235 | unsigned char DACCVIOL : 1; // Data access violation 236 | unsigned char UnusedBits : 1; 237 | unsigned char MUNSTKERR : 1; // Unstacking error 238 | unsigned char MSTKERR : 1; // Stacking error 239 | unsigned char MLSPERR : 1; // Floating-point lazy state preservation (M4/M7) 240 | unsigned char UnusedBits2 : 1; 241 | unsigned char MMARVALID : 1; // Indicates the MMAR is valid 242 | } bits; 243 | } mfsr; // Memory Management Fault Status Register (0xE000ED28) 244 | unsigned int mmar; // Memory Management Fault Address Register (0xE000ED34) 245 | 246 | union { 247 | unsigned char value; 248 | struct { 249 | unsigned char IBUSERR : 1; // Instruction access violation 250 | unsigned char PRECISERR : 1; // Precise data access violation 251 | unsigned char IMPREISERR : 1; // Imprecise data access violation 252 | unsigned char UNSTKERR : 1; // Unstacking error 253 | unsigned char STKERR : 1; // Stacking error 254 | unsigned char LSPERR : 1; // Floating-point lazy state preservation (M4/M7) 255 | unsigned char UnusedBits : 1; 256 | unsigned char BFARVALID : 1; // Indicates BFAR is valid 257 | } bits; 258 | } bfsr; // Bus Fault Status Register (0xE000ED29) 259 | unsigned int bfar; // Bus Fault Manage Address Register (0xE000ED38) 260 | 261 | union { 262 | unsigned short value; 263 | struct { 264 | unsigned short UNDEFINSTR : 1; // Attempts to execute an undefined instruction 265 | unsigned short INVSTATE : 1; // Attempts to switch to an invalid state (e.g., ARM) 266 | unsigned short INVPC : 1; // Attempts to do an exception with a bad value in the EXC_RETURN number 267 | unsigned short NOCP : 1; // Attempts to execute a coprocessor instruction 268 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 269 | unsigned short STKOF : 1; // Indicates a stack overflow error has occured 270 | unsigned short UnusedBits : 3; 271 | #else 272 | unsigned short UnusedBits : 4; 273 | #endif 274 | unsigned short UNALIGNED : 1; // Indicates that an unaligned access fault has taken place 275 | unsigned short DIVBYZERO0 : 1; // Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set) 276 | } bits; 277 | } ufsr; // Usage Fault Status Register (0xE000ED2A) 278 | 279 | union { 280 | unsigned int value; 281 | struct { 282 | unsigned int UnusedBits : 1; 283 | unsigned int VECTBL : 1; // Indicates hard fault is caused by failed vector fetch 284 | unsigned int UnusedBits2 : 28; 285 | unsigned int FORCED : 1; // Indicates hard fault is taken because of bus fault/memory management fault/usage fault 286 | unsigned int DEBUGEVT : 1; // Indicates hard fault is triggered by debug event 287 | } bits; 288 | } hfsr; // Hard Fault Status Register (0xE000ED2C) 289 | 290 | union { 291 | unsigned int value; 292 | struct { 293 | unsigned int HALTED : 1; // Halt requested in NVIC 294 | unsigned int BKPT : 1; // BKPT instruction executed 295 | unsigned int DWTTRAP : 1; // DWT match occurred 296 | unsigned int VCATCH : 1; // Vector fetch occurred 297 | unsigned int EXTERNAL : 1; // EDBGRQ signal asserted 298 | } bits; 299 | } dfsr; // Debug Fault Status Register (0xE000ED30) 300 | 301 | unsigned int afsr; // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional) 302 | }; 303 | 304 | /* assert for developer. */ 305 | #define CMB_ASSERT(EXPR) \ 306 | if (!(EXPR)) \ 307 | { \ 308 | cmb_println("(%s) has assert failed at %s.", #EXPR, __FUNCTION__); \ 309 | while (1); \ 310 | } 311 | 312 | /* ELF(Executable and Linking Format) file extension name for each compiler */ 313 | #if defined(__CC_ARM) || defined(__CLANG_ARM) || defined(__ARMCC_VERSION) 314 | #define CMB_ELF_FILE_EXTENSION_NAME ".axf" 315 | #elif defined(__ICCARM__) 316 | #define CMB_ELF_FILE_EXTENSION_NAME ".out" 317 | #elif defined(__GNUC__) 318 | #define CMB_ELF_FILE_EXTENSION_NAME ".elf" 319 | #else 320 | #error "not supported compiler" 321 | #endif 322 | 323 | #ifndef cmb_println 324 | #error "cmb_println isn't defined in 'cmb_cfg.h'" 325 | #endif 326 | 327 | #ifndef CMB_CPU_PLATFORM_TYPE 328 | #error "CMB_CPU_PLATFORM_TYPE isn't defined in 'cmb_cfg.h'" 329 | #endif 330 | 331 | #if (defined(CMB_USING_BARE_METAL_PLATFORM) && defined(CMB_USING_OS_PLATFORM)) 332 | #error "CMB_USING_BARE_METAL_PLATFORM and CMB_USING_OS_PLATFORM only one of them can be used" 333 | #elif defined(CMB_USING_OS_PLATFORM) 334 | #if !defined(CMB_OS_PLATFORM_TYPE) 335 | #error "CMB_OS_PLATFORM_TYPE isn't defined in 'cmb_cfg.h'" 336 | #endif /* !defined(CMB_OS_PLATFORM_TYPE) */ 337 | #if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) 338 | #include 339 | #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII) 340 | #include 341 | #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII) 342 | #include 343 | #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS) 344 | #include 345 | extern uint32_t *vTaskStackAddr(void);/* need to modify the FreeRTOS/tasks source code */ 346 | extern uint32_t vTaskStackSize(void); 347 | extern char * vTaskName(void); 348 | #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTX5) 349 | #include "rtx_os.h" 350 | #else 351 | #error "not supported OS type" 352 | #endif /* (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) */ 353 | #endif /* (defined(CMB_USING_BARE_METAL_PLATFORM) && defined(CMB_USING_OS_PLATFORM)) */ 354 | 355 | /* include or export for supported cmb_get_msp, cmb_get_psp and cmb_get_sp function */ 356 | #if defined(__CC_ARM) 357 | static __inline __asm uint32_t cmb_get_msp(void) { 358 | mrs r0, msp 359 | bx lr 360 | } 361 | static __inline __asm uint32_t cmb_get_psp(void) { 362 | mrs r0, psp 363 | bx lr 364 | } 365 | static __inline __asm uint32_t cmb_get_sp(void) { 366 | mov r0, sp 367 | bx lr 368 | } 369 | #elif defined(__CLANG_ARM) 370 | __attribute__( (always_inline) ) static __inline uint32_t cmb_get_msp(void) { 371 | uint32_t result; 372 | __asm volatile ("mrs %0, msp" : "=r" (result) ); 373 | return (result); 374 | } 375 | __attribute__( (always_inline) ) static __inline uint32_t cmb_get_psp(void) { 376 | uint32_t result; 377 | __asm volatile ("mrs %0, psp" : "=r" (result) ); 378 | return (result); 379 | } 380 | __attribute__( (always_inline) ) static __inline uint32_t cmb_get_sp(void) { 381 | uint32_t result; 382 | __asm volatile ("mov %0, sp" : "=r" (result) ); 383 | return (result); 384 | } 385 | #elif defined(__ICCARM__) 386 | /* IAR iccarm specific functions */ 387 | /* Close Raw Asm Code Warning */ 388 | #pragma diag_suppress=Pe940 389 | static uint32_t cmb_get_msp(void) 390 | { 391 | __asm("mrs r0, msp"); 392 | __asm("bx lr"); 393 | } 394 | static uint32_t cmb_get_psp(void) 395 | { 396 | __asm("mrs r0, psp"); 397 | __asm("bx lr"); 398 | } 399 | static uint32_t cmb_get_sp(void) 400 | { 401 | __asm("mov r0, sp"); 402 | __asm("bx lr"); 403 | } 404 | #pragma diag_default=Pe940 405 | #elif defined(__GNUC__) 406 | __attribute__( ( always_inline ) ) static inline uint32_t cmb_get_msp(void) { 407 | register uint32_t result; 408 | __asm volatile ("MRS %0, msp\n" : "=r" (result) ); 409 | return(result); 410 | } 411 | __attribute__( ( always_inline ) ) static inline uint32_t cmb_get_psp(void) { 412 | register uint32_t result; 413 | __asm volatile ("MRS %0, psp\n" : "=r" (result) ); 414 | return(result); 415 | } 416 | __attribute__( ( always_inline ) ) static inline uint32_t cmb_get_sp(void) { 417 | register uint32_t result; 418 | __asm volatile ("MOV %0, sp\n" : "=r" (result) ); 419 | return(result); 420 | } 421 | #else 422 | #error "not supported compiler" 423 | #endif 424 | 425 | #ifndef RT_WEAK 426 | #define RT_WEAK rt_weak 427 | #endif 428 | 429 | #endif /* _CMB_DEF_H_ */ 430 | -------------------------------------------------------------------------------- /cmb_flash_log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2019, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2019-12-14 armink the first version 9 | */ 10 | 11 | #define DBG_TAG "cmb_log" 12 | #define DBG_LVL DBG_LOG 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | //#define CMB_USING_FAL_FLASH_LOG 20 | //#define CMB_USING_FAL_BACKUP_LOG_TO_FILE 21 | 22 | #if defined(CMB_USING_FAL_FLASH_LOG) 23 | 24 | #if !defined(PKG_USING_FAL) || !defined(RT_USING_DFS) 25 | #error "please enable the FAL package and DFS component" 26 | #endif 27 | 28 | #include 29 | #include 30 | 31 | #ifndef CMB_FAL_FLASH_LOG_PART 32 | #define CMB_FAL_FLASH_LOG_PART "cmb_log" 33 | #endif 34 | 35 | #ifndef CMB_LOG_FILE_PATH 36 | #define CMB_LOG_FILE_PATH "/log/cmb.log" 37 | #endif 38 | 39 | /* cmb flash log partition write granularity, default: 8 bytes */ 40 | #ifndef CMB_FLASH_LOG_PART_WG 41 | #define CMB_FLASH_LOG_PART_WG 8 42 | #endif 43 | 44 | /* the log length's size which saved in flash */ 45 | #define CMB_LOG_LEN_SIZE MAX(sizeof(size_t), CMB_FLASH_LOG_PART_WG) 46 | 47 | #ifndef MIN 48 | #define MIN(a, b) (a < b ? a : b) 49 | #endif 50 | #ifndef MAX 51 | #define MAX(a, b) (a > b ? a : b) 52 | #endif 53 | 54 | static const struct fal_partition *cmb_log_part = NULL; 55 | 56 | /** 57 | * write cmb log to flash partition @see CMB_FLASH_LOG_PART 58 | * 59 | * @param log log buffer 60 | * @param len log length 61 | */ 62 | void cmb_flash_log_write(const char *log, size_t len) 63 | { 64 | 65 | static uint32_t addr = 0; 66 | 67 | /* addr out of partition length */ 68 | if(addr >= cmb_log_part->len) 69 | return; 70 | 71 | uint8_t len_buf[CMB_LOG_LEN_SIZE] = { 0 }; 72 | static rt_bool_t first_write = RT_TRUE; 73 | 74 | if (first_write) 75 | { 76 | fal_partition_erase_all(cmb_log_part); 77 | first_write = RT_FALSE; 78 | } 79 | 80 | /* write log length */ 81 | rt_memcpy(len_buf, (uint8_t *)&len, sizeof(size_t)); 82 | fal_partition_write(cmb_log_part, addr, len_buf, sizeof(len_buf)); 83 | addr += CMB_LOG_LEN_SIZE; 84 | /* write log content */ 85 | fal_partition_write(cmb_log_part, addr, (uint8_t *)log, len); 86 | addr += RT_ALIGN(len, CMB_FLASH_LOG_PART_WG); 87 | } 88 | 89 | #if defined(RT_USING_ULOG) 90 | 91 | static void ulog_cmb_flash_log_backend_output(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, 92 | const char *log, size_t len) 93 | { 94 | if (!rt_strcmp(tag, CMB_LOG_TAG)) 95 | { 96 | cmb_flash_log_write(log, len); 97 | } 98 | } 99 | 100 | int ulog_cmb_flash_log_backend_init(void) 101 | { 102 | static struct ulog_backend backend; 103 | 104 | cmb_log_part = fal_partition_find(CMB_FAL_FLASH_LOG_PART); 105 | RT_ASSERT(cmb_log_part != NULL); 106 | 107 | backend.init = RT_NULL; 108 | backend.output = ulog_cmb_flash_log_backend_output; 109 | 110 | ulog_backend_register(&backend, "cmb_flash_log", RT_FALSE); 111 | 112 | return 0; 113 | } 114 | INIT_APP_EXPORT(ulog_cmb_flash_log_backend_init); 115 | 116 | #else 117 | void cmb_flash_log_println(const char *fmt, ...) 118 | { 119 | va_list args; 120 | rt_size_t length; 121 | static char rt_log_buf[RT_CONSOLEBUF_SIZE]; 122 | 123 | va_start(args, fmt); 124 | 125 | length = rt_vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args); 126 | if (length > RT_CONSOLEBUF_SIZE - 1 - 2) 127 | length = RT_CONSOLEBUF_SIZE - 3; 128 | 129 | /* add CRLF */ 130 | rt_log_buf[length++] = '\r'; 131 | rt_log_buf[length++] = '\n'; 132 | 133 | cmb_flash_log_write(rt_log_buf, length); 134 | 135 | va_end(args); 136 | } 137 | #endif /* RT_USING_ULOG */ 138 | 139 | 140 | #ifdef CMB_USING_FAL_BACKUP_LOG_TO_FILE 141 | int cmb_backup_flash_log_to_file(void) 142 | { 143 | cmb_log_part = fal_partition_find(CMB_FAL_FLASH_LOG_PART); 144 | RT_ASSERT(cmb_log_part != NULL); 145 | 146 | size_t len; 147 | int result = 0; 148 | uint32_t addr = 0; 149 | rt_bool_t has_read_log = RT_FALSE; 150 | int log_fd = -1; 151 | 152 | while (1) 153 | { 154 | result = fal_partition_read(cmb_log_part, addr, (uint8_t *)&len, sizeof(size_t)); 155 | if ((len != 0xFFFFFFFF) && (result >= 0)) 156 | { 157 | /* addr out of partition length */ 158 | if(addr >= cmb_log_part->len) 159 | break; 160 | 161 | char log_buf[ULOG_LINE_BUF_SIZE]; 162 | 163 | if (!has_read_log) 164 | { 165 | has_read_log = RT_TRUE; 166 | LOG_I("An CmBacktrace log was found on flash. Now will backup it to file ("CMB_LOG_FILE_PATH")."); 167 | //TODO check the folder 168 | log_fd = open(CMB_LOG_FILE_PATH, O_WRONLY | O_CREAT | O_APPEND); 169 | if (log_fd < 0) { 170 | LOG_E("Open file ("CMB_LOG_FILE_PATH") failed."); 171 | break; 172 | } 173 | } 174 | addr += CMB_LOG_LEN_SIZE; 175 | /* read log content */ 176 | result = fal_partition_read(cmb_log_part, addr, (uint8_t *)log_buf, MIN(ULOG_LINE_BUF_SIZE, len)); 177 | if (result < 0) 178 | { 179 | break; 180 | } 181 | addr += RT_ALIGN(len, CMB_FLASH_LOG_PART_WG); 182 | /* backup log to file */ 183 | write(log_fd, log_buf, MIN(ULOG_LINE_BUF_SIZE, len)); 184 | } 185 | else 186 | { 187 | break; 188 | } 189 | } 190 | 191 | if (has_read_log) 192 | { 193 | if (log_fd >= 0) 194 | { 195 | LOG_I("Backup the CmBacktrace flash log to file ("CMB_LOG_FILE_PATH") successful."); 196 | close(log_fd); 197 | fal_partition_erase_all(cmb_log_part); 198 | } 199 | } 200 | 201 | return 0; 202 | } 203 | INIT_APP_EXPORT(cmb_backup_flash_log_to_file); 204 | #endif /* CMB_USING_FAL_BACKUP_LOG_TO_FILE */ 205 | 206 | #endif /* defined(CMB_USING_FLASH_LOG_BACKUP) */ 207 | -------------------------------------------------------------------------------- /cmb_port.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the CmBacktrace Library. 3 | * 4 | * Copyright (c) 2016-2018, zylx, <1346773219@qq.com> 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: Initialize function and other general function. 26 | * Created on: 2016-12-15 27 | */ 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #if defined(__CC_ARM) 34 | #pragma O1 35 | #elif defined(__CLANG_ARM) 36 | #pragma optimize ("O1") 37 | #elif defined(__ICCARM__) 38 | #pragma optimize=none 39 | #elif defined(__GNUC__) 40 | #pragma GCC optimize ("O0") 41 | #endif 42 | 43 | #if defined(__CC_ARM) 44 | static __inline __asm void cmb_set_psp(uint32_t psp) { 45 | msr psp, r0 46 | bx lr 47 | } 48 | #elif defined(__CLANG_ARM) 49 | __attribute__( (always_inline) ) static __inline void cmb_set_psp(uint32_t psp) { 50 | __asm volatile ("msr psp, %0" :: "r" (psp) ); 51 | } 52 | #elif defined(__ICCARM__) 53 | /* IAR iccarm specific functions */ 54 | /* Close Raw Asm Code Warning */ 55 | #pragma diag_suppress=Pe940 56 | static void cmb_set_psp(uint32_t psp) 57 | { 58 | __asm("msr psp, r0"); 59 | __asm("bx lr"); 60 | } 61 | #pragma diag_default=Pe940 62 | #elif defined(__GNUC__) 63 | __attribute__( ( always_inline ) ) static inline void cmb_set_psp(uint32_t psp) { 64 | __asm volatile ("MSR psp, %0\n\t" :: "r" (psp) ); 65 | } 66 | #else 67 | #error "not supported compiler" 68 | #endif 69 | 70 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33) 71 | 72 | void rt_cm_backtrace_exception_hook(void *context) 73 | { 74 | #define CMB_LR_WORD_OFFSET_START 0 75 | #define CMB_LR_WORD_OFFSET_END 80 76 | 77 | #define EXC_RETURN_SECURE_STATE_MASK (1UL << 0) /* always 0: Non-secure state */ 78 | #define EXC_RETURN_RESERVED_MASK (1UL << 1) /* reserved */ 79 | #define EXC_RETURN_RET_STACK_MASK (1UL << 2) /* 0: return to MSP, 1: return to PSP */ 80 | #define EXC_RETURN_RET_MODE_MASK (1UL << 3) /* 0: return to handler mode, 1: return to thread mode */ 81 | #define EXC_RETURN_FRAME_TYPE_MASK (1UL << 4) /* 0: 26 word(use FPU), 1: 8 word(not use FPU)*/ 82 | #define EXC_RETURN_STACKING_RULE_MASK (1UL << 5) /* always 1: default rules for stacking the callee register */ 83 | #define EXC_RETURN_SECURE_STACK_MASK (1UL << 6) /* always 0: Non-secure stack used */ 84 | 85 | uint8_t lr_offset = 0; 86 | uint32_t lr; 87 | uint32_t *other_info_sp = 0; 88 | 89 | rt_interrupt_enter(); 90 | 91 | #ifdef RT_USING_FINSH 92 | extern long list_thread(void); 93 | list_thread(); 94 | #endif 95 | 96 | /* 97 | ****************************************************************************************************************** 98 | * 99 | * 1. Return stack is PSP 100 | * 101 | * PSP: [ sizeof(uint32_t) * 13 ] 102 | * ---------------------------------------------------------------------|------------------------------------------ 103 | * | EXC_RETURN | tz | lr | psplim | control | R4 - R11 | R0 | R1 | R2 | R3 | LR | PC | xPSR | 104 | * ---------------------------------------------------------------------|------------------------------------------ 105 | * |<--- psp, need to change to point to context |<--- context 106 | * |<--- other info sp 107 | * 108 | * MSP: | looking for EXC_RETURN -------->\\\ 109 | * --------------------|-----------------------------------------------------|------------------------------------- 110 | * | x x x x x x x x x x | EXC_RETURN | 111 | * --------------------|-----------------------------------------------------|------------------------------------- 112 | * |<--- current SP |<--- pressed before calling 113 | * | rt_hw_hard_fault_exception 114 | * 115 | ****************************************************************************************************************** 116 | * 117 | * 2. Return stack is MSP 118 | * 119 | * MSP: | looking for EXC_RETURN ------->\\\ [ sizeof(uint32_t) * 13 ] 120 | * -----------|----------------------------------------|------------|-----------------------|---------------------- 121 | * | x x x x x x x x x x | EXC_RETURN | EXC_RETURN ...... R11 | R0 ...... xPSR | 122 | * -----------|----------------------------------------|------------|-----------------------|---------------------- 123 | * |<--- current SP |<--- pressed before calling |<--- context 124 | * | rt_hw_hard_fault_exception 125 | * |<--- other info sp 126 | * 127 | ****************************************************************************************************************** 128 | */ 129 | 130 | /* 131 | * Look for EXC_RETURN in MSP. 132 | * 133 | * Starting from the current SP(MSP), keep looking forward until you find the LR(EXC_RETURN) 134 | * that was pressed into the MSP before calling rt_hw_hard_fault_exception in the HardFault_Handler function 135 | * in the context_rvds.S file. 136 | */ 137 | for (lr_offset = CMB_LR_WORD_OFFSET_START; lr_offset <= CMB_LR_WORD_OFFSET_END; lr_offset++) 138 | { 139 | lr = *((uint32_t *)(cmb_get_sp() + sizeof(uint32_t) * lr_offset)); 140 | if ((lr == 0xFFFFFFBC) || (lr == 0xFFFFFFAC) || \ 141 | (lr == 0xFFFFFFB0) || (lr == 0xFFFFFFA0) || (lr == 0xFFFFFFB8) ||(lr == 0xFFFFFFA8)) 142 | { 143 | break; 144 | } 145 | } 146 | 147 | other_info_sp = (uint32_t *)((uint32_t)context - sizeof(uint32_t) * 13); /* The stack grows down */ 148 | 149 | if (lr & EXC_RETURN_RET_STACK_MASK) 150 | { 151 | /* 152 | * Return stack is psp 153 | * 154 | * the PSP is changed by RT-Thread HardFault_Handler, so restore it to HardFault context 155 | * PSP will be used in cm_backtrace_fault, so must restore it. 156 | * 157 | * 4 * 13: [ exc_return, tz, lr, psplim, control, r4 - r11 ], skip it, point to [R0 - xPSR] 158 | */ 159 | cmb_set_psp(cmb_get_psp() + sizeof(uint32_t) * 13); 160 | } 161 | 162 | cm_backtrace_fault(lr, (uint32_t)context); 163 | 164 | /* 165 | * print other information 166 | */ 167 | cmb_println("====================== other information ====================="); 168 | cmb_println(" EXC_RETURN: %08x", *other_info_sp++); 169 | cmb_println(" tz : %08x", *other_info_sp++); 170 | cmb_println(" lr : %08x", *other_info_sp++); 171 | cmb_println(" psplim : %08x", *other_info_sp++); 172 | cmb_println(" conrtol : %08x", *other_info_sp++); 173 | cmb_println(" R4 : %08x R5 : %08x R6 : %08x R7 : %08x ", \ 174 | *other_info_sp, *(other_info_sp + 1), *(other_info_sp + 2), *(other_info_sp + 3)); 175 | cmb_println(" R8 : %08x R9 : %08x R10: %08x R11: %08x ", \ 176 | *(other_info_sp + 4), *(other_info_sp + 5), *(other_info_sp + 6), *(other_info_sp + 7)); 177 | other_info_sp += 8; 178 | cmb_println("=============================================================="); 179 | 180 | cmb_println("Current system tick: %ld", rt_tick_get()); 181 | 182 | rt_interrupt_leave(); 183 | } 184 | 185 | #else 186 | 187 | void rt_cm_backtrace_exception_hook(void *context) 188 | { 189 | uint8_t lr_offset = 0; 190 | uint32_t lr; 191 | 192 | #define CMB_LR_WORD_OFFSET_START 6 193 | #define CMB_LR_WORD_OFFSET_END 20 194 | #define CMB_SP_WORD_OFFSET (lr_offset + 1) 195 | 196 | #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M0) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M3) 197 | #define EXC_RETURN_MASK 0x0000000F // Bits[31:4] 198 | #else 199 | #define EXC_RETURN_MASK 0x0000000F // Bits[31:5] 200 | #endif 201 | 202 | rt_interrupt_enter(); 203 | 204 | #ifdef RT_USING_FINSH 205 | extern long list_thread(void); 206 | list_thread(); 207 | #endif 208 | 209 | /* the PSP is changed by RT-Thread HardFault_Handler, so restore it to HardFault context */ 210 | #if (defined (__VFP_FP__) && !defined(__SOFTFP__)) || (defined (__ARMVFP__)) || (defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP)) 211 | cmb_set_psp(cmb_get_psp() + 4 * 10); 212 | #else 213 | cmb_set_psp(cmb_get_psp() + 4 * 9); 214 | #endif 215 | 216 | /* auto calculate the LR offset */ 217 | for (lr_offset = CMB_LR_WORD_OFFSET_START; lr_offset <= CMB_LR_WORD_OFFSET_END; lr_offset ++) 218 | { 219 | lr = *((uint32_t *)(cmb_get_sp() + sizeof(uint32_t) * lr_offset)); 220 | /* 221 | * Cortex-M0: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/Babefdjc.html 222 | * Cortex-M3: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Babefdjc.html 223 | * Cortex-M4: http://infocenter.arm.com/help/topic/com.arm.doc.dui0553b/DUI0553.pdf P41 224 | * Cortex-M7: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0646c/Babefdjc.html 225 | */ 226 | if ((lr == 0xFFFFFFF1) || (lr == 0xFFFFFFF9) || (lr == 0xFFFFFFFD) || (lr == 0xFFFFFFE1) || (lr == 0xFFFFFFE9) || (lr == 0xFFFFFFED)) 227 | { 228 | break; 229 | } 230 | } 231 | 232 | cm_backtrace_fault(lr, cmb_get_sp() + sizeof(uint32_t) * CMB_SP_WORD_OFFSET); 233 | 234 | cmb_println("Current system tick: %ld", rt_tick_get()); 235 | rt_interrupt_leave(); 236 | } 237 | 238 | #endif 239 | 240 | void rt_cm_backtrace_assert_hook(const char* ex, const char* func, rt_size_t line) 241 | { 242 | rt_interrupt_enter(); 243 | 244 | #ifdef RT_USING_FINSH 245 | extern long list_thread(void); 246 | list_thread(); 247 | #endif 248 | 249 | cmb_println(""); 250 | cmb_println("(%s) has assert failed at %s:%ld.", ex, func, line); 251 | 252 | cm_backtrace_assert(cmb_get_sp()); 253 | 254 | cmb_println("Current system tick: %ld", rt_tick_get()); 255 | 256 | rt_interrupt_leave(); 257 | } 258 | 259 | RT_WEAK rt_err_t exception_hook(void *context) { 260 | volatile uint8_t _continue = 1; 261 | 262 | rt_cm_backtrace_exception_hook(context); 263 | 264 | while (_continue == 1); 265 | 266 | return RT_EOK; 267 | } 268 | 269 | RT_WEAK void assert_hook(const char* ex, const char* func, rt_size_t line) { 270 | volatile uint8_t _continue = 1; 271 | 272 | rt_cm_backtrace_assert_hook(ex, func, line); 273 | 274 | while (_continue == 1); 275 | } 276 | 277 | int rt_cm_backtrace_init(void) { 278 | static rt_bool_t is_init = RT_FALSE; 279 | 280 | if (is_init) 281 | { 282 | return 0; 283 | } 284 | 285 | cm_backtrace_init("rt-thread","1.0","1.0"); 286 | 287 | rt_hw_exception_install(exception_hook); 288 | 289 | rt_assert_set_hook(assert_hook); 290 | 291 | is_init = RT_TRUE; 292 | return 0; 293 | } 294 | INIT_DEVICE_EXPORT(rt_cm_backtrace_init); 295 | 296 | long cmb_test(int argc, char **argv) { 297 | volatile int * SCB_CCR = (volatile int *) 0xE000ED14; // SCB->CCR 298 | int x, y, z; 299 | 300 | if (argc < 2) 301 | { 302 | rt_kprintf("Please input 'cmb_test ' \n"); 303 | return 0; 304 | } 305 | 306 | if (!strcmp(argv[1], "DIVBYZERO")) 307 | { 308 | *SCB_CCR |= (1 << 4); /* bit4: DIV_0_TRP. */ 309 | x = 10; 310 | y = rt_strlen(""); 311 | z = x / y; 312 | rt_kprintf("z:%d\n", z); 313 | 314 | return 0; 315 | } 316 | else if (!strcmp(argv[1], "UNALIGNED")) 317 | { 318 | volatile int * p; 319 | volatile int value; 320 | *SCB_CCR |= (1 << 3); /* bit3: UNALIGN_TRP. */ 321 | 322 | p = (int *) 0x00; 323 | value = *p; 324 | rt_kprintf("addr:0x%02X value:0x%08X\r\n", (int) p, value); 325 | 326 | p = (int *) 0x04; 327 | value = *p; 328 | rt_kprintf("addr:0x%02X value:0x%08X\r\n", (int) p, value); 329 | 330 | p = (int *) 0x03; 331 | value = *p; 332 | rt_kprintf("addr:0x%02X value:0x%08X\r\n", (int) p, value); 333 | 334 | return 0; 335 | } 336 | else if (!strcmp(argv[1], "ASSERT")) 337 | { 338 | RT_ASSERT(0); 339 | } 340 | return 0; 341 | } 342 | MSH_CMD_EXPORT(cmb_test, cm_backtrace_test: cmb_test ); 343 | --------------------------------------------------------------------------------