Skip to content

feat(perf): always-visible main-process heap + rss badge in header#76

Merged
Juliusolsson05 merged 1 commit into
mainfrom
feat/system-perf-header
May 11, 2026
Merged

feat(perf): always-visible main-process heap + rss badge in header#76
Juliusolsson05 merged 1 commit into
mainfrom
feat/system-perf-header

Conversation

@Juliusolsson05

Copy link
Copy Markdown
Owner

Summary

Adds a 1 Hz live readout of the Electron main-process v8 heap and RSS to the header strip, gated on `CC_SHELL_PERF=1`. Renders null when the flag is off, so default users see no change.

Motivated by today's OOM that crashed at ~2.69 GiB / 2.78 GiB cap with two back-to-back Mark-Compact GCs freeing 0 MB each — i.e. fully-retained heap, not a classic leak. With this badge sitting in red 30+ minutes earlier, you'd have had a visible warning instead of a sudden kill. PR #48's heap-watchdog already dumps a forensic snapshot at the same 80% threshold; this PR is the user-facing counterpart so growth is visible BEFORE the snapshot fires.

What you see

Badge in header (always visible when CC_SHELL_PERF=1):
```
heap 2.10 GB / 4.00 GB ▁▂▃▄▅▆▇ rss 3.2 GB
```

  • Heap color: green <60% of cap, yellow 60–80%, red >80%
  • Inline 60-second sparkline of heap-used ratio (same color as text)
  • RSS in muted text (no cap to color-zone against)

Click expands to popover (same 430px / border / shadow / position as the existing PerformancePanel):

  • Current heap / RSS / external / arrayBuffers
  • Peak heap + RSS over the buffered 10-minute window
  • 60s growth rate for each metric (positive growth highlighted)
  • Overlaid SVG line chart: heap-used (green) + RSS (sky), with a dashed red heap-cap reference line

Implementation

  • `shared/performance/types.ts` — new `SystemPerformanceStats` type with WHY comment explaining why it's NOT `PanePerformanceSnapshot` (per-agent) or `PerformanceSnapshot` (on-disk logs).
  • `main/ipc/performance.ts` — new `performance:system-stats` handler returning `process.memoryUsage() + v8.getHeapStatistics()`. Gated on `performanceService.getConfig().enabled`.
  • `preload/api/performance.ts` — new `getSystemPerformanceStats` method.
  • `renderer/src/features/system-perf/` (new dir):
    • `useSystemPerfPoller.ts` — probe-then-poll. First tick decides whether to schedule the 1 Hz interval; if the flag is off no interval is started. Ring buffer of 600 samples (10 min).
    • `ui/SystemPerfBadge.tsx` — header strip + sparkline.
    • `ui/SystemPerfPopover.tsx` — expanded view, mirrors `PerformancePanel` chrome (430px / right-anchored / shadow-xl).
    • `ui/SystemPerfHeader.tsx` — orchestrator. Owns local open state.
  • `app/App.tsx` — mounts `` next to the existing perf toggle in the title bar.

What this is NOT

  • Not per-agent telemetry. `PerformancePanel` already covers per-pane cpu/memory.
  • Not tunable. Zone thresholds (60/80%) hardcoded for now.
  • Not persisted. Ring buffer is renderer-only, in-memory.
  • Not in the command palette. Click on the badge is the only entry point.

Test plan

  • `npm run build:app` clean (13.3s, no new TS errors)
  • Manual: `CC_SHELL_PERF=1 npm run dev`, observe badge populates within 1s
  • Manual: idle / load many sessions, watch sparkline grow
  • Manual: click badge, popover opens with growing chart
  • Manual: `CC_SHELL_PERF=0 npm run dev`, confirm badge does NOT appear
  • Manual: trigger high heap pressure, confirm zone transitions green → yellow → red

🤖 Generated with Claude Code

Adds a 1 Hz live readout of the Electron main-process v8 heap and
RSS to the header strip, gated on CC_SHELL_PERF=1. Renders null
when the flag is off, so default users see no change.

The badge shows:
- heap used / heap cap with color zones (green <60%, yellow 60–80%,
  red >80% of v8.heap_size_limit)
- a 60-second sparkline of heap-used ratio (matches the zone color)
- RSS in muted text

Click expands to a PerformancePanel-sized popover (430px, same
border/shadow/positioning) with:
- current heap / RSS / external / arrayBuffers values
- peak heap + RSS over the buffered 10-minute window
- 60s growth rate for each metric (positive growth highlighted)
- overlaid SVG line chart of heap-used (green) + RSS (sky) with a
  dashed red heap-cap reference line

Motivation: today's OOM crashed at ~2.69 GiB / 2.78 GiB cap with
GC freeing 0 MB twice and a node::Buffer::New frame in the stack.
With the badge in red 30+ minutes earlier, the user would have had
a visible warning instead of a sudden kill. PR #48's heap-watchdog
already dumps a forensic snapshot at the same 80% threshold; this
PR is the user-facing counterpart so they can SEE growth before
the snapshot fires.

Implementation:
- New SystemPerformanceStats type in shared/performance/types.ts
- New IPC handler performance:system-stats in main/ipc/performance.ts
  (returns process.memoryUsage + v8.getHeapStatistics on every call)
- New preload API getSystemPerformanceStats
- New feature dir src/renderer/src/features/system-perf/ with:
  - useSystemPerfPoller hook (probe-then-poll, ring buffer of 600
    samples, fail-silent if IPC throws)
  - SystemPerfBadge (header strip with inline sparkline)
  - SystemPerfPopover (mirrors PerformancePanel chrome)
  - SystemPerfHeader (orchestrator, owns local open state)
- App.tsx mounts <SystemPerfHeader /> next to the existing perf
  toggle in the title bar

No new test scripts, no changes to existing perf telemetry pipeline,
no new persisted state, no command-palette entry (YAGNI — single
click on the badge is the only entry point users need).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant