|
1 | 1 | //! Debugging a crash (exception)
|
2 | 2 | //!
|
3 |
| -//! The `cortex-m-rt` crate provides functionality for this through a default exception handler. |
4 |
| -//! When an exception is hit, the default handler will trigger a breakpoint and in this debugging |
5 |
| -//! context the stacked registers are accessible. |
| 3 | +//! Most crash conditions trigger a hard fault exception, whose handler is defined via |
| 4 | +//! `exception!(HardFault, ..)`. The `HardFault` handler has access to the exception frame, a |
| 5 | +//! snapshot of the CPU registers at the moment of the exception. |
6 | 6 | //!
|
7 |
| -//! In you run the example below, you'll be able to inspect the state of your program under the |
8 |
| -//! debugger using these commands: |
| 7 | +//! This program crashes and the `HardFault` handler prints to the console the contents of the |
| 8 | +//! `ExceptionFrame` and then triggers a breakpoint. From that breakpoint one can see the backtrace |
| 9 | +//! that led to the exception. |
9 | 10 | //!
|
10 | 11 | //! ``` text
|
11 | 12 | //! (gdb) continue
|
12 | 13 | //! Program received signal SIGTRAP, Trace/breakpoint trap.
|
13 | 14 | //! __bkpt () at asm/bkpt.s:3
|
14 | 15 | //! 3 bkpt
|
15 | 16 | //!
|
16 |
| -//! (gdb) finish |
17 |
| -//! Run till exit from #0 __bkpt () at asm/bkpt.s:3 |
18 |
| -//! Note: automatically using hardware breakpoints for read-only addresses. |
19 |
| -//! crash::hf (_ef=0x20004fa0) at examples/crash.rs:102 |
20 |
| -//! 99 asm::bkpt(); |
| 17 | +//! (gdb) backtrace |
| 18 | +//! #0 __bkpt () at asm/bkpt.s:3 |
| 19 | +//! #1 0x080030b4 in cortex_m::asm::bkpt () at $$/cortex-m-0.5.0/src/asm.rs:19 |
| 20 | +//! #2 rust_begin_unwind (args=..., file=..., line=99, col=5) at $$/panic-semihosting-0.2.0/src/lib.rs:87 |
| 21 | +//! #3 0x08001d06 in core::panicking::panic_fmt () at libcore/panicking.rs:71 |
| 22 | +//! #4 0x080004a6 in crash::hard_fault (ef=0x20004fa0) at examples/crash.rs:99 |
| 23 | +//! #5 0x08000548 in UserHardFault (ef=0x20004fa0) at <exception macros>:10 |
| 24 | +//! #6 0x0800093a in HardFault () at asm.s:5 |
| 25 | +//! Backtrace stopped: previous frame identical to this frame (corrupt stack?) |
| 26 | +//! ``` |
21 | 27 | //!
|
22 |
| -//! (gdb) # Exception frame = program state during the crash |
23 |
| -//! (gdb) print/x *_ef |
24 |
| -//! $1 = cortex_m_rt::ExceptionFrame { |
25 |
| -//! r0: 0x2fffffff, |
26 |
| -//! r1: 0x2fffffff, |
27 |
| -//! r2: 0x80006b0, |
28 |
| -//! r3: 0x80006b0, |
29 |
| -//! r12: 0x20000000, |
30 |
| -//! lr: 0x800040f, |
31 |
| -//! pc: 0x800066a, |
32 |
| -//! xpsr: 0x61000000 |
33 |
| -//! } |
| 28 | +//! In the console output one will find the state of the Program Counter (PC) register at the time |
| 29 | +//! of the exception. |
34 | 30 | //!
|
35 |
| -//! (gdb) # Where did we come from? |
36 |
| -//! (gdb) backtrace |
37 |
| -//! #0 crash::hf (_ef=0x20004fa0) at examples/crash.rs:102 |
38 |
| -//! #1 0x080004ac in UserHardFault (ef=0x20004fa0) at <exception macros>:9 |
39 |
| -//! #2 <signal handler called> |
40 |
| -//! #3 0x0800066a in core::ptr::read_volatile (src=0x2fffffff) at /checkout/src/libcore/ptr.rs:452 |
41 |
| -//! #4 0x0800040e in crash::main () at examples/crash.rs:85 |
42 |
| -//! #5 0x08000456 in main () at <main macros>:3 |
| 31 | +//! ``` text |
| 32 | +//! panicked at 'HardFault at ExceptionFrame { |
| 33 | +//! r0: 0x2fffffff, |
| 34 | +//! r1: 0x2fffffff, |
| 35 | +//! r2: 0x080051d4, |
| 36 | +//! r3: 0x080051d4, |
| 37 | +//! r12: 0x20000000, |
| 38 | +//! lr: 0x08000435, |
| 39 | +//! pc: 0x08000ab6, |
| 40 | +//! xpsr: 0x61000000 |
| 41 | +//! }', examples/crash.rs:106:5 |
| 42 | +//! ``` |
| 43 | +//! |
| 44 | +//! This register contains the address of the instruction that caused the exception. In GDB one can |
| 45 | +//! disassemble the program around this address to observe the instruction that caused the |
| 46 | +//! exception. |
43 | 47 | //!
|
44 |
| -//! (gdb) # Nail down the location of the crash |
45 |
| -//! (gdb) disassemble/m _ef.pc |
| 48 | +//! ``` text |
| 49 | +//! (gdb) disassemble/m 0x08000ab6 |
46 | 50 | //! Dump of assembler code for function core::ptr::read_volatile:
|
47 |
| -//! 451 pub unsafe fn read_volatile<T>(src: *const T) -> T {} |
48 |
| -//! 0x08000662 <+0>: sub sp, #16 |
49 |
| -//! 0x08000664 <+2>: mov r1, r0 |
50 |
| -//! 0x08000666 <+4>: str r0, [sp, #8] |
| 51 | +//! 451 pub unsafe fn read_volatile<T>(src: *const T) -> T { |
| 52 | +//! 0x08000aae <+0>: sub sp, #16 |
| 53 | +//! 0x08000ab0 <+2>: mov r1, r0 |
| 54 | +//! 0x08000ab2 <+4>: str r0, [sp, #8] |
51 | 55 | //!
|
52 | 56 | //! 452 intrinsics::volatile_load(src)
|
53 |
| -//! 0x08000668 <+6>: ldr r0, [sp, #8] |
54 |
| -//! 0x0800066a <+8>: ldr r0, [r0, #0] |
55 |
| -//! 0x0800066c <+10>: str r0, [sp, #12] |
56 |
| -//! 0x0800066e <+12>: ldr r0, [sp, #12] |
57 |
| -//! 0x08000670 <+14>: str r1, [sp, #4] |
58 |
| -//! 0x08000672 <+16>: str r0, [sp, #0] |
59 |
| -//! 0x08000674 <+18>: b.n 0x8000676 <core::ptr::read_volatile+20> |
| 57 | +//! 0x08000ab4 <+6>: ldr r0, [sp, #8] |
| 58 | +//! -> 0x08000ab6 <+8>: ldr r0, [r0, #0] |
| 59 | +//! 0x08000ab8 <+10>: str r0, [sp, #12] |
| 60 | +//! 0x08000aba <+12>: ldr r0, [sp, #12] |
| 61 | +//! 0x08000abc <+14>: str r1, [sp, #4] |
| 62 | +//! 0x08000abe <+16>: str r0, [sp, #0] |
| 63 | +//! 0x08000ac0 <+18>: b.n 0x8000ac2 <core::ptr::read_volatile+20> |
60 | 64 | //!
|
61 | 65 | //! 453 }
|
62 |
| -//! 0x08000676 <+20>: ldr r0, [sp, #0] |
63 |
| -//! 0x08000678 <+22>: add sp, #16 |
64 |
| -//! 0x0800067a <+24>: bx lr |
| 66 | +//! 0x08000ac2 <+20>: ldr r0, [sp, #0] |
| 67 | +//! 0x08000ac4 <+22>: add sp, #16 |
| 68 | +//! 0x08000ac6 <+24>: bx lr |
65 | 69 | //!
|
66 | 70 | //! End of assembler dump.
|
67 | 71 | //! ```
|
68 | 72 | //!
|
| 73 | +//! `ldr r0, [r0, #0]` caused the exception. This instruction tried to load (read) a 32-bit word |
| 74 | +//! from the address stored in the register `r0`. Looking again at the contents of `ExceptionFrame` |
| 75 | +//! we see that the `r0` contained the address `0x2FFF_FFFF` when this instruction was executed. |
| 76 | +//! |
69 | 77 | //! ---
|
70 | 78 |
|
71 | 79 | #![no_main]
|
|
74 | 82 | extern crate cortex_m;
|
75 | 83 | #[macro_use]
|
76 | 84 | extern crate cortex_m_rt as rt;
|
77 |
| -extern crate panic_abort; |
| 85 | +extern crate panic_semihosting; |
78 | 86 |
|
79 | 87 | use core::ptr;
|
80 | 88 |
|
81 |
| -use cortex_m::asm; |
82 | 89 | use rt::ExceptionFrame;
|
83 | 90 |
|
84 |
| -main!(main); |
| 91 | +entry!(main); |
85 | 92 |
|
86 |
| -#[inline(always)] |
87 | 93 | fn main() -> ! {
|
88 | 94 | unsafe {
|
| 95 | + // read an address outside of the RAM region; causes a HardFault exception |
89 | 96 | ptr::read_volatile(0x2FFF_FFFF as *const u32);
|
90 | 97 | }
|
91 | 98 |
|
92 | 99 | loop {}
|
93 | 100 | }
|
94 | 101 |
|
95 |
| -exception!(DefaultHandler, dh); |
| 102 | +// define the hard fault handler |
| 103 | +exception!(HardFault, hard_fault); |
96 | 104 |
|
97 |
| -#[inline(always)] |
98 |
| -fn dh(_nr: u8) { |
99 |
| - asm::bkpt(); |
| 105 | +fn hard_fault(ef: &ExceptionFrame) -> ! { |
| 106 | + panic!("HardFault at {:#?}", ef); |
100 | 107 | }
|
101 | 108 |
|
102 |
| -exception!(HardFault, hf); |
103 |
| - |
104 |
| -#[inline(always)] |
105 |
| -fn hf(_ef: &ExceptionFrame) -> ! { |
106 |
| - asm::bkpt(); |
| 109 | +// define the default exception handler |
| 110 | +exception!(*, default_handler); |
107 | 111 |
|
108 |
| - loop {} |
| 112 | +fn default_handler(irqn: i16) { |
| 113 | + panic!("Unhandled exception (IRQn = {})", irqn); |
109 | 114 | }
|
110 |
| - |
111 |
| -interrupts!(DefaultHandler); |
|
0 commit comments