Production-grade distributed profiler for CPU & GPU workloads built with Rust and eBPF
Aperture is a low-overhead profiling system that uses eBPF to collect performance data directly from the Linux kernel. It captures CPU stack traces, lock contention events, and syscall latencies — then aggregates, stores, and visualizes them through an interactive web dashboard.
The system follows an agent-aggregator architecture: lightweight agents run on target hosts collecting profiling data with less than 1% overhead, while a central aggregator service receives, stores, and serves the data via gRPC and REST APIs.
| Capability | How it works |
|---|---|
| CPU Profiling | perf_event software CPU clock sampling at configurable frequency (default 99 Hz). Captures both user and kernel stack traces. |
| Lock Contention | Traces futex WAIT/WAKE operations via sys_enter_futex/sys_exit_futex tracepoints. Measures actual wait duration per lock address. |
| Syscall Tracing | Raw tracepoints on sys_enter/sys_exit for all syscalls. Tracks per-syscall latency distributions, error rates, and call counts. |
| Symbol Resolution | Automatic kernel + userspace symbol resolution using blazesym. Resolves /proc/kallsyms for kernel and /proc/PID/maps + DWARF for userspace. |
| Distributed Aggregation | gRPC transport with authentication, in-memory ring buffer, and optional ClickHouse persistence. Differential profiling across time windows. |
| Web Dashboard | React-based UI with interactive flamegraphs, top functions table, syscall analysis, differential profiling, timeline view, and alert management. |
| Alert Engine | Threshold-based alerts on buffer utilization, push errors, ClickHouse flush failures, and event throughput. REST API for rule CRUD and evaluation. |
| Data Export | JSON download and Brendan Gregg collapsed-stack format — compatible with flamegraph.pl, speedscope, Grafana Pyroscope. |
| WASM Filters | Programmable event filtering with WebAssembly (wasmtime). Fuel-limited execution, sandboxed memory, no host access. |
| Prometheus Metrics | Built-in /metrics endpoint exposing push rates, buffer state, ClickHouse flush stats, and more. |
Target Host(s) Aggregator
┌──────────────────────────┐ gRPC ┌────────────────────────┐
│ ┌────────────────────┐ │──────────────▶│ gRPC Server (:50051) │
│ │ eBPF Programs │ │ │ HTTP/REST API (:9090) │
│ │ cpu-profiler │ │ │ In-Memory Buffer │
│ │ lock-profiler │ │ │ Alert Engine │
│ │ syscall-tracer │ │ │ ClickHouse (optional) │
│ └────────┬───────────┘ │ └───────────┬────────────┘
│ ▼ │ │
│ ┌────────────────────┐ │ │ REST API
│ │ aperture-agent │ │ ▼
│ │ Symbol Resolver │ │ ┌────────────────────────┐
│ │ WASM Filter (opt) │ │ │ Web Dashboard (React) │
│ └────────────────────┘ │ │ Flamegraphs, Top Fn, │
└──────────────────────────┘ │ Syscalls, Diff, Alerts│
└────────────────────────┘
- Linux with kernel 5.10+ (for eBPF) — agents run on Linux only
- Root access on the profiling host (eBPF requires
CAP_BPFor root) - Rust stable toolchain (the aggregator, CLI, and shared crates build on any platform)
- Rust nightly +
rust-srccomponent (for eBPF programs only) - Node.js 18+ (for the web dashboard)
The easiest way to get everything running:
git clone https://github.com/hamzzy/Aperture.git
cd aperture
docker compose up -d # Starts ClickHouse + Aggregator + AgentThis brings up:
- ClickHouse on
:8123(HTTP) and:9000(native) - Aggregator on
:9090(REST API) and:50051(gRPC) - Agent pushing CPU profiles to the aggregator
Then start the dashboard:
cd ui && npm install && npm run dev
# Open http://localhost:5173curl -fsSL https://raw.githubusercontent.com/hamzzy/aperture/main/scripts/install.sh | bashDownloads the latest release binaries for your architecture (x86_64 or aarch64) and installs to /usr/local/bin.
git clone https://github.com/hamzzy/aperture.git
cd aperture
# Build aggregator + CLI (any platform)
cargo build --release --bin aperture-aggregator --bin aperture-cli
# Build eBPF programs (Linux, requires nightly)
rustup install nightly && rustup component add rust-src --toolchain nightly
cargo +nightly build -Zbuild-std=core --target bpfel-unknown-none \
--bin cpu-profiler --bin lock-profiler --bin syscall-tracer --release
# Build agent (Linux only)
cargo build --release --bin aperture-agentGenerate a flamegraph SVG without any aggregator:
sudo ./target/release/aperture-agent --mode cpu --duration 30s --output flamegraph.svg# Terminal 1: Start ClickHouse
docker compose up -d clickhouse
# Terminal 2: Start aggregator
export APERTURE_CLICKHOUSE_ENDPOINT="http://127.0.0.1:8123"
export APERTURE_CLICKHOUSE_DATABASE="aperture"
cargo run --release -p aperture-aggregator --features clickhouse-storage
# Terminal 3: Start agent (Linux, requires root)
sudo ./target/release/aperture-agent \
--mode all \
--aggregator http://localhost:50051 \
--duration 1h
# Terminal 4: Start dashboard
cd ui && npm install && npm run dev# CPU profiling at 99 Hz, system-wide
sudo aperture-agent --mode cpu --duration 30s --aggregator http://HOST:50051
# CPU profiling for a specific process
sudo aperture-agent --mode cpu --pid 1234 --duration 5m --aggregator http://HOST:50051
# Lock contention tracing
sudo aperture-agent --mode lock --duration 30s --aggregator http://HOST:50051
# Syscall latency tracing
sudo aperture-agent --mode syscall --duration 30s --aggregator http://HOST:50051
# All modes simultaneously
sudo aperture-agent --mode all --duration 1h --aggregator http://HOST:50051
# Local flamegraph (no aggregator)
sudo aperture-agent --mode cpu --duration 30s --output flamegraph.svg| Mode | Flag | What it collects |
|---|---|---|
| CPU | --mode cpu |
Stack traces via perf_event sampling (default 99 Hz) |
| Lock | --mode lock |
Futex wait/wake events with hold durations |
| Syscall | --mode syscall |
Per-syscall latency, error codes, call counts |
| All | --mode all |
All three modes running concurrently |
# Query the in-memory buffer
aperture-cli query --endpoint http://127.0.0.1:50051 --limit 10
# Aggregate CPU events from storage
aperture-cli aggregate --endpoint http://127.0.0.1:50051 --event_type cpu --limit 100
# Differential profiling (compare two time windows)
aperture-cli diff --endpoint http://127.0.0.1:50051 --event_type cpu --limit 100| Variable | Default | Description |
|---|---|---|
APERTURE_AUTH_TOKEN |
— | Bearer token for gRPC authentication |
APERTURE_CLICKHOUSE_ENDPOINT |
— | ClickHouse HTTP URL (enables persistence) |
APERTURE_CLICKHOUSE_DATABASE |
aperture |
ClickHouse database name |
APERTURE_CLICKHOUSE_PASSWORD |
— | ClickHouse password |
APERTURE_ADMIN_LISTEN |
0.0.0.0:9090 |
HTTP admin/API bind address |
APERTURE_AGGREGATOR_LISTEN |
0.0.0.0:50051 |
gRPC bind address |
Full documentation is available at the Docusaurus site (cd docs-site && npm start):
- Getting Started — Installation, first profile, full stack setup
- Architecture — System design, data flow, eBPF programs, BPF maps
- API Reference — REST endpoints, gRPC RPCs, Prometheus metrics
- Run Examples — Docker, OrbStack, CLI usage scenarios
- Symbol Resolution — Fixing unresolved hex addresses
- WASM Filters — Writing custom event filters
- Kubernetes — DaemonSet + Deployment manifests
- Alerting — Threshold rules, evaluation, REST API
- Roadmap — Roadmap and future plans
Contributions are welcome. Please read CONTRIBUTING.md before submitting a pull request.
Quick version:
- Fork the repo and create a feature branch from
main - Make your changes — run
cargo fmt,cargo clippy, andcargo testbefore committing - Open a pull request with a clear description of what changed and why
See CONTRIBUTING.md for the full development setup, coding standards, and PR process.
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Built with:
