Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add defmt::panic! #266

Merged
merged 20 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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