Skip to main content

ADB-based Android performance metric recorder with live TUI and HTML report

Project description

androidperf

A CLI + web UI that records Android app runtime performance over ADB — CPU, RAM, network, FPS/jank, battery, thermal, threads, disk I/O, GC events, and screen transitions — draws a live terminal dashboard while it runs, drops a self-contained HTML report at the end, and automatically surfaces plain-English insights with fix recommendations.

┌── androidperf · com.example.app · Pixel 7 (sdk 34)     samples 42  elapsed 00:41 ──┐
│ screen HomeActivity     battery 87% charging 31°C      thermal moderate skin 35°C  │
├──── CPU ────┬── Memory ─────────┬──── Network ────┬──── FPS ────┐
│             │                   │                 │             │
│   19.2 %    │  Total PSS 199 MB │   ↓ rx 5 KB/s   │  fps  58.2  │
│    ▁▂▄▆█    │  Java       39 MB │   ↑ tx 2 KB/s   │  jank 3.1 % │
│             │  Native     44 MB │                 │  p95    18  │
│ per-process │  Graphics   20 MB │       ▁▂        │     █▆▄▂▁   │
│     CPU     │         ▁▂        │       ▁▂        │             │
└─────────────┴───────────────────┴─────────────────┴─────────────┘
                             Ctrl+C to stop

Requirements

Python 3.11 or newer
adb Android Platform Tools on $PATH. macOS: brew install --cask android-platform-tools. Debian/Ubuntu: sudo apt install adb. Windows/other: https://developer.android.com/tools/releases/platform-tools. Verify with adb version.
Android device Physical device or emulator running Android 7.0 (SDK 24) or newerdumpsys gfxinfo framestats needs SDK 24+.
USB debugging Enabled in Developer Options, and the host authorized (adb devices shows the serial as device, not unauthorized).
Python deps Installed automatically: adbutils, typer, rich, jinja2, plotly, pandas. Web UI additionally needs fastapi, uvicorn, websockets (pip install androidperf[ui]).

adb is not a Python package — it's a standalone binary. If androidperf devices says "command not found", that's the missing piece.

Install

pipx install androidperf          # recommended — isolated, globally available
pipx install "androidperf[ui]"    # with web UI support

# or
pip install androidperf
pip install "androidperf[ui]"     # with web UI support

From source:

git clone https://github.com/jitinsharma/androidperf.git
cd androidperf

# Option A — globally available, isolated venv managed by pipx:
pipx install .
pipx install --editable .          # if you plan to edit the code

# Option B — a regular venv with the dev extras (pytest, ruff):
python3.11 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

Quickstart

# 1. See connected devices
androidperf devices

# 2. Find the package you want to profile
androidperf packages --filter com.android

# 3. Record interactively (prompts for device + package)
androidperf record

# 4. Record a specific package for 60 s at 1 s cadence
androidperf record --package com.android.settings --interval 1 --duration 60

# 5. Launch the web UI (configure + record + view reports in a browser)
androidperf ui

If --package is omitted, record drops into an interactive prompt: type a substring, pick a number.

Attach to an already-running app instead of launching it:

androidperf record --package com.example.app --no-launch

Stop early with Ctrl+C — the JSON and HTML are still written with whatever samples were collected. After the session, a stat-card panel prints in the terminal, and the HTML report opens the run directory.

Each run writes to runs/<timestamp>-<pkg>/:

  • samples.json — raw time-series data including GC events
  • report.html — single self-contained file, opens offline
  • heap_histogram.json — class instance counts (debug builds / emulators only)
  • heap.hprof — raw heap dump (kept for re-analysis; delete to save space)

What gets measured

Metric Source
CPU % (per-core summed) top -n 1 -b -p <pid>
RAM: PSS / Java / Native / Graphics / Code / Stack dumpsys meminfo <package>
Object counts: Activities, Views, AppContexts dumpsys meminfo <package>
Network rx/tx (per-tick deltas) /proc/net/xt_qtaguid/statsdumpsys netstats on Android 10+
Frame rendering rate, jank %, frame p50/p90/p95/p99 dumpsys gfxinfo <package>
Battery: level, temp, voltage, status dumpsys battery
Thermal: status, skin/cpu/gpu/battery °C dumpsys thermalservice
Thread count (per-process, instantaneous) /proc/<pid>/status
Thread names (library attribution) /proc/<pid>/task/*/status
Context switches, disk I/O (per-tick deltas) /proc/<pid>/io
GC events: reason, freed KB, timestamp logcat -s art (background tail)
Activity transitions dumpsys activity activities
Active fragment (top-level + deepest visible child) dumpsys activity <component> — AndroidX FragmentManager apps only
Heap histogram: class → instance count + shallow bytes am dumpheap + built-in hprof parser (debug builds / emulators only)

CPU is reported as top's raw %CPU — i.e., summed across cores. 200% means the process is using two cores worth of time.

A note on FPS: Android UI rendering is demand-driven. The app submits a frame only when something on screen changes (animation tick, scroll, view invalidation), so frames/sec goes to zero on a static screen — that's correct, not a bug. androidperf reports jank % and p95 frame time as the primary smoothness metrics.

Insights

After each session, androidperf runs an analysis pass over the collected time-series and surfaces findings with plain-English descriptions. Findings appear as severity-coloured cards in the HTML report and in the web UI done panel.

Category What's detected
memory Rapid Java heap growth (linear regression), GC-correlated jank, allocation-pressure GCs, Explicit / NativeAlloc GCs, Activity leak (obj count never decreasing), excessive View counts
cpu Sustained high CPU (>70% in >30% of ticks), thermal throttle jank, network-driven CPU spikes
jank Chronic jank (>10% mean over rendering ticks), disk-write jank correlation
threads Thread count growth / leak (with activity + library attribution), thread storm >100 concurrent
network Chatty network (>80% ticks with traffic), large payload spikes (>1 MB)

Thread findings include library attribution from thread names — e.g. "+12 threads during [NetworkFragment]: OkHttp (8), arch_disk_io (4)" — by diffing the /proc thread-name set between consecutive samples.

Heap histogram

For debug builds and emulators, androidperf automatically captures a heap dump at the end of each session using am dumpheap, parses the hprof binary with a built-in minimal parser (no external dependencies), and includes a searchable class histogram in the HTML report. App-package classes are highlighted.

The histogram shows instance counts and shallow bytes at the moment the session ends. It is most useful when an Activity-leak finding fires — it tells you which Activity subclasses are over-retained.

On release builds and stock Android 10+ devices, am dumpheap requires the app to be debuggable; the heap step is silently skipped and the rest of the report is unaffected.

HTML report

The report opens with a compact screen-timeline swim-lane (Activity + Fragment rows) showing coloured segments across the session. Below that:

  • Insights — severity-coloured finding cards (critical / warning / info).
  • Heap histogram — searchable table of classes by instance count (when available).
  • Metric charts — interactive Plotly charts with unified hover showing the active activity + fragment at each timestamp.
  • Summary stat cards — averages, peaks, network totals, battery delta.

No network required to view the report; plotly.js is inlined.

Web UI

androidperf ui          # default port 8421
androidperf ui --port 9000

Opens a browser at http://localhost:8421. Select a device and package, configure the recording, press Start, watch metrics update in real time, then press Stop. The done panel shows top findings and links to the full HTML report. Past runs are listed in the sidebar.

REST + WebSocket API:

Endpoint Description
GET /api/devices List connected ADB devices
GET /api/packages?serial=S&filter=F List installed packages
GET /api/runs List past runs
GET /api/runs/{id}/report Serve the pre-generated HTML report
GET /api/runs/{id}/samples Raw samples.json payload
GET /api/runs/{id}/insights Insights findings as JSON
GET /api/runs/{id}/heap Heap histogram as JSON
WS /ws/record Start / stream / stop a live recording

Commands

Command Purpose
androidperf devices List connected ADB devices.
androidperf packages [--filter X] [--serial S] [--limit N] List installed packages.
androidperf record --package PKG [...] Record a session; write JSON + HTML.
androidperf report SAMPLES_JSON Regenerate HTML from an existing run.
androidperf ui [--port N] [--runs-dir DIR] Launch the web UI.
androidperf version Print installed version.

Development

.venv/bin/pytest -q     # 60 parser + insight + report tests (no device required)
ruff check src          # lint

Parsers live under src/androidperf/collectors/ and are tested against captured command output in tests/fixtures/. When you hit a device whose output differs, add a fixture file + an assertion and the parser can be updated without any device plugged in.

Layout

src/androidperf/
├── cli.py                  # Typer entry point
├── device.py               # adb detection, package list, app launch
├── session.py              # polling loop, signal handling, JSON + hprof writer
├── summary.py              # shared summary-card computation (HTML + terminal)
├── collectors/
│   ├── cpu.py  memory.py  network.py  fps.py
│   ├── battery.py  thermal.py  activity.py  fragments.py
│   ├── procfs.py           # thread count, context switches, disk I/O, thread names
│   ├── logcat.py           # background ART GC event tail
│   ├── heapdump.py         # am dumpheap capture + adb pull
│   └── hprof_parse.py      # minimal hprof binary parser → class histogram
├── insights/
│   ├── engine.py           # analyze() entry point
│   ├── models.py           # Finding dataclass
│   └── detectors/          # memory, cpu, jank, threads, network
├── ui/                     # live.py (Live dashboard) + summary.py (end-of-run panel)
├── report/                 # Jinja2 + Plotly → self-contained HTML
└── server/                 # FastAPI app, WebSocket streaming, static web UI

See plan/ for architecture and future-direction docs.

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

androidperf-0.2.0.tar.gz (929.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

androidperf-0.2.0-py3-none-any.whl (71.9 kB view details)

Uploaded Python 3

File details

Details for the file androidperf-0.2.0.tar.gz.

File metadata

  • Download URL: androidperf-0.2.0.tar.gz
  • Upload date:
  • Size: 929.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for androidperf-0.2.0.tar.gz
Algorithm Hash digest
SHA256 1438bdd75283c02893539acf9e37bf78aff053b8edde92d6f1595cafb4304cc5
MD5 4bbeb079f207c17f993eb6a766e67dee
BLAKE2b-256 36de07539973c5c80f9b6d1f73ecdb36fd4b9e9407a3bb05ff6ef64059219d16

See more details on using hashes here.

File details

Details for the file androidperf-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: androidperf-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 71.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for androidperf-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 46830dc16d2b5a46ee6a219cf1ce33de0d11f793d356686bbf4af540f1ee6a57
MD5 1836ccc29f4164cade5498a2f0ba2c25
BLAKE2b-256 592a1bd556138ce538bb1e007bfb05f47b3406537f9008a5debc3e5e4124bd66

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page