Skip to content

Commit

Permalink
Merge pull request #266 from knurling-rs/panic
Browse files Browse the repository at this point in the history
add defmt::panic!
  • Loading branch information
japaric authored Nov 23, 2020
2 parents b22578f + 7957f9d commit e537f96
Show file tree
Hide file tree
Showing 20 changed files with 696 additions and 30 deletions.
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- [Filtering](./filtering.md)
- [#[timestamp]](./timestamp.md)
- [#[global_logger]](./global-logger.md)
- [panic! and assert!](./panic.md)
- [Printers](./printers.md)
- [Migrating from git defmt to stable defmt](./migration.md)
- [Design & impl details](./design.md)
Expand Down
38 changes: 38 additions & 0 deletions book/src/panic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# `panic!` and `assert!`

The `defmt` crate provides its own version of `panic!`-like and `assert!`-like macros.
The `defmt` version of these macros will log the panic message using `defmt` and then call `core::panic!` (by default).
Because the panic message is formatted using `defmt!` the format string must use the same syntax as the logging macros (e.g. `info!`).

## `#[defmt::panic_handler]`

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.
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.

[`panic-probe`]: https://crates.io/crates/panic-probe
[`panic-abort`]: https://crates.io/crates/panic-abort

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.
This attribute must be placed on a function with signature `fn() -> !`.
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.
For example:

<!-- NOTE(ignore) we can't compile this test because the `panic_handler` defined here collides with the one in `std` -->

``` rust, ignore
#[panic_handler] // built-in ("core") attribute
fn core_panic(info: &core::panic::PanicInfo) -> ! {
print(info); // e.g. using RTT
reset()
}
#[defmt::panic_handler] // defmt's attribute
fn defmt_panic() -> ! {
// leave out the printing part here
reset()
}
```

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.

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).
1 change: 1 addition & 0 deletions defmt.x.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ EXTERN(_defmt_acquire);
EXTERN(_defmt_release);
EXTERN(__defmt_default_timestamp);
PROVIDE(_defmt_timestamp = __defmt_default_timestamp);
PROVIDE(_defmt_panic = __defmt_default_panic);

SECTIONS
{
Expand Down
16 changes: 16 additions & 0 deletions firmware/qemu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@ defmt-error = [] # log at ERROR level
default = ["defmt-default"]
alloc = ["defmt/alloc", "alloc-cortex-m"]

[[bin]]
name = "assert"
test = false

[[bin]]
name = "assert-eq"
test = false

[[bin]]
name = "assert-ne"
test = false

[[bin]]
name = "panic"
test = false

[[bin]]
name = "log"
test = false
Expand Down
3 changes: 3 additions & 0 deletions firmware/qemu/src/bin/assert-eq.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
0.000000 ERROR panicked at 'assertion failed: `(left == right)`: dev'
left: `41`
right: `43`
3 changes: 3 additions & 0 deletions firmware/qemu/src/bin/assert-eq.release.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
0.000000 ERROR panicked at 'assertion failed: `(left == right)`: release'
left: `41`
right: `43`
25 changes: 25 additions & 0 deletions firmware/qemu/src/bin/assert-eq.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#![no_std]
#![no_main]

use cortex_m_rt::entry;

use defmt_semihosting as _; // global logger

#[entry]
fn main() -> ! {
let x = 42;
defmt::debug_assert_eq!(x - 1, x + 1, "dev");
defmt::assert_eq!(x - 1, x + 1, "release");
defmt::unreachable!();
}

// like `panic-semihosting` but doesn't print to stdout (that would corrupt the defmt stream)
#[cfg(target_os = "none")]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
use cortex_m_semihosting::debug;

loop {
debug::exit(debug::EXIT_SUCCESS)
}
}
2 changes: 2 additions & 0 deletions firmware/qemu/src/bin/assert-ne.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
0.000000 ERROR panicked at 'assertion failed: `(left != right)`: dev'
left/right: `42`
2 changes: 2 additions & 0 deletions firmware/qemu/src/bin/assert-ne.release.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
0.000000 ERROR panicked at 'assertion failed: `(left != right)`: release'
left/right: `42`
25 changes: 25 additions & 0 deletions firmware/qemu/src/bin/assert-ne.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#![no_std]
#![no_main]

use cortex_m_rt::entry;

use defmt_semihosting as _; // global logger

#[entry]
fn main() -> ! {
let x = 42;
defmt::debug_assert_ne!(x, x, "dev");
defmt::assert_ne!(x, x, "release");
defmt::unreachable!();
}

// like `panic-semihosting` but doesn't print to stdout (that would corrupt the defmt stream)
#[cfg(target_os = "none")]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
use cortex_m_semihosting::debug;

loop {
debug::exit(debug::EXIT_SUCCESS)
}
}
1 change: 1 addition & 0 deletions firmware/qemu/src/bin/assert.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.000000 ERROR panicked at 'assertion failed: dev'
1 change: 1 addition & 0 deletions firmware/qemu/src/bin/assert.release.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.000000 ERROR panicked at 'assertion failed: release'
26 changes: 26 additions & 0 deletions firmware/qemu/src/bin/assert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#![no_std]
#![no_main]

use cortex_m_rt::entry;

use defmt_semihosting as _; // global logger

#[entry]
fn main() -> ! {
let dev = false;
let release = false;
defmt::debug_assert!(dev);
defmt::assert!(release);
defmt::unreachable!();
}

// like `panic-semihosting` but doesn't print to stdout (that would corrupt the defmt stream)
#[cfg(target_os = "none")]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
use cortex_m_semihosting::debug;

loop {
debug::exit(debug::EXIT_SUCCESS)
}
}
1 change: 1 addition & 0 deletions firmware/qemu/src/bin/panic.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.000000 ERROR panicked at 'The answer is 42'
1 change: 1 addition & 0 deletions firmware/qemu/src/bin/panic.release.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.000000 ERROR panicked at 'The answer is 42'
22 changes: 22 additions & 0 deletions firmware/qemu/src/bin/panic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#![no_std]
#![no_main]

use cortex_m_rt::entry;

use defmt_semihosting as _; // global logger

#[entry]
fn main() -> ! {
defmt::panic!("The answer is {:?}", 42);
}

// like `panic-semihosting` but doesn't print to stdout (that would corrupt the defmt stream)
#[cfg(target_os = "none")]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
use cortex_m_semihosting::debug;

loop {
debug::exit(debug::EXIT_SUCCESS)
}
}
6 changes: 5 additions & 1 deletion firmware/qemu/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ set -o errexit
function test() {
local bin=$1
local features=${2-,}

cargo rb "$bin" --features "$features" | tee "src/bin/$bin.out.new" | diff "src/bin/$bin.out" -
cargo rrb "$bin" --features "$features" | tee "src/bin/$bin.release.out.new" | diff "src/bin/$bin.release.out" -
}

test "log"
test "panic"
test "assert"
test "assert-eq"
test "assert-ne"
if rustc -V | grep nightly; then
test "alloc" "alloc"
fi
Loading

0 comments on commit e537f96

Please sign in to comment.