pyroscope_pprofrs
A signal-based CPU profiling backend built on pprof-rs for the Pyroscope Rust agent.
Repository Health
Technical Analysis
pyroscope_pprofrs plugs pprof-rs’s signal-based CPU sampling profiler into the Pyroscope Rust agent as a Backend implementation. It periodically interrupts the running process via SIGPROF, unwinds each sampled thread’s call stack, and converts the resulting frames into the stack-trace format the Pyroscope agent ships to a Pyroscope server for continuous profiling.
The crate started life as a standalone package published independently on crates.io, and its functionality now also lives inside the main pyroscope crate itself, gated behind the backend-pprof-rs feature flag with the same profiler, collector, and backtrace-unwinding code. The standalone pyroscope_pprofrs crate remains published and widely used (8M+ downloads) for existing integrations that depend on it directly.
What You Get
- A ready-made
Backendimplementation (pprof_backend) that plugs directly intopyroscope::PyroscopeAgentwith a single builder call - Configurable sampling frequency via
PprofConfig(defaults to 100 Hz) - Signal-based (SIGPROF) stack sampling with a custom timer, avoiding the overhead of always-on instrumentation
- Cross-platform stack unwinding backends (the vendored
backtrace/framehopmachinery) for Linux and macOS on x86_64/aarch64 - Thread tagging support so profiles can be filtered or grouped by thread rules before being reported
Common Use Cases
- Adding continuous CPU profiling to a long-running Rust service so flame graphs are always available in Pyroscope without attaching a debugger
- Diagnosing CPU hotspots in production Rust workloads (web servers, background workers) with low, sampling-based overhead
- Correlating CPU usage spikes with specific code paths across microservices reporting into a shared Pyroscope backend
- Building custom profiling backends for the
pyroscopeagent by using this crate’sBackendtrait implementation as a reference
Under The Hood
Architecture
The crate implements the Pyroscope agent’s Backend trait in src/backend/pprof.rs: initialize() builds a ProfilerGuard (from the vendored pprof-rs-derived profiler in src/backend/pprofrs/) at a configured sample rate, report() calls dump_report() to drain the profiler’s collector, converts its Report/Frames/Symbol types into the agent’s own StackBuffer/StackTrace/StackFrame types via a set of From wrapper conversions, and clears the buffer for the next reporting interval. The profiler itself (profiler.rs, collector.rs, timer.rs, backtrace/) is a self-contained signal-handler-driven sampler: a once_cell-backed global PROFILER guarded by a spin::RwLock, a nix-based SIGPROF timer, and a pluggable backtrace unwinder (with a framehop-based implementation under backtrace/framehop_unwinder/). Nothing here depends on the async runtime or on how the host application is structured — it is purely an add-on component wired in through the agent’s builder pattern.
Tech Stack
Rust, gated behind the backend-pprof-rs Cargo feature (only compiled for linux/macos on x86_64/aarch64). Core dependencies: nix (signal handling), once_cell and spin (lock-free-ish global state), smallvec and tempfile for sample buffering, framehop, object, memmap2, and aligned-vec for stack unwinding and binary parsing, and symbolic-demangle for Rust/C++ symbol demangling. It is one of several optional backends (backend-jemalloc is a sibling feature for heap profiling) that plug into the main pyroscope agent crate.
Code Quality
The repository runs cargo fmt --all, cargo clippy --all-targets --all-features -- -D warnings, and cargo test as pre-commit/CI gates (.github/workflows/ci-rust.yml), and errors are modeled with a dedicated Error/Result type in backend/pprofrs/error.rs rather than panics. Test coverage for this specific backend is thin: tests/pprof_backend.rs contains a single smoke test that exercises the initialize/report lifecycle under an allocation workload but makes no assertions on the resulting data (the test’s own comment notes it is “not reliable” and kept only as a smoke test). Naming and module layout are consistent with the rest of the workspace, and unsafe signal/pointer handling is isolated to the profiler internals rather than spread through the backend adapter.
What Makes It Unique
Rather than reimplement a profiler from scratch, this crate is a purpose-built adapter that reuses pprof-rs-style signal-based sampling (originally built for TiKV) and retargets its output specifically at Pyroscope’s stack-trace/report format, giving Rust services drop-in continuous profiling without needing a separate agent process. Its logic has since been folded into the main pyroscope crate as a feature flag, making the standalone crate effectively a stable, independently-versioned distribution of the same profiling backend for consumers who depend on it directly.