import type { BrowserWindow } from 'electron'; import type { AppMetricsSnapshot } from '../app-metrics'; import { buildHighMemoryDiagnosticPayload } from './high-memory-snapshot.rules'; import { collectImmediateRendererSamples } from './immediate-renderer-samples.collector'; import { collectSessionContext } from './session-context.collector'; import type { HighMemoryAlertRecord } from './high-memory-alert.store'; import type { PerfDiagEntry } from './diagnostics.models'; import { PerfDiagWriter } from './diagnostics.writer'; export type HighMemoryCaptureReason = 'manual' | 'threshold'; export interface CaptureHighMemoryDiagnosticsInput { userDataPath: string; sessionStartedAt: number; metrics: AppMetricsSnapshot; totalWorkingSetKb: number; writer: PerfDiagWriter | null; mainWindow: BrowserWindow | null; reason: HighMemoryCaptureReason; } export async function captureHighMemoryDiagnostics( input: CaptureHighMemoryDiagnosticsInput ): Promise { const detectedAt = Date.now(); const writer = input.writer ?? new PerfDiagWriter({ userDataPath: input.userDataPath, sessionId: `${input.reason}-${detectedAt.toString(36)}-${process.pid}` }); const immediateRendererEntries = await collectImmediateRendererSamples(input.mainWindow); const environment = collectSessionContext({ sessionStartedAt: input.sessionStartedAt, userDataPath: input.userDataPath }); appendEntries(writer, immediateRendererEntries); appendEntries(writer, [ { collectedAt: detectedAt, source: 'main', type: 'environment', payload: { ...environment } }, { collectedAt: detectedAt, source: 'main', type: 'high-memory', payload: buildHighMemoryDiagnosticPayload({ detectedAt, totalWorkingSetKb: input.totalWorkingSetKb, metrics: input.metrics, environment, mainProcessMemory: process.memoryUsage(), ringEntries: writer.bufferedEntries, immediateRendererEntries, sessionId: writer.sessionId }) } ]); await writer.flushSnapshot( input.reason === 'manual' ? 'manual-export' : 'high-memory-threshold' ); return { logFilePath: writer.snapshotFilePath, detectedAt, peakWorkingSetKb: input.totalWorkingSetKb, sessionId: writer.sessionId, reason: input.reason }; } function appendEntries(writer: PerfDiagWriter, entries: readonly PerfDiagEntry[]): void { for (const entry of entries) { writer.append(entry); } }