We're happy to announce the newest crate published as part of our knurling project: panic-probe.
panic-probe is a simple panic and fault handler that integrates into probe-run, our host-side tooling for embedded programs. When using panic-probe in your firmware, probe-run will detect both Rust panics as well as HardFaults raised by the Cortex-M processor, and exit with an error code after printing a stack backtrace.
Using panic-probe
is as easy as using any other panic handler: Simply add it to your Cargo.toml
and use
it inside your embedded application:
[dependencies.panic-probe]
git = "https://github.com/knurling-rs/probe-run"
branch = "main"
use panic_probe as _; // <- link the panic handler
Note that by default, panic-probe
will not display the panic message, since that means linking
in a lot of formatting code from core::fmt
, which is fairly heavy.
If you do want to see the panic message, you can enable either the print-rtt
or the print-defmt
features. These will use rtt-target
or defmt
to display the panic message, respectively.
(currently, panic-probe
is only available as a git dependency – we will publish it to crates.io
along with defmt once that is ready)
Running on-device tests with cargo test
The most useful feature that panic-probe
enables is the ability to use cargo test
to run tests
on the device. We've already blogged about how you can use probe-run
to make
cargo run
automatically flash and run your app on a microcontroller. With panic-probe
, you can
actually take it a step further by using it for cargo test
as well!
The easiest way to get started with this is to use our crate template. It contains a testsuite
crate that collects integration tests to be run on the device. You can run cargo test -p testsuite
to run them.
Using the template, an example test (stored somewhere in testsuite/tests/
) could look like this:
use template as _; // defmt transport + `panic-probe` panic handler
use nrf52840_hal::{
pac::{Peripherals, TEMP},
Temp,
};
fn test_temp(temp: TEMP) {
defmt::info!("testing temperature sensor...");
let mut temp = Temp::new(temp);
let reading = temp.measure();
assert!(reading > -10, "temp = {}°C. that's too cold!", reading);
assert!(reading < 50, "temp = {}°C. that's too hot!", reading);
}
#[entry]
fn main() -> ! {
let periph = Peripherals::take().unwrap();
defmt::info!("running on-device tests...");
test_temp(periph.TEMP);
template::exit();
}
This test will take a temperature reading using the temperature sensor integrated into the nRF52840, and assert that the reading is between -10°C and 50°C.
The output of the (failing) test would now look similar to this:
$ cargo test -p testsuite
Compiling testsuite v0.1.0 (/path/to/app-template/testsuite)
Finished test [optimized + debuginfo] target(s) in 0.31s
Running target/thumbv7em-none-eabihf/debug/deps/test-6aa66ea9f1d
flashing program ..
DONE
resetting device
0.000000 INFO running on-device tests...
└─ tests/test.rs:25
0.000001 INFO testing temperature sensor...
└─ tests/test.rs:12
0.000002 ERROR panicked at 'temp = 63°C. that's too hot!',
testsuite/tests/test.rs:18:5
└─ probe-run/panic-probe/src/lib.rs:151
stack backtrace:
0: 0x00002aee - __bkpt
1: 0x000050e6 - panic_probe::imp::__cortex_m_rt_HardFault
2: 0x0000539a - HardFault
<exception entry>
3: 0x00002b00 - __udf
4: 0x00004f6e - cortex_m::asm::udf
5: 0x00004fac - rust_begin_unwind
6: 0x00002d44 - core::panicking::panic_fmt
7: 0x00000924 - test::test_temp
8: 0x000009e8 - test::__cortex_m_rt_main
9: 0x0000092e - main
10: 0x00005378 - ResetTrampoline
11: 0x0000536e - Reset
error: test failed, to rerun pass '-p testsuite --test test'
…and Cargo will correctly detect the test as failing and exit accordingly. When the test passes, it should look like this instead:
$ cargo test -p testsuite
Compiling testsuite v0.1.0 (/path/to/app-template/testsuite)
Finished test [optimized + debuginfo] target(s) in 0.26s
Running target/thumbv7em-none-eabihf/debug/deps/test-6aa66ea9f1d
flashing program ..
DONE
resetting device
0.000000 INFO running on-device tests...
└─ tests/test.rs:24
0.000001 INFO testing temperature sensor...
└─ tests/test.rs:12
stack backtrace:
0: 0x00002a6a - __bkpt
1: 0x000015fe - template::exit
2: 0x000009a2 - test::__cortex_m_rt_main
3: 0x000008e2 - main
4: 0x000052f4 - ResetTrampoline
5: 0x000052ea - Reset
Sponsor this work
probe-run
and panic-probe
are Knurling projects and can be funded through GitHub
sponsors. Sponsors get early access to the tools we are building. Thank
you to all of the people already sponsoring our work through the Knurling project!