Crates.io | utrace |
lib.rs | utrace |
version | 0.1.1 |
source | src |
created_at | 2024-04-29 10:49:13.492236 |
updated_at | 2024-04-29 10:49:13.492236 |
description | Instrumentation-based profiling library for embedded targets with async support |
homepage | |
repository | https://github.com/gubik123/utrace |
max_upload_size | |
id | 1224075 |
size | 14,596 |
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.
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.
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.
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.