Module cortex_m_quickstart::examples::_4_crash [] [src]

Debugging a crash (exception)

Most crash conditions trigger a hard fault exception, whose handler is defined via exception!(HardFault, ..). The HardFault handler has access to the exception frame, a snapshot of the CPU registers at the moment of the exception.

This program crashes and the HardFault handler prints to the console the contents of the ExceptionFrame and then triggers a breakpoint. From that breakpoint one can see the backtrace that led to the exception.

(gdb) continue
Program received signal SIGTRAP, Trace/breakpoint trap.
__bkpt () at asm/bkpt.s:3
3         bkpt

(gdb) backtrace
#0  __bkpt () at asm/bkpt.s:3
#1  0x080030b4 in cortex_m::asm::bkpt () at $$/cortex-m-0.5.0/src/asm.rs:19
#2  rust_begin_unwind (args=..., file=..., line=99, col=5) at $$/panic-semihosting-0.2.0/src/lib.rs:87
#3  0x08001d06 in core::panicking::panic_fmt () at libcore/panicking.rs:71
#4  0x080004a6 in crash::hard_fault (ef=0x20004fa0) at examples/crash.rs:99
#5  0x08000548 in UserHardFault (ef=0x20004fa0) at <exception macros>:10
#6  0x0800093a in HardFault () at asm.s:5
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

In the console output one will find the state of the Program Counter (PC) register at the time of the exception.

panicked at 'HardFault at ExceptionFrame {
    r0: 0x2fffffff,
    r1: 0x2fffffff,
    r2: 0x080051d4,
    r3: 0x080051d4,
    r12: 0x20000000,
    lr: 0x08000435,
    pc: 0x08000ab6,
    xpsr: 0x61000000
}', examples/crash.rs:106:5

This register contains the address of the instruction that caused the exception. In GDB one can disassemble the program around this address to observe the instruction that caused the exception.

(gdb) disassemble/m 0x08000ab6
Dump of assembler code for function core::ptr::read_volatile:
451     pub unsafe fn read_volatile<T>(src: *const T) -> T {
   0x08000aae <+0>:     sub     sp, #16
   0x08000ab0 <+2>:     mov     r1, r0
   0x08000ab2 <+4>:     str     r0, [sp, #8]

452         intrinsics::volatile_load(src)
   0x08000ab4 <+6>:     ldr     r0, [sp, #8]
-> 0x08000ab6 <+8>:     ldr     r0, [r0, #0]
   0x08000ab8 <+10>:    str     r0, [sp, #12]
   0x08000aba <+12>:    ldr     r0, [sp, #12]
   0x08000abc <+14>:    str     r1, [sp, #4]
   0x08000abe <+16>:    str     r0, [sp, #0]
   0x08000ac0 <+18>:    b.n     0x8000ac2 <core::ptr::read_volatile+20>

453     }
   0x08000ac2 <+20>:    ldr     r0, [sp, #0]
   0x08000ac4 <+22>:    add     sp, #16
   0x08000ac6 <+24>:    bx      lr

End of assembler dump.

ldr r0, [r0, #0] caused the exception. This instruction tried to load (read) a 32-bit word from the address stored in the register r0. Looking again at the contents of ExceptionFrame we see that the r0 contained the address 0x2FFF_FFFF when this instruction was executed.



#![no_main]
#![no_std]

extern crate cortex_m;
#[macro_use]
extern crate cortex_m_rt as rt;
extern crate panic_semihosting;

use core::ptr;

use rt::ExceptionFrame;

entry!(main);

fn main() -> ! {
    unsafe {
        // read an address outside of the RAM region; causes a HardFault exception
        ptr::read_volatile(0x2FFF_FFFF as *const u32);
    }

    loop {}
}

// define the hard fault handler
exception!(HardFault, hard_fault);

fn hard_fault(ef: &ExceptionFrame) -> ! {
    panic!("HardFault at {:#?}", ef);
}

// define the default exception handler
exception!(*, default_handler);

fn default_handler(irqn: i16) {
    panic!("Unhandled exception (IRQn = {})", irqn);
}