From 29348d823d58a3e3b9db08f512d4c1c65b1f977d Mon Sep 17 00:00:00 2001 From: Senthil Ramakrishnan Date: Fri, 12 Jan 2018 14:54:44 -0600 Subject: [PATCH] Support for generating core/register/thread-info dump on fault exceptions --- .../TARGET_CORTEX_M/TOOLCHAIN_ARM/except.S | 155 ++++++++++ .../TARGET_CORTEX_M/TOOLCHAIN_GCC/except.S | 190 +++++++++++++ .../TARGET_CORTEX_M/TOOLCHAIN_IAR/except.S | 151 ++++++++++ rtos/TARGET_CORTEX/mbed_rtx_fault_handler.c | 213 ++++++++++++++ rtos/TARGET_CORTEX/mbed_rtx_fault_handler.h | 50 ++++ tools/debug_tools/crash_log_parser/README.md | 77 +++++ .../crash_log_parser/crash_log_parser.py | 266 ++++++++++++++++++ 7 files changed, 1102 insertions(+) create mode 100644 rtos/TARGET_CORTEX/TARGET_CORTEX_M/TOOLCHAIN_ARM/except.S create mode 100644 rtos/TARGET_CORTEX/TARGET_CORTEX_M/TOOLCHAIN_GCC/except.S create mode 100644 rtos/TARGET_CORTEX/TARGET_CORTEX_M/TOOLCHAIN_IAR/except.S create mode 100644 rtos/TARGET_CORTEX/mbed_rtx_fault_handler.c create mode 100644 rtos/TARGET_CORTEX/mbed_rtx_fault_handler.h create mode 100644 tools/debug_tools/crash_log_parser/README.md create mode 100644 tools/debug_tools/crash_log_parser/crash_log_parser.py diff --git a/rtos/TARGET_CORTEX/TARGET_CORTEX_M/TOOLCHAIN_ARM/except.S b/rtos/TARGET_CORTEX/TARGET_CORTEX_M/TOOLCHAIN_ARM/except.S new file mode 100644 index 0000000000..656d8c38ec --- /dev/null +++ b/rtos/TARGET_CORTEX/TARGET_CORTEX_M/TOOLCHAIN_ARM/except.S @@ -0,0 +1,155 @@ +;/* +; * Copyright (c) 2014-2018 ARM Limited. All rights reserved. +; * +; * SPDX-License-Identifier: Apache-2.0 +; * +; * Licensed under the Apache License, Version 2.0 (the License); you may +; * not use this file except in compliance with the License. +; * You may obtain a copy of the License at +; * +; * www.apache.org/licenses/LICENSE-2.0 +; * +; * Unless required by applicable law or agreed to in writing, software +; * distributed under the License is distributed on an AS IS BASIS, WITHOUT +; * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; * See the License for the specific language governing permissions and +; * limitations under the License. +; * +; * ----------------------------------------------------------------------------- +; * +; * Title: Cortex-M Fault Exception handlers ( Common for both ARMv7M and ARMV6M ) +; * +; * ----------------------------------------------------------------------------- +; */ + + IF :LNOT::DEF:__DOMAIN_NS +__DOMAIN_NS EQU 1 + ENDIF + +FAULT_TYPE_HARD_FAULT EQU 0x10 +FAULT_TYPE_MEMMANAGE_FAULT EQU 0x20 +FAULT_TYPE_BUS_FAULT EQU 0x30 +FAULT_TYPE_USAGE_FAULT EQU 0x40 + + PRESERVE8 + THUMB + + AREA |.text|, CODE, READONLY + +HardFault_Handler\ + PROC + EXPORT HardFault_Handler + LDR R3,=FAULT_TYPE_HARD_FAULT + B Fault_Handler + ENDP + +MemManage_Handler\ + PROC + EXPORT MemManage_Handler + LDR R3,=FAULT_TYPE_MEMMANAGE_FAULT + B Fault_Handler + ENDP + +BusFault_Handler\ + PROC + EXPORT BusFault_Handler + LDR R3,=FAULT_TYPE_BUS_FAULT + B Fault_Handler + ENDP + +UsageFault_Handler\ + PROC + EXPORT UsageFault_Handler + LDR R3,=FAULT_TYPE_USAGE_FAULT + B Fault_Handler + ENDP + +Fault_Handler PROC + EXPORT Fault_Handler + IF __DOMAIN_NS = 1 + IMPORT osRtxInfo + IMPORT mbed_fault_handler + IMPORT mbed_fault_context + + MRS R0,MSP + LDR R1,=0x4 + MOV R2,LR + TST R2,R1 ; Check EXC_RETURN for bit 2 + BEQ Fault_Handler_Continue + MRS R0,PSP + +Fault_Handler_Continue + MOV R12,R3 + LDR R1,=mbed_fault_context + LDR R2,[R0] ; Capture R0 + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#4] ; Capture R1 + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#8] ; Capture R2 + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#12] ; Capture R3 + STR R2,[R1] + ADDS R1,#4 + STMIA R1!,{R4-R7} ; Capture R4..R7 + MOV R7,R8 ; Capture R8 + STR R7,[R1] + ADDS R1,#4 + MOV R7,R9 ; Capture R9 + STR R7,[R1] + ADDS R1,#4 + MOV R7,R10 ; Capture R10 + STR R7,[R1] + ADDS R1,#4 + MOV R7,R11 ; Capture R11 + STR R7,[R1] + ADDS R1,#4 + LDR R2,[R0,#16] ; Capture R12 + STR R2,[R1] + ADDS R1,#8 ; Add 8 here to capture LR next, we will capture SP later + LDR R2,[R0,#20] ; Capture LR + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#24] ; Capture PC + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#28] ; Capture xPSR + STR R2,[R1] + ADDS R1,#4 + ; Adjust stack pointer to its original value and capture it + MOV R3,R0 + ADDS R3,#0x20 ; Add 0x20 to get the SP value prior to exception + LDR R6,=0x200 + TST R2,R6 ; Check for if STK was aligned by checking bit-9 in xPSR value + BEQ Fault_Handler_Continue1 + ADDS R3,#0x4 + +Fault_Handler_Continue1 + MOV R5,LR + LDR R6,=0x10 ; Check for bit-4 to see if FP context was saved + TST R5,R6 + BNE Fault_Handler_Continue2 + ADDS R3,#0x48 ; 16 FP regs + FPCSR + 1 Reserved + +Fault_Handler_Continue2 + MOV R4,R1 + SUBS R4,#0x10 ; Set the location of SP in ctx + STR R3,[R4] ; Capture the adjusted SP + MRS R2,PSP ; Get PSP + STR R2,[R1] + ADDS R1,#4 + MRS R2,MSP ; Get MSP + STR R2,[R1] + ADDS R1,#4 + LDR R3,=mbed_fault_handler ; Load address of mbedFaultHandler + MOV R0,R12 + LDR R1,=mbed_fault_context + LDR R2,=osRtxInfo + BLX R3 + ENDIF + B . ; Just in case we come back here + ENDP + + END diff --git a/rtos/TARGET_CORTEX/TARGET_CORTEX_M/TOOLCHAIN_GCC/except.S b/rtos/TARGET_CORTEX/TARGET_CORTEX_M/TOOLCHAIN_GCC/except.S new file mode 100644 index 0000000000..77c6763690 --- /dev/null +++ b/rtos/TARGET_CORTEX/TARGET_CORTEX_M/TOOLCHAIN_GCC/except.S @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014-2018 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ----------------------------------------------------------------------------- + * + * Title: Cortex-M Fault Exception handlers ( Common for both ARMv7M and ARMV6M ) + * + * ----------------------------------------------------------------------------- + */ + + + .file "except_cm0.S" + .syntax unified + + .ifndef __DOMAIN_NS + .equ __DOMAIN_NS, 1 + .endif + + .equ FAULT_TYPE_HARD_FAULT, 0x10 + .equ FAULT_TYPE_MEMMANAGE_FAULT, 0x20 + .equ FAULT_TYPE_BUS_FAULT, 0x30 + .equ FAULT_TYPE_USAGE_FAULT, 0x40 + + .thumb + .section ".text" + .align 2 + +//HardFault_Handler + .thumb_func + .type HardFault_Handler, %function + .global HardFault_Handler + .fnstart + .cantunwind + +HardFault_Handler: + LDR R3,=FAULT_TYPE_HARD_FAULT + B Fault_Handler + + .fnend + .size HardFault_Handler, .-HardFault_Handler + +//MemManage_Handler + .thumb_func + .type MemManage_Handler, %function + .global MemManage_Handler + .fnstart + .cantunwind + +MemManage_Handler: + LDR R3,=FAULT_TYPE_MEMMANAGE_FAULT + B Fault_Handler + + .fnend + .size MemManage_Handler, .-MemManage_Handler + +//BusFault_Handler + .thumb_func + .type BusFault_Handler, %function + .global BusFault_Handler + .fnstart + .cantunwind + +BusFault_Handler: + LDR R3,=FAULT_TYPE_BUS_FAULT + B Fault_Handler + + .fnend + .size BusFault_Handler, .-BusFault_Handler + +//UsageFault_Handler + .thumb_func + .type UsageFault_Handler, %function + .global UsageFault_Handler + .fnstart + .cantunwind + +UsageFault_Handler: + LDR R3,=FAULT_TYPE_USAGE_FAULT + B Fault_Handler + + .fnend + .size UsageFault_Handler, .-UsageFault_Handler + +//Common Fault_Handler to capture the context + .thumb_func + .type Fault_Handler, %function + .global Fault_Handler + .fnstart + .cantunwind + +Fault_Handler: + .if __DOMAIN_NS == 1 + MRS R0,MSP + LDR R1,=0x4 + MOV R2,LR + TST R2,R1 // Check EXC_RETURN for bit 2 + BEQ Fault_Handler_Continue + MRS R0,PSP + +Fault_Handler_Continue: + MOV R12,R3 + LDR R1,=mbed_fault_context + LDR R2,[R0] // Capture R0 + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#4] // Capture R1 + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#8] // Capture R2 + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#12] // Capture R3 + STR R2,[R1] + ADDS R1,#4 + STMIA R1!,{R4-R7} // Capture R4..R7 + MOV R7,R8 // Capture R8 + STR R7,[R1] + ADDS R1,#4 + MOV R7,R9 // Capture R9 + STR R7,[R1] + ADDS R1,#4 + MOV R7,R10 // Capture R10 + STR R7,[R1] + ADDS R1,#4 + MOV R7,R11 // Capture R11 + STR R7,[R1] + ADDS R1,#4 + LDR R2,[R0,#16] // Capture R12 + STR R2,[R1] + ADDS R1,#8 // Add 8 here to capture LR next, we will capture SP later + LDR R2,[R0,#20] // Capture LR + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#24] // Capture PC + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#28] // Capture xPSR + STR R2,[R1] + ADDS R1,#4 + // Adjust stack pointer to its original value and capture it + MOV R3,R0 + ADDS R3,#0x20 // Add 0x20 to get the SP value prior to exception + LDR R6,=0x200 + TST R2,R6 // Check for if STK was aligned by checking bit-9 in xPSR value + BEQ Fault_Handler_Continue1 + ADDS R3,#0x4 + +Fault_Handler_Continue1: + MOV R5,LR + LDR R6,=0x10 // Check for bit-4 to see if FP context was saved + TST R5,R6 + BNE Fault_Handler_Continue2 + ADDS R3,#0x48 // 16 FP regs + FPCSR + 1 Reserved + +Fault_Handler_Continue2: + MOV R4,R1 + SUBS R4,#0x10 // Set the location of SP in ctx + STR R3,[R4] // Capture the adjusted SP + MRS R2,PSP // Get PSP + STR R2,[R1] + ADDS R1,#4 + MRS R2,MSP // Get MSP + STR R2,[R1] + ADDS R1,#4 + LDR R3,=mbed_fault_handler // Load address of mbedFaultHandler + MOV R0,R12 + LDR R1,=mbed_fault_context + LDR R2,=osRtxInfo + BLX R3 + .endif + B . // Just in case we come back here + + .fnend + .size Fault_Handler, .-Fault_Handler + + .end diff --git a/rtos/TARGET_CORTEX/TARGET_CORTEX_M/TOOLCHAIN_IAR/except.S b/rtos/TARGET_CORTEX/TARGET_CORTEX_M/TOOLCHAIN_IAR/except.S new file mode 100644 index 0000000000..186b3d7c06 --- /dev/null +++ b/rtos/TARGET_CORTEX/TARGET_CORTEX_M/TOOLCHAIN_IAR/except.S @@ -0,0 +1,151 @@ +;/* +; * Copyright (c) 2014-2018 ARM Limited. All rights reserved. +; * +; * SPDX-License-Identifier: Apache-2.0 +; * +; * Licensed under the Apache License, Version 2.0 (the License); you may +; * not use this file except in compliance with the License. +; * You may obtain a copy of the License at +; * +; * www.apache.org/licenses/LICENSE-2.0 +; * +; * Unless required by applicable law or agreed to in writing, software +; * distributed under the License is distributed on an AS IS BASIS, WITHOUT +; * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; * See the License for the specific language governing permissions and +; * limitations under the License. +; * +; * ----------------------------------------------------------------------------- +; * +; * Title: Cortex-M Fault Exception handlers ( Common for both ARMv7M and ARMV6M ); +; * +; * ----------------------------------------------------------------------------- +; */ + + + NAME except_cm0.s + +#ifndef __DOMAIN_NS +#define __DOMAIN_NS 1 +#endif + +FAULT_TYPE_HARD_FAULT EQU 0x10 +FAULT_TYPE_MEMMANAGE_FAULT EQU 0x20 +FAULT_TYPE_BUS_FAULT EQU 0x30 +FAULT_TYPE_USAGE_FAULT EQU 0x40 + + PRESERVE8 + SECTION .rodata:DATA:NOROOT(2) + + THUMB + SECTION .text:CODE:NOROOT(2) + + +HardFault_Handler + EXPORT HardFault_Handler + LDR R3,=FAULT_TYPE_HARD_FAULT + B Fault_Handler + +MemManage_Handler + EXPORT MemManage_Handler + LDR R3,=FAULT_TYPE_MEMMANAGE_FAULT + B Fault_Handler + +BusFault_Handler + EXPORT BusFault_Handler + LDR R3,=FAULT_TYPE_BUS_FAULT + B Fault_Handler + +UsageFault_Handler + EXPORT UsageFault_Handler + LDR R3,=FAULT_TYPE_USAGE_FAULT + B Fault_Handler + +Fault_Handler + EXPORT Fault_Handler +#if (__DOMAIN_NS == 1) + IMPORT osRtxInfo + IMPORT mbed_fault_context + IMPORT mbed_fault_handler + + MRS R0,MSP + LDR R1,=0x4 + MOV R2,LR + TST R2,R1 ; Check EXC_RETURN for bit 2 + BEQ Fault_Handler_Continue + MRS R0,PSP + +Fault_Handler_Continue + MOV R12,R3 + LDR R1,=mbed_fault_context + LDR R2,[R0] ; Capture R0 + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#4] ; Capture R1 + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#8] ; Capture R2 + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#12] ; Capture R3 + STR R2,[R1] + ADDS R1,#4 + STMIA R1!,{R4-R7} ; Capture R4..R7 + MOV R7,R8 ; Capture R8 + STR R7,[R1] + ADDS R1,#4 + MOV R7,R9 ; Capture R9 + STR R7,[R1] + ADDS R1,#4 + MOV R7,R10 ; Capture R10 + STR R7,[R1] + ADDS R1,#4 + MOV R7,R11 ; Capture R11 + STR R7,[R1] + ADDS R1,#4 + LDR R2,[R0,#16] ; Capture R12 + STR R2,[R1] + ADDS R1,#8 ; Add 8 here to capture LR next, we will capture SP later + LDR R2,[R0,#20] ; Capture LR + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#24] ; Capture PC + STR R2,[R1] + ADDS R1,#4 + LDR R2,[R0,#28] ; Capture xPSR + STR R2,[R1] + ADDS R1,#4 + ; Adjust stack pointer to its original value and capture it + MOV R3,R0 + ADDS R3,#0x20 ; Add 0x20 to get the SP value prior to exception + LDR R6,=0x200 + TST R2,R6 ; Check for if STK was aligned by checking bit-9 in xPSR value + BEQ Fault_Handler_Continue1 + ADDS R3,#0x4 + +Fault_Handler_Continue1 + MOV R5,LR + LDR R6,=0x10 ; Check for bit-4 to see if FP context was saved + TST R5,R6 + BNE Fault_Handler_Continue2 + ADDS R3,#0x48 ; 16 FP regs + FPCSR + 1 Reserved + +Fault_Handler_Continue2 + MOV R4,R1 + SUBS R4,#0x10 ; Set the location of SP in ctx + STR R3,[R4] ; Capture the adjusted SP + MRS R2,PSP ; Get PSP + STR R2,[R1] + ADDS R1,#4 + MRS R2,MSP ; Get MSP + STR R2,[R1] + ADDS R1,#4 + LDR R3,=mbed_fault_handler ; Load address of mbedFaultHandler + MOV R0,R12 + LDR R1,=mbed_fault_context + LDR R2,=osRtxInfo + BLX R3 +#endif + B . ; Just in case we come back here + + END diff --git a/rtos/TARGET_CORTEX/mbed_rtx_fault_handler.c b/rtos/TARGET_CORTEX/mbed_rtx_fault_handler.c new file mode 100644 index 0000000000..8366e50a88 --- /dev/null +++ b/rtos/TARGET_CORTEX/mbed_rtx_fault_handler.c @@ -0,0 +1,213 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rtx_os.h" +#include "mbed_rtx.h" +#include "mbed_rtx_fault_handler.h" +#include "hal/serial_api.h" + +//Global for populating the context in exception handler +mbed_fault_context_t mbed_fault_context; + +//Structure to capture the context +void fault_print_init(void); +void fault_print_str(char *fmtstr, uint32_t *values); +void hex_to_str(uint32_t value, char *hex_star); +void print_context_info(void); +void print_threads_info(osRtxThread_t *); +void print_thread(osRtxThread_t *thread); +void print_register(char *regtag, uint32_t regval); + +#if DEVICE_SERIAL +extern int stdio_uart_inited; +extern serial_t stdio_uart; +#endif + +__NO_RETURN void mbed_fault_handler (uint32_t fault_type, void *mbed_fault_context_in, void *osRtxInfoIn) +{ + fault_print_init(); + fault_print_str("\n++ MbedOS Fault Handler ++\n\nFaultType: ",NULL); + + switch( fault_type ) { + case HARD_FAULT_EXCEPTION: fault_print_str("HardFault",NULL); break; + case MEMMANAGE_FAULT_EXCEPTION: fault_print_str("MemManageFault",NULL); break; + case BUS_FAULT_EXCEPTION: fault_print_str("BusFault",NULL); break; + case USAGE_FAULT_EXCEPTION: fault_print_str("UsageFault",NULL); break; + default: fault_print_str("Unknown Fault",NULL); break; + } + fault_print_str("\n\nContext:",NULL); + print_context_info(); + + fault_print_str("\n\nThread Info:\nCurrent:",NULL); + print_thread(((osRtxInfo_t *)osRtxInfoIn)->thread.run.curr); + + fault_print_str("\nNext:",NULL); + print_thread(((osRtxInfo_t *)osRtxInfoIn)->thread.run.next); + + fault_print_str("\nWait Threads:",NULL); + osRtxThread_t *threads = ((osRtxInfo_t *)osRtxInfoIn)->thread.wait_list; + print_threads_info(threads); + + fault_print_str("\nDelay Threads:",NULL); + threads = ((osRtxInfo_t *)osRtxInfoIn)->thread.delay_list; + print_threads_info(threads); + + fault_print_str("\nIdle Thread:",NULL); + threads = ((osRtxInfo_t *)osRtxInfoIn)->thread.idle; + print_threads_info(threads); + + fault_print_str("\n\n-- MbedOS Fault Handler --\n\n",NULL); + + /* Just spin here, we have alrady crashed */ + for (;;) {} +} + +void print_context_info() +{ + //Context Regs + fault_print_str( "\nR0 : %" + "\nR1 : %" + "\nR2 : %" + "\nR3 : %" + "\nR4 : %" + "\nR5 : %" + "\nR6 : %" + "\nR7 : %" + "\nR8 : %" + "\nR9 : %" + "\nR10 : %" + "\nR11 : %" + "\nR12 : %" + "\nSP : %" + "\nLR : %" + "\nPC : %" + "\nxPSR : %" + "\nPSP : %" + "\nMSP : %", (uint32_t *)&mbed_fault_context); + + //Capture CPUID to get core/cpu info + fault_print_str("\nCPUID: %",(uint32_t *)&SCB->CPUID); + +#if !defined(TARGET_M0) && !defined(TARGET_M0P) + //Capture fault information registers to infer the cause of exception + uint32_t FSR[7] = {0}; + + FSR[0] = SCB->HFSR; + //Split/Capture CFSR into MMFSR, BFSR, UFSR + FSR[1] = 0xFF & SCB->CFSR;//MMFSR + FSR[2] = (0xFF00 & SCB->CFSR) >> 8;//BFSR + FSR[3] = (0xFFFF0000 & SCB->CFSR) >> 16;//UFSR + FSR[4] = SCB->DFSR; + FSR[5] = SCB->AFSR; + FSR[6] = SCB->SHCSR; + fault_print_str("\nHFSR : %" + "\nMMFSR: %" + "\nBFSR : %" + "\nUFSR : %" + "\nDFSR : %" + "\nAFSR : %" + "\nSHCSR: %",FSR); + + //Print MMFAR only if its valid as indicated by MMFSR + if(FSR[1] & 0x80) { + fault_print_str("\nMMFAR: %",(uint32_t *)&SCB->MMFAR); + } + //Print BFAR only if its valid as indicated by BFSR + if(FSR[2] & 0x80) { + fault_print_str("\nBFAR : %",(uint32_t *)&SCB->BFAR); + } +#endif + +} + +/* Prints thread info from a list */ +void print_threads_info(osRtxThread_t *threads) +{ + while(threads != NULL) { + print_thread( threads ); + threads = threads->thread_next; + } +} + +/* Prints info of a thread(using osRtxThread_t struct)*/ +void print_thread(osRtxThread_t *thread) +{ + uint32_t data[5]; + + data[0]=thread->state; + data[1]=thread->thread_addr; + data[2]=thread->stack_size; + data[3]=(uint32_t)thread->stack_mem; + data[4]=thread->sp; + fault_print_str("\nState: % EntryFn: % Stack Size: % Mem: % SP: %", data); +} + +/* Initializes std uart for spitting the info out */ +void fault_print_init() +{ +#if DEVICE_SERIAL + if (!stdio_uart_inited) { + serial_init(&stdio_uart, STDIO_UART_TX, STDIO_UART_RX); + } +#endif +} + +/* Limited print functionality which prints the string out to +stdout/uart without using stdlib by directly calling serial-api +and also uses less resources +The fmtstr contains the format string for printing and for every % +found in that it fetches a uint32 value from values buffer +and prints it in hex format. +*/ +void fault_print_str(char *fmtstr, uint32_t *values) +{ +#if DEVICE_SERIAL + int i = 0; + int idx = 0; + int vidx = 0; + char hex_str[9]={0}; + + while(fmtstr[i] != '\0') { + if(fmtstr[i] == '\n' || fmtstr[i] == '\r') { + serial_putc(&stdio_uart, '\r'); + } else { + if(fmtstr[i]=='%') { + hex_to_str(values[vidx++],hex_str); + for(idx=7; idx>=0; idx--) { + serial_putc(&stdio_uart, hex_str[idx]); + } + } else { + serial_putc(&stdio_uart, fmtstr[i]); + } + } + i++; + } +#endif +} + +/* Converts a uint32 to hex char string */ +void hex_to_str(uint32_t value, char *hex_str) +{ + char hex_char_map[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + int i = 0; + + //Return without converting if hex_str is not provided + if(hex_str == NULL) return; + + for(i=7; i>=0; i--) { + hex_str[i] = hex_char_map[(value & (0xf << (i * 4))) >> (i * 4)]; + } +} diff --git a/rtos/TARGET_CORTEX/mbed_rtx_fault_handler.h b/rtos/TARGET_CORTEX/mbed_rtx_fault_handler.h new file mode 100644 index 0000000000..db5a92fdbe --- /dev/null +++ b/rtos/TARGET_CORTEX/mbed_rtx_fault_handler.h @@ -0,0 +1,50 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//Fault context struct +//WARNING: DO NOT CHANGE THIS STRUCT WITHOUT MAKING CORRESPONDING CHANGES in except.S files. +//Offset of these registers are used by fault handler in except.S +typedef struct { + uint32_t R0; + uint32_t R1; + uint32_t R2; + uint32_t R3; + uint32_t R4; + uint32_t R5; + uint32_t R6; + uint32_t R7; + uint32_t R8; + uint32_t R9; + uint32_t R10; + uint32_t R11; + uint32_t R12; + uint32_t SP; + uint32_t LR; + uint32_t PC; + uint32_t xPSR; + uint32_t PSP; + uint32_t MSP; +} mbed_fault_context_t; + +//Fault type definitions +//WARNING: DO NOT CHANGE THESE VALUES WITHOUT MAKING CORRESPONDING CHANGES in except.S files. +#define HARD_FAULT_EXCEPTION (0x10) //Keep some gap between values for any future insertion/expansion +#define MEMMANAGE_FAULT_EXCEPTION (0x20) +#define BUS_FAULT_EXCEPTION (0x30) +#define USAGE_FAULT_EXCEPTION (0x40) + +__NO_RETURN void mbed_fault_handler (uint32_t fault_type, void *mbed_fault_context_in, void *osRtxInfoIn); + diff --git a/tools/debug_tools/crash_log_parser/README.md b/tools/debug_tools/crash_log_parser/README.md new file mode 100644 index 0000000000..e412a44817 --- /dev/null +++ b/tools/debug_tools/crash_log_parser/README.md @@ -0,0 +1,77 @@ +## Crash Log Parser Tool +This post-processing tool can be used to parse crash log generated by Mbed-OS when an exception happens. + +## Capturing crash log +When an exception happens Mbed-OS will print out the crash information to STDOUT. +The crash information contains register context at the time exception and current threads in the system. +The information printed out to STDOUT will be similar to below. + +++ MbedOS Fault Handler ++ + +FaultType: HardFault + +Context: +R0 : 0000AAA3 +R1 : 20002070 +R2 : 00009558 +R3 : 00412A02 +R4 : E000ED14 +R5 : 00000000 +R6 : 00000000 +R7 : 00000000 +R8 : 00000000 +R9 : 00000000 +R10 : 00000000 +R11 : 00000000 +R12 : 0000BCE5 +SP : 20002070 +LR : 00009E75 +PC : 00009512 +xPSR : 01000000 +PSP : 20002008 +MSP : 2002FFD8 +CPUID: 410FC241 +HFSR : 40000000 +MMFSR: 00000000 +BFSR : 00000000 +UFSR : 00000100 +DFSR : 00000008 +AFSR : 00000000 +SHCSR: 00000000 + +Thread Info: +Current: +State: 00000002 EntryFn: 0000ADF5 Stack Size: 00001000 Mem: 20001070 SP: 20002030 +Next: +State: 00000002 EntryFn: 0000ADF5 Stack Size: 00001000 Mem: 20001070 SP: 20002030 +Wait Threads: +State: 00000083 EntryFn: 0000AA1D Stack Size: 00000300 Mem: 20000548 SP: 200007D8 +Delay Threads: +Idle Thread: +State: 00000001 EntryFn: 00009F59 Stack Size: 00000200 Mem: 20000348 SP: 20000508 + +-- MbedOS Fault Handler -- + + +To generate more information copy and save this crash information to a text file and run the crash_log_parser.py tool as below. +NOTE: Make sure you copy the section with text "MbedOS Fault Handler" as the this tool looks for that header. + +## Running the Crash Log Parser +crash_log_parser.py -i -e -m +For example: +crashlogparse.py -i crash.log -e C:\MyProject\BUILD\k64f\arm\mbed-os-hf-handler.elf -m C:\MyProject\BUILD\k64f\arm\mbed-os-hf-handler.map + +An example output from running crash_log_parser is shown below. + +Parsed Crash Info: + Crash location = zero_div_test() [0000693E] + Caller location = $Super$$main [00009E99] + Stack Pointer at the time of crash = [20001CC0] + Target/Fault Info: + Processor Arch: ARM-V7M or above + Processor Variant: C24 + Forced exception, a fault with configurable priority has been escalated to HardFault + Divide by zero error has occurred + +Done parsing... + diff --git a/tools/debug_tools/crash_log_parser/crash_log_parser.py b/tools/debug_tools/crash_log_parser/crash_log_parser.py new file mode 100644 index 0000000000..e9564c82aa --- /dev/null +++ b/tools/debug_tools/crash_log_parser/crash_log_parser.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python + +from __future__ import print_function +from os import path +import re, bisect +from subprocess import check_output +import sys + +#arm-none-eabi-nm -nl +NM_EXEC = "arm-none-eabi-nm" +OPT = "-nlC" +ptn = re.compile("([0-9a-f]*) ([Tt]) ([^\t\n]*)(?:\t(.*):([0-9]*))?") +fnt = None +fnt = "" + +class ElfHelper(object): + def __init__(self, p,m): + if path.isfile(p): + elf_path = p + if path.isfile(m): + map_path = m + + op = check_output([NM_EXEC, OPT, elf_path]) + map_file = open(map_path) + self.maplines = map_file.readlines() + self.matches = ptn.findall(op) + self.addrs = [int(x[0],16) for x in self.matches] + #print(self.maplines) + + def function_addrs(self): + return self.addrs + + def function_name_for_addr(self, addr): + i = bisect.bisect_right(self.addrs, addr) + funcname = self.matches[i-1][2] + return funcname + + def file_name_for_function_name(self, funcname): + for eachline in self.maplines: + #print("%s:%s"%(eachline,funcname)) + result = eachline.find(funcname) + if(result != -1): + break + toks = eachline.split() + #print("%s:%s"%(str(funcname),str(toks))) + if(len(toks) <= 0): + print("WARN: Unable to find %s in map file"%(str(funcname))) + #return funcname + return ("%s [FUNCTION]"%(str(funcname))) + else: + #return toks[-1].replace("./BUILD/","").replace("/","\\") + return toks[-1] + +def parseHFSR(hfsr): + if(hfsr != 0): + if( int(hfsr,16) & 0x80000000 ): + print("\t\tDebug Event Occured") + if( int(hfsr,16) & 0x40000000 ): + print("\t\tForced exception, a fault with configurable priority has been escalated to HardFault") + if( int(hfsr,16) & 0x2 ): + print("\t\tVector table read fault has occurred") + +def parseMMFSR(mmfsr,mmfar): + if(mmfsr != 0): + if( int(mmfsr,16) & 0x20 ): + print("\t\tA MemManage fault occurred during FP lazy state preservation") + if( int(mmfsr,16) & 0x10 ): + print("\t\tA derived MemManage fault occurred on exception entry") + if( int(mmfsr,16) & 0x8 ): + print("\t\tA derived MemManage fault occurred on exception return") + if( int(mmfsr,16) & 0x2 ): + if( int(mmfsr,16) & 0x80 ): + print("\t\tData access violation. Faulting address: %s"%(str(mmfar))) + else: + print("\t\tData access violation. WARNING: Fault address in MMFAR is NOT valid") + if( int(mmfsr,16) & 0x1 ): + print("\t\tMPU or Execute Never (XN) default memory map access violation on an instruction fetch has occurred") + +def parseBFSR(bfsr,bfar): + if(bfsr != 0): + if( int(bfsr,16) & 0x20 ): + print("\t\tA bus fault occurred during FP lazy state preservation") + if( int(bfsr,16) & 0x10 ): + print("\t\tA derived bus fault has occurred on exception entry") + if( int(bfsr,16) & 0x8 ): + print("\t\tA derived bus fault has occurred on exception return") + if( int(bfsr,16) & 0x4 ): + print("\t\tImprecise data access error has occurred") + if( int(bfsr,16) & 0x2 ): + if( int(bfsr,16) & 0x80 ): + print("\t\tA precise data access error has occurred. Faulting address: %s"%(str(bfar))) + else: + print("\t\tA precise data access error has occurred. WARNING: Fault address in BFAR is NOT valid") + if( int(bfsr,16) & 0x1 ): + print("\t\tA bus fault on an instruction prefetch has occurred") + +def parseUFSR(ufsr): + if(ufsr != 0): + if( int(ufsr,16) & 0x200 ): + print("\t\tDivide by zero error has occurred") + if( int(ufsr,16) & 0x100 ): + print("\t\tUnaligned access error has occurred") + if( int(ufsr,16) & 0x8 ): + print("\t\tA coprocessor access error has occurred. This shows that the coprocessor is disabled or not present") + if( int(ufsr,16) & 0x4 ): + print("\t\tAn integrity check error has occurred on EXC_RETURN") + if( int(ufsr,16) & 0x2 ): + print("\t\tInstruction executed with invalid EPSR.T or EPSR.IT field( This may be caused by Thumb bit not being set in branching instruction )") + if( int(ufsr,16) & 0x1 ): + print("\t\tThe processor has attempted to execute an undefined instruction") + +def parseCPUID(cpuid): + if( ( ( int(cpuid,16) & 0xF0000 ) >> 16 ) == 0xC ): + print("\t\tProcessor Arch: ARM-V6M") + else: + print("\t\tProcessor Arch: ARM-V7M or above") + + print("\t\tProcessor Variant: %X"%(( ( int(cpuid,16) & 0xFFF0 ) >> 4 ))) + + +def main(input): + global fnt, pc_val, pc_name, lr_val, lr_name, sp_val, hfsr_val, mmfsr_val, ufsr_val, bfsr_val, cpuid_val, mmfar_val, bfar_val + start_parsing = False + mmfar_val = 0 + bfar_val = 0 + crash_file = open(input) + lines = crash_file.readlines() + + cnt = 0 + for eachline in lines: + idx = eachline.find("MbedOS Fault Handler") + if(-1 != idx): + break + cnt=cnt+1 + + if(idx == -1): + print("ERROR: Unable to find \"MbedOS Fault Handler\" header") + return + + + print("\n\nParsed Crash Info:") + for i in range(cnt,len(lines)): + eachline=lines[i] + if(-1 != eachline.find("--- MbedOS Fault Handler ---")): + break + #print(eachline) + + if(eachline.startswith("PC")): + l = re.findall(r"[\w']+", eachline) + l = [x.strip() for x in l if x != ''] + tag, pc_val = l + pc_name = fnt.function_name_for_addr(int(pc_val,16)) + + if(eachline.startswith("LR")): + l = re.findall(r"[\w']+", eachline) + l = [x.strip() for x in l if x != ''] + tag, lr_val = l + lr_name = fnt.function_name_for_addr(int(lr_val,16)) + + if(eachline.startswith("SP")): + l = re.findall(r"[\w']+", eachline) + l = [x.strip() for x in l if x != ''] + tag, sp_val = l + + if(eachline.startswith("HFSR")): + l = re.findall(r"[\w']+", eachline) + l = [x.strip() for x in l if x != ''] + tag, hfsr_val = l + + if(eachline.startswith("MMFSR")): + l = re.findall(r"[\w']+", eachline) + l = [x.strip() for x in l if x != ''] + tag, mmfsr_val = l + + if(eachline.startswith("BFSR")): + l = re.findall(r"[\w']+", eachline) + l = [x.strip() for x in l if x != ''] + tag, bfsr_val = l + + if(eachline.startswith("UFSR")): + l = re.findall(r"[\w']+", eachline) + l = [x.strip() for x in l if x != ''] + tag, ufsr_val = l + + if(eachline.startswith("CPUID")): + l = re.findall(r"[\w']+", eachline) + l = [x.strip() for x in l if x != ''] + tag, cpuid_val = l + + if(eachline.startswith("MMFAR")): + l = re.findall(r"[\w']+", eachline) + l = [x.strip() for x in l if x != ''] + tag, mmfar_val = l + + if(eachline.startswith("BFAR")): + l = re.findall(r"[\w']+", eachline) + l = [x.strip() for x in l if x != ''] + tag, bfar_val = l + + print("\tCrash location = %s [%s] (based on PC value)"%(pc_name.strip(),str(pc_val))) + print("\tCaller location = %s [%s] (based on LR value)"%(lr_name.strip(),str(lr_val))) + print("\tStack Pointer at the time of crash = [%s]"%(str(sp_val))) + + print("\tTarget/Fault Info:") + parseCPUID(cpuid_val) + parseHFSR(hfsr_val) + parseMMFSR(mmfsr_val,mmfar_val) + parseBFSR(bfsr_val,bfar_val) + parseUFSR(ufsr_val) + + print("\nDone parsing...") + + +if __name__ == '__main__': + import argparse + + parser = argparse.ArgumentParser( + description='Analyse mbed-os crash log') + + # specify arguments + parser.add_argument('-i','--input', metavar='', type=str, + help='path to input file') + parser.add_argument('-e','--elfpath', metavar='', type=str, + help='path to elf file') + parser.add_argument('-m','--mappath', metavar='', type=str, + help='path to map file') + + # get and validate arguments + args = parser.parse_args() + + p = args.elfpath + m = args.mappath + i = args.input + if(p == None or m == None or i == None): + print("Invalid arguments, exiting") + parser.print_usage() + sys.exit(1) + + if not path.exists(p): + print("Elf path %s does not exist"%(str(p))) + parser.print_usage() + sys.exit(1) + + if not path.exists(m): + print("Map path %s does not exist"%(str(m))) + parser.print_usage() + sys.exit(1) + + if not path.exists(i): + print("Input crash log path %s does not exist"%(str(i))) + parser.print_usage() + sys.exit(1) + + print("Inputs:") + print("\tCrash Log: %s"%(i)) + print("\tElf Path: %s"%(p)) + print("\tMap Path: %s"%(m)) + + fnt = ElfHelper(p,m) + + # parse input and write to output + main(i) + + + +