pyroscope-rs
Official Rust agent for Grafana Pyroscope, sampling CPU and memory and streaming pprof-encoded profiles to a Pyroscope server.
Repository Health
Technical Analysis
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.
Used by 2 apps in this directory
PostHog
Analytics · Monitoring · Developer Tools
The all-in-one open source product platform combining analytics, session replay, feature flags, error tracking, AI observability, and a built-in data warehouse in a single self-hostable stack.
Pyroscope
Monitoring · Devops · Developer Tools
An open-source, horizontally scalable continuous profiling platform that pinpoints CPU, memory, and I/O bottlenecks down to the exact line of code, built by Grafana Labs alongside Loki, Tempo, and Mimir.