A hardfault handler to dump useful registers to aid debugging of program exceptions. Often knowing the ProgramCounter (PC) at fault time is enough to find a useful location in source code.

The library installs an alternate version of the HardFault_Handler to replace the system default. The replaced handler is able to emit useful registers on the system console to aid debugging. You can enhance the handler to emit more useful information.

How would one test this..

Here's how one might produce an exception to trigger a hard-fault. You would not want to do this in your project, but the snippet helps demonstrate the function of this library. The snippet comes from a shared project: PointerError.

// Example of hanging when reading a bad pointer
/* Refer to http://www.freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html */

#include "mbed.h"

DigitalOut myled(LED1);

unsigned x[10] = {0};

int main() {
    unsigned *ok_ptr = x;
    unsigned *bad_ptr = (unsigned*)0xe600b0; // not in RAM!
    printf("ok_ptr = 0x%08X, bad_ptr = 0x%08X\n", (unsigned)ok_ptr, (unsigned)bad_ptr);

    unsigned ok_read = ok_ptr[0];
    printf("ok_read = %d\n", ok_read);

    unsigned bad_read = bad_ptr[0];
    printf("bad_read = %d\n", bad_read);

    while(1) {
        myled = 1;
        wait(0.2);
        myled = 0;
        wait(0.2);
    }
}

Files at this revision

API Documentation at this revision

Comitter:
rgrover1
Date:
Wed Jan 29 15:27:24 2014 +0000
Child:
1:c325619800b2
Commit message:
initial checkin

Changed in this revision

getRegistersFromStack.cpp Show annotated file Show diff for this revision Revisions of this file
hardfault.s Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/getRegistersFromStack.cpp	Wed Jan 29 15:27:24 2014 +0000
@@ -0,0 +1,43 @@
+#include "mbed.h"
+#include <stdint.h>
+
+/* Reference: http://www.freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html */
+
+/*
+ * The stack frame of the fault handler contains the state of the ARM Cortex-M
+ * registers at the time that the fault occurred. The code below shows how to
+ * read the register values from the stack into C variables. Once this is done,
+ * the values of the variables can be inspected or written to the console just
+ * as an other variable.
+ */
+extern "C" void
+prvGetRegistersFromStack(uint32_t *pulFaultStackAddress)
+{
+    /* These are volatile to try and prevent the compiler/linker optimising them
+     * away as the variables never actually get used.  If the debugger won't
+     * show the values of the variables, make them global my moving their
+     * declaration outside of this function.*/
+
+    /*
+     * Only the ProgramCounter (PC) is useful in this particular case.
+     */
+    // volatile uint32_t r0;
+    // volatile uint32_t r1;
+    // volatile uint32_t r2;
+    // volatile uint32_t r3;
+    // volatile uint32_t r12;
+    // volatile uint32_t lr; /* Link register. */
+    volatile uint32_t pc; /* Program counter. */
+    // volatile uint32_t psr;/* Program status register. */
+
+    // r0 = pulFaultStackAddress[ 0 ];
+    // r1 = pulFaultStackAddress[ 1 ];
+    // r2 = pulFaultStackAddress[ 2 ];
+    // r3 = pulFaultStackAddress[ 3 ];
+    // r12 = pulFaultStackAddress[ 4 ];
+    // lr = pulFaultStackAddress[ 5 ];
+    pc = pulFaultStackAddress[ 6 ];
+    // psr = pulFaultStackAddress[ 7 ];
+
+    error("\r\nHardFault_Handler: from pc = 0x%08x\r\n", (unsigned)pc);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hardfault.s	Wed Jan 29 15:27:24 2014 +0000
@@ -0,0 +1,44 @@
+; Reference: http://www.freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html
+
+; On the Cortex-M3, the processor core automatically saves some interesting
+; registers to the stack on initial exception entry. The following
+; HardFault_Handler is a short assembly function to determine which stack (MSP
+; or PSP) was being used when the fault occurred; once this is done, the fault
+; handler assembly code passes a pointer to the stack frame into a C function
+; called prvGetRegistersFromStack() which then aids user-debugging.
+
+    AREA asm_func, CODE, READONLY
+
+; Export HardFault_Handler function location so that linker can find it
+    EXPORT HardFault_Handler
+HardFault_Handler
+
+    TST LR, #4 ; test bit 2 of the EXC_RETURN placed in LR to determine which
+               ; stack was in use before entering the handler
+
+    ; move the appropriate stack pointer value into R0
+    ITE EQ
+    MRSEQ R0, MSP
+    MRSNE R0, PSP
+
+    LDR R1, [R0, #24] ; get stacked PC
+
+    ; call prvGetRegistersFromStack() with R0 set as the stack pointer at the
+    ; time of exception entry.
+    LDR R2, HANDLER2_ADDRESS_CONST
+    BX R2
+    IMPORT prvGetRegistersFromStack
+    ALIGN 4
+HANDLER2_ADDRESS_CONST DCD prvGetRegistersFromStack
+
+    END
+
+    ; call prvGetRegistersFromStack() with R0 set as the stack pointer at the
+    ; time of exception entry.
+    LDR R2, HANDLER2_ADDRESS_CONST
+    BX R2
+    IMPORT prvGetRegistersFromStack
+    ALIGN 4
+HANDLER2_ADDRESS_CONST DCD prvGetRegistersFromStack
+
+    END