| Crates.io | perfetto-recorder |
| lib.rs | perfetto-recorder |
| version | 0.3.1 |
| created_at | 2025-11-22 09:29:36.332678+00 |
| updated_at | 2026-01-12 21:13:25.056871+00 |
| description | A low overhead way to record perfetto traces from Rust code |
| homepage | |
| repository | https://github.com/davidlattimore/perfetto-recorder |
| max_upload_size | |
| id | 1945106 |
| size | 87,337 |
This crate can be used to record Perfetto traces showing internal spans of what your application is doing. See perfetto.dev for more information about Perfetto.
use perfetto_recorder::scope;
use perfetto_recorder::TraceBuilder;
use perfetto_recorder::ThreadTraceData;
perfetto_recorder::start()?;
{
scope!("foo", value = 1_u64, foo = 2_i64, baz = "baz");
// Do some work.
}
TraceBuilder::new()?
.process_thread_data(&ThreadTraceData::take_current_thread())
.write_to_file("out.pftrace");
The duration of the span foo will be recorded along with the supplied arguments.
If your application uses multiple threads, then you'll need to call
ThreadTraceData::take_current_thread() from each thread in order to gather the trace data from
those threads. See examples/rayon.rs for an example.
Counter tracks allow you to record time-series data like CPU usage, memory usage, frame rates, etc.:
use perfetto_recorder::{CounterUnit, TraceBuilder};
perfetto_recorder::start()?;
let mut trace = TraceBuilder::new()?;
// Create counter tracks
let cpu_counter = trace.create_counter_track(
"CPU Usage",
CounterUnit::Custom("%".to_string()),
1, // Unit multiplier
false, // Not incremental (absolute values)
);
let memory_counter = trace.create_counter_track(
"Memory Usage",
CounterUnit::SizeBytes,
1024 * 1024, // Convert to MB
false,
);
// Record counter values at different timestamps
trace.record_counter_f64(cpu_counter, perfetto_recorder::time(), 42.5);
trace.record_counter_i64(memory_counter, perfetto_recorder::time(), 1024);
trace.write_to_file("counters.pftrace")?;
You should then be able to open counters.pftrace in the perfetto UI.
The enable feature enables recording of spans. This is off by default because it's assumed that
you don't want to captuure span information during normal running and only want to opt-in when
you're analysing performance.
Turn this feature on in order to get faster span captures. Typically, this feature would be expected
to reduce span overhead from about 115 ns to about 50 ns. This feature does however incur about a 20
ms delay at program startup. If you use this feature, it's suggested that you enable it together
with the enable feature. e.g. something like this.
[features]
perfetto = ["perfetto-recorder/enable", "perfetto-recorder/fastant"]
The primary reason why this crate exists is in order to reduce the overhead of recording a span. If the overhead is too high, then parts of the application with lots of spans will use more time, making the trace less accurately reflect the way that the application behaves without tracing.
The other crates in this space all seem to build on top of tracing, which due to levels of abstraction adds substantial overhead.
These are some benchmark results on an AMD Ryzen 9955HX:
When actually converting the trace to Perfetto format, this crate then incurs a cost of approximately 563 ns per span. By doing this work later, once the application has finished, we avoid having too much effect on the actual runtime of the application. It's possible that our conversion cost could be further reduced by writing the perfetto format directly rather than converting to an a Prost in-memory representation first, but this hasn't been a priority.
This crate doesn't support async usage. Put another way, it assumes that a span opened on one thread will be closed on that same thread. tracing-perfetto-sdk-layer has support for async.
This crate doesn't support flow events (arrows linking different parts of traces).
tracing-perfetto-sdk-layer also has support for receiving perfetto tracing data from the system, allowing the trace to also include things like scheduling events.
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Wild by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.