Reference
Server and agent flags, HTTP API endpoints, supported perf events, and the usual things to check when something looks off.
Server CLI
| Flag | Default | Description |
|---|---|---|
--port PORT | 9999 | TCP port the agent connects to. |
--http-port PORT | 8080 | HTTP port for the web UI. |
--source-dir DIR | . | Root of the source tree for line annotation. |
--binary PATH | — | Unstripped binary (enables addr2line). |
--map PATH | — | GNU ld linker map file (optional symbol fallback). |
--path-map FROM=TO | — | Rewrite compile-time paths (e.g. /build/src=/home/user/src). |
--addr2line PATH | — | Custom addr2line binary (overrides bundled and PATH). |
--readelf PATH | — | Custom readelf binary. |
--toolchain-prefix PREFIX | — | Cross-compile prefix (e.g. arm-linux-gnueabihf-). Derives addr2line + readelf. |
--sysroot DIR | — | Sysroot for resolving shared-library modules and source files. |
--max-samples N | 500000 | Raw-sample ring buffer cap (aggregates always cover the full session). Roughly 1.7 KB of RSS per retained sample, so the default plateaus near 1.1 GB on a busy multi-threaded target. |
--sessions-dir DIR | ~/.perflens/sessions | Where saved sessions are stored (PERFLENS_HOME moves the whole ~/.perflens root). |
--http-bind ADDR | 127.0.0.1 | Web UI bind address (0.0.0.0 to expose — the UI has no auth). |
--browse-root DIR | ~ | Directory the wizard file picker is confined to. |
--token SECRET | — | Pairing code to present to the agent (or PERFLENS_TOKEN). The Live Debug wizard can supply one per connection instead. |
--inline / --no-inline | on | Enable/disable inline-function resolution via addr2line -i. |
--import FILE | — | Import a perf.data file at startup as a session. |
Agent CLI
The agent runs in one of three modes — pick one.
| Mode | Description |
|---|---|
--listen | Daemon: bind --port, wait for the server to connect in via the UI wizard. |
--server HOST | Daemon: dial out to the server. Reconnects with exponential backoff. |
--output FILE | Headless: collect once, write to file (- for stdout). Requires --pid. |
Common options:
| Flag | Default | Description |
|---|---|---|
--pid PID | — | Process to profile (required for --output; set via UI wizard in daemon modes). |
--port PORT | 9999 | TCP port (listen or connect). |
--frequency HZ | 99 | perf record -F sampling frequency. |
--duration SECS | 8 | Length of each collection round. |
--rounds N | 1 | Number of rounds (--output mode). |
--bind ADDR | 0.0.0.0 | Address to listen on in --listen mode. |
--token SECRET | — | Pairing code the server must present before any command runs (or PERFLENS_TOKEN). Generated and written to the agent log in --listen mode when not supplied. Never sent over the wire. |
--update | — | Self-update from the latest GitHub release, then exit. |
--version | — | Print version and exit. |
HTTP API
| Endpoint | Method | Description |
|---|---|---|
/api/status | GET | Server + agent connection state, sample totals. |
/api/stream | GET | Server-Sent Events: status, agent, data_version (carries the event-type list), perf_stat, metrics (discriminated by payload type). |
/api/snapshot?event= | GET | Cached per-event snapshot (gzip); clients fetch it when SSE data_version bumps. Omit event for every event at once. |
/api/sessions?offset=&limit= | GET | List saved sessions, paginated (metadata only). |
/api/sessions/<id> | GET | Lazy-replay a session (parses raw chunks on demand, cached). |
/api/sessions/<id> | DELETE | Delete a saved session. |
/api/sessions/<id>/export?format=&event= | GET | Export a session: collapsed stacks, full json, or an svg flame graph. |
/api/sessions/import | POST | Import an uploaded perf.data file as a session (needs perf on the server). |
/api/live/export?format=&event= | GET | Export the live in-memory profile in the same three formats. |
/api/source?file=&event=&tid= | GET | Annotated source for a single file (optionally per-thread). |
/api/threads?event= | GET | Thread overview with sample counts and top functions. |
/api/threads/<tid>?event= | GET | Per-thread flame graph + function summary. |
/api/window?event=&start=&end=&tid= | GET | Flame graph + function summary for samples received in a time range (timeline scrubbing). |
/api/index/status | GET | Source-index / DWARF file-list state (truncated preview). |
/api/index/files?offset=&limit=&q= | GET | Paginated DWARF source-file list. |
/api/metrics/current | GET | Latest device-health snapshot per type. |
/api/metrics/history?type=&start= | GET | Health metrics time series. |
/api/agent | GET | Agent connection info (address + hello/platform). |
/api/agent | DELETE | Disconnect the active agent (triggers a normal session save). |
/api/agent/connect | POST | Connect out to a --listen agent ({"host": …, "port": …, "token": …}). |
/api/agent/command | POST | Relay a command to the connected agent (start, stop, pause, resume, configure, configure_metrics, list_processes, reprobe, verify_pid, verify_perf, ping, status, update). |
/api/wizard | GET/PUT | Persisted Live Debug wizard state. |
/api/browse?path= | GET | File picker listing (confined to --browse-root). |
/api/config | GET/PATCH | Runtime binary / source dir / path map / toolchain / sysroot config, as one typed model. |
/* | GET | Static files from ui/. |
Every failure renders as {"error": {"code": …, "message": …}}
with a real status code — 400 validation, 403 permission, 404 missing,
409 wrong server state, 413 too large, 502 agent transport. The schema is
published at /api/openapi.json.
Supported perf events
The agent probes each event before use and only emits the ones the kernel
actually supports. On hybrid CPUs, events are reported per-cluster —
e.g. cpu_core/cycles/ and cpu_atom/cycles/, and
never a bare cycles. The names the agent's start
response lists are the ones it asked for, so on that hardware they
are not the keys the data arrives under: read the real names from the SSE
data_version stamp's event_types. The server will
resolve a bare name onto the per-cluster ones when that is unambiguous, and
name the candidates when it is not.
| Event | Typical use | Mode |
|---|---|---|
cycles | CPU time / hot paths | record + stat |
instructions | IPC, retired instruction count | record + stat |
cache-misses | Last-level cache misses | record + stat |
cache-references | LLC accesses | record + stat |
branch-misses | Branch prediction misses | record + stat |
branch-instructions | Total branches | record + stat |
page-faults | Minor/major page faults | stat only |
context-switches | Scheduling pressure | stat only |
cpu-migrations | Inter-CPU movement | stat only |
MCP server
perflens mcp serves profiling data to LLM agents over the
Model Context Protocol, so an agent can answer “why is this slow”
against real perf data and drive a live run on a device. It is
a client of the HTTP API above, so a perflens serve must be
running. The SDK is an optional dependency:
pip install 'perflens[mcp]' # or: uv tool install 'perflens[mcp]'
claude mcp add perflens -- perflens mcp
| Flag | Description |
|---|---|
--server-url URL | PerfLens HTTP API to query (default $PERFLENS_MCP_URL or http://127.0.0.1:8080). |
--read-only | Omit the agent-control and export tools, so the agent can analyse but cannot touch a device or write files. |
Nineteen tools. Analysis (read-only): perflens_status,
perflens_list_sessions, perflens_hot_functions,
perflens_hot_stacks, perflens_perf_stat,
perflens_list_source_files, perflens_source_hotlines,
perflens_compare, perflens_threads,
perflens_thread_detail, perflens_device_metrics,
perflens_metrics_history. Device control:
perflens_agent_info, perflens_agent_connect,
perflens_list_processes, perflens_start_profiling,
perflens_stop_profiling, perflens_collection_pause.
Plus perflens_export, which writes collapsed stacks / JSON / SVG
to a file.
Profiles are large — a single event’s snapshot runs from kilobytes
to megabytes — so every tool returns a ranked, capped view and tells the
agent exactly how to page for more. Config mutation, session deletion and the
file browser are deliberately not exposed. The companion skill at
skills/perflens-profiling/ teaches the profiling method the tools
support; install it with
cp -r skills/perflens-profiling ~/.claude/skills/.
Building release packages
./build_package.sh # server wheel/sdist + native C agent
./build_package.sh --server # Python wheel + sdist only
./build_package.sh --agent-c # C agent only (native static binary)
Outputs land in dist/:
dist/
├── perflens-<ver>-py3-none-any.whl # server (uvx / pipx / pip)
├── perflens-<ver>.tar.gz # server sdist
├── perflens-agent-c-<ver>.tar.gz # agent tarball
└── perflens-agent-linux-<arch> # agent raw binary (stable name)
CI
.github/workflows/test.yml runs the pytest suite on
Python 3.10–3.13 (parser, aggregator differentials against
device-captured fixtures, source mapper, HTTP API, MCP tools, provisioning
against a fake release server, and the C-agent wire protocol driven
through a fake framing server with a perf shim), plus a
frontend job: OpenAPI schema drift check, vitest unit tests, and a
self-contained Playwright browser E2E that replays a fixture session
through the real UI.
.github/workflows/build.yml lints (ruff),
runs the tests, builds and smoke-runs the Python wheel, builds the
static C agent for five architectures (x86_64, aarch64, aarch64_be,
armv7, armeb), and builds static addr2line/readelf tools bundles
for perflens provision. Big-endian agent targets use
musl toolchains from musl.cc since Ubuntu only ships
little-endian sysroots. Tagged pushes (v*) create a
GitHub Release and attach all artifacts — including raw
perflens-agent-linux-<arch> binaries with stable
names that install-agent.sh and the agent's
--update fetch — and publish the package to
PyPI via Trusted Publishing (OIDC, no stored tokens).
Troubleshooting
perf_event_paranoid too high
The agent warns at startup if /proc/sys/kernel/perf_event_paranoid > 1 and the UI may show a limited event set.
sudo sysctl -w kernel.perf_event_paranoid=1
No function names
Compile with -g and don't strip. file ./myprogram should say not stripped and with debug_info.
No source line mapping
Double-check --binary points at the exact unstripped binary running on the target and --source-dir contains the source files. If the build root differs from your checkout, rewrite paths with --path-map /build/src=/home/me/src.
Agent can't connect
The server must be reachable on --port. Sanity-check with nc -zv <server-ip> 9999.
Container: one of the two perf record modes fails
Which one depends on the container, so probe rather than assume. Some
environments strip the perf capability set: -p <pid>
returns empty and a system-wide perf record -a works. An
unprivileged LXC container is the opposite case — at
perf_event_paranoid=1, per-PID recording works and
-a fails outright with “Failure to open any events for
recording”. perf_event_paranoid is not namespaced, so it
is read-only from inside the container and lowering it requires the host.
Call-graph probing hangs / slow startup
Capability probing tests fp, dwarf, then lbr in sequence — this adds ~10–20 s on a typical target (longer on slow or hybrid-CPU hardware) on first connection. One-time cost; subsequent rounds skip probing.
Project layout
perflens/
├── install-agent.sh # curl-able agent installer (no sudo)
├── agent-c/
│ ├── src/ # C agent modules (agent.h + 10 .c files)
│ ├── Makefile # native + cross-compile targets
│ └── vendor/zstd/ # vendored zstd amalgamation
├── pyproject.toml # pip/uv package (console script: perflens)
├── src/perflens/ # the server package
│ ├── app.py # AppContext + lifecycle + main()
│ ├── config.py # ServerConfig, CLI parsing, tool probing
│ ├── state.py # profiling/metrics state + rebuild worker
│ ├── agentlink.py # agent TCP wire protocol + session
│ ├── sessions.py # persistence, replay, perf.data import
│ ├── web.py # FastAPI/uvicorn HTTP layer + SSE hub
│ ├── api/ # Pydantic v2 schemas + response helpers
│ ├── mcp/ # MCP server (optional [mcp] extra)
│ ├── cli.py # serve/import/push-agent/provision/mcp
│ ├── parser.py # perf script / perf stat parser
│ ├── aggregator.py # incremental per-event aggregation
│ ├── source_mapper.py # addr2line pipeline + path remap
│ ├── symcache.py # persistent caches (~/.perflens/cache)
│ ├── provision.py # user-space static-tools download
│ └── ui/ # built React app (Vite output, ships in the wheel)
├── frontend/ # React 19 + TypeScript + Vite SPA source
│ ├── src/ # api client, stores, flamegraph, components
│ ├── e2e/ # Playwright browser E2E (self-contained)
│ └── openapi.json # committed schema (CI drift-checked)
├── skills/perflens-profiling/ # agent skill for the MCP server
├── tests/
│ ├── test_*.py # pytest (parser, aggregator, HTTP, MCP, agent, ...)
│ ├── fixtures/ # gzipped perf sessions from real devices
│ ├── sample_workload.c
│ └── Makefile
├── build_package.sh
├── .github/workflows/test.yml # pytest matrix + vitest + playwright e2e
└── .github/workflows/build.yml # lint + test + wheel + agents + release