perf: diagnoistics improvements
This commit is contained in:
201
electron/diagnostics/high-memory-snapshot.rules.spec.ts
Normal file
201
electron/diagnostics/high-memory-snapshot.rules.spec.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
import {
|
||||
describe,
|
||||
expect,
|
||||
it
|
||||
} from 'vitest';
|
||||
import type { PerfDiagEntry } from './diagnostics.models';
|
||||
import {
|
||||
buildHighMemoryDiagnosticPayload,
|
||||
buildHighMemorySummary,
|
||||
extractLatestRendererSamples,
|
||||
extractProcessHistory,
|
||||
formatMemoryUsageMb,
|
||||
rankProcessesByWorkingSet,
|
||||
summarizeRingBuffer
|
||||
} from './high-memory-snapshot.rules';
|
||||
|
||||
function createProcess(overrides: Partial<{
|
||||
pid: number;
|
||||
type: string;
|
||||
workingSetKb: number | null;
|
||||
peakWorkingSetKb: number | null;
|
||||
privateBytesKb: number | null;
|
||||
creationTime: number | null;
|
||||
cpuPercent: number | null;
|
||||
}> = {}) {
|
||||
return {
|
||||
pid: 1,
|
||||
type: 'Tab',
|
||||
workingSetKb: 1024,
|
||||
peakWorkingSetKb: null,
|
||||
privateBytesKb: null,
|
||||
creationTime: null,
|
||||
cpuPercent: null,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
describe('high-memory-snapshot.rules', () => {
|
||||
it('ranks processes by working set and computes share percentages', () => {
|
||||
const tabProcess = createProcess({ pid: 1, type: 'Tab', workingSetKb: 512_000 });
|
||||
const gpuProcess = createProcess({ pid: 2, type: 'GPU', workingSetKb: 1_536_000 });
|
||||
const ranked = rankProcessesByWorkingSet([tabProcess, gpuProcess], 2_048_000);
|
||||
|
||||
expect(ranked[0]?.type).toBe('GPU');
|
||||
expect(ranked[0]?.sharePercent).toBe(75);
|
||||
expect(ranked[1]?.sharePercent).toBe(25);
|
||||
});
|
||||
|
||||
it('extracts the latest renderer store, heap, and component samples', () => {
|
||||
const entries: PerfDiagEntry[] = [
|
||||
{
|
||||
collectedAt: 1,
|
||||
source: 'renderer',
|
||||
type: 'store',
|
||||
payload: { domains: { chat: 100 } }
|
||||
},
|
||||
{
|
||||
collectedAt: 2,
|
||||
source: 'renderer',
|
||||
type: 'heap',
|
||||
payload: { usedJsHeapMb: 120 }
|
||||
},
|
||||
{
|
||||
collectedAt: 3,
|
||||
source: 'renderer',
|
||||
type: 'components',
|
||||
payload: { suspectedLeaks: [{ name: 'ChatMessageItem', count: 40, expected: 20 }] }
|
||||
},
|
||||
{
|
||||
collectedAt: 4,
|
||||
source: 'renderer',
|
||||
type: 'store',
|
||||
payload: { domains: { chat: 500 } }
|
||||
}
|
||||
];
|
||||
|
||||
expect(extractLatestRendererSamples(entries)).toEqual({
|
||||
store: { domains: { chat: 500 } },
|
||||
heap: { usedJsHeapMb: 120 },
|
||||
components: { suspectedLeaks: [{ name: 'ChatMessageItem', count: 40, expected: 20 }] }
|
||||
});
|
||||
});
|
||||
|
||||
it('extracts recent process history from the ring buffer', () => {
|
||||
const entries: PerfDiagEntry[] = [
|
||||
{
|
||||
collectedAt: 1,
|
||||
source: 'main',
|
||||
type: 'process',
|
||||
payload: { totalWorkingSetKb: 1000 }
|
||||
},
|
||||
{
|
||||
collectedAt: 2,
|
||||
source: 'main',
|
||||
type: 'session',
|
||||
payload: { event: 'noop' }
|
||||
},
|
||||
{
|
||||
collectedAt: 3,
|
||||
source: 'main',
|
||||
type: 'process',
|
||||
payload: { totalWorkingSetKb: 2000 }
|
||||
}
|
||||
];
|
||||
|
||||
expect(extractProcessHistory(entries)).toEqual([{ collectedAt: 1, totalWorkingSetKb: 1000 }, { collectedAt: 3, totalWorkingSetKb: 2000 }]);
|
||||
});
|
||||
|
||||
it('summarizes ring buffer entry counts', () => {
|
||||
expect(summarizeRingBuffer([
|
||||
{ collectedAt: 1, source: 'main', type: 'process', payload: {} },
|
||||
{ collectedAt: 2, source: 'renderer', type: 'heap', payload: {} },
|
||||
{ collectedAt: 3, source: 'main', type: 'process', payload: {} }
|
||||
])).toEqual({
|
||||
'main:process': 2,
|
||||
'renderer:heap': 1
|
||||
});
|
||||
});
|
||||
|
||||
it('builds a high-memory summary with threshold context', () => {
|
||||
const summary = buildHighMemorySummary(
|
||||
2_200_000,
|
||||
[createProcess({ workingSetKb: 2_200_000 })],
|
||||
1_700_000_000_000
|
||||
);
|
||||
|
||||
expect(summary.totalWorkingSetGb).toBe('2.10');
|
||||
expect(summary.thresholdGb).toBe('2.00');
|
||||
expect(summary.topProcesses).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('builds a comprehensive high-memory diagnostic payload', () => {
|
||||
const payload = buildHighMemoryDiagnosticPayload({
|
||||
detectedAt: 1_700_000_000_000,
|
||||
totalWorkingSetKb: 2_200_000,
|
||||
metrics: {
|
||||
collectedAt: 1_700_000_000_000,
|
||||
processes: [
|
||||
createProcess({
|
||||
workingSetKb: 2_200_000,
|
||||
peakWorkingSetKb: 2_300_000,
|
||||
privateBytesKb: 1_800_000,
|
||||
creationTime: 1,
|
||||
cpuPercent: 12
|
||||
})
|
||||
]
|
||||
},
|
||||
environment: { appVersion: '1.0.0' },
|
||||
mainProcessMemory: {
|
||||
rss: 64 * 1024 * 1024,
|
||||
heapTotal: 32 * 1024 * 1024,
|
||||
heapUsed: 16 * 1024 * 1024,
|
||||
external: 8 * 1024 * 1024,
|
||||
arrayBuffers: 1024
|
||||
},
|
||||
ringEntries: [
|
||||
{
|
||||
collectedAt: 1,
|
||||
source: 'main',
|
||||
type: 'process',
|
||||
payload: { totalWorkingSetKb: 2_000_000 }
|
||||
}
|
||||
],
|
||||
immediateRendererEntries: [
|
||||
{
|
||||
collectedAt: 2,
|
||||
source: 'renderer',
|
||||
type: 'heap',
|
||||
payload: { usedJsHeapMb: 300, route: '/room/abc' }
|
||||
}
|
||||
],
|
||||
sessionId: 'session-1'
|
||||
});
|
||||
|
||||
expect(payload.event).toBe('high-memory-threshold');
|
||||
expect(payload.summary).toMatchObject({
|
||||
totalWorkingSetKb: 2_200_000
|
||||
});
|
||||
|
||||
expect(payload.processHistory).toHaveLength(1);
|
||||
expect(payload.recentRendererSamples).toEqual({
|
||||
store: null,
|
||||
heap: { usedJsHeapMb: 300, route: '/room/abc' },
|
||||
components: null
|
||||
});
|
||||
|
||||
expect(formatMemoryUsageMb({
|
||||
rss: 64 * 1024 * 1024,
|
||||
heapTotal: 32 * 1024 * 1024,
|
||||
heapUsed: 16 * 1024 * 1024,
|
||||
external: 8 * 1024 * 1024,
|
||||
arrayBuffers: 1024
|
||||
})).toEqual({
|
||||
rssMb: 64,
|
||||
heapTotalMb: 32,
|
||||
heapUsedMb: 16,
|
||||
externalMb: 8,
|
||||
arrayBuffersMb: 0
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user