Repository navigation
feat(desktop): add native memory breakdown to heap captures - #58311
Conversation
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
4df7ad9 to
023d32b
Compare
There was a problem hiding this comment.
Looks sound overall. Tests and type checks pass (tsc for gui-extensions, util, protocol, server and client). I couldn't run the Electron part here (memory-infra recording, page stats, memory.json/gpu.json), so the trace summary is only checked against the hand-made trace in the test.
Public API impact:
POST /api/debug/heap-dump: the response gains amemoryobject (rss,heapTotal,heapUsed,external,arrayBuffers, in bytes). It is required in the OpenAPI spec and inDebugHeapDumpOutputin the client. This is additive and compatible, but typed callers talking to an older server will getundefineddespite the non-optional type. The desktop already handles this.- Desktop capture folder: adds
memory.json,memory-infra.jsonandgpu.json. Inmanifest.json, the top-levelmetricsandmainMemorymove tobefore.{metrics,mainMemory}andafter.{metrics,mainMemory}, andfilesandapp.uptimeSecondsare new. Any script readingmanifest.json.metricsneeds to update.
Two concerns inline: the new page-stats step can hang the whole capture on an unresponsive window, and the memory-infra summary may pick a dump that not every process has reported into yet.
Related: #58268
|
|
||
| const native = await recordNative(directory) | ||
| const windows = ctx.windows.list() | ||
| const pages = await Promise.all(windows.map((window) => describeWindow(window))) |
There was a problem hiding this comment.
describeWindow runs before the server and window snapshots start, and executeJavaScript never settles if a renderer is hung or busy, which is likely in the leaking app this tool is for. A single stuck window would then block the whole capture, including the server snapshot, which used to run alongside the windows. Could the page stats get a timeout (e.g. race with a few seconds and record an error), so the snapshots still run?
There was a problem hiding this comment.
Fixed in 0cbe78c. Page stats now race a 3 second timeout. A window that does not answer gets { "error": "The window did not answer within 3 seconds." } in memory.json, and the snapshots run as before.
| // events (the browser in two). The latest dump with allocators is the latest complete picture. | ||
| const dumps = events.flatMap((event) => | ||
| event.ph === "v" && event.args?.dumps ? [{ ...event, dumps: event.args.dumps }] : [], | ||
| ) |
There was a problem hiding this comment.
latest is the last dump id that has any allocators event, so if stopRecording cuts into a global dump that only some processes have reported (detailed dumps get slower under memory pressure), current holds just those processes. The rest silently drop out of memory.json, and the browser may show only one of its two allocator events. Picking the latest dump per pid, or the latest id that every dumping process has both totals and allocators for, would match the "latest complete dump" described in the PR.
There was a problem hiding this comment.
Fixed in 0cbe78c. summarize now uses the latest dump id that every dumping process reported both totals and allocators for, and only falls back to the latest id with any allocators when no dump is complete. The test adds a cut-off final dump (only the renderer reported) and fails without the fix.
There was a problem hiding this comment.
Both earlier points are fixed. Page stats now give up after 3 seconds, so a stuck window no longer blocks the snapshots. summarize now prefers the latest dump that every process sent both totals and allocators for, and the new test case covers it. The memory-infra and server-heap tests and tsc in packages/gui-extensions pass. I couldn't run the Electron capture itself here. This push doesn't change the public API, and I have nothing new to raise.
A heap snapshot only holds objects the garbage collectors own, so on adais the app window's 1,186 MB showed up as ~300 MB of heap and 886 MB of nothing. Capture heap snapshots now also records Chromium's per-process, per-allocator memory dump and summarizes it, and the server reports its own memory.
What the folder holds now
How
sequenceDiagram participant M as main (debug/main.ts) participant C as Chromium tracing participant W as app windows participant S as server M->>M: getAppMetrics, memoryUsage, v8 heap stats, system memory (before) M->>C: startRecording(memory-infra, detailed dump every 1s) C-->>M: memory-infra.json after 2.5s M->>M: summarize → memory.json M->>W: executeJavaScript: DOM size, images, canvases, performance.memory par M->>S: POST /api/debug/heap-dump?directory=… → { path, pid, memory } and M->>W: takeHeapSnapshot, one window at a time end M->>M: writeHeapSnapshot(main), getGPUInfo, metrics (after), manifest.jsonmanifest.jsonalso keeps metrics after.contentTracingrecording covers every Chromium process (browser, renderers, GPU, utility).summarize(debug/memory-infra.ts) takes the latest complete dump: totals and allocators arrive in separate events per dump id, and the browser splits its allocators over two. It reports effective sizes (shared memory counted once), top-level allocators, and sub-allocators of at least 1 MiB up to three levels deep. If another trace is running (DevTools recording a profile), the manifest records the error and the snapshots still run.HeapSnapshot.writereadsprocess.memoryUsage()just before writing and the endpoint returns it asmemory; the SIGUSR1 log line includesrssandheapUsed. The desktop records it per server snapshot; servers released before this leave it out.<use>count, viewport, device pixel ratio, zoom,performance.memory.v8.getHeapStatistics()and heap spaces,process.getSystemMemoryInfo(),app.getGPUInfo("basic"), app uptime.Follow-up to #58259. Docs (
troubleshooting.mdx) and theopencode-devskill describe the new files.Added by the opencode bot (AI-generated).
Related: #58268
Public API: adds required
memoryto the POST /api/debug/heap-dump response; desktop manifest.json movesmetrics/mainMemoryunderbefore/after(additive for the API; manifest readers must update)