Reference

Reference

Server and agent flags, HTTP API endpoints, supported perf events, and the usual things to check when something looks off.

Server CLI

FlagDefaultDescription
--port PORT9999TCP port the agent connects to.
--http-port PORT8080HTTP port for the web UI.
--source-dir DIR.Root of the source tree for line annotation.
--binary PATHUnstripped binary (enables addr2line).
--map PATHGNU ld linker map file (optional symbol fallback).
--path-map FROM=TORewrite compile-time paths (e.g. /build/src=/home/user/src).
--addr2line PATHCustom addr2line binary (overrides bundled and PATH).
--readelf PATHCustom readelf binary.
--toolchain-prefix PREFIXCross-compile prefix (e.g. arm-linux-gnueabihf-). Derives addr2line + readelf.
--sysroot DIRSysroot for resolving shared-library modules and source files.
--max-samples N500000Raw-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/sessionsWhere saved sessions are stored (PERFLENS_HOME moves the whole ~/.perflens root).
--http-bind ADDR127.0.0.1Web 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 SECRETPairing code to present to the agent (or PERFLENS_TOKEN). The Live Debug wizard can supply one per connection instead.
--inline / --no-inlineonEnable/disable inline-function resolution via addr2line -i.
--import FILEImport a perf.data file at startup as a session.

Agent CLI

The agent runs in one of three modes — pick one.

ModeDescription
--listenDaemon: bind --port, wait for the server to connect in via the UI wizard.
--server HOSTDaemon: dial out to the server. Reconnects with exponential backoff.
--output FILEHeadless: collect once, write to file (- for stdout). Requires --pid.

Common options:

FlagDefaultDescription
--pid PIDProcess to profile (required for --output; set via UI wizard in daemon modes).
--port PORT9999TCP port (listen or connect).
--frequency HZ99perf record -F sampling frequency.
--duration SECS8Length of each collection round.
--rounds N1Number of rounds (--output mode).
--bind ADDR0.0.0.0Address to listen on in --listen mode.
--token SECRETPairing 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.
--updateSelf-update from the latest GitHub release, then exit.
--versionPrint version and exit.

HTTP API

EndpointMethodDescription
/api/statusGETServer + agent connection state, sample totals.
/api/streamGETServer-Sent Events: status, agent, data_version (carries the event-type list), perf_stat, metrics (discriminated by payload type).
/api/snapshot?event=GETCached per-event snapshot (gzip); clients fetch it when SSE data_version bumps. Omit event for every event at once.
/api/sessions?offset=&limit=GETList saved sessions, paginated (metadata only).
/api/sessions/<id>GETLazy-replay a session (parses raw chunks on demand, cached).
/api/sessions/<id>DELETEDelete a saved session.
/api/sessions/<id>/export?format=&event=GETExport a session: collapsed stacks, full json, or an svg flame graph.
/api/sessions/importPOSTImport an uploaded perf.data file as a session (needs perf on the server).
/api/live/export?format=&event=GETExport the live in-memory profile in the same three formats.
/api/source?file=&event=&tid=GETAnnotated source for a single file (optionally per-thread).
/api/threads?event=GETThread overview with sample counts and top functions.
/api/threads/<tid>?event=GETPer-thread flame graph + function summary.
/api/window?event=&start=&end=&tid=GETFlame graph + function summary for samples received in a time range (timeline scrubbing).
/api/index/statusGETSource-index / DWARF file-list state (truncated preview).
/api/index/files?offset=&limit=&q=GETPaginated DWARF source-file list.
/api/metrics/currentGETLatest device-health snapshot per type.
/api/metrics/history?type=&start=GETHealth metrics time series.
/api/agentGETAgent connection info (address + hello/platform).
/api/agentDELETEDisconnect the active agent (triggers a normal session save).
/api/agent/connectPOSTConnect out to a --listen agent ({"host": …, "port": …, "token": …}).
/api/agent/commandPOSTRelay a command to the connected agent (start, stop, pause, resume, configure, configure_metrics, list_processes, reprobe, verify_pid, verify_perf, ping, status, update).
/api/wizardGET/PUTPersisted Live Debug wizard state.
/api/browse?path=GETFile picker listing (confined to --browse-root).
/api/configGET/PATCHRuntime binary / source dir / path map / toolchain / sysroot config, as one typed model.
/*GETStatic 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.

EventTypical useMode
cyclesCPU time / hot pathsrecord + stat
instructionsIPC, retired instruction countrecord + stat
cache-missesLast-level cache missesrecord + stat
cache-referencesLLC accessesrecord + stat
branch-missesBranch prediction missesrecord + stat
branch-instructionsTotal branchesrecord + stat
page-faultsMinor/major page faultsstat only
context-switchesScheduling pressurestat only
cpu-migrationsInter-CPU movementstat 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
FlagDescription
--server-url URLPerfLens HTTP API to query (default $PERFLENS_MCP_URL or http://127.0.0.1:8080).
--read-onlyOmit 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