pyroscope-rs

Official Rust agent for Grafana Pyroscope, sampling CPU and memory and streaming pprof-encoded profiles to a Pyroscope server.

SDK
Cargo
v2.1.1
219stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
76/100Good
Development Activity72
Maintenance80
Community76
Maturity56
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
76/100Good
Architecture80
Code Quality78
Innovation74
Learning Curve70

pyroscope is Grafana Pyroscope’s official Rust integration: a background agent that samples a running process on a timer, batches the resulting stack data, and pushes it to a Pyroscope server as continuous profiling data. It ships with a pluggable Backend trait so sampling strategy is swappable behind Cargo feature flags: a vendored pprof-rs-derived CPU sampler (backend-pprof-rs) and a jemalloc-based heap/allocation profiler (backend-jemalloc) are both built in.

The agent is built around PyroscopeAgentBuilder, which wires a server URL, application name, sample rate, and backend into a typestate-guarded PyroscopeAgent whose start()/stop()/shutdown() calls are only valid in the right order. Internally, a platform-specific Timer thread (kqueue on macOS, epoll on Linux, a sleep-based fallback elsewhere) drives sampling, a SessionManager batches reports off a channel, and the session layer encodes them to protobuf-based pprof payloads (via prost), gzip-compresses them, and posts them with reqwest.

What You Get

  • A PyroscopeAgentBuilder that wires server URL, application name, sample rate, tags, and a chosen backend into a ready-to-run agent
  • A typestate-guarded agent API where start()/stop()/shutdown() are only callable in valid states, catching lifecycle misuse at compile time
  • A pluggable Backend trait with two built-in implementations selectable via Cargo features: backend-pprof-rs (CPU sampling) and backend-jemalloc (heap/allocation profiling)
  • Platform-specific sampling timers (kqueue on macOS, epoll on Linux, a portable sleep fallback elsewhere) driving consistent sample intervals
  • Built-in pprof protobuf encoding (via prost) and gzip compression for pushing profiles to a Pyroscope server over HTTP with reqwest

Common Use Cases

  • Running always-on CPU profiling in a production Rust service and viewing flame graphs in Grafana Pyroscope
  • Adding heap/allocation profiling via the jemalloc backend to track memory growth over time
  • Comparing CPU or memory profiles across deploys to catch performance regressions before they page someone
  • Standardizing profiling across a polyglot fleet where other services already report to Pyroscope from Python, Ruby, or Go agents

Under The Hood

Architecture The agent is layered around a typestate-driven PyroscopeAgent (src/pyroscope.rs): PyroscopeAgentBuilder assembles a PyroscopeConfig plus a chosen Backend implementation, and PhantomData markers encode agent state (ready/running) so start()/stop()/shutdown() are only callable in the correct order. A platform-specific Timer (src/timer/kqueue.rs, epoll.rs, sleep.rs) runs on its own thread and signals the backend to sample on each tick; captured Report data flows over an mpsc channel into a SessionManager (src/session.rs), which batches samples into a Session, encodes them via encode/pprof.rs and the generated protobuf types under encode/gen, gzip-compresses the payload with libflate, and posts it to the Pyroscope server with reqwest. Sampling strategy itself is fully pluggable: backend/pprof.rs and backend/jemalloc.rs implement the same Backend trait behind Cargo feature flags, so swapping CPU vs. memory profiling is a dependency-config change, not a rewrite.

Tech Stack Rust edition 2021, built on reqwest (with rustls or native-tls selectable via features) for HTTP delivery, prost for protobuf-encoded pprof payloads, libflate for gzip compression, uuid and thiserror for identifiers and typed errors. CPU sampling no longer depends on the external pprof-rs crate directly; the sampler is vendored in-tree under src/backend/pprofrs (backtrace, framehop, symbolic-demangle, memmap2, findshlibs, aligned-vec) behind the backend-pprof-rs feature, while backend-jemalloc pulls in jemalloc_pprof for heap profiling. Timer implementations are conditionally compiled per target OS. The crate is published to crates.io and docs.rs with CI defined under ci/.

Code Quality Integration coverage lives under tests/ (agent.rs, session.rs, timer.rs, timer-epoll.rs, pprof_backend.rs) plus a backend-local tests.rs, exercising the agent lifecycle, session batching, and platform timers rather than relying only on unit tests. Errors are typed and propagated through a dedicated PyroscopeError/Result (src/error.rs) instead of panicking, and the typestate pattern turns a class of lifecycle bugs into compile errors. Module-level rustdoc comments include runnable, testable code examples for the primary agent-setup flow. Formatting is enforced via a repo-root rustfmt.toml and CI.

API Design Getting a profiler running is a single fluent builder chain — PyroscopeAgentBuilder::new(url, app_name, sample_rate, spy_name, spy_version, backend).build()? — with the most commonly used types re-exported at the crate root (pyroscope::PyroscopeAgent, pyroscope::Result). Switching between CPU and memory profiling backends is a Cargo feature flip plus a different backend constructor rather than a different integration path, which keeps the common case low-boilerplate; deeper backend tuning (e.g. PprofConfig, jemalloc setup) does require reading into feature-specific submodules.

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