mirror of
https://github.com/Boof2015/prism.git
synced 2026-08-19 20:09:43 +02:00
logging
This commit is contained in:
+117
-1
@@ -1,5 +1,6 @@
|
|||||||
import { app, BrowserWindow, desktopCapturer, dialog, ipcMain, Menu, screen, session, shell } from 'electron'
|
import { app, BrowserWindow, desktopCapturer, dialog, ipcMain, Menu, screen, session, shell } from 'electron'
|
||||||
import type { BrowserWindowConstructorOptions, MenuItemConstructorOptions, OpenDialogOptions, WebContents } from 'electron'
|
import type { BrowserWindowConstructorOptions, MenuItemConstructorOptions, OpenDialogOptions, WebContents } from 'electron'
|
||||||
|
import { appendFile, mkdir, writeFile } from 'node:fs/promises'
|
||||||
import { extname, join, resolve } from 'path'
|
import { extname, join, resolve } from 'path'
|
||||||
import type {
|
import type {
|
||||||
AstraControlCommand,
|
AstraControlCommand,
|
||||||
@@ -22,7 +23,7 @@ import type {
|
|||||||
ThemeLibrarySnapshot,
|
ThemeLibrarySnapshot,
|
||||||
} from '../types/theme'
|
} from '../types/theme'
|
||||||
import { RESIZE_DIRECTIONS, type ResizeDirection } from '../types/windowResize'
|
import { RESIZE_DIRECTIONS, type ResizeDirection } from '../types/windowResize'
|
||||||
import type { PerformanceMemorySnapshot } from '../types/performance'
|
import type { PerformanceMemoryLogRecord, PerformanceMemorySnapshot } from '../types/performance'
|
||||||
import { normalizeProfile } from '../shared/profileState'
|
import { normalizeProfile } from '../shared/profileState'
|
||||||
import { calculateResizedWindowBounds } from '../shared/windowResize'
|
import { calculateResizedWindowBounds } from '../shared/windowResize'
|
||||||
import { FileBackedProfileLibrary } from './profileLibrary'
|
import { FileBackedProfileLibrary } from './profileLibrary'
|
||||||
@@ -70,6 +71,12 @@ const POPOUT_DEFAULTS = {
|
|||||||
minWidth: 220,
|
minWidth: 220,
|
||||||
minHeight: 160,
|
minHeight: 160,
|
||||||
}
|
}
|
||||||
|
const MEMORY_LOG_DIRECTORY_NAME = 'Prism Logs'
|
||||||
|
const MEMORY_LOG_FILE_NAME = 'memory-trace-latest.csv'
|
||||||
|
const memoryLogSessionStartedAt = Date.now()
|
||||||
|
let memoryLogInitialized = false
|
||||||
|
let memoryLogInitialization: Promise<void> | null = null
|
||||||
|
let memoryLogWriteQueue: Promise<void> = Promise.resolve()
|
||||||
|
|
||||||
function kilobytesToMegabytes(value: number | undefined): number {
|
function kilobytesToMegabytes(value: number | undefined): number {
|
||||||
return Math.round((((value ?? 0) / 1024) * 10)) / 10
|
return Math.round((((value ?? 0) / 1024) * 10)) / 10
|
||||||
@@ -90,6 +97,101 @@ function sumWorkingSet(metrics: Electron.ProcessMetric[], predicate: (metric: El
|
|||||||
return kilobytesToMegabytes(totalKilobytes)
|
return kilobytesToMegabytes(totalKilobytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMemoryLogDirectory(): string {
|
||||||
|
return join(app.getPath('documents'), MEMORY_LOG_DIRECTORY_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMemoryLogPath(): string {
|
||||||
|
return join(getMemoryLogDirectory(), MEMORY_LOG_FILE_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeCsvField(value: string | number | boolean | null): string {
|
||||||
|
if (value === null) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = String(value)
|
||||||
|
if (!/[",\n]/.test(text)) {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
return `"${text.replace(/"/g, '""')}"`
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureMemoryLogFile(): Promise<void> {
|
||||||
|
if (memoryLogInitialized) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!memoryLogInitialization) {
|
||||||
|
memoryLogInitialization = (async () => {
|
||||||
|
await mkdir(getMemoryLogDirectory(), { recursive: true })
|
||||||
|
const header = [
|
||||||
|
'session_started_at_iso',
|
||||||
|
'captured_at_iso',
|
||||||
|
'elapsed_s',
|
||||||
|
'app_mb',
|
||||||
|
'main_mb',
|
||||||
|
'renderer_mb',
|
||||||
|
'renderer_private_mb',
|
||||||
|
'gpu_mb',
|
||||||
|
'utility_mb',
|
||||||
|
'js_heap_used_mb',
|
||||||
|
'js_heap_limit_mb',
|
||||||
|
'renderer_delta_mb',
|
||||||
|
'app_delta_mb',
|
||||||
|
'frame_target',
|
||||||
|
'docked_render_fps',
|
||||||
|
'is_capturing',
|
||||||
|
'capture_status',
|
||||||
|
'visible_scopes',
|
||||||
|
'popped_out_scopes',
|
||||||
|
].join(',')
|
||||||
|
await writeFile(getMemoryLogPath(), `${header}\n`, 'utf8')
|
||||||
|
memoryLogInitialized = true
|
||||||
|
})().finally(() => {
|
||||||
|
memoryLogInitialization = null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await memoryLogInitialization
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueMemoryLogWrite(record: PerformanceMemoryLogRecord): void {
|
||||||
|
const line = [
|
||||||
|
new Date(memoryLogSessionStartedAt).toISOString(),
|
||||||
|
new Date(record.capturedAt).toISOString(),
|
||||||
|
record.elapsedSeconds.toFixed(1),
|
||||||
|
record.appMb.toFixed(1),
|
||||||
|
record.mainMb.toFixed(1),
|
||||||
|
record.rendererMb.toFixed(1),
|
||||||
|
record.rendererPrivateMb === null ? '' : record.rendererPrivateMb.toFixed(1),
|
||||||
|
record.gpuMb.toFixed(1),
|
||||||
|
record.utilityMb.toFixed(1),
|
||||||
|
record.jsHeapUsedMb === null ? '' : record.jsHeapUsedMb.toFixed(1),
|
||||||
|
record.jsHeapLimitMb === null ? '' : record.jsHeapLimitMb.toFixed(1),
|
||||||
|
record.rendererDeltaMb.toFixed(1),
|
||||||
|
record.appDeltaMb.toFixed(1),
|
||||||
|
record.frameTarget,
|
||||||
|
Math.round(record.dockedRenderFps),
|
||||||
|
record.isCapturing,
|
||||||
|
record.captureStatus,
|
||||||
|
record.visibleScopes.join('|'),
|
||||||
|
record.poppedOutScopes.join('|'),
|
||||||
|
]
|
||||||
|
.map((field) => escapeCsvField(field))
|
||||||
|
.join(',')
|
||||||
|
|
||||||
|
memoryLogWriteQueue = memoryLogWriteQueue
|
||||||
|
.then(async () => {
|
||||||
|
await ensureMemoryLogFile()
|
||||||
|
await appendFile(getMemoryLogPath(), `${line}\n`, 'utf8')
|
||||||
|
})
|
||||||
|
.catch((error: unknown) => {
|
||||||
|
console.error('Failed to write memory trace log:', error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function getProfileLibrary(): FileBackedProfileLibrary {
|
function getProfileLibrary(): FileBackedProfileLibrary {
|
||||||
if (!profileLibrary) {
|
if (!profileLibrary) {
|
||||||
profileLibrary = new FileBackedProfileLibrary(
|
profileLibrary = new FileBackedProfileLibrary(
|
||||||
@@ -923,6 +1025,16 @@ function setupIPC(): void {
|
|||||||
return getWindowFromSender(event.sender)?.isAlwaysOnTop() ?? true
|
return getWindowFromSender(event.sender)?.isAlwaysOnTop() ?? true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('performance:get-memory-log-path', async () => {
|
||||||
|
await ensureMemoryLogFile()
|
||||||
|
return getMemoryLogPath()
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('performance:reveal-memory-log', async () => {
|
||||||
|
await ensureMemoryLogFile()
|
||||||
|
shell.showItemInFolder(getMemoryLogPath())
|
||||||
|
})
|
||||||
|
|
||||||
ipcMain.handle('performance:get-memory-snapshot', async (event): Promise<PerformanceMemorySnapshot> => {
|
ipcMain.handle('performance:get-memory-snapshot', async (event): Promise<PerformanceMemorySnapshot> => {
|
||||||
const metrics = app.getAppMetrics()
|
const metrics = app.getAppMetrics()
|
||||||
const senderPid = event.sender.getOSProcessId()
|
const senderPid = event.sender.getOSProcessId()
|
||||||
@@ -942,6 +1054,10 @@ function setupIPC(): void {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('performance:append-memory-log', async (_event, record: PerformanceMemoryLogRecord) => {
|
||||||
|
queueMemoryLogWrite(record)
|
||||||
|
})
|
||||||
|
|
||||||
ipcMain.handle('audio:get-desktop-sources', async () => {
|
ipcMain.handle('audio:get-desktop-sources', async () => {
|
||||||
const sources = await desktopCapturer.getSources({ types: ['screen'] })
|
const sources = await desktopCapturer.getSources({ types: ['screen'] })
|
||||||
return sources.map((s) => ({ id: s.id, name: s.name }))
|
return sources.map((s) => ({ id: s.id, name: s.name }))
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import type {
|
|||||||
} from '../types/theme'
|
} from '../types/theme'
|
||||||
import type { ResizeDirection } from '../types/windowResize'
|
import type { ResizeDirection } from '../types/windowResize'
|
||||||
import type { VisualizerDSP } from '../renderer/audio/native/visualizer-dsp'
|
import type { VisualizerDSP } from '../renderer/audio/native/visualizer-dsp'
|
||||||
import type { PerformanceMemorySnapshot } from '../types/performance'
|
import type { PerformanceMemoryLogRecord, PerformanceMemorySnapshot } from '../types/performance'
|
||||||
|
|
||||||
type NativeAddonModule = VisualizerDSP & NativeCaptureAPI
|
type NativeAddonModule = VisualizerDSP & NativeCaptureAPI
|
||||||
|
|
||||||
@@ -50,6 +50,11 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
repositionWindow: (position: 'top' | 'bottom') => ipcRenderer.send('window:reposition', position),
|
repositionWindow: (position: 'top' | 'bottom') => ipcRenderer.send('window:reposition', position),
|
||||||
toggleAlwaysOnTop: () => ipcRenderer.send('window:toggle-always-on-top'),
|
toggleAlwaysOnTop: () => ipcRenderer.send('window:toggle-always-on-top'),
|
||||||
isAlwaysOnTop: () => ipcRenderer.invoke('window:is-always-on-top'),
|
isAlwaysOnTop: () => ipcRenderer.invoke('window:is-always-on-top'),
|
||||||
|
getPerformanceMemoryLogPath: () => ipcRenderer.invoke('performance:get-memory-log-path') as Promise<string>,
|
||||||
|
revealPerformanceMemoryLog: () => ipcRenderer.invoke('performance:reveal-memory-log') as Promise<void>,
|
||||||
|
appendPerformanceMemoryLog: (record: PerformanceMemoryLogRecord) => {
|
||||||
|
return ipcRenderer.invoke('performance:append-memory-log', record) as Promise<void>
|
||||||
|
},
|
||||||
getPerformanceMemorySnapshot: async () => {
|
getPerformanceMemorySnapshot: async () => {
|
||||||
const snapshot = await ipcRenderer.invoke('performance:get-memory-snapshot') as PerformanceMemorySnapshot
|
const snapshot = await ipcRenderer.invoke('performance:get-memory-snapshot') as PerformanceMemorySnapshot
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export default function BottomBar({ onClose, onHeightChange }: BottomBarProps):
|
|||||||
const rootRef = useRef<HTMLDivElement | null>(null)
|
const rootRef = useRef<HTMLDivElement | null>(null)
|
||||||
const [astraBaseUrlInput, setAstraBaseUrlInput] = useState('')
|
const [astraBaseUrlInput, setAstraBaseUrlInput] = useState('')
|
||||||
const [astraTokenInput, setAstraTokenInput] = useState('')
|
const [astraTokenInput, setAstraTokenInput] = useState('')
|
||||||
|
const [memoryLogPath, setMemoryLogPath] = useState('')
|
||||||
|
|
||||||
const hiddenScopes = useSettingsStore((s) => s.hiddenScopes)
|
const hiddenScopes = useSettingsStore((s) => s.hiddenScopes)
|
||||||
const scopeOrder = useSettingsStore((s) => s.scopeOrder)
|
const scopeOrder = useSettingsStore((s) => s.scopeOrder)
|
||||||
@@ -95,6 +96,26 @@ export default function BottomBar({ onClose, onHeightChange }: BottomBarProps):
|
|||||||
setAstraTokenInput(astraState.config.token)
|
setAstraTokenInput(astraState.config.token)
|
||||||
}, [astraState.config.baseUrl, astraState.config.token])
|
}, [astraState.config.baseUrl, astraState.config.token])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let disposed = false
|
||||||
|
void (async () => {
|
||||||
|
try {
|
||||||
|
const path = await window.electronAPI.getPerformanceMemoryLogPath()
|
||||||
|
if (!disposed) {
|
||||||
|
setMemoryLogPath(path)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
if (!disposed) {
|
||||||
|
setMemoryLogPath('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposed = true
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!onHeightChange || !rootRef.current) return
|
if (!onHeightChange || !rootRef.current) return
|
||||||
|
|
||||||
@@ -440,6 +461,17 @@ export default function BottomBar({ onClose, onHeightChange }: BottomBarProps):
|
|||||||
<div className="settings-status-pill bottom-bar__fps-pill" title="Docked visualizer render FPS">
|
<div className="settings-status-pill bottom-bar__fps-pill" title="Docked visualizer render FPS">
|
||||||
<span>{roundedDockedRenderFps} FPS</span>
|
<span>{roundedDockedRenderFps} FPS</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="settings-chip"
|
||||||
|
title={memoryLogPath || 'Reveal the latest memory trace CSV'}
|
||||||
|
onClick={() => {
|
||||||
|
void window.electronAPI.revealPerformanceMemoryLog()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Log File
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{memorySample ? (
|
{memorySample ? (
|
||||||
|
|||||||
Vendored
+4
-1
@@ -29,7 +29,7 @@ import type {
|
|||||||
ThemeLibrarySnapshot,
|
ThemeLibrarySnapshot,
|
||||||
} from '../types/theme'
|
} from '../types/theme'
|
||||||
import type { ResizeDirection } from '../types/windowResize'
|
import type { ResizeDirection } from '../types/windowResize'
|
||||||
import type { PerformanceMemorySnapshot } from '../types/performance'
|
import type { PerformanceMemoryLogRecord, PerformanceMemorySnapshot } from '../types/performance'
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@@ -48,6 +48,9 @@ declare global {
|
|||||||
repositionWindow: (position: 'top' | 'bottom') => void
|
repositionWindow: (position: 'top' | 'bottom') => void
|
||||||
toggleAlwaysOnTop: () => void
|
toggleAlwaysOnTop: () => void
|
||||||
isAlwaysOnTop: () => Promise<boolean>
|
isAlwaysOnTop: () => Promise<boolean>
|
||||||
|
getPerformanceMemoryLogPath: () => Promise<string>
|
||||||
|
revealPerformanceMemoryLog: () => Promise<void>
|
||||||
|
appendPerformanceMemoryLog: (record: PerformanceMemoryLogRecord) => Promise<void>
|
||||||
getPerformanceMemorySnapshot: () => Promise<PerformanceMemorySnapshot>
|
getPerformanceMemorySnapshot: () => Promise<PerformanceMemorySnapshot>
|
||||||
getDesktopSources: () => Promise<{ id: string; name: string }[]>
|
getDesktopSources: () => Promise<{ id: string; name: string }[]>
|
||||||
getCaptureBackendSupport: () => Promise<CaptureBackendSupport>
|
getCaptureBackendSupport: () => Promise<CaptureBackendSupport>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { create } from 'zustand'
|
import { create } from 'zustand'
|
||||||
import {
|
import {
|
||||||
isVisualizerFrameTarget,
|
isVisualizerFrameTarget,
|
||||||
|
type PerformanceMemoryLogRecord,
|
||||||
type PerformanceMemorySnapshot,
|
type PerformanceMemorySnapshot,
|
||||||
type VisualizerFrameTarget,
|
type VisualizerFrameTarget,
|
||||||
} from '../../types/performance'
|
} from '../../types/performance'
|
||||||
@@ -88,6 +89,17 @@ interface ChromiumPerformanceMemory {
|
|||||||
let memoryMonitorTimer: ReturnType<typeof setInterval> | null = null
|
let memoryMonitorTimer: ReturnType<typeof setInterval> | null = null
|
||||||
let memoryMonitorRefCount = 0
|
let memoryMonitorRefCount = 0
|
||||||
let memoryMonitorInFlight = false
|
let memoryMonitorInFlight = false
|
||||||
|
let contextStoreAccessorsPromise: Promise<{
|
||||||
|
getAudioState: () => {
|
||||||
|
isCapturing: boolean
|
||||||
|
captureStatus: 'idle' | 'connecting' | 'capturing' | 'error'
|
||||||
|
}
|
||||||
|
getSettingsState: () => {
|
||||||
|
scopeOrder: string[]
|
||||||
|
hiddenScopes: Set<string>
|
||||||
|
scopePopouts: Record<string, { poppedOut?: boolean }>
|
||||||
|
}
|
||||||
|
}> | null = null
|
||||||
|
|
||||||
function isMainWindowContext(): boolean {
|
function isMainWindowContext(): boolean {
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
@@ -117,6 +129,34 @@ async function collectMemorySnapshot(): Promise<PerformanceMemorySnapshot | null
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getContextStoreAccessors(): Promise<{
|
||||||
|
getAudioState: () => {
|
||||||
|
isCapturing: boolean
|
||||||
|
captureStatus: 'idle' | 'connecting' | 'capturing' | 'error'
|
||||||
|
}
|
||||||
|
getSettingsState: () => {
|
||||||
|
scopeOrder: string[]
|
||||||
|
hiddenScopes: Set<string>
|
||||||
|
scopePopouts: Record<string, { poppedOut?: boolean }>
|
||||||
|
}
|
||||||
|
} | null> {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!contextStoreAccessorsPromise) {
|
||||||
|
contextStoreAccessorsPromise = Promise.all([
|
||||||
|
import('./audioStore'),
|
||||||
|
import('./settingsStore'),
|
||||||
|
]).then(([audioStoreModule, settingsStoreModule]) => ({
|
||||||
|
getAudioState: () => audioStoreModule.useAudioStore.getState(),
|
||||||
|
getSettingsState: () => settingsStoreModule.useSettingsStore.getState(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return contextStoreAccessorsPromise
|
||||||
|
}
|
||||||
|
|
||||||
async function sampleMemory(): Promise<void> {
|
async function sampleMemory(): Promise<void> {
|
||||||
if (memoryMonitorInFlight) {
|
if (memoryMonitorInFlight) {
|
||||||
return
|
return
|
||||||
@@ -129,6 +169,8 @@ async function sampleMemory(): Promise<void> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const contextStoreAccessors = await getContextStoreAccessors()
|
||||||
|
let logRecord: PerformanceMemoryLogRecord | null = null
|
||||||
usePerformanceStore.setState((state) => {
|
usePerformanceStore.setState((state) => {
|
||||||
const baseline = state.memoryHistory[0] ?? snapshot
|
const baseline = state.memoryHistory[0] ?? snapshot
|
||||||
const nextHistory = [...state.memoryHistory, snapshot]
|
const nextHistory = [...state.memoryHistory, snapshot]
|
||||||
@@ -138,14 +180,41 @@ async function sampleMemory(): Promise<void> {
|
|||||||
|
|
||||||
const baselineRendererMb = baseline.rendererPrivateMb ?? baseline.rendererMb
|
const baselineRendererMb = baseline.rendererPrivateMb ?? baseline.rendererMb
|
||||||
const currentRendererMb = snapshot.rendererPrivateMb ?? snapshot.rendererMb
|
const currentRendererMb = snapshot.rendererPrivateMb ?? snapshot.rendererMb
|
||||||
|
const rendererDeltaMb = Math.round((currentRendererMb - baselineRendererMb) * 10) / 10
|
||||||
|
const appDeltaMb = Math.round((snapshot.appMb - baseline.appMb) * 10) / 10
|
||||||
|
const settingsState = contextStoreAccessors?.getSettingsState()
|
||||||
|
const audioState = contextStoreAccessors?.getAudioState()
|
||||||
|
const visibleScopes = settingsState
|
||||||
|
? settingsState.scopeOrder.filter((kind) => !settingsState.hiddenScopes.has(kind))
|
||||||
|
: []
|
||||||
|
const poppedOutScopes = settingsState
|
||||||
|
? settingsState.scopeOrder.filter((kind) => settingsState.scopePopouts[kind]?.poppedOut)
|
||||||
|
: []
|
||||||
|
|
||||||
|
logRecord = {
|
||||||
|
...snapshot,
|
||||||
|
elapsedSeconds: Math.round(((snapshot.capturedAt - baseline.capturedAt) / 100)) / 10,
|
||||||
|
rendererDeltaMb,
|
||||||
|
appDeltaMb,
|
||||||
|
frameTarget: state.frameTarget,
|
||||||
|
dockedRenderFps: state.dockedRenderFps,
|
||||||
|
isCapturing: audioState?.isCapturing ?? false,
|
||||||
|
captureStatus: audioState?.captureStatus ?? 'idle',
|
||||||
|
visibleScopes,
|
||||||
|
poppedOutScopes,
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
memorySample: snapshot,
|
memorySample: snapshot,
|
||||||
memoryHistory: nextHistory,
|
memoryHistory: nextHistory,
|
||||||
rendererMemoryDeltaMb: Math.round((currentRendererMb - baselineRendererMb) * 10) / 10,
|
rendererMemoryDeltaMb: rendererDeltaMb,
|
||||||
appMemoryDeltaMb: Math.round((snapshot.appMb - baseline.appMb) * 10) / 10,
|
appMemoryDeltaMb: appDeltaMb,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (logRecord && typeof window !== 'undefined' && typeof window.electronAPI?.appendPerformanceMemoryLog === 'function') {
|
||||||
|
void window.electronAPI.appendPerformanceMemoryLog(logRecord)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
memoryMonitorInFlight = false
|
memoryMonitorInFlight = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,18 @@ export interface PerformanceMemorySnapshot {
|
|||||||
jsHeapLimitMb: number | null
|
jsHeapLimitMb: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PerformanceMemoryLogRecord extends PerformanceMemorySnapshot {
|
||||||
|
elapsedSeconds: number
|
||||||
|
rendererDeltaMb: number
|
||||||
|
appDeltaMb: number
|
||||||
|
frameTarget: VisualizerFrameTarget
|
||||||
|
dockedRenderFps: number
|
||||||
|
isCapturing: boolean
|
||||||
|
captureStatus: 'idle' | 'connecting' | 'capturing' | 'error'
|
||||||
|
visibleScopes: string[]
|
||||||
|
poppedOutScopes: string[]
|
||||||
|
}
|
||||||
|
|
||||||
export function isVisualizerFrameTarget(value: unknown): value is VisualizerFrameTarget {
|
export function isVisualizerFrameTarget(value: unknown): value is VisualizerFrameTarget {
|
||||||
return VISUALIZER_FRAME_TARGETS.includes(value as VisualizerFrameTarget)
|
return VISUALIZER_FRAME_TARGETS.includes(value as VisualizerFrameTarget)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user