Drop a tiny agent on any Linux box, point it at a PID, and watch flame
graphs, function tables, perf stat metrics, and line-level
annotated source update live — no Node, no Docker,
no sudo.
Counts update as each perf record round streams in. Flip to flame graph, click a function, land in source with line-level heat. Zero polling — Server-Sent Events.
perf pipeline.
Remote perf record, real-time SSE streaming, flame graphs,
per-thread analysis, and source-level heat — without leaving the browser.
The agent runs perf record in 8-second rounds. Each round is zstd-compressed and pushed over TCP. Browser sees flame graphs update as new data arrives.
Hand-rolled SVG flame graphs — no charting library. Zoom into a frame, hover for sample counts, search by function name, breadcrumb back to root.
addr2line pipelined in batches of 500. Hot lines are heat-colored red/amber/green so you spot the cost without leaving the file.
Snapshot a baseline or pick a saved session, and the flame graph recolors by change — red grew, blue shrank — while the function table shows per-function Δ. Did-my-fix-help in one glance.
Drag across a Device Health sparkline — a CPU spike, say — and the flame graph and function table rebuild from only the samples collected in that window.
Filter flame graphs, function tables, and source annotations by thread. A dedicated Threads tab shows per-tid CPU breakdowns, top functions, and an optional real-time Live CPU column fed by the agent.
Single static C agent binary (~2 MB, vendored zstd, zero runtime deps) — runs on anything from bare-metal embedded boards to servers. One-line curl install, built-in self-update.
Agent enumerates which perf events the kernel actually supports, tries call-graph modes (fp → dwarf → lbr), and picks the first that produces non-empty stacks.
One --toolchain-prefix derives addr2line and readelf. --sysroot resolves shared libraries and source files under a target tree, like perf --symfs.
Every session is saved as raw chunks on disk. Replay any past session lazily through the UI — or import a perf.data file directly with --import.
perflens mcp exposes the profile to any MCP client — hot functions, hot stacks, IPC and miss rates, source hot lines, session comparison, per-thread breakdowns — so an agent can find the bottleneck and, when asked, drive a live run on the device. See the Reference.
--server: agent dials out to the server (reconnects with backoff). --listen: agent waits, server connects in through the UI's Live Debug wizard.
Every screenshot below is captured from the real UI, by a Playwright harness
that CI re-runs on every change — a live 199 Hz profile of
tests/matrixlab, a 25-thread test workload, plus deterministic
shots replayed from the committed regression fixtures.
perf.data.
1–5 for tabs, / to search,
t for theme, ? for this overlay.
The server installs from PyPI with uvx/pipx/pip;
static agent binaries are attached to every release. Pick the
install flavor and follow three steps.
Python 3.10+, no sudo, everything user-space. pipx install perflens or pip install --user perflens work too.
uvx perflens serve \
--source-dir /path/to/sources \
--binary /path/to/unstripped-binary
# UI: http://localhost:8080
The agent is one static binary with zero dependencies — one-line install, no sudo. Two connect modes are supported.
curl -fsSL https://raw.githubusercontent.com/harshithsunku/perflens/master/install-agent.sh | sh
# Agent dials out to the server (with reconnect/backoff)
~/.perflens/bin/perflens-agent --server <server-ip>
# Or — agent listens, you connect from the UI's Live Debug wizard
~/.perflens/bin/perflens-agent --listen
Browse to http://<server-ip>:8080. With --server the UI switches as soon as the agent connects; with --listen, click Live Debug and point at the agent.
Zero runtime dependencies; vendored zstd; cross-compiles from a single Makefile.
cd agent-c
make # native x86_64
make CROSS=aarch64-linux-gnu- # ARM64 little-endian
make CROSS=aarch64_be-linux-musl- # ARM64 big-endian
make CROSS=arm-linux-gnueabihf- # ARMv7 little-endian
make CROSS=armeb-linux-musleabihf- # ARMv7 big-endian
The output is one ~2 MB binary. No libc surprises, no Python needed.
scp perflens-agent user@device:/tmp/
ssh user@device /tmp/perflens-agent --server <server-ip>
Cross-compiles for five architectures; prebuilt binaries are attached to every release.
git clone https://github.com/harshithsunku/perflens.git
cd perflens
uv venv && uv pip install -e .
.venv/bin/perflens serve \
--source-dir /path/to/source \
--binary /path/to/myprogram \
--port 9999 \
--http-port 8080
curl -fsSL https://raw.githubusercontent.com/harshithsunku/perflens/master/install-agent.sh | sh
~/.perflens/bin/perflens-agent --server <server-ip>
http://<server-ip>:8080That's it. The UI auto-switches into the profiling view when samples start flowing.
perf;
local needs Python 3.10+ and uv/pip.
addr2line/readelf come from binutils — if
missing, perflens provision downloads static builds into
~/.perflens/bin (no sudo). For source-level annotation,
your binary must be compiled with -g and not stripped.
perf record on the target. The agent flattens the trace
with perf script, compresses with zstd, frames with a
5-byte header, and pushes over TCP. The server decompresses, parses
per-event sample lists, builds flame graph trees, pipes addresses
through addr2line in batches, and broadcasts the result
to every connected browser via Server-Sent Events.
Typical zstd ratio on real perf script output: 20–40×.
Read the architecture →A small, deliberate user-space stack — FastAPI/uvicorn/orjson/zstandard, installed with uvx. The React UI ships prebuilt inside the wheel, so Node is a build-time dependency only. No sudo, no Docker.
perf script format drifts across kernel versions. The parser handles 2.6 through 6.x output with optional [cpu], pid/tid, and flags fields.
If a piece of code doesn't earn its complexity, it gets cut. The agent stays zero-dependency C; the server keeps a four-package install.
MIT-licensed. No proprietary names, IPs, credentials, or company-specific anything in the code or docs.