{"id":50794172,"url":"https://github.com/singhpratech/gpuviewer","last_synced_at":"2026-06-12T13:30:19.461Z","repository":{"id":363113608,"uuid":"1261433121","full_name":"singhpratech/gpuviewer","owner":"singhpratech","description":"The GPU flight recorder — cross-vendor GPU monitor with always-on history, narrated throttle/OOM/hang causes, and scroll-back replay. NVIDIA/AMD/Intel, Linux, pure Rust.","archived":false,"fork":false,"pushed_at":"2026-06-07T14:52:47.000Z","size":315,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-07T15:03:54.902Z","etag":null,"topics":["amd","gpu","gpu-monitoring","intel","linux","monitoring","nvidia","nvml","observability","ratatui","rust","tui"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/singhpratech.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-06T17:17:16.000Z","updated_at":"2026-06-07T14:56:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/singhpratech/gpuviewer","commit_stats":null,"previous_names":["singhpratech/gpuviewer"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/singhpratech/gpuviewer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/singhpratech%2Fgpuviewer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/singhpratech%2Fgpuviewer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/singhpratech%2Fgpuviewer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/singhpratech%2Fgpuviewer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/singhpratech","download_url":"https://codeload.github.com/singhpratech/gpuviewer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/singhpratech%2Fgpuviewer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34247459,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amd","gpu","gpu-monitoring","intel","linux","monitoring","nvidia","nvml","observability","ratatui","rust","tui"],"created_at":"2026-06-12T13:30:18.447Z","updated_at":"2026-06-12T13:30:19.436Z","avatar_url":"https://github.com/singhpratech.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"docs/assets/icon/gpuviewer.svg\" width=\"128\" alt=\"gpuviewer icon — a dark GPU-chip plate carrying a cyan utilization waveform that peaks and drops, crossed by an orange flight-recorder playhead\"\u003e\n\n# gpuviewer\n\n### — the GPU flight recorder —\n\n**It was already recording. Scroll back to 02:14 — it'll tell you why.**\n\n[![CI](https://github.com/singhpratech/gpuviewer/actions/workflows/ci.yml/badge.svg)](https://github.com/singhpratech/gpuviewer/actions/workflows/ci.yml)\n[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license)\n[![Platform](https://img.shields.io/badge/platform-Linux%20·%20Windows%20·%20macOS-success)](#status)\n[![Vendors](https://img.shields.io/badge/GPUs-NVIDIA%20·%20AMD%20·%20Intel%20·%20Apple-76b900)](#status)\n[![Rust](https://img.shields.io/badge/built%20with-Rust-f74c00?logo=rust\u0026logoColor=white)](#architecture)\n\n\u003c/div\u003e\n\n---\n\nYour training run stalled at 02:14. You were asleep. Every other monitor would have shown\nyou a beautiful live gauge — of a moment that no longer exists.\n\n**gpuviewer** is a GPU monitor — Linux (NVIDIA · AMD · Intel) validated on real hardware;\nWindows and macOS Apple Silicon support newly in-tree, compiles + CI-tested with hardware\nvalidation pending ([status](#status)) — that records persistent,\nper-process history and a narrated event log **just by being open** — no daemon to install,\nno recording you had to remember to start. The next morning you scroll back to the moment\nit happened and read the story: the throttle onset with clock deltas, the VRAM climb with\nan ETA, the process that exited and what it freed. Facts are asserted plainly; inferences\nare always labeled **\"likely\"** and expand to the raw evidence behind them. One\nunprivileged binary.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/assets/replay.svg\" width=\"920\" alt=\"gpuviewer replay view: braille charts, process table, throttling gauges, and the narrated story feed, scrolled back to a throttle onset\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\u003csub\u003e\u003cem\u003eThe built-in demo (\u003ccode\u003egpuviewer demo\u003c/code\u003e, simulated data, labeled as such) opens\nalready scrolled back to the night's last throttle onset — the first thing you see is the answer, not a gauge.\u003c/em\u003e\u003c/sub\u003e\u003c/p\u003e\n\n## Three altitudes on one recording\n\n| | view | question it answers |\n|---|---|---|\n| 🔭 | **Timeline** (`t`) | *\"What did the whole night look like?\"* — hours of history as solid strips, event markers underneath |\n| 🎞️ | **Replay** (`r`, or `Enter` on anything) | *\"What exactly happened at 02:14?\"* — full charts, processes, gauges, story feed at any recorded moment |\n| 📡 | **Live** | *\"What is it doing right now?\"* — and it's recording the other two views while you watch |\n\nYou move between them with single keys: spot the anomaly on the timeline, `Enter` to drill\ninto replay at that exact column, `Esc` back to live. The cursor line always shows the\nnearest recorded event.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/assets/timeline.svg\" width=\"920\" alt=\"gpuviewer timeline view: hours of utilization and VRAM history as solid strips with an event lane and time cursor\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\u003csub\u003e\u003cem\u003eThe timeline zooms from 1h to 7d (\u003ccode\u003e+\u003c/code\u003e/\u003ccode\u003e-\u003c/code\u003e). Each column is the recorded peak —\nand time that wasn't recorded stays \u003cstrong\u003eblank\u003c/strong\u003e, never painted as zero. The footer says exactly how much is real: \"12h (8h recorded)\".\u003c/em\u003e\u003c/sub\u003e\u003c/p\u003e\n\n## What it tells you\n\nExample narrations, in the exact shape the event engine emits them. The tag is the\nconfidence tier: facts are observed state transitions; inferences always say \"likely\" in\nthe sentence itself and carry the raw numbers in an auditable evidence field.\n\n- `[fact]` GPU0 began throttling (thermal) — clocks 2520→1815 MHz\n- `[fact]` GPU0 stopped throttling after 1m 31s\n- `[likely]` GPU0 VRAM 92% and climbing ~270 MiB/min — likely full in ~8 min (largest holder: python pid 4521)\n- `[likely]` GPU0 sat idle 47s while python (pid 4521) stayed attached — likely a dataloader or checkpoint stall\n- `[likely]` GPU0: python (pid 4521) likely hung — held 20.1 GiB for 10m 12s with zero GPU activity, process still alive\n- `[likely]` ollama (pid 7777) loaded 11.3 GiB but GPU1 is ~idle while its CPU runs hot — likely partial CPU offload (model may not fit in VRAM)\n- `[fact]` collection stalled 4.2s — a backend probe blocked; the data gap is recorded, last good frame at 02:14:31\n- `[fact]` python (pid 4521) left GPU0, freeing 21.3 GiB\n\nInference thresholds are deliberately conservative — a hang is only narrated after ten\nunbroken minutes of held VRAM with zero engine activity, and any break in the premise drops\nthe claim silently. A confidently-wrong narration is worse than no narration.\n\n## The morning-after digest\n\nPlain text, no ANSI, paste-able into Slack or a bug report:\n\n```console\n$ gpuviewer report --since 22:00\ngpuviewer report — 2026-06-06 22:00 .. 2026-06-07 08:41 (23 events: 17 facts, 6 inferences)\n\nGPU0 (GeForce RTX 4090): util avg 81% / max 99%, temp max 88°C, mem max 23.3 GiB, throttle buckets 14\nGPU1 (Radeon RX 7900 XTX): util avg 12% / max 87%, temp max 71°C, mem max 12.4 GiB, throttle buckets 0\n\n23:41:07  INFO  [fact]    ollama (pid 7777) attached to GPU1, using 11.3 GiB\n02:14:31  WARN  [fact]    GPU0 began throttling (thermal) — clocks 2520→1815 MHz\n02:14:48  INFO  [likely]  GPU0 sat idle 17s while python (pid 4521) stayed attached — likely a dataloader or checkpoint stall\n02:16:02  INFO  [fact]    GPU0 stopped throttling after 1m 31s\n03:02:11  WARN  [likely]  GPU0 VRAM 92% and climbing ~270 MiB/min — likely full in ~8 min (largest holder: python pid 4521)\n06:58:40  INFO  [fact]    python (pid 4521) left GPU0, freeing 21.3 GiB\n```\n\n## Install\n\n```sh\n# Linux / macOS\ncurl -fsSL https://raw.githubusercontent.com/singhpratech/gpuviewer/main/install.sh | sh\n# Windows (PowerShell)\nirm https://raw.githubusercontent.com/singhpratech/gpuviewer/main/install.ps1 | iex\n```\n\nThe scripts verify the download's SHA256 against the release's checksum file before\ninstalling, drop a single binary in your user bin dir, and never need root/admin. They\nwork once the first release is tagged; until then `cargo build --release` always does.\n\nPackaged builds (Linux `tar.gz`/`.deb`/`.rpm`/AppImage, Windows `zip`, macOS `tar.gz` —\nwith checksums and build-provenance attestations) are produced per release tag. Every\ninstall path, platform note (Windows SmartScreen, macOS quarantine), and verification step\nlives in [`docs/packaging/installing.md`](docs/packaging/installing.md).\n\n## Quick start\n\n```sh\ncargo build --release                # binary at target/release/gpuviewer\ngpuviewer                            # live TUI — already recording\ngpuviewer demo                       # 8h simulated incident, opens at the throttle onset\ngpuviewer report --since 12h         # the digest above, from your real history\ngpuviewer export --since 2h oom.gpvr # shareable incident slice\ngpuviewer view oom.gpvr              # replays anywhere — no GPU required\n```\n\nNo GPU? No problem — `--mock` (also the automatic fallback) runs the full TUI on simulated\nGPUs. Mock data is always labeled \"(mock data)\" and records to a separate database, never\nyour real history.\n\n**Boot-time recording.** \"Always-on\" means by virtue of normal use — there is deliberately\nno daemon. If you want the recording running from login instead, run the headless stream\nunder a systemd user unit; a ready-made example with mild, GPU-safe hardening (and a\ncomment explaining every directive) ships at\n[`docs/packaging/gpuviewer.service`](docs/packaging/gpuviewer.service) —\n`systemctl --user enable --now gpuviewer.service`. The per-database instance lock makes\nthis safe alongside the interactive TUI: while the unit holds the recording, a TUI you\nopen by hand runs live-only (and says so), and `report`/replay/`view` read the same\nhistory concurrently.\n\n**Scripting and agents.** One NDJSON frame per tick plus that tick's narrated events, every\nmetric nullable, versioned and conformance-tested:\n\n```sh\ngpuviewer --json --once              # one frame + its events to stdout, then exit 0\n```\n\nThe stream contract — frames *and* events in one timestamped stream, JSON Schema, written\ncompatibility promise — is [`docs/spec/ndjson-v1.md`](docs/spec/ndjson-v1.md). Events can\nalso drive your own plumbing: `--on-event 'CMD'` runs a command per event with\n`GPV_EVENT_*` in the environment (rate-capped), e.g.\n`--on-event 'curl -s -d \"$GPV_EVENT_TITLE\" ntfy.sh/mytopic'`.\n\n## How it compares\n\nThe honest version: several good tools own one half of the flight-recorder sentence. The\ncombination — always-on by virtue of normal use, per-process, scroll-back replay, narrated\ncauses under a facts-vs-\"likely\" contract, in one unprivileged binary — is the part that's\nours.\n\n| | gpuviewer | nvtop | all-smi | qmassa | LACT | gpud | netdata |\n|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n| Always-on recording, no setup/daemon | ✓ | — | — ¹ | — ¹ | — | ◐ ⁴ | ◐ ³ |\n| Scroll-back replay in-tool | ✓ | — | ✓ ¹ | ✓ ¹ | — | — | ✓ ³ |\n| Narrated causes, facts vs \"likely\" | ✓ | — | — | — | ◐ ² | ◐ ⁴ | — |\n| Per-process attribution (NVIDIA + AMD + Intel incl. xe) | ✓ | ✓ | ◐ ⁵ | ◐ ⁵ | — | — | — |\n| Single unprivileged binary | ✓ | ✓ | ✓ | — ⁶ | — | ◐ ⁴ | — |\n\n¹ all-smi (`record` → `view --replay`) and qmassa (`-t` → `replay`) both ship real TUI\nreplay — **of recordings you explicitly started beforehand**. Great when you knew the\nincident was coming; no help for the one you didn't predict.\n\n² LACT decodes throttle causes on all three vendors and shades them on its charts — in a\nlive GUI with an in-memory window (ephemeral; no persisted event log, no replay).\n\n³ netdata's history is the real thing (per-second, ~14-day default retention, scrub-back\ndashboards) — device-level, as a daemon with per-collector GPU configuration, no per-process\nGPU, and threshold alerts rather than causes.\n\n⁴ gpud ships genuine fact-tier events (Xid, kmsg, hw-slowdown) from a single binary — built\nas a fleet-health daemon, NVIDIA-focused, no scroll-back timeline, no narration.\n\n⁵ all-smi: multi-vendor including Intel i915 + xe, but no AMD. qmassa: AMD + Intel, no\nNVIDIA.\n\n⁶ qmassa's deeper telemetry is sudo-gated.\n\nIf one of those fits your problem better, use it — LACT for fan/OC control, netdata for\nfleet dashboards, gpud for cluster health verdicts. (Cells reflect our reading of each tool\nas of June 2026; corrections welcome.)\n\n## The honesty contract\n\nA flight recorder you can't trust is decoration. Every design decision below exists because\na confidently-wrong number would kill the whole premise:\n\n- **Facts vs inferences.** Every event carries `confidence: fact | likely`. Facts (a\n  throttle bit set, a process gone from the list) are asserted plainly. Inferences (stall,\n  hang, spillover, OOM ETA) always read as hedged and carry an `evidence` field with the raw\n  numbers — visible in the TUI, the digest, and the JSON stream.\n- **\"Utilization\" is duty-cycle, not saturation.** It measures the fraction of time at least\n  one kernel was resident — a GPU at \"100%\" may be nowhere near compute- or bandwidth-bound.\n  It is never presented as capacity used.\n- **Absence is a normal outcome, not an error.** Every metric is nullable. Driver\n  `NOT_SUPPORTED`, missing sysfs/hwmon files, MIG mode, and privilege walls render as\n  \"unavailable\" — never zero, never a crash. Where the reason is knowable it is explained\n  in-UI: on WSL2, per-process GPU attribution is unavailable *at the driver level* and the\n  process pane says so; without root/`CAP_SYS_PTRACE`, fdinfo can only attribute your own\n  processes and the pane shows a \"your processes only\" hint rather than pretending the list\n  is complete.\n- **Unrecorded time renders blank.** On the timeline and every chart, a gap in the recording\n  is a visible hole — never interpolated, never painted as zero activity.\n- **The recorder reports on itself.** A blocked driver probe or missed tick becomes a\n  recorded `[fact]` event (\"collection stalled … the data gap is recorded\") — a hole in the\n  recording never masquerades as the GPU having gone quiet. A quarantined-and-recreated\n  history file is narrated as a history reset, for the same reason.\n- **Polling must not perturb what it measures.** Idle GPUs are polled on a stretched cadence\n  (up to 5× the interval) so monitoring doesn't keep them awake or break GFXOFF; the\n  effective cadence is shown in the footer rather than hidden; `--no-backoff` opts out.\n- **Self-impact is measured, not assumed.** On the dev machine (RTX 4090 Laptop + Intel\n  iGPU, two backends polled at a 1 s interval), 60 s of headless recording costs\n  1.6 CPU-seconds — ≈2.7% of one core — at ~25 MiB RSS, and the full TUI measures\n  the same ≈2.8%. Numbers vary with GPU count, driver, and kernel; remeasure yours with\n  `/usr/bin/time -v gpuviewer --json \u003e /dev/null`.\n- **Mock data is always labeled.** The footer says \"(mock data)\" exactly when the data is\n  mock — including replays of mock recordings — and mock/demo runs record to separate\n  database files, never your real history.\n- **No vendor SDK is hard-linked, no vendor CLI is exec'd.** NVML is loaded at runtime; AMD\n  and Intel are read straight from the kernel's sysfs/fdinfo interfaces.\n\n## The journey\n\nThis project was built research-first, and the research is in the repo:\n\n1. **Study the field before writing code.** [`docs/research/`](docs/research/) holds the\n   June-2026 evidence — the market gap, every competitor's actual capabilities (with issue\n   numbers), the vendor API minefield, and the stack decision record\n   ([`04-synthesis.md`](docs/research/04-synthesis.md)). The architecture wasn't guessed;\n   it was argued in writing.\n2. **Learn from other people's scars.** No vendor SDK is hard-linked because soname churn\n   broke btop on AMD twice. NVML is loaded by its versioned name (`libnvidia-ml.so.1`)\n   because the bare `.so` only exists with the CUDA toolkit installed. Polling is adaptive\n   because monitoring tools have kept GPUs awake (bottom #1291) and broken GFXOFF. WSL2's\n   per-process wall is detected and explained because crashing on it is a known trap\n   (nvtop #459).\n3. **Mock-first, so everything is testable.** The full suite passes on machines with no\n   GPU: a deterministic mock backend implements the same trait as the real ones, sysfs/fdinfo\n   collectors read from committed fixture trees, and the NDJSON contract has a conformance\n   suite that runs the built binary against the spec.\n4. **Ship the recorder, then the narrator, then the time machine.** History rollups came\n   first, then the event engine with the facts-vs-likely contract, then scroll-back replay,\n   then `.gpvr` export, and most recently the zoomed-out timeline — each layer riding on the\n   one before it.\n\n## Status\n\n**Working pre-release (v0.1.0).** Install paths:\n[`docs/packaging/installing.md`](docs/packaging/installing.md).\n\n| Platform | Backends | Tier |\n|---|---|---|\n| Linux | NVIDIA (NVML, runtime-loaded) · AMD (sysfs/`gpu_metrics`/fdinfo) · Intel (i915 + xe) | Developed and validated on real hardware |\n| Windows | NVIDIA (NVML device truth + PDH per-process fill, joined by LUID↔PCI match) · cross-vendor WDDM (PDH: NVIDIA/AMD/Intel device + per-process) | Compiles + CI-tested; hardware validation pending |\n| macOS Apple Silicon | Device-level only — per-process GPU is OS-prohibited and the UI says so, never fakes it | Compiles + CI-tested; hardware validation pending |\n\nShipped — in the binary today:\n\n- **Backends:** NVIDIA (NVML, runtime-loaded — never hard-linked), AMD (sysfs/hwmon +\n  versioned `gpu_metrics` throttle decoders v1.1–v3.0 + fdinfo), Intel (fdinfo in both the\n  i915 and xe dialects + sysfs), deterministic mock fallback. Devices deduped across\n  backends by PCI address.\n- **Always-on recording:** SQLite (WAL) 10s/1m rollups + an append-only event log, with\n  per-process rollups (memory, util, CPU%, container identity when knowable), retention\n  sweeps, and corrupt-database quarantine. Raw 1 Hz samples never touch disk — the live\n  window rides in RAM rings.\n- **Timeline overview:** the whole recording as solid strips, 1h–7d zoom, peak-per-column\n  (labeled as such), event lane, time cursor with nearest-event readout, `Enter` drills into\n  replay at the cursor. Gaps stay blank.\n- **Scroll-back replay:** `r` from the live view, or `Enter` on any event in the story feed\n  to jump straight to it; scrub by 10s/5m; works on your real history, the demo, and\n  exported recordings.\n- **Narrated events:** throttle onset/recovery with clock deltas, process attach/exit with\n  freed VRAM, VRAM-pressure ETA, idle gap, suspected hang, CPU spillover, collector\n  stall/slow-probe self-reports, history reset.\n- **Chart styles:** braille step-outline (default) or solid fill — `c` toggles, your pick;\n  both break honestly at recording gaps.\n- **Subcommands and sinks:** `report` (plain-text digest), `demo` (pre-seeded incident),\n  `export`/`view` (shareable `.gpvr` incident files that replay anywhere, no GPU required),\n  `--json` (NDJSON contract v1 with JSON Schema and a conformance test that runs the built\n  binary), `--on-event` command sink, adaptive idle backoff.\n- **Tests:** the full suite passes on machines with no GPU — everything runs against the\n  mock backend and committed sysfs/fdinfo fixture trees.\n\nIn progress / not yet:\n\n- First published binaries — the release pipeline (tar.gz/zip/deb/rpm, drafted per tag,\n  provenance-attested) is in-tree; artifacts appear with the next tagged release.\n- Real-hardware soak across the driver matrix — a manual pre-release checklist by design;\n  CI stays GPU-free. Windows and macOS are at the compiles + CI-tested tier until then.\n- iced GUI (v2) — see roadmap.\n\n## Architecture\n\n```\ngpuviewer-core      trait GpuBackend (nvtop's vtable, translated to Rust)\n                    ├─ nvidia: nvml-wrapper (runtime dlopen, never hard-linked)\n                    │          + on Windows: per-process fill from the shared PDH snapshot\n                    ├─ amd:    sysfs/hwmon/gpu_metrics(v1.1–v3.0)/fdinfo (zero library deps)\n                    ├─ intel:  fdinfo (i915 + xe dialects) + sysfs\n                    ├─ wddm:   Windows cross-vendor (DXGI + PDH + D3DKMT — OS surfaces only)\n                    ├─ apple:  macOS device-level (Metal; private tiers gated post-WWDC26)\n                    └─ mock:   deterministic simulation (CI + demo; no GPU required)\ngpuviewer-history   RAM rings (live window) → 10s/1m SQLite rollups + event log\ngpuviewer-tui       ratatui — live · timeline · replay, story feed\n                    + report · demo · export (.gpvr) · view   (+ --json mode)\n```\n\nBuilt in Rust, all-Rust dependency tree. Missing drivers degrade gracefully,\n`NOT_SUPPORTED` is a normal per-metric outcome, and the tool never out-consumes what it\nmonitors (adaptive polling; low-power cadence surfaced in the footer).\n\n### Keybinds\n\nEvery navigation key has a letter alias — Mac laptops have no `PgUp`/`PgDn`/`Home`/`End`\nand terminals tend to eat the `Fn`+arrow substitutes. One rule across all modes:\n`w`/`a`/`s`/`d` mirror the arrows, `A`/`D` (shifted) mirror `PgUp`/`PgDn`, `g`/`G`\nmirror `Home`/`End`.\n\n| Live | |\n|---|---|\n| `q` / `Esc` | quit |\n| `←` `→` / `a` `d` / `Tab` / `Shift-Tab` | switch device |\n| `p` | pause/resume collection |\n| `↑` `↓` / `w` `s` then `Enter` | jump to a story-feed event |\n| `r` | replay at the newest recorded moment |\n| `t` | timeline overview |\n| `c` | chart style: braille ↔ solid |\n\n| Replay | |\n|---|---|\n| `Esc` / `r` | back to live (inert in `view` — a file has no live mode behind it) |\n| `←` `→` / `a` `d` | scrub 10s |\n| `PgUp` / `PgDn` / `A` / `D` | scrub 5m |\n| `Home` / `g` | oldest recorded moment |\n| `End` / `G` | newest recorded moment |\n| `↑` `↓` / `w` `s` then `Enter` | jump to the selected event |\n| `t` / `c` | timeline · chart style |\n\n| Timeline | |\n|---|---|\n| `t` / `Esc` | back to live (to the pinned replay in `view` — a file has no live mode) |\n| `+` / `-` | zoom: 1h · 3h · 6h · 12h · 24h · 48h · 7d |\n| `←` `→` / `a` `d` | move the time cursor (footer shows the nearest event) |\n| `PgUp` / `PgDn` / `A` / `D` | jump 10 columns |\n| `Home` / `End` / `g` / `G` | window edges |\n| `Tab` / `Shift-Tab` | switch device |\n| `Enter` | **drill into replay at the cursor** |\n\n### Retention defaults\n\n| | granularity | kept for |\n|---|---|---|\n| recent past | 10s rollups | 48 hours |\n| long tail | 1m rollups | 30 days |\n| event log | every event | 30 days |\n\nHistory lives at `$XDG_DATA_HOME/gpuviewer/history.db`\n(`~/.local/share/gpuviewer/history.db`); `--db` overrides it, `--no-persist` disables\nrecording (which the replay view and `report` need). `--mock` records to `history-mock.db`\nand `demo` to `history-demo.db` — your real history is never polluted by simulations.\n\n### Roadmap\n\n- **v1.5 — Windows NVIDIA:** NVML + PDH dual-source — NVML for device truth, Windows GPU\n  performance counters for the per-process VRAM/util that NVML architecturally cannot see\n  under WDDM (an honest per-process number where Task Manager misleads). *Dual-source\n  fusion in-tree: compiles + CI-tested, hardware validation pending.* The cross-vendor\n  WDDM backend (device + per-process AMD/Intel/NVIDIA via PDH) landed alongside it, same\n  tier.\n- **v2 — macOS Apple Silicon:** device-level telemetry only (per-process GPU is\n  OS-prohibited, and we say so in-UI rather than fake it) + an iced GUI from the same\n  core. *Device-level backend in-tree: compiles + CI-tested, hardware validation pending;\n  GUI not started.*\n- **v2+:** Prometheus exporter, multi-host views, vendor-depth Windows AMD/Intel beyond\n  the WDDM device tier.\n\nDeliberately not chasing: fan/OC control (LACT owns it), a daemon/client split (\"always-on\"\nmeans by virtue of normal use — for boot-time recording, run `gpuviewer --json` under your\nown systemd user unit; an example ships at\n[`docs/packaging/gpuviewer.service`](docs/packaging/gpuviewer.service)), cluster views, and\neBPF causal tracing (we hand off to profilers: \"idle gap at 02:14:31 — if recurring,\ncapture a trace\").\n\n## License\n\nLicensed under either of\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or \u003chttp://www.apache.org/licenses/LICENSE-2.0\u003e)\n- MIT license ([LICENSE-MIT](LICENSE-MIT) or \u003chttp://opensource.org/licenses/MIT\u003e)\n\nat your option.\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsinghpratech%2Fgpuviewer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsinghpratech%2Fgpuviewer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsinghpratech%2Fgpuviewer/lists"}