mirror of
https://github.com/stablyai/orca.git
synced 2026-10-16 16:03:26 +00:00
Add renderer surface crash breadcrumbs
This commit is contained in:
@@ -11,13 +11,27 @@ describe('renderer crash diagnostics', () => {
|
||||
let setIntervalMock: ReturnType<typeof vi.fn>
|
||||
let clearIntervalMock: ReturnType<typeof vi.fn>
|
||||
let removeEventListenerMock: ReturnType<typeof vi.fn>
|
||||
let rootElement: {
|
||||
childElementCount: number
|
||||
getBoundingClientRect: ReturnType<typeof vi.fn>
|
||||
}
|
||||
let bodyElement: {
|
||||
getBoundingClientRect: ReturnType<typeof vi.fn>
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules()
|
||||
listeners = new Map()
|
||||
recordBreadcrumbMock = vi.fn()
|
||||
setIntervalMock = vi.fn(() => 1)
|
||||
setIntervalMock = vi.fn(() => setIntervalMock.mock.calls.length + 1)
|
||||
clearIntervalMock = vi.fn()
|
||||
rootElement = {
|
||||
childElementCount: 1,
|
||||
getBoundingClientRect: vi.fn(() => ({ width: 1200, height: 800 }))
|
||||
}
|
||||
bodyElement = {
|
||||
getBoundingClientRect: vi.fn(() => ({ width: 1200, height: 800 }))
|
||||
}
|
||||
removeEventListenerMock = vi.fn((type: string, listener: Listener) => {
|
||||
listeners.set(
|
||||
type,
|
||||
@@ -38,6 +52,14 @@ describe('renderer crash diagnostics', () => {
|
||||
removeEventListener: removeEventListenerMock,
|
||||
setInterval: setIntervalMock,
|
||||
clearInterval: clearIntervalMock,
|
||||
innerWidth: 1200,
|
||||
innerHeight: 800,
|
||||
devicePixelRatio: 1.5,
|
||||
getComputedStyle: vi.fn((element: unknown) =>
|
||||
element === rootElement
|
||||
? { display: 'block', visibility: 'visible', backgroundColor: 'rgba(0, 0, 0, 0)' }
|
||||
: { display: 'block', visibility: 'visible', backgroundColor: 'rgb(10, 10, 10)' }
|
||||
),
|
||||
performance: {
|
||||
memory: {
|
||||
usedJSHeapSize: 32 * 1024 * 1024,
|
||||
@@ -46,6 +68,12 @@ describe('renderer crash diagnostics', () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
vi.stubGlobal('document', {
|
||||
visibilityState: 'visible',
|
||||
body: bodyElement,
|
||||
hasFocus: vi.fn(() => true),
|
||||
getElementById: vi.fn((id: string) => (id === 'root' ? rootElement : null))
|
||||
})
|
||||
vi.doMock('../components/browser-pane/webview-registry', () => ({
|
||||
getBrowserWebviewMemoryProfile: () => ({
|
||||
browserWebviewCount: 4,
|
||||
@@ -68,12 +96,12 @@ describe('renderer crash diagnostics', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('installs startup, error, rejection, and memory breadcrumbs once', () => {
|
||||
it('installs startup, error, rejection, memory, and surface breadcrumbs once', () => {
|
||||
diagnostics.installRendererCrashDiagnostics()
|
||||
diagnostics.installRendererCrashDiagnostics()
|
||||
|
||||
expect(window.addEventListener).toHaveBeenCalledTimes(2)
|
||||
expect(setIntervalMock).toHaveBeenCalledTimes(1)
|
||||
expect(setIntervalMock).toHaveBeenCalledTimes(2)
|
||||
expect(recordBreadcrumbMock).toHaveBeenCalledWith({
|
||||
name: 'renderer_memory',
|
||||
data: {
|
||||
@@ -85,6 +113,27 @@ describe('renderer crash diagnostics', () => {
|
||||
registeredBrowserGuests: 3
|
||||
}
|
||||
})
|
||||
expect(recordBreadcrumbMock).toHaveBeenCalledWith({
|
||||
name: 'renderer_surface',
|
||||
data: {
|
||||
reason: 'startup',
|
||||
visibilityState: 'visible',
|
||||
hasFocus: true,
|
||||
innerWidth: 1200,
|
||||
innerHeight: 800,
|
||||
devicePixelRatio: 1.5,
|
||||
rootPresent: true,
|
||||
rootChildElementCount: 1,
|
||||
rootWidth: 1200,
|
||||
rootHeight: 800,
|
||||
bodyWidth: 1200,
|
||||
bodyHeight: 800,
|
||||
rootDisplay: 'block',
|
||||
rootVisibility: 'visible',
|
||||
rootBackgroundColor: 'rgba(0, 0, 0, 0)',
|
||||
bodyBackgroundColor: 'rgb(10, 10, 10)'
|
||||
}
|
||||
})
|
||||
|
||||
listeners.get('error')?.[0]?.({
|
||||
message: 'boom',
|
||||
@@ -116,14 +165,14 @@ describe('renderer crash diagnostics', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('disposes global listeners and the memory interval', () => {
|
||||
it('disposes global listeners and diagnostic intervals', () => {
|
||||
diagnostics.installRendererCrashDiagnostics()
|
||||
|
||||
diagnostics._disposeRendererCrashDiagnosticsForTests()
|
||||
|
||||
expect(removeEventListenerMock).toHaveBeenCalledWith('error', expect.any(Function))
|
||||
expect(removeEventListenerMock).toHaveBeenCalledWith('unhandledrejection', expect.any(Function))
|
||||
expect(clearIntervalMock).toHaveBeenCalledWith(1)
|
||||
expect(clearIntervalMock).toHaveBeenCalledTimes(2)
|
||||
expect(listeners.get('error')).toHaveLength(0)
|
||||
expect(listeners.get('unhandledrejection')).toHaveLength(0)
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
import { getBrowserWebviewMemoryProfile } from '../components/browser-pane/webview-registry'
|
||||
|
||||
const RENDERER_MEMORY_SAMPLE_INTERVAL_MS = 60_000
|
||||
const RENDERER_SURFACE_SAMPLE_INTERVAL_MS = 60_000
|
||||
const BYTES_PER_MEGABYTE = 1024 * 1024
|
||||
|
||||
type BrowserPerformanceMemory = {
|
||||
@@ -15,6 +16,7 @@ type BrowserPerformanceMemory = {
|
||||
|
||||
let rendererCrashDiagnosticsInstalled = false
|
||||
let rendererMemoryInterval: number | null = null
|
||||
let rendererSurfaceInterval: number | null = null
|
||||
|
||||
export function recordRendererCrashBreadcrumb(
|
||||
name: string,
|
||||
@@ -49,6 +51,11 @@ export function installRendererCrashDiagnostics(): void {
|
||||
RENDERER_MEMORY_SAMPLE_INTERVAL_MS
|
||||
)
|
||||
}
|
||||
recordRendererSurface('startup')
|
||||
rendererSurfaceInterval = window.setInterval(
|
||||
() => recordRendererSurface('interval'),
|
||||
RENDERER_SURFACE_SAMPLE_INTERVAL_MS
|
||||
)
|
||||
}
|
||||
|
||||
export function _disposeRendererCrashDiagnosticsForTests(): void {
|
||||
@@ -66,6 +73,10 @@ function disposeRendererCrashDiagnostics(): void {
|
||||
window.clearInterval(rendererMemoryInterval)
|
||||
rendererMemoryInterval = null
|
||||
}
|
||||
if (rendererSurfaceInterval !== null) {
|
||||
window.clearInterval(rendererSurfaceInterval)
|
||||
rendererSurfaceInterval = null
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof import.meta !== 'undefined' && import.meta.hot) {
|
||||
@@ -114,6 +125,40 @@ function recordRendererMemory(reason: string): void {
|
||||
)
|
||||
}
|
||||
|
||||
function recordRendererSurface(reason: string): void {
|
||||
if (typeof document === 'undefined' || typeof window === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
const root = document.getElementById('root')
|
||||
const rootRect = root?.getBoundingClientRect()
|
||||
const bodyRect = document.body?.getBoundingClientRect()
|
||||
const rootStyle = root ? window.getComputedStyle(root) : undefined
|
||||
const bodyStyle = document.body ? window.getComputedStyle(document.body) : undefined
|
||||
|
||||
recordRendererCrashBreadcrumb(
|
||||
'renderer_surface',
|
||||
compactBreadcrumbData({
|
||||
reason,
|
||||
visibilityState: document.visibilityState,
|
||||
hasFocus: typeof document.hasFocus === 'function' ? document.hasFocus() : undefined,
|
||||
innerWidth: window.innerWidth,
|
||||
innerHeight: window.innerHeight,
|
||||
devicePixelRatio: window.devicePixelRatio,
|
||||
rootPresent: Boolean(root),
|
||||
rootChildElementCount: root?.childElementCount,
|
||||
rootWidth: rootRect ? Math.round(rootRect.width) : undefined,
|
||||
rootHeight: rootRect ? Math.round(rootRect.height) : undefined,
|
||||
bodyWidth: bodyRect ? Math.round(bodyRect.width) : undefined,
|
||||
bodyHeight: bodyRect ? Math.round(bodyRect.height) : undefined,
|
||||
rootDisplay: rootStyle?.display,
|
||||
rootVisibility: rootStyle?.visibility,
|
||||
rootBackgroundColor: rootStyle?.backgroundColor,
|
||||
bodyBackgroundColor: bodyStyle?.backgroundColor
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function getPerformanceMemory(): BrowserPerformanceMemory | undefined {
|
||||
if (typeof window === 'undefined') {
|
||||
return undefined
|
||||
|
||||
Reference in New Issue
Block a user