pprof

A CPU profiler for Rust programs with flamegraph and pprof protobuf export

Library
Cargo
v0.14.1
1,647stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
44/100Fair
Development Activity12
Maintenance0
Community64
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
70/100Good
Architecture74
Code Quality72
Innovation66
Learning Curve68

pprof-rs is a CPU profiler that embeds directly into a running Rust program instead of requiring an external process like perf. It uses a signal-based sampling approach: a setitimer timer fires at a configurable frequency, and the signal handler captures a backtrace via backtrace-rs, aggregating stack samples into a report. Originally built for TiKV, it’s now used across the Rust ecosystem wherever teams need production-safe, in-process CPU profiling.

Reports can be rendered as human-readable stack counts, SVG flamegraphs, or Google’s pprof protobuf format for use with the standard pprof toolchain. It also ships a Criterion integration so benchmarks can automatically emit flamegraphs per benchmark run, and supports frame post-processing to rename or group thread names before rendering.

What You Get

  • In-process CPU profiling via a ProfilerGuardBuilder with configurable sampling frequency and a symbol blocklist
  • Flamegraph SVG generation through the optional flamegraph feature (backed by inferno)
  • pprof protobuf report output via prost-codec or protobuf-codec, compatible with the standard pprof CLI
  • A Criterion profiler integration (pprof::criterion::PProfProfiler) that emits a flamegraph per benchmark automatically
  • Frame post-processing hooks to rename/group thread names (e.g. collapsing worker-1, worker-2 into worker) before rendering

Common Use Cases

  • Profiling a long-running Rust service (e.g. a database or distributed system like TiKV) in production without attaching an external profiler
  • Generating flamegraphs for a Criterion benchmark suite to spot hot paths during optimization
  • Exporting pprof-format profiles from a Rust service so they can be visualized with Google’s standard pprof tooling alongside profiles from other languages
  • Diagnosing CPU hotspots in multi-threaded async workloads by capturing and grouping per-thread stack samples

Under The Hood

Architecture - The crate centers on profiler.rs (601 lines), which installs a SIGPROF handler via setitimer and, on each tick, walks the stack through backtrace-rs and records it in a lock-free Collector. report.rs (344 lines) aggregates raw samples into a Report/Frames tree and exposes renderers for plain-text, SVG flamegraph (via inferno), and pprof protobuf (via the proto/profile.proto schema, compiled with either prost or the protobuf crate depending on which codec feature is enabled). lib.rs is a thin 98-line facade that re-exports ProfilerGuardBuilder, Report, and the optional criterion::PProfProfiler integration, keeping the public surface small while the sampling/aggregation logic stays internal.

Tech Stack - Pure Rust (100%, edition 2021, MSRV 1.74) with no C/FFI beyond what backtrace and symbolic-demangle provide. Core dependencies are backtrace for stack unwinding, nix for signal/timer syscalls, spin for a lock-free collector, and smallvec/aligned-vec for allocation-light sample storage. Everything past the core (flamegraph rendering via inferno, protobuf codecs via prost/protobuf, Criterion integration, frame-pointer unwinding) is gated behind Cargo features, so a default install stays minimal.

Code Quality - 18 #[test]/#[cfg(test)] blocks are spread across the collector, report, and profiler modules, covering sample aggregation and report generation rather than the signal-handling path itself (which is inherently hard to unit test). Error handling goes through a dedicated thiserror-based Error enum in error.rs rather than panics or stringly-typed errors. Naming is consistent and narrowly scoped (ProfilerGuard, Collector, Frames, Report), and the README documents the signal-safety tradeoffs (deadlock avoidance, blocklist rationale) that the implementation relies on.

API Design - The primary flow is three calls: build a ProfilerGuardBuilder, call .build() to start sampling, then .report().build() to get a Report. Feature-gated extension points (.flamegraph(file), .pprof(), frames_post_processor()) layer on without changing the base API, and the Criterion profiler plugs in via the standard Criterion::default().with_profiler(...) extension point rather than a bespoke wrapper. The main ergonomic cost is that several practically-required options (flamegraph, protobuf output) require explicit Cargo feature flags, so first-time users must read the README’s feature table before anything beyond plain-text reports works.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search