In this guide, you'll learn how to:
#[tracefn] attribute macro for automatic function tracing.tracing crate with Perfetto as a backend.TracePacket with GPU event protos.NOTE: The Rust SDK is a community-maintained project. It may not have the same level of support, stability, or feature coverage as the official C++ SDK.
The Perfetto Rust SDK provides safe and ergonomic bindings for the Perfetto tracing framework. It is a language binding built on top of the C SDK: it wraps the Perfetto C API (via bindgen) with Rust abstractions for tracing sessions, data sources, and track events, so Rust events use the same core and produce the same trace format as every other Perfetto SDK.
The SDK is split into several crates:
| Crate | Description |
|---|---|
perfetto-sdk | Core SDK with tracing sessions, data sources, and track events |
perfetto-sdk-sys | Low-level FFI bindings to the Perfetto C API |
perfetto-sdk-derive | #[tracefn] proc macro for automatic function instrumentation |
perfetto-sdk-protos-gpu | GPU event protobuf bindings extending TracePacket |
perfetto-sdk-protos-trace-processor | Trace processor protobuf bindings |
tracing-perfetto-sdk | tracing-subscriber Layer for Perfetto |
Add the SDK to your Cargo.toml:
[dependencies] perfetto-sdk = "1"
By default, this compiles and statically links the bundled Perfetto C library. No external dependencies are required.
Initialize Perfetto and define your tracing categories:
use perfetto_sdk::producer::*; use perfetto_sdk::track_event::*; use perfetto_sdk::{scoped_track_event, track_event_begin, track_event_end, track_event_instant}; // Define tracing categories. Each category can be independently // enabled or disabled in the trace configuration. perfetto_sdk::track_event_categories! { pub mod my_categories { ("rendering", "Events from the graphics subsystem", []), ("network", "Network upload and download statistics", []), } } use my_categories as perfetto_te_ns; fn main() { Producer::init( ProducerInitArgsBuilder::new() .backends(Backends::IN_PROCESS) .build(), ); TrackEvent::init(); my_categories::register().unwrap(); // Scoped event — ends when the scope exits. scoped_track_event!("rendering", "DrawPlayer", |ctx: &mut EventContext| { ctx.add_debug_arg("player_number", TrackEventDebugArg::Uint64(1)); }, |_| {} ); // Manual begin/end events. track_event_begin!("rendering", "DrawGame"); track_event_end!("rendering"); // Instant event. track_event_instant!("rendering", "VSync"); }
For self-contained trace collection without a tracing service, create a TracingSession with the in-process backend. See contrib/rust-sdk/perfetto/examples/tracing_session.rs for a complete working example.
To connect to a running Perfetto tracing service (traced), use the system backend instead:
use perfetto_sdk::producer::*; Producer::init( ProducerInitArgsBuilder::new() .backends(Backends::SYSTEM) .build(), );
Your application acts as a producer, and the system tracing service controls when tracing starts and stops. Record a trace using the system tracing tools.
#[tracefn]The perfetto-sdk-derive crate provides a proc macro that automatically instruments a function with a track event. It captures all input parameters as debug annotations.
[dependencies] perfetto-sdk = "1" perfetto-sdk-derive = "1"
Then annotate functions with #[tracefn]:
// Assuming categories are defined as in the example above. use perfetto_sdk::producer::*; use perfetto_sdk::track_event::*; perfetto_sdk::track_event_categories! { pub mod my_categories { ("rendering", "Events from the graphics subsystem", []), } } use my_categories as perfetto_te_ns; use perfetto_sdk_derive::tracefn; #[tracefn("rendering")] fn draw_player(player_number: u32, x: f64, y: f64) { // A "draw_player" track event is emitted automatically. // player_number, x, and y are captured as debug annotations. }
The macro wraps the function body in a scoped_track_event! so the event spans the full function execution. The category name is passed as the macro argument.
tracing crateIf your application uses the Rust tracing crate, you can use tracing-perfetto-sdk to send events to Perfetto without changing your existing instrumentation:
[dependencies] tracing = "0.1" tracing-subscriber = "0.3" tracing-perfetto-sdk = "1"
Initialize and install the layer:
use tracing_subscriber::prelude::*; tracing_perfetto_sdk::init(); tracing_subscriber::registry() .with(tracing_perfetto_sdk::PerfettoLayer::new()) .init(); // Standard tracing macros emit Perfetto events. let _span = tracing::info_span!("DrawGame").entered(); tracing::info!(player = 1, "drawing player");
Spans become duration slices and events become instant events, with fields captured as debug annotations and source locations attached automatically.
The perfetto-sdk-protos-gpu crate extends TracePacket with GPU-specific fields for emitting GPU counter events, render stage events, Vulkan events, and more.
[dependencies] perfetto-sdk = "1" perfetto-sdk-protos-gpu = "1"
Import the TracePacketExt trait to access the GPU fields:
use perfetto_sdk_protos_gpu::protos::trace::trace_packet::prelude::*; use perfetto_sdk_protos_gpu::protos::trace::gpu::gpu_counter_event::*; fn emit_gpu_counter(packet: &mut perfetto_sdk::protos::trace::trace_packet::TracePacket) { packet.set_gpu_counter_event(|event: &mut GpuCounterEvent| { event.set_counters(|counter: &mut GpuCounterEventGpuCounter| { counter.set_counter_id(1); counter.set_double_value(42.0); }); }); }
This is typically used inside a custom data source's trace callback. See contrib/rust-sdk/perfetto-protos-gpu/examples/gpu_counters.rs for a complete example.
Track events can be extended with custom protobuf fields using the extension mechanism. The perfetto-sdk-protos-gpu crate defines GPU extensions such as gpu_api that tag track events with the GPU API type.
Use set_proto_fields on EventContext to add extension fields:
// Assuming categories are defined as in the example above. use perfetto_sdk::producer::*; use perfetto_sdk::track_event::*; use perfetto_sdk::track_event_instant; perfetto_sdk::track_event_categories! { pub mod my_categories { ("rendering", "Events from the graphics subsystem", []), } } use my_categories as perfetto_te_ns; use perfetto_sdk_protos_gpu::protos::trace::gpu::gpu_track_event::{ GpuApi, TrackEventExtFieldNumber, }; track_event_instant!("rendering", "cuLaunchKernel", |ctx: &mut EventContext| { ctx.set_proto_fields(&TrackEventProtoFields { fields: &[TrackEventProtoField::VarInt( TrackEventExtFieldNumber::GpuApi as u32, GpuApi::GpuApiCuda as u64, )], }); });
The extension field appears in the trace as gpu_api: GPU_API_CUDA on the track event, which the trace processor decodes into the gpu_api column of the slice table args.