panic_probe/
lib.rs

1//! Panic handler for `probe-run`.
2//!
3//! When this panic handler is used, panics will make `probe-run` print a backtrace and exit with a
4//! non-zero status code, indicating failure. This building block can be used to run on-device
5//! tests.
6//!
7//! # Panic Messages
8//!
9//! By default, `panic-probe` *ignores* the panic message. You can enable one of the following
10//! features to print it instead:
11//!
12//! - `print-rtt`: Prints the panic message over plain RTT (via `rtt-target`). RTT must be
13//!   initialized by the app.
14//! - `print-defmt`: Prints the panic message via [defmt]'s transport (note that defmt will not be
15//!   used to efficiently format the message).
16//!
17//! [defmt]: https://github.com/knurling-rs/defmt/
18
19#![no_std]
20#![cfg(target_os = "none")]
21#![doc(html_logo_url = "https://knurling.ferrous-systems.com/knurling_logo_light_text.svg")]
22
23#[cfg(not(cortex_m))]
24compile_error!("`panic-probe` only supports Cortex-M targets (thumbvN-none-eabi[hf])");
25
26// Functionality `cfg`d out on platforms with OS/libstd.
27#[cfg(target_os = "none")]
28mod imp {
29    use core::panic::PanicInfo;
30    use core::sync::atomic::{AtomicBool, Ordering};
31
32    #[cfg(feature = "print-rtt")]
33    use crate::print_rtt::print;
34
35    #[cfg(feature = "print-defmt")]
36    use crate::print_defmt::print;
37
38    #[cfg(not(any(feature = "print-rtt", feature = "print-defmt")))]
39    fn print(_: &core::panic::PanicInfo) {}
40
41    #[panic_handler]
42    fn panic(info: &PanicInfo) -> ! {
43        static PANICKED: AtomicBool = AtomicBool::new(false);
44
45        cortex_m::interrupt::disable();
46
47        // Guard against infinite recursion, just in case.
48        if !PANICKED.load(Ordering::Relaxed) {
49            PANICKED.store(true, Ordering::Relaxed);
50
51            print(info);
52        }
53
54        crate::hard_fault();
55    }
56}
57
58/// Trigger a `HardFault` via `udf` instruction.
59///
60/// This function may be used to as `defmt::panic_handler` to avoid double prints.
61///
62/// # Examples
63///
64/// ```
65/// #[defmt::panic_handler]
66/// fn panic() -> ! {
67///     panic_probe::hard_fault();
68/// }
69/// ```
70#[cfg(target_os = "none")]
71pub fn hard_fault() -> ! {
72    // If `UsageFault` is enabled, we disable that first, since otherwise `udf` will cause that
73    // exception instead of `HardFault`.
74    #[cfg(not(any(armv6m, armv8m_base)))]
75    {
76        const SHCSR: *mut u32 = 0xE000ED24usize as _;
77        const USGFAULTENA: usize = 18;
78
79        unsafe {
80            let mut shcsr = core::ptr::read_volatile(SHCSR);
81            shcsr &= !(1 << USGFAULTENA);
82            core::ptr::write_volatile(SHCSR, shcsr);
83        }
84    }
85
86    cortex_m::asm::udf();
87}
88
89#[cfg(feature = "print-rtt")]
90mod print_rtt {
91    use core::panic::PanicInfo;
92    use rtt_target::rprintln;
93
94    pub fn print(info: &PanicInfo) {
95        rprintln!("{}", info);
96    }
97}
98
99#[cfg(feature = "print-defmt")]
100mod print_defmt {
101    use core::panic::PanicInfo;
102
103    pub fn print(info: &PanicInfo) {
104        defmt::error!("{}", defmt::Display2Format(info));
105    }
106}