ForewordCmBacktrace (Cortex Microcontroller Backtrace)是一款針對(duì) ARM Cortex-M 系列 MCU
的錯(cuò)誤代碼自動(dòng)追蹤,、定位,,錯(cuò)誤原因自動(dòng)分析的開源庫(kù)。主要特性如下: 支持的錯(cuò)誤包括:
斷言(assert)
故障(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault)
故障原因 自動(dòng)診斷 :可在故障發(fā)生時(shí),,自動(dòng)分析出故障的原因,,定位發(fā)生故障的代碼位置,而無(wú)需
再手動(dòng)分析繁雜的故障寄存器,;
輸出錯(cuò)誤現(xiàn)場(chǎng)的 函數(shù)調(diào)用棧(需配合 addr2line 工具進(jìn)行精確定位),,還原發(fā)生錯(cuò)誤時(shí)的現(xiàn)場(chǎng)信息
,定位問(wèn)題代碼位置,、邏輯更加快捷、精準(zhǔn),。也可以在正常狀態(tài)下使用該庫(kù),,獲取當(dāng)前的函數(shù)調(diào)用
棧;
支持 裸機(jī) 及以下操作系統(tǒng)平臺(tái):
RT-Thread
UCOS
FreeRTOS(需修改源碼)
根據(jù)錯(cuò)誤現(xiàn)場(chǎng)狀態(tài),輸出對(duì)應(yīng)的 線程棧 或 C 主棧,;
故障診斷信息支持多國(guó)語(yǔ)言(目前:簡(jiǎn)體中文,、英文);
適配 Cortex-M0/M3/M4/M7 MCU,;
支持 IAR,、KEIL、GCC 編譯器,; https://github.com/armink/CmBacktrace
從介紹上來(lái)說(shuō),,非常友好,發(fā)生斷言,,Hard Fault 等情況可以幫忙記錄故障原因,,打印輸出寄存器
狀態(tài),堆棧等內(nèi)容,,實(shí)際使用時(shí)配合 Flash 保存這些信息也非常容易,,從而在一個(gè)復(fù)雜系統(tǒng)中可以
找到某些隱藏的BUG,進(jìn)而提升產(chǎn)品的穩(wěn)定性,,非常不錯(cuò) 詳細(xì)的介紹在 github 上也有,,當(dāng)然直接看源碼更合適。 Source Codecmb_cfg.h#ifndef _CMB_CFG_H_
#define _CMB_CFG_H_
/* print line, must config by user */
#define cmb_println(...) /* e.g., printf(__VA_ARGS__);printf("\r\n") */
首先 ... 表示給入的參數(shù)是一個(gè)不定長(zhǎng)度的參數(shù),, __VA_ARGS__ 則是把這個(gè)參數(shù)列表給傳入
對(duì)應(yīng)使用的函數(shù)中,,需要注意的是 ... 只能代替最后面的宏參數(shù)
/* enable bare metal(no OS) platform */
/* #define CMB_USING_BARE_METAL_PLATFORM */
/* enable OS platform */
#define CMB_USING_OS_PLATFORM
/* OS platform type, must config when CMB_USING_OS_PLATFORM is enable */
/* #define CMB_OS_PLATFORM_TYPE CMB_OS_PLATFORM_RTT or CMB_OS_PLATFORM_UCOSII or CMB_OS_PLATFORM_UCOSIII or CMB_OS_PLATFORM_FREERTOS */
#define CMB_OS_PLATFORM_TYPE CMB_OS_PLATFORM_FREERTOS
/* cpu platform type, must config by user */
/* #define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M0 or CMB_CPU_ARM_CORTEX_M3 or CMB_CPU_ARM_CORTEX_M4 or CMB_CPU_ARM_CORTEX_M7 */
#define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M7
/* enable dump stack information */
#define CMB_USING_DUMP_STACK_INFO
/* language of print information */
/* #define CMB_PRINT_LANGUAGE CMB_PRINT_LANGUAGE_ENGLISH(default) or CMB_PRINT_LANUUAGE_CHINESE */
#endif /* _CMB_CFG_H_ */ cmb_fault.SAREA |.text|, CODE, READONLY, ALIGN=2
THUMB
REQUIRE8
PRESERVE8
; NOTE: If use this file's HardFault_Handler, please comments the HardFault_Handler code on other file.
IMPORT cm_backtrace_fault
EXPORT HardFault_Handler
HardFault_Handler PROC
MOV r0, lr ; get lr
MOV r1, sp ; get stack pointer (current is MSP)
BL cm_backtrace_fault
Fault_Loop
BL Fault_Loop ;while(1)
ENDP
END 這里是重寫了 HardFault 的中斷處理函數(shù),通過(guò)把 lr 和 sp 存入 r0 r1 (他們作為參數(shù))調(diào)用
cm_backtrace_fault 進(jìn)行錯(cuò)誤追蹤處理 ,,處理結(jié)束后程序死在這里,。 由于這里重構(gòu)了 HardFault 中斷,那么原本的中斷函數(shù)就需要注釋掉了,,當(dāng)然也可以自己重寫Hard
Fault ,,不用他這里的 HardFault 。 cmb_def.h定義了許多會(huì)使用到的寄存器結(jié)構(gòu)體,,以及和 OS,、編譯器、平臺(tái)相關(guān)的宏定義 cm_backtrace.h#ifndef _CORTEXM_BACKTRACE_H_
#define _CORTEXM_BACKTRACE_H_
#include "cmb_def.h"
void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver);
void cm_backtrace_firmware_info(void);
size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp);
void cm_backtrace_assert(uint32_t sp);
void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp);
#endif /* _CORTEXM_BACKTRACE_H_ */ 這里定義了幾個(gè)會(huì)被調(diào)用的函數(shù) 其中 cm_backtrace_init 需要在系統(tǒng)啟動(dòng)之后 立馬進(jìn)行初始化,,保證可以在出錯(cuò)之前完成 cm_backtrace_fault 就是 HardFault 調(diào)用的處理函數(shù) cm_backtrace_assert 是系統(tǒng)斷言中必須要加入的函數(shù) cm_backtrace_call_stack 和 cm_backtrace_firmware_info 則是 斷言和 HardFault 調(diào)用的
子函數(shù) cm_backtrace.cvoid cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver) {
strncpy(fw_name, firmware_name, CMB_NAME_MAX);
strncpy(hw_ver, hardware_ver, CMB_NAME_MAX);
strncpy(sw_ver, software_ver, CMB_NAME_MAX);
設(shè)定好對(duì)應(yīng)的固件名 硬件版本 軟件版本(這里的軟件版本其實(shí)可以對(duì)應(yīng)git commit號(hào))
#if defined(__CC_ARM)
main_stack_start_addr = (uint32_t)&CSTACK_BLOCK_START(CMB_CSTACK_BLOCK_NAME);
main_stack_size = (uint32_t)&CSTACK_BLOCK_END(CMB_CSTACK_BLOCK_NAME) - main_stack_start_addr;
code_start_addr = (uint32_t)&CODE_SECTION_START(CMB_CODE_SECTION_NAME);
code_size = (uint32_t)&CODE_SECTION_END(CMB_CODE_SECTION_NAME) - code_start_addr;
#elif defined(__ICCARM__)
main_stack_start_addr = (uint32_t)__section_begin(CMB_CSTACK_BLOCK_NAME);
main_stack_size = (uint32_t)__section_end(CMB_CSTACK_BLOCK_NAME) - main_stack_start_addr;
code_start_addr = (uint32_t)__section_begin(CMB_CODE_SECTION_NAME);
code_size = (uint32_t)__section_end(CMB_CODE_SECTION_NAME) - code_start_addr;
#elif defined(__GNUC__)
main_stack_start_addr = (uint32_t)(&CMB_CSTACK_BLOCK_START);
main_stack_size = (uint32_t)(&CMB_CSTACK_BLOCK_END) - main_stack_start_addr;
code_start_addr = (uint32_t)(&CMB_CODE_SECTION_START);
code_size = (uint32_t)(&CMB_CODE_SECTION_END) - code_start_addr;
#else
#error "not supported compiler"
#endif
根據(jù)編譯器決定具體的棧地址 棧大小,,代碼區(qū)地址,代碼大小
init_ok = true;
} 打印硬件信息 void cm_backtrace_firmware_info(void) {
cmb_println(print_info[PRINT_FIRMWARE_INFO], fw_name, hw_ver, sw_ver);
} 如果是在用戶態(tài)發(fā)生的問(wèn)題,,也就是 thread 中出現(xiàn)的問(wèn)題 ,,根據(jù) OS 獲取對(duì)應(yīng)的線程棧地址和大
小 static void get_cur_thread_stack_info(uint32_t sp, uint32_t *start_addr, size_t *size) {
CMB_ASSERT(start_addr);
CMB_ASSERT(size);
#if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT)
*start_addr = (uint32_t) rt_thread_self()->stack_addr;
*size = rt_thread_self()->stack_size;
#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII)
extern OS_TCB *OSTCBCur;
*start_addr = (uint32_t) OSTCBCur->OSTCBStkBottom;
*size = OSTCBCur->OSTCBStkSize * sizeof(OS_STK);
#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII)
#error "not implemented, I hope you can do this"
//TODO 待實(shí)現(xiàn)
#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS)
*start_addr = (uint32_t)vTaskStackAddr();
*size = vTaskStackSize() * sizeof( StackType_t );
#endif
} 獲取出錯(cuò)任務(wù)的任務(wù)名 static const char *get_cur_thread_name(void) {
#if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT)
return rt_thread_self()->name;
#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII)
extern OS_TCB *OSTCBCur;
#if OS_TASK_NAME_SIZE > 0 || OS_TASK_NAME_EN > 0
return (const char *)OSTCBCur->OSTCBTaskName;
#else
return NULL;
#endif /* OS_TASK_NAME_SIZE > 0 || OS_TASK_NAME_EN > 0 */
#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII)
#error "not implemented, I hope you can do this"
//TODO 待實(shí)現(xiàn)
#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS)
return vTaskName();
#endif
} 打印出錯(cuò)任務(wù)的所有棧內(nèi)容 static void dump_cur_thread_stack(uint32_t stack_start_addr, size_t stack_size, uint32_t *stack_pointer) {
cmb_println(print_info[PRINT_THREAD_STACK_INFO]);
for (; (uint32_t) stack_pointer < stack_start_addr + stack_size; stack_pointer++) {
cmb_println(" addr: %08x data: %08x", stack_pointer, *stack_pointer);
}
cmb_println("====================================");
} 打印當(dāng)前系統(tǒng)的棧內(nèi)容 static void dump_main_stack(uint32_t stack_start_addr, size_t stack_size, uint32_t *stack_pointer) {
cmb_println(print_info[PRINT_MAIN_STACK_INFO]);
for (; (uint32_t) stack_pointer < stack_start_addr + stack_size; stack_pointer++) {
cmb_println(" addr: %08x data: %08x", stack_pointer, *stack_pointer);
}
cmb_println("====================================");
} 通過(guò)這里獲取對(duì)應(yīng)的棧中調(diào)用的所有函數(shù)信息 size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp) {
uint32_t stack_start_addr = main_stack_start_addr, pc;
size_t depth = 0, stack_size = main_stack_size;
bool regs_saved_lr_is_valid = false;
if (on_fault) {
/* first depth is PC */
buffer[depth++] = regs.saved.pc;
/* second depth is from LR, so need decrease a word to PC */
pc = regs.saved.lr - sizeof(size_t);
if ((pc >= code_start_addr) && (pc <= code_start_addr + code_size) && (depth < CMB_CALL_STACK_MAX_DEPTH)
&& (depth < size)) {
buffer[depth++] = pc;
regs_saved_lr_is_valid = true;
}
#ifdef CMB_USING_OS_PLATFORM
/* program is running on thread before fault */
if (on_thread_before_fault) {
get_cur_thread_stack_info(sp, &stack_start_addr, &stack_size);
}
} else {
/* OS environment */
if (cmb_get_sp() == cmb_get_psp()) {
get_cur_thread_stack_info(sp, &stack_start_addr, &stack_size);
}
#endif /* CMB_USING_OS_PLATFORM */
}
/* copy called function address */
for (; sp < stack_start_addr + stack_size; sp += sizeof(size_t)) {
/* the *sp value may be LR, so need decrease a word to PC */
pc = *((uint32_t *) sp) - sizeof(size_t);
if ((pc >= code_start_addr) && (pc <= code_start_addr + code_size) && (depth < CMB_CALL_STACK_MAX_DEPTH)
&& (depth < size)) {
/* the second depth function may be already saved, so need ignore repeat */
if ((depth == 2) && regs_saved_lr_is_valid && (pc == buffer[1])) {
continue;
}
buffer[depth++] = pc;
}
}
return depth;
} 這里則是通過(guò)調(diào)用上面的函數(shù),打印出來(lái)所有的調(diào)用棧信息 static void print_call_stack(uint32_t sp) {
size_t i, cur_depth = 0;
uint32_t call_stack_buf[CMB_CALL_STACK_MAX_DEPTH] = {0};
cur_depth = cm_backtrace_call_stack(call_stack_buf, CMB_CALL_STACK_MAX_DEPTH, sp);
for (i = 0; i < cur_depth; i++) {
sprintf(call_stack_info + i * (8 + 1), "%08lx", call_stack_buf[i]);
call_stack_info[i * (8 + 1) + 8] = ' ';
}
if (cur_depth) {
cmb_println(print_info[PRINT_CALL_STACK_INFO], fw_name, CMB_ELF_FILE_EXTENSION_NAME, cur_depth * (8 + 1),
call_stack_info);
} else {
cmb_println(print_info[PRINT_CALL_STACK_ERR]);
}
} 斷言函數(shù) void cm_backtrace_assert(uint32_t sp) {
CMB_ASSERT(init_ok);
#ifdef CMB_USING_OS_PLATFORM
uint32_t cur_stack_pointer = cmb_get_sp();
#endif
cmb_println("");
cm_backtrace_firmware_info();
#ifdef CMB_USING_OS_PLATFORM
/* OS environment */
這里是根據(jù)是中斷中發(fā)生 ASSERT 還是線程中發(fā)生 ASSERT 來(lái)決定輸出內(nèi)容
if (cur_stack_pointer == cmb_get_msp()) {
cmb_println(print_info[PRINT_ASSERT_ON_HANDLER]);
#ifdef CMB_USING_DUMP_STACK_INFO
dump_main_stack(main_stack_start_addr, main_stack_size, (uint32_t *) sp);
#endif /* CMB_USING_DUMP_STACK_INFO */
} else if (cur_stack_pointer == cmb_get_psp()) {
cmb_println(print_info[PRINT_ASSERT_ON_THREAD], get_cur_thread_name());
#ifdef CMB_USING_DUMP_STACK_INFO
uint32_t stack_start_addr;
size_t stack_size;
get_cur_thread_stack_info(sp, &stack_start_addr, &stack_size);
dump_cur_thread_stack(stack_start_addr, stack_size, (uint32_t *) sp);
#endif /* CMB_USING_DUMP_STACK_INFO */
}
#else
/* bare metal(no OS) environment */
#ifdef CMB_USING_DUMP_STACK_INFO
dump_main_stack(main_stack_start_addr, main_stack_size, (uint32_t *) sp);
#endif /* CMB_USING_DUMP_STACK_INFO */
#endif /* CMB_USING_OS_PLATFORM */
print_call_stack(sp);
} 根據(jù)獲取到的狀態(tài)寄存器等 判斷具體是什么出錯(cuò) static void fault_diagnosis(void) {
if (regs.hfsr.bits.VECTBL) {
cmb_println(print_info[PRINT_HFSR_VECTBL]);
}
if (regs.hfsr.bits.FORCED) {
/* Memory Management Fault */
if (regs.mfsr.value) {
if (regs.mfsr.bits.IACCVIOL) {
cmb_println(print_info[PRINT_MFSR_IACCVIOL]);
}
if (regs.mfsr.bits.DACCVIOL) {
cmb_println(print_info[PRINT_MFSR_DACCVIOL]);
}
if (regs.mfsr.bits.MUNSTKERR) {
cmb_println(print_info[PRINT_MFSR_MUNSTKERR]);
}
if (regs.mfsr.bits.MSTKERR) {
cmb_println(print_info[PRINT_MFSR_MSTKERR]);
}
...
/* Debug Fault */
if (regs.hfsr.bits.DEBUGEVT) {
if (regs.dfsr.value) {
if (regs.dfsr.bits.HALTED) {
cmb_println(print_info[PRINT_DFSR_HALTED]);
}
if (regs.dfsr.bits.BKPT) {
cmb_println(print_info[PRINT_DFSR_BKPT]);
}
if (regs.dfsr.bits.DWTTRAP) {
cmb_println(print_info[PRINT_DFSR_DWTTRAP]);
}
if (regs.dfsr.bits.VCATCH) {
cmb_println(print_info[PRINT_DFSR_VCATCH]);
}
if (regs.dfsr.bits.EXTERNAL) {
cmb_println(print_info[PRINT_DFSR_EXTERNAL]);
}
}
}
} 如果是 M4 或者是 M7 由于自帶了 FPU ,,出錯(cuò)時(shí) 如果開啟了 FPU sp 中存的內(nèi)容有很大一部分是
與 FPU 有關(guān)的,,所以需要排除這一部分 #if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7)
static uint32_t statck_del_fpu_regs(uint32_t fault_handler_lr, uint32_t sp) {
statck_has_fpu_regs = (fault_handler_lr & (1UL << 4)) == 0 ? true : false;
/* the stack has S0~S15 and FPSCR registers when statck_has_fpu_regs is true, double word align */
return statck_has_fpu_regs == true ? sp + sizeof(size_t) * 18 : sp;
}
#endif 最重要的追蹤函數(shù)主體 void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp) {
獲取到棧指針
uint32_t stack_pointer = fault_handler_sp, saved_regs_addr = stack_pointer;
const char *regs_name[] = { "R0 ", "R1 ", "R2 ", "R3 ", "R12", "LR ", "PC ", "PSR" };
#ifdef CMB_USING_DUMP_STACK_INFO
拿到系統(tǒng)堆棧信息
uint32_t stack_start_addr = main_stack_start_addr;
size_t stack_size = main_stack_size;
#endif
確保被初始化過(guò),,否則系統(tǒng)堆棧信息等都可能是錯(cuò)的
CMB_ASSERT(init_ok);
/* only call once */
CMB_ASSERT(!on_fault);
on_fault = true;
cmb_println("");
cm_backtrace_firmware_info();
#ifdef CMB_USING_OS_PLATFORM
有操作系統(tǒng)下,判斷出問(wèn)題點(diǎn)是在中斷還是線程部分,,打印相關(guān)信息
on_thread_before_fault = fault_handler_lr & (1UL << 2);
/* check which stack was used before (MSP or PSP) */
if (on_thread_before_fault) {
cmb_println(print_info[PRINT_FAULT_ON_THREAD], get_cur_thread_name() != NULL ? get_cur_thread_name() : "NO_NAME");
saved_regs_addr = stack_pointer = cmb_get_psp();
#ifdef CMB_USING_DUMP_STACK_INFO
get_cur_thread_stack_info(stack_pointer, &stack_start_addr, &stack_size);
#endif /* CMB_USING_DUMP_STACK_INFO */
} else {
cmb_println(print_info[PRINT_FAULT_ON_HANDLER]);
}
#else
/* bare metal(no OS) environment */
cmb_println(print_info[PRINT_FAULT_ON_HANDLER]);
#endif /* CMB_USING_OS_PLATFORM */
/* delete saved R0~R3, R12, LR,PC,xPSR registers space */
stack_pointer += sizeof(size_t) * 8;
#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7)
stack_pointer = statck_del_fpu_regs(fault_handler_lr, stack_pointer);
#endif /* (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) */
輸出棧中所有內(nèi)容
/* dump stack information */
#ifdef CMB_USING_DUMP_STACK_INFO
#ifdef CMB_USING_OS_PLATFORM
if (on_thread_before_fault) {
dump_cur_thread_stack(stack_start_addr, stack_size, (uint32_t *) stack_pointer);
} else {
dump_main_stack(stack_start_addr, stack_size, (uint32_t *) stack_pointer);
}
#else
/* bare metal(no OS) environment */
dump_main_stack(stack_start_addr, stack_size, (uint32_t *) stack_pointer);
#endif /* CMB_USING_OS_PLATFORM */
#endif /* CMB_USING_DUMP_STACK_INFO */
輸出寄存器中的信息
/* dump register */
cmb_println(print_info[PRINT_REGS_TITLE]);
regs.saved.r0 = ((uint32_t *)saved_regs_addr)[0]; // Register R0
regs.saved.r1 = ((uint32_t *)saved_regs_addr)[1]; // Register R1
regs.saved.r2 = ((uint32_t *)saved_regs_addr)[2]; // Register R2
regs.saved.r3 = ((uint32_t *)saved_regs_addr)[3]; // Register R3
regs.saved.r12 = ((uint32_t *)saved_regs_addr)[4]; // Register R12
regs.saved.lr = ((uint32_t *)saved_regs_addr)[5]; // Link register LR
regs.saved.pc = ((uint32_t *)saved_regs_addr)[6]; // Program counter PC
regs.saved.psr.value = ((uint32_t *)saved_regs_addr)[7]; // Program status word PSR
cmb_println(" %s: %08x %s: %08x %s: %08x %s: %08x", regs_name[0], regs.saved.r0,
regs_name[1], regs.saved.r1,
regs_name[2], regs.saved.r2,
regs_name[3], regs.saved.r3);
cmb_println(" %s: %08x %s: %08x %s: %08x %s: %08x", regs_name[4], regs.saved.r12,
regs_name[5], regs.saved.lr,
regs_name[6], regs.saved.pc,
regs_name[7], regs.saved.psr.value);
cmb_println("==============================================================");
/* the Cortex-M0 is not support fault diagnosis */
#if (CMB_CPU_PLATFORM_TYPE != CMB_CPU_ARM_CORTEX_M0)
regs.syshndctrl.value = CMB_SYSHND_CTRL; // System Handler Control and State Register
regs.mfsr.value = CMB_NVIC_MFSR; // Memory Fault Status Register
regs.mmar = CMB_NVIC_MMAR; // Memory Management Fault Address Register
regs.bfsr.value = CMB_NVIC_BFSR; // Bus Fault Status Register
regs.bfar = CMB_NVIC_BFAR; // Bus Fault Manage Address Register
regs.ufsr.value = CMB_NVIC_UFSR; // Usage Fault Status Register
regs.hfsr.value = CMB_NVIC_HFSR; // Hard Fault Status Register
regs.dfsr.value = CMB_NVIC_DFSR; // Debug Fault Status Register
regs.afsr = CMB_NVIC_AFSR; // Auxiliary Fault Status Register
fault_diagnosis();
#endif
print_call_stack(stack_pointer);
} 查錯(cuò)當(dāng)發(fā)生錯(cuò)誤以后,,使用 addr2line 命令,查看函數(shù)調(diào)用棧詳細(xì)信息,,并定位錯(cuò)誤代碼 Addr2line 工具(它是標(biāo)準(zhǔn)的 GNU Binutils 中的一部分)是一個(gè)可以將指令的地址和可執(zhí)行映像轉(zhuǎn)換成文件名,、函數(shù)名和源代碼行數(shù)的工具。這種功能對(duì)于將跟蹤地址轉(zhuǎn)換成更有意義的內(nèi)容來(lái)說(shuō)簡(jiǎn)直是太棒了,。
通過(guò)上面的函數(shù),,會(huì)在串口或者什么其他輸出地方,輸出棧的函數(shù)指針的具體地址,,進(jìn)而就可以定位
出來(lái)具體是哪行出錯(cuò)了,,當(dāng)然調(diào)用 addr2line 命令應(yīng)該在程序的 bin/mot/elf 文件目錄下 如下,只需要到對(duì)應(yīng)的目錄下執(zhí)行下面的代碼即可 Firmware name: xxxxx, hardware version: xxxxx, software version: xxxxx
Fault on thread God
=================== Registers information ====================
R0 : 2000ba2c R1 : 2000903c R2 : 00140006 R3 : 00140006
R12: 2000a594 LR : 0807a5ad PC : 08076ea8 PSR: 01070000
==============================================================
Bus fault is caused by precise data access violation
The bus fault occurred address is 0014000e
Show more call stack info by run: addr2line -e xxxx.elf -a -f 08076ea8 0807a5a9 0807d9dc 080769bf 0807d9d9 0807d517 0807d32b 080563bd 0804c207 0804c357 0804e65b 0804f89d 0804f6c7 080742af 080742c1 08077005 Summary總的來(lái)說(shuō)大部分和寄存器以及出錯(cuò)相關(guān)的信息基本都來(lái)自于 Cortex M3 Technical Reference Manual Cortex M4 Technical Reference Manual Cortex M7 Technical Reference Manual
如果只用 trace 還不夠完美,,需要有一個(gè)用來(lái)長(zhǎng)時(shí)間保存出錯(cuò)信息的記錄功能,。 比如把所有出錯(cuò)信息寫到 flash 中去,或者是 log 記錄中去,。 CmBacktrace 的作者也提供了對(duì)應(yīng)的功能,,不過(guò)其中用 flash 來(lái)做數(shù)據(jù)庫(kù),有點(diǎn)沒意義,。 flash
燒寫次數(shù)有限,,而且刷寫新內(nèi)容時(shí)需要 sector 式的擦除,對(duì)于每條數(shù)據(jù)存儲(chǔ)也需要定義嚴(yán)格的格式
不是很推薦吧,,會(huì)上數(shù)據(jù)庫(kù)的應(yīng)用,,也不會(huì)差一個(gè) SD 的。 當(dāng)然你也可以在 CmBacktrace 中增加一些其他信息的代碼,,比如當(dāng)發(fā)生錯(cuò)誤以后,,如果能獲取到電
池電量的話,盡量獲取到,,然后輸出,,因?yàn)楹苡锌赡苁且驗(yàn)榈碗娪|發(fā)了什么異常情況,而這種情況很
可能并不一定,,什么都有,,所以用低電排除一些沒必要追究的異常,是有意義的 Quotehttp://blog.csdn.net/jean_bai/article/details/45952247 https://github.com/armink/CmBacktrace https://github.com/armink/EasyLogger https://github.com/armink/EasyFlash http://blog.csdn.net/lhf_tiger/article/details/9088609 https://www.ibm.com/developerworks/cn/linux/l-graphvis/ http://blog.csdn.net/whz_zb/article/details/7604760
|