Skip to content

Commit e537f96

Browse files
authored
Merge pull request #266 from knurling-rs/panic
add defmt::panic!
2 parents b22578f + 7957f9d commit e537f96

20 files changed

Lines changed: 696 additions & 30 deletions

book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
- [Filtering](./filtering.md)
1515
- [#[timestamp]](./timestamp.md)
1616
- [#[global_logger]](./global-logger.md)
17+
- [panic! and assert!](./panic.md)
1718
- [Printers](./printers.md)
1819
- [Migrating from git defmt to stable defmt](./migration.md)
1920
- [Design & impl details](./design.md)

book/src/panic.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# `panic!` and `assert!`
2+
3+
The `defmt` crate provides its own version of `panic!`-like and `assert!`-like macros.
4+
The `defmt` version of these macros will log the panic message using `defmt` and then call `core::panic!` (by default).
5+
Because the panic message is formatted using `defmt!` the format string must use the same syntax as the logging macros (e.g. `info!`).
6+
7+
## `#[defmt::panic_handler]`
8+
9+
Because `defmt::panic!` invokes `core::panic!` this can result in the panic message being printed twice if your `#[core::panic_handler]` also prints the panic message.
10+
This is the case if you use [`panic-probe`] with the `print-defmt` feature enabled but not an issue if you are using the [`panic-abort`] crate, for example.
11+
12+
[`panic-probe`]: https://crates.io/crates/panic-probe
13+
[`panic-abort`]: https://crates.io/crates/panic-abort
14+
15+
To avoid this issue you can use the `#[defmt::panic_handler]` to *override* the panicking behavior of `defmt::panic`-like and `defmt::assert`-like macros.
16+
This attribute must be placed on a function with signature `fn() -> !`.
17+
In this function you'll want to replicate the panicking behavior of the Rust `#[panic_handler]` but leave out the part that prints the panic message.
18+
For example:
19+
20+
<!-- NOTE(ignore) we can't compile this test because the `panic_handler` defined here collides with the one in `std` -->
21+
22+
``` rust, ignore
23+
#[panic_handler] // built-in ("core") attribute
24+
fn core_panic(info: &core::panic::PanicInfo) -> ! {
25+
print(info); // e.g. using RTT
26+
reset()
27+
}
28+
29+
#[defmt::panic_handler] // defmt's attribute
30+
fn defmt_panic() -> ! {
31+
// leave out the printing part here
32+
reset()
33+
}
34+
```
35+
36+
If you are using the `panic-probe` crate then you should "abort" (call `cortex_m::asm::udf`) from `#[defmt::panic_handler]` to match its behavior.
37+
38+
NOTE: even if you don't run into the "double panic message printed" issue you may still want to use `#[defmt::panic_handler]` because this way `defmt::panic` and `defmt::assert` will *not* go through the `core::panic` machinery and that *may* reduce code size (we recommend you measure the effect of the change).

defmt.x.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ EXTERN(_defmt_acquire);
33
EXTERN(_defmt_release);
44
EXTERN(__defmt_default_timestamp);
55
PROVIDE(_defmt_timestamp = __defmt_default_timestamp);
6+
PROVIDE(_defmt_panic = __defmt_default_panic);
67

78
SECTIONS
89
{

firmware/qemu/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,22 @@ defmt-error = [] # log at ERROR level
2424
default = ["defmt-default"]
2525
alloc = ["defmt/alloc", "alloc-cortex-m"]
2626

27+
[[bin]]
28+
name = "assert"
29+
test = false
30+
31+
[[bin]]
32+
name = "assert-eq"
33+
test = false
34+
35+
[[bin]]
36+
name = "assert-ne"
37+
test = false
38+
39+
[[bin]]
40+
name = "panic"
41+
test = false
42+
2743
[[bin]]
2844
name = "log"
2945
test = false
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
0.000000 ERROR panicked at 'assertion failed: `(left == right)`: dev'
2+
left: `41`
3+
right: `43`
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
0.000000 ERROR panicked at 'assertion failed: `(left == right)`: release'
2+
left: `41`
3+
right: `43`

firmware/qemu/src/bin/assert-eq.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use cortex_m_rt::entry;
5+
6+
use defmt_semihosting as _; // global logger
7+
8+
#[entry]
9+
fn main() -> ! {
10+
let x = 42;
11+
defmt::debug_assert_eq!(x - 1, x + 1, "dev");
12+
defmt::assert_eq!(x - 1, x + 1, "release");
13+
defmt::unreachable!();
14+
}
15+
16+
// like `panic-semihosting` but doesn't print to stdout (that would corrupt the defmt stream)
17+
#[cfg(target_os = "none")]
18+
#[panic_handler]
19+
fn panic(_: &core::panic::PanicInfo) -> ! {
20+
use cortex_m_semihosting::debug;
21+
22+
loop {
23+
debug::exit(debug::EXIT_SUCCESS)
24+
}
25+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
0.000000 ERROR panicked at 'assertion failed: `(left != right)`: dev'
2+
left/right: `42`
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
0.000000 ERROR panicked at 'assertion failed: `(left != right)`: release'
2+
left/right: `42`

firmware/qemu/src/bin/assert-ne.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use cortex_m_rt::entry;
5+
6+
use defmt_semihosting as _; // global logger
7+
8+
#[entry]
9+
fn main() -> ! {
10+
let x = 42;
11+
defmt::debug_assert_ne!(x, x, "dev");
12+
defmt::assert_ne!(x, x, "release");
13+
defmt::unreachable!();
14+
}
15+
16+
// like `panic-semihosting` but doesn't print to stdout (that would corrupt the defmt stream)
17+
#[cfg(target_os = "none")]
18+
#[panic_handler]
19+
fn panic(_: &core::panic::PanicInfo) -> ! {
20+
use cortex_m_semihosting::debug;
21+
22+
loop {
23+
debug::exit(debug::EXIT_SUCCESS)
24+
}
25+
}

0 commit comments

Comments
 (0)