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

Customizable timestamps #343

Merged
merged 5 commits into from
Jan 15, 2021
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
2 changes: 1 addition & 1 deletion book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
- [Display hints](./hints.md)
- [Implementing format](./format.md)
- [Filtering](./filtering.md)
- [#[timestamp]](./timestamp.md)
- [Timestamps](./timestamps.md)
- [#[global_logger]](./global-logger.md)
- [panic! and assert!](./panic.md)
- [Printers](./printers.md)
Expand Down
9 changes: 6 additions & 3 deletions book/src/design-timestamp.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Timestamp

In the current implementation timestamps are absolute (time elapsed since the start of the program) and in microseconds.
Timestamps are LEB128 encoded before serialization.
`defmt::timestamp!` needs to be as efficient as possible, because it is implicitly invoked on every single log invocation.

> TODO we may want to consider using delta encoding in the future
The timestamp format string index is not transmitted over the wire.
Instead, it is marked with a `defmt_timestamp` tag and the decoder loads it from the ELF file.
Linker magic is used to make sure that it doesn't get defined twice, and that the symbol doesn't get discarded (which can happen since its address is never used).

The `µs` format specifier was introduced to allow replicating the timestamp format of previous defmt versions, which always used a LEB128-encoded `u64` timestamp and treated it as a number of microseconds.
1 change: 1 addition & 0 deletions book/src/hints.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The following display hints are currently supported:
- `?`, `core::fmt::Debug`-like
- `b`, binary
- `a`, ASCII
- `µs`, microseconds (formats integers as time stamps)

The first 4 display hints resemble what's supported in `core::fmt`. Examples below:

Expand Down
42 changes: 11 additions & 31 deletions book/src/timestamp.md → book/src/timestamps.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,10 @@
# #[timestamp]
# Timestamps

*Applications* that, directly or transitively, use any of `defmt` logging macros may define a `#[timestamp]` function or include one in their dependency graph.
*Applications* that, directly or transitively, use any of `defmt` logging macros may use the `timestamp!` macro to define additional data to be included in every log frame.

All logs are timestamped.
The `#[timestamp]` function specifies how the timestamp is computed.
By default (if no `#[timestamp]` function is provided), a timestamp of 0 will be used.
This function must have signature `fn() -> u64` and on each invocation *should* return a non-decreasing value.
The function is not `unsafe` meaning that it must be thread-safe and interrupt-safe.
The `timestamp!` macro may only be used once throughout the crate graph. Its syntax is the same as for the other logging macros (`info!`, etc.), except that `timestamp!` is global and so cannot access any local variables.

## No timestamp (default behavior)

When no `#[timestamp]` function is used, defmt will use one equivalent to this:

``` rust
# extern crate defmt;
#[defmt::timestamp]
fn timestamp() -> u64 {
0
}
```
By default, no timestamp is provided or transferred over the defmt sink.

## Atomic timestamp

Expand All @@ -28,30 +14,28 @@ A simple `timestamp` function that does not depend on device specific features a
# extern crate defmt;
# use std::sync::atomic::{AtomicUsize, Ordering};
// WARNING may overflow and wrap-around in long lived apps
#[defmt::timestamp]
fn timestamp() -> u64 {
static COUNT: AtomicUsize = AtomicUsize::new(0);
COUNT.fetch_add(1, Ordering::Relaxed) as u64
}
static COUNT: AtomicUsize = AtomicUsize::new(0);
defmt::timestamp!("{=usize}", COUNT.fetch_add(1, Ordering::Relaxed));
```

## Hardware timestamp

A `timestamp` function that uses a device-specific monotonic timer can directly read a MMIO register.
It's OK if the function returns `0` while the timer is disabled.

The `µs` display hint can be used to format an integer value as a time in microseconds (eg. `1_000_000` may be displayed as `1.000000`).

``` rust
# extern crate defmt;
# fn monotonic_timer_counter_register() -> *mut u32 {
# static mut X: u32 = 0;
# unsafe { &mut X as *mut u32 }
# }
// WARNING may overflow and wrap-around in long lived apps
#[defmt::timestamp]
fn timestamp() -> u64 {
defmt::timestamp!("{=u32:µs}", {
// NOTE(interrupt-safe) single instruction volatile read operation
unsafe { monotonic_timer_counter_register().read_volatile() as u64 }
}
unsafe { monotonic_timer_counter_register().read_volatile() }
});

# fn enable_monotonic_counter() {}
fn main() {
Expand Down Expand Up @@ -96,7 +80,3 @@ count: u64 <- (high1 << 32) | low
```

The loop should be kept as tight as possible and the read operations must be single-instruction operations.

## Inter-operation with built-in attributes

The `#[timestamp]` attribute cannot be used together with the `export_name` or `no_mangle` attributes
Loading