Crate utrace

source ·
Expand description

§utrace

utrace is an instrumentation-based profiling tool for embedded applications. It is intended to be platform-agnostic, async-friendly and low-overhead. The principle of operation of utrace was inspired by the fantastic defmt logging library.

§Usage

Main user-facing APIs are two procedural macros that can be used to insert instrumentation - the attribute #[trace] and the function-like trace_here. Possible usages are demonstrated in this snippet:

#[utrace::trace]
async fn do_something() {

}

#[utrace::trace]
fn do_something_else() {

}

{
    utrace::trace_here!();
    ...
    ...
}

When #[trace] instruments an async function, the instants of the respective future creation, dropping and poll spans will be reported by default.

The current implementation assumes that traced Futures are not reentrant (it implies that only one instance of an instrumented async function is pending at each moment). If this is not the case, Future lifecycle tracking will be broken.

§Trace information timestamping and transport

While tracing instrumentation itself is platform-agnostic, it requires a way of obtaining timestamps and a channel for data transfer from dut to the host system.

To provide a timestamp function to the library, use the #[timestamp] macro. For example:

#[utrace::timestamp]
fn utrace_timestamp_fn() -> u64 {
    (Tim15::now() - <Tim15 as Monotonic>::ZERO).to_micros()
}

In the current version, the signature of the timestamp function must be rust fn() -> u64.

To define a transport, annotate a function with #[default_transport] like this:

#[utrace::default_transport]
pub fn write(buf: &[u8]) {
    ...
}

The current implementation provides the implementation of RTT-based transport in utrace_rtt crate.

Note, that current implementation requires an implementation of a critical section. For example, if you are using single-core ARM MCU, you can add

cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }

to your Cargo.toml.

§Trace data interpretation

The metadata, required for trace interpretation is stored in the output elf binary. Correct bundling of this metadata requires passing utrace_linker.x script to a linker during your binary linking. It could be done either in build.rs script by adding something like

println!("cargo::rustc-link-arg=-Tutrace_linker.x");

or by adding

rustflags = [
  "-C", "link-arg=-Tutrace_linker.x"
]

to your .cargo/config.toml. The first method should be prefered to the second one.

To extract metadata and interpret trace data stream, utrace_parser crate should be used. This crate’s package provides a binary, called utrace-capture, which can receive raw trace stream from TCP connection or stdin and write the trace in chrome://tracing format. To install it, execute

cargo install --locked utrace_parser --features="cli"

Let’s assume that OpenOCD is used for DUT interface. In this case, you should have something like this in OpenOCD init script:

rtt setup 0x20000000 0x20000 "SEGGER RTT"
rtt server start 9001 0
rtt start

This will tell OpenOCD to listen on port 9001 and send raw RTT data from channel 0 to a connected client. To capture and save trace stream in this configuration, run

utrace-capture <path to firmware elf executable> --tcp localhost:9001 --out-ct trace_out

Traces will be captured in trace_out_xxx.json files. To finish capture, press Ctrl+C. These traces can be opened with chrome://tracing if you are using Chrome browser, or with Perfetto UI.

Note, that probe-rs (cargo embed) RTT feature tries to connect to a server, instead of listening on a port, so you will need to use --tcp-server flag, eg:

utrace-capture <path to firmware elf executable> --tcp-server 0.0.0.0:9001 --out-ct trace_out

Trace data can also be captured from stdin using --stdin flag.

Re-exports§

Modules§

  • Internal RAII tracer implementation.

Macros§

  • This macro should be used if you want to trace a specific execution span. It will emit trace span from it’s point of invocation to the end of the block it was invoked in.

Functions§

  • This function can be called during initialization. Its current purpose is to establish reference time, relative to which all events will be timestamped and to emit Reset packet. This packet can be used by trace stream capture tool to start a new trace. It is possible to call init multiple times during execution to logically separate the trace into several parts.

Attribute Macros§

  • This macro provides a transport implementation for utrace.
  • This macro should be used to define timestamp function which will be used by utrace to obtain event timestamps.
  • This attribute can be applied to functions and async functions to instrument them. By default, when applied to a function, it will trace function entry and function exit. If applied to async fn, it will report creation, drop and poll spans of the respective Future.