Problem
When the client starts burning CPU, nothing notices. A recent investigation found a tab that had sustained ~18% of a core for three days — roughly 13.7 hours of CPU — with the user unaware until the machine was audited from outside the browser.
Worse, the condition stopped mid-investigation and the trigger was never identified. There was no record of what had been running, so three days of evidence evaporated. A watchdog would have caught the onset, sampled it while it was live, and named the culprit.
Proposal
A lightweight, always-on monitor that detects sustained main-thread work while the client is idle, and records what was active at the time.
Detection
PerformanceObserver on longtask — count and total duration per interval. Cheap and natively supported.
- Event-loop lag sampler — a low-frequency timer (every ~500 ms is plenty) measuring its own scheduling delay. A healthy idle client sits near 1 ms; measured at 1 ms average, 7 ms max on a clean session.
- Animation-frame rate — a self-perpetuating rAF loop is a common runaway shape. Instrument by wrapping
requestAnimationFrame to count callers, rather than installing a rAF loop of your own, which would force 60 fps and mask the very thing being measured. (This mistake was made during the investigation and produced a false positive.)
Idle definition
Only fire when the user is not interacting: no keystrokes or pointer events for N seconds. Sustained main-thread work during active use is expected; during idle it is a bug.
Attribution
The signal is only useful with context. On trip, capture a snapshot of what is live:
- Which subsystems are active: audio, MIDI, voice chat, haptics/gamepad, editors, file transfers
- Inbound message rate and output lines/sec
- React render counts for the hot components
- Current room,
AudioContext.state, connection state
Output
Write structured records into the diagnostics ring buffer from #100, and log a single console warning naming the top contributors. The goal is to turn a three-day archaeology dig into one line the user can copy and paste.
Cost
The watchdog must not become the thing it is measuring. A longtask observer plus a 500 ms sampler is negligible. Keep sampling coarse and avoid per-event instrumentation in the steady state.
Why the naive approaches do not work
Externally measured per-thread CPU can tell you that a renderer thread is hot but not what it is running — Chrome renderers expose no thread names or start addresses to outside tooling, and no URL on the command line. Chrome's own background throttling also distorts the picture: the same tab measured 24.1% in the foreground and 1.6–4.2% backgrounded, which makes external sampling easy to misread. In-page instrumentation is the only thing that can attribute the work.
Acceptance criteria
Related
Problem
When the client starts burning CPU, nothing notices. A recent investigation found a tab that had sustained ~18% of a core for three days — roughly 13.7 hours of CPU — with the user unaware until the machine was audited from outside the browser.
Worse, the condition stopped mid-investigation and the trigger was never identified. There was no record of what had been running, so three days of evidence evaporated. A watchdog would have caught the onset, sampled it while it was live, and named the culprit.
Proposal
A lightweight, always-on monitor that detects sustained main-thread work while the client is idle, and records what was active at the time.
Detection
PerformanceObserveronlongtask— count and total duration per interval. Cheap and natively supported.requestAnimationFrameto count callers, rather than installing a rAF loop of your own, which would force 60 fps and mask the very thing being measured. (This mistake was made during the investigation and produced a false positive.)Idle definition
Only fire when the user is not interacting: no keystrokes or pointer events for N seconds. Sustained main-thread work during active use is expected; during idle it is a bug.
Attribution
The signal is only useful with context. On trip, capture a snapshot of what is live:
AudioContext.state, connection stateOutput
Write structured records into the diagnostics ring buffer from #100, and log a single console warning naming the top contributors. The goal is to turn a three-day archaeology dig into one line the user can copy and paste.
Cost
The watchdog must not become the thing it is measuring. A
longtaskobserver plus a 500 ms sampler is negligible. Keep sampling coarse and avoid per-event instrumentation in the steady state.Why the naive approaches do not work
Externally measured per-thread CPU can tell you that a renderer thread is hot but not what it is running — Chrome renderers expose no thread names or start addresses to outside tooling, and no URL on the command line. Chrome's own background throttling also distorts the picture: the same tab measured 24.1% in the foreground and 1.6–4.2% backgrounded, which makes external sampling easy to misread. In-page instrumentation is the only thing that can attribute the work.
Acceptance criteria
Related