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);
    }
}

getRegistersFromStack.cpp

Committer:
rgrover1
Date:
2014-01-29
Revision:
1:c325619800b2
Parent:
0:68900ac9bc4b

File content as of revision 1:c325619800b2:

#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);
}