Suppress recoverable Chromium child crash reports (#2762)

This commit is contained in:
Neil 2026-05-24 18:17:03 -07:00 committed by GitHub
parent b24cc8087a
commit 4a8253a99e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 159 additions and 11 deletions

View File

@ -105,6 +105,113 @@ describe('shouldRecordProcessGoneCrash', () => {
).toBe(false)
})
it('skips Windows control termination killed events outside expected lifecycle teardown', () => {
expect(
shouldRecordProcessGoneCrash({
source: 'renderer',
reason: 'killed',
exitCode: -1073741510,
expectedTeardown: 'none'
})
).toBe(false)
expect(
shouldRecordProcessGoneCrash({
source: 'child',
reason: 'killed',
exitCode: 1073807364,
expectedTeardown: 'none'
})
).toBe(false)
})
it('skips recoverable Chromium child process exits', () => {
expect(
shouldRecordProcessGoneCrash({
source: 'child',
processType: 'GPU',
reason: 'crashed',
exitCode: -2147483645,
expectedTeardown: 'none'
})
).toBe(false)
expect(
shouldRecordProcessGoneCrash({
source: 'child',
processType: 'gpu',
reason: 'killed',
exitCode: 1,
expectedTeardown: 'none'
})
).toBe(false)
expect(
shouldRecordProcessGoneCrash({
source: 'child',
processType: 'Utility',
serviceName: 'network.mojom.NetworkService',
reason: 'killed',
exitCode: 9,
expectedTeardown: 'none'
})
).toBe(false)
expect(
shouldRecordProcessGoneCrash({
source: 'child',
processType: 'Utility',
serviceName: 'network.mojom.NetworkService',
reason: 'crashed',
exitCode: -1,
expectedTeardown: 'none'
})
).toBe(false)
})
it('still records unknown child process crashes', () => {
expect(
shouldRecordProcessGoneCrash({
source: 'child',
processType: 'Utility',
serviceName: 'com.orca.unexpected',
reason: 'crashed',
exitCode: 5,
expectedTeardown: 'none'
})
).toBe(true)
})
it('still records severe Chromium child process failures', () => {
expect(
shouldRecordProcessGoneCrash({
source: 'child',
processType: 'GPU',
reason: 'oom',
exitCode: 1,
expectedTeardown: 'none'
})
).toBe(true)
expect(
shouldRecordProcessGoneCrash({
source: 'child',
processType: 'Utility',
serviceName: 'network.mojom.NetworkService',
reason: 'launch-failed',
exitCode: -1,
expectedTeardown: 'none'
})
).toBe(true)
})
it('still records renderer kills from the recent Linux crash-report cluster', () => {
expect(
shouldRecordProcessGoneCrash({
source: 'renderer',
processType: 'renderer',
reason: 'killed',
exitCode: 61696,
expectedTeardown: 'none'
})
).toBe(true)
})
it('records non-SIGTERM killed process exits outside expected lifecycle teardown', () => {
expect(
shouldRecordProcessGoneCrash({
@ -121,7 +228,8 @@ describe('shouldRecordProcessGoneCrash', () => {
expect(
shouldRecordProcessGoneCrash({
source: 'child',
processType: 'GPU',
processType: 'Utility',
serviceName: 'com.orca.unexpected',
reason: 'killed',
exitCode: 9,
expectedTeardown: 'renderer-reload'

View File

@ -1,26 +1,64 @@
export type ProcessGoneSource = 'renderer' | 'child'
export type ExpectedTeardownScope = 'none' | 'renderer-reload' | 'app-shutdown'
const WINDOWS_CONTROL_TERMINATION_EXIT_CODES = new Set([0xc000013a, 0x40010004])
const RECOVERABLE_CHILD_PROCESS_TYPES = new Set(['gpu'])
const RECOVERABLE_UTILITY_SERVICE_NAMES = new Set(['network.mojom.NetworkService'])
const RECOVERABLE_CHILD_PROCESS_REASONS = new Set(['crashed', 'killed'])
function isWindowsControlTerminationExitCode(exitCode: number | null): boolean {
if (exitCode === null) {
return false
}
return WINDOWS_CONTROL_TERMINATION_EXIT_CODES.has(exitCode >>> 0)
}
function isRecoverableChromiumChildProcess({
source,
processType,
serviceName,
reason
}: {
source: ProcessGoneSource
processType?: string
serviceName?: string
reason: string
}): boolean {
if (source !== 'child') {
return false
}
if (!RECOVERABLE_CHILD_PROCESS_REASONS.has(reason)) {
return false
}
const normalizedProcessType = processType?.toLowerCase()
if (normalizedProcessType && RECOVERABLE_CHILD_PROCESS_TYPES.has(normalizedProcessType)) {
return true
}
return (
normalizedProcessType === 'utility' &&
serviceName !== undefined &&
RECOVERABLE_UTILITY_SERVICE_NAMES.has(serviceName)
)
}
export function shouldRecordProcessGoneCrash({
source,
processType,
serviceName,
reason,
exitCode,
expectedTeardown
}: {
source: ProcessGoneSource
processType: string
processType?: string
serviceName?: string
reason: string
exitCode: number | null
expectedTeardown: ExpectedTeardownScope
}): boolean {
// Why: Chromium's GPU helper can emit a crash-shaped exit while Electron is
// already intentionally exiting/relaunching; that is shutdown noise.
if (
expectedTeardown === 'app-shutdown' &&
source === 'child' &&
processType.toLowerCase() === 'gpu'
) {
// Why: GPU and Network Service exits are recoverable Chromium child-process
// churn; treating them as app crashes creates noisy user prompts.
if (isRecoverableChromiumChildProcess({ source, processType, serviceName, reason })) {
return false
}
// Why: Electron reports intentional reload/update/quit teardown as `killed`.
@ -29,8 +67,9 @@ export function shouldRecordProcessGoneCrash({
return true
}
// Why: Electron reports expected Chromium teardown during reload/update as
// `killed` + SIGTERM. Treat real crash reasons as reportable, but skip this.
if (exitCode === 15) {
// `killed` + SIGTERM or Windows control termination statuses. Treat real
// crash reasons as reportable, but skip these normal termination shapes.
if (exitCode === 15 || isWindowsControlTerminationExitCode(exitCode)) {
return false
}
if (expectedTeardown === 'app-shutdown') {

View File

@ -531,6 +531,7 @@ function recordProcessGoneCrash(
!shouldRecordProcessGoneCrash({
source,
processType,
serviceName: typeof details.serviceName === 'string' ? details.serviceName : undefined,
reason,
exitCode,
expectedTeardown: getExpectedTeardownScope(webContentsId)