From 62b59ee6d908b9eff2b0fb0b253d22c7e5d3a121 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Wed, 22 Jul 2026 16:35:44 -0700 Subject: [PATCH] fix(diagnostics): bound heap profile contributor calls --- src/renderer/src/lib/renderer-memory-profile.test.ts | 8 ++++++++ src/renderer/src/lib/renderer-memory-profile.ts | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/lib/renderer-memory-profile.test.ts b/src/renderer/src/lib/renderer-memory-profile.test.ts index 6b3c09b4d5b..7a0d90dda93 100644 --- a/src/renderer/src/lib/renderer-memory-profile.test.ts +++ b/src/renderer/src/lib/renderer-memory-profile.test.ts @@ -88,6 +88,14 @@ describe('collectRendererMemoryProfileCounts', () => { expect(Object.keys(collectRendererMemoryProfileCounts())).toHaveLength(64) expect(skippedContributor).not.toHaveBeenCalled() }) + + it('caps contributor calls when contributors return no counts', () => { + const contributors = Array.from({ length: 100 }, () => vi.fn(() => ({}))) + contributors.forEach((contributor, index) => register(`empty-${index}`, contributor)) + + expect(collectRendererMemoryProfileCounts()).toEqual({}) + expect(contributors.filter((contributor) => contributor.mock.calls.length > 0)).toHaveLength(64) + }) }) describe('summarizeStateCollectionSizes', () => { diff --git a/src/renderer/src/lib/renderer-memory-profile.ts b/src/renderer/src/lib/renderer-memory-profile.ts index 85efbcd3f03..b27e374b8c6 100644 --- a/src/renderer/src/lib/renderer-memory-profile.ts +++ b/src/renderer/src/lib/renderer-memory-profile.ts @@ -17,6 +17,8 @@ const contributors = new Map() const MAX_COUNTS_PER_CONTRIBUTOR = 32 // Why: individually bounded contributors can still create unbounded near-OOM work in aggregate. const MAX_PROFILE_COUNTS = 64 +// Why: empty contributors do not consume the count budget but must not make collection unbounded. +const MAX_PROFILE_CONTRIBUTORS = 64 export function registerRendererMemoryProfileContributor( name: string, @@ -33,10 +35,12 @@ export function registerRendererMemoryProfileContributor( export function collectRendererMemoryProfileCounts(): RendererMemoryProfileCounts { const counts: RendererMemoryProfileCounts = {} let collected = 0 + let visited = 0 for (const [name, contributor] of contributors) { - if (collected >= MAX_PROFILE_COUNTS) { + if (collected >= MAX_PROFILE_COUNTS || visited >= MAX_PROFILE_CONTRIBUTORS) { break } + visited += 1 // Why: a broken contributor must never take down memory reporting itself. try { const contribution = contributor()