gpuviewer

The GPU flight recorder

It was already recording.

Your training run flatlined at 02:14. You were asleep. Every other GPU monitor would show you a beautiful live gauge — of a moment that no longer exists. gpuviewer keeps per-process history and a narrated event log just by being open, so the next morning you scroll back and read what happened, in plain English.

Recording GPU0 · RTX 4090 8h recorded
22:0000:0002:0004:0006:00

Drag the playhead, click an event tick, or use

02:14 Fact throttle_start

GPU0 throttling — thermal

clocks 2610 → 1725 MHz · temp 84 °C (slowdown 83 °C) · NVML clocks-event bitmask: SW_THERMAL_SLOWDOWN

$ curl -fsSL https://raw.githubusercontent.com/singhpratech/gpuviewer/main/install.sh | sh
Install Source

Why it exists

A gauge tells you the present.
The present is lying about
the night.

Every GPU tool on your box samples right now and throws it away. That is fine when you are watching. It is useless at 09:00 when the run is dead, the process is gone, and the only evidence left is a loss curve that stops. gpuviewer's job is to still have the evidence — downsampled 10s and 1m rollups plus an append-only event log in a local SQLite file, written by the same binary you were already running. No daemon to install. No recording you had to remember to start.

One recording, three altitudes

Zoom out to find it. Zoom in to read it.

The same recording answers three different questions. Single keys move between them — spot the anomaly on the timeline, Enter to drill into replay at that exact column, Esc back to live.

t

Timeline

“What did the whole night look like?”

Hours of history as solid strips with an event lane underneath. Zooms 1h → 7d. Each column is the recorded peak — and time that was never recorded stays blank, never painted as zero.

r

Replay

“What exactly happened at 02:14?”

Full charts, process table, gauges and story feed at any recorded moment. Seeks anchor to events, so you land on the onset rather than near it.

live

Live

“What is it doing right now?”

The ordinary monitor view — and it is recording the other two while you watch. Idle cadence backs off automatically so polling does not keep the GPU awake.

gpuviewer replay view: braille charts, a process table, throttle gauges and the narrated story feed, scrolled back to a throttle onset
The built-in demo (gpuviewer demo, simulated data and labeled as such) opens already scrolled back to the night's last throttle onset — the first thing you see is the answer, not a gauge.
gpuviewer timeline view: hours of utilization and VRAM history as solid strips with an event lane and a time cursor
The footer states exactly how much of the window is real: “12h (8h recorded)”.

The honesty contract

A confidently wrong narration would kill the whole idea.

Anything that reads a story out of telemetry can invent one. These are the rules the code actually enforces, not aspirations — they are why you can trust the 02:14 answer.

Facts and inferences are different objects

A throttle bit that was set is a fact. “Your dataloader is starving the GPU” is an inference — it is labeled likely, always, and expands to the raw evidence it was derived from.

Unobservable is not zero

A metric the driver will not answer renders as unavailable, never as 0. An open throttle episode whose bitmask goes unreadable is dropped silently rather than closed with a fabricated “recovered”.

Unrecorded time stays blank

Charts never bridge a gap longer than 15 s. Time when gpuviewer was not running is a hole in the strip, and the footer tells you how much of the window is real.

Utilization is duty-cycle, and says so

NVML “utilization” means at least one kernel was resident — not that the GPU was saturated. It is labeled as duty-cycle rather than presented as capacity.

An incomplete process list admits it

Reading other users' processes needs elevated privileges. Unprivileged, you get a “your processes only” hint instead of a list that quietly pretends to be complete.

The recorder narrates its own failures

Collector stalls, lost and returned devices, and session start/stop marks are events too — so a gap in the story is explained by the recorder, not left for you to guess at.

Support

Three platforms, four vendors, one binary.

Missing drivers degrade instead of failing, so the fastest way to find out what you get on your machine is to run it — gpuviewer --mock works even with no GPU at all. Found something off? Open an issue — reports from real hardware are the most useful thing you can send.

PlatformVendorsSourcesPer-process
Linux NVIDIA · AMD · Intel NVML · sysfs / gpu_metrics · fdinfo (i915 + xe) Yes — pid, VRAM, util, CPU%, container
Windows NVIDIA · AMD · Intel NVML · PDH · DXGI / D3DKMT Yes — via PDH, where Task Manager misleads
macOS
Apple Silicon
Apple Metal No — the OS prohibits it for third-party tools

On macOS the missing process table is an operating-system restriction, not a gap in the app — gpuviewer says so in the UI and blames the OS rather than going quiet. Device-level memory there is Metal's unified-memory working-set budget, which is not the same thing as discrete VRAM, and it is labeled that way.

Install

One unprivileged binary.

No daemon, no root, no kernel module. The installers verify a SHA-256 checksum before anything lands on disk. Prebuilt binaries are not code-signed — the release notes carry the Gatekeeper and SmartScreen steps.

Linux & macOS

$ curl -fsSL https://raw.githubusercontent.com/\
singhpratech/gpuviewer/main/install.sh | sh

Windows (PowerShell)

> irm https://raw.githubusercontent.com/singhpratech/\
gpuviewer/main/install.ps1 | iex

From source

$ cargo install --git https://github.com/singhpratech/gpuviewer \
    gpuviewer-tui
# needs Rust 1.95+

Try it with no GPU

$ gpuviewer demo     # 8h of simulated history,
                     # opens scrolled back
$ gpuviewer --mock   # simulated live GPUs

Then, the next morning

$ gpuviewer report --since 12h          # plain-text digest of the night
$ gpuviewer export --since 12h out.gpvr # a shareable incident slice
$ gpuviewer view out.gpvr               # replay it anywhere — no GPU needed
$ gpuviewer --json                      # NDJSON stream for scripting