diff --git a/dev-packages/node-integration-tests/suites/anr/basic-session.js b/dev-packages/node-integration-tests/suites/anr/basic-session.js index c6415b6358da..db3f442bba8b 100644 --- a/dev-packages/node-integration-tests/suites/anr/basic-session.js +++ b/dev-packages/node-integration-tests/suites/anr/basic-session.js @@ -10,7 +10,7 @@ setTimeout(() => { Sentry.init({ dsn: process.env.SENTRY_DSN, release: '1.0', - integrations: [Sentry.anrIntegration({ captureStackTrace: true, anrThreshold: 100 })], + integrations: [Sentry.anrIntegration({ anrThreshold: 100 })], autoSessionTracking: true, }); diff --git a/dev-packages/node-integration-tests/suites/anr/basic.js b/dev-packages/node-integration-tests/suites/anr/basic.js index b1dddf958d46..a870557ab408 100644 --- a/dev-packages/node-integration-tests/suites/anr/basic.js +++ b/dev-packages/node-integration-tests/suites/anr/basic.js @@ -11,7 +11,7 @@ Sentry.init({ dsn: process.env.SENTRY_DSN, release: '1.0', autoSessionTracking: false, - integrations: [Sentry.anrIntegration({ captureStackTrace: true, anrThreshold: 100 })], + integrations: [Sentry.anrIntegration({ anrThreshold: 100 })], }); Sentry.setUser({ email: 'person@home.com' }); diff --git a/dev-packages/node-integration-tests/suites/anr/basic.mjs b/dev-packages/node-integration-tests/suites/anr/basic.mjs index c3e74222f587..ad7c588e4e3d 100644 --- a/dev-packages/node-integration-tests/suites/anr/basic.mjs +++ b/dev-packages/node-integration-tests/suites/anr/basic.mjs @@ -11,7 +11,7 @@ Sentry.init({ dsn: process.env.SENTRY_DSN, release: '1.0', autoSessionTracking: false, - integrations: [Sentry.anrIntegration({ captureStackTrace: true, anrThreshold: 100 })], + integrations: [Sentry.anrIntegration({ anrThreshold: 100 })], }); Sentry.setUser({ email: 'person@home.com' }); diff --git a/dev-packages/node-integration-tests/suites/anr/forked.js b/dev-packages/node-integration-tests/suites/anr/forked.js index 06529096cca5..46b23d3a91eb 100644 --- a/dev-packages/node-integration-tests/suites/anr/forked.js +++ b/dev-packages/node-integration-tests/suites/anr/forked.js @@ -8,11 +8,11 @@ setTimeout(() => { }, 10000); Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', + dsn: process.env.SENTRY_DSN, release: '1.0', autoSessionTracking: false, debug: true, - integrations: [Sentry.anrIntegration({ captureStackTrace: true, anrThreshold: 100 })], + integrations: [Sentry.anrIntegration({ anrThreshold: 100 })], }); Sentry.setUser({ email: 'person@home.com' }); diff --git a/dev-packages/node-integration-tests/suites/anr/forker.js b/dev-packages/node-integration-tests/suites/anr/forker.js index c1ac5e1ccd1c..ee563d8fcd5c 100644 --- a/dev-packages/node-integration-tests/suites/anr/forker.js +++ b/dev-packages/node-integration-tests/suites/anr/forker.js @@ -1,7 +1,7 @@ const { fork } = require('child_process'); const { join } = require('path'); -const child = fork(join(__dirname, 'forked.js'), { stdio: 'inherit' }); +const child = fork(join(__dirname, 'forked.js'), { stdio: 'inherit', env: process.env }); child.on('exit', () => { process.exit(); }); diff --git a/dev-packages/node-integration-tests/suites/anr/isolated.mjs b/dev-packages/node-integration-tests/suites/anr/isolated.mjs index 2f36575fbbd2..c31d893d26a2 100644 --- a/dev-packages/node-integration-tests/suites/anr/isolated.mjs +++ b/dev-packages/node-integration-tests/suites/anr/isolated.mjs @@ -11,7 +11,7 @@ Sentry.init({ dsn: process.env.SENTRY_DSN, release: '1.0', autoSessionTracking: false, - integrations: [Sentry.anrIntegration({ captureStackTrace: true, anrThreshold: 100 })], + integrations: [Sentry.anrIntegration({ anrThreshold: 100 })], }); async function longWork() { diff --git a/dev-packages/node-integration-tests/suites/anr/should-exit-forced.js b/dev-packages/node-integration-tests/suites/anr/should-exit-forced.js index 01ee6f283819..878e5d2a0eb9 100644 --- a/dev-packages/node-integration-tests/suites/anr/should-exit-forced.js +++ b/dev-packages/node-integration-tests/suites/anr/should-exit-forced.js @@ -2,11 +2,11 @@ const Sentry = require('@sentry/node'); function configureSentry() { Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', + dsn: process.env.SENTRY_DSN, release: '1.0', autoSessionTracking: false, debug: true, - integrations: [Sentry.anrIntegration({ captureStackTrace: true })], + integrations: [Sentry.anrIntegration()], }); } diff --git a/dev-packages/node-integration-tests/suites/anr/should-exit.js b/dev-packages/node-integration-tests/suites/anr/should-exit.js index 5b3d23bf8cff..de53d0b9cc08 100644 --- a/dev-packages/node-integration-tests/suites/anr/should-exit.js +++ b/dev-packages/node-integration-tests/suites/anr/should-exit.js @@ -2,11 +2,11 @@ const Sentry = require('@sentry/node'); function configureSentry() { Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', + dsn: process.env.SENTRY_DSN, release: '1.0', autoSessionTracking: false, debug: true, - integrations: [Sentry.anrIntegration({ captureStackTrace: true })], + integrations: [Sentry.anrIntegration()], }); } diff --git a/dev-packages/node-integration-tests/suites/anr/stop-and-start.js b/dev-packages/node-integration-tests/suites/anr/stop-and-start.js index 4f9fc9bc64db..48d42c870691 100644 --- a/dev-packages/node-integration-tests/suites/anr/stop-and-start.js +++ b/dev-packages/node-integration-tests/suites/anr/stop-and-start.js @@ -7,10 +7,10 @@ setTimeout(() => { process.exit(); }, 10000); -const anr = Sentry.anrIntegration({ captureStackTrace: true, anrThreshold: 100 }); +const anr = Sentry.anrIntegration({ anrThreshold: 100 }); Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', + dsn: process.env.SENTRY_DSN, release: '1.0', debug: true, autoSessionTracking: false, diff --git a/dev-packages/node-integration-tests/suites/anr/test.ts b/dev-packages/node-integration-tests/suites/anr/test.ts index 6af5d46bfb50..4872ae724574 100644 --- a/dev-packages/node-integration-tests/suites/anr/test.ts +++ b/dev-packages/node-integration-tests/suites/anr/test.ts @@ -39,20 +39,20 @@ const EXPECTED_ANR_EVENT = { mechanism: { type: 'ANR' }, stacktrace: { frames: expect.arrayContaining([ - { + expect.objectContaining({ colno: expect.any(Number), lineno: expect.any(Number), filename: expect.any(String), function: '?', in_app: true, - }, - { + }), + expect.objectContaining({ colno: expect.any(Number), lineno: expect.any(Number), filename: expect.any(String), function: 'longWork', in_app: true, - }, + }), ]), }, }, @@ -82,7 +82,7 @@ conditionalTest({ min: 16 })('should report ANR when event loop blocked', () => }); test('should exit', done => { - const runner = createRunner(__dirname, 'should-exit.js').start(); + const runner = createRunner(__dirname, 'should-exit.js').withMockSentryServer().start(); setTimeout(() => { expect(runner.childHasExited()).toBe(true); @@ -91,7 +91,7 @@ conditionalTest({ min: 16 })('should report ANR when event loop blocked', () => }); test('should exit forced', done => { - const runner = createRunner(__dirname, 'should-exit-forced.js').start(); + const runner = createRunner(__dirname, 'should-exit-forced.js').withMockSentryServer().start(); setTimeout(() => { expect(runner.childHasExited()).toBe(true); @@ -113,11 +113,14 @@ conditionalTest({ min: 16 })('should report ANR when event loop blocked', () => }); test('from forked process', done => { - createRunner(__dirname, 'forker.js').expect({ event: EXPECTED_ANR_EVENT }).start(done); + createRunner(__dirname, 'forker.js').withMockSentryServer().expect({ event: EXPECTED_ANR_EVENT }).start(done); }); test('worker can be stopped and restarted', done => { - createRunner(__dirname, 'stop-and-start.js').expect({ event: EXPECTED_ANR_EVENT }).start(done); + createRunner(__dirname, 'stop-and-start.js') + .withMockSentryServer() + .expect({ event: EXPECTED_ANR_EVENT }) + .start(done); }); const EXPECTED_ISOLATED_EVENT = { @@ -132,13 +135,13 @@ conditionalTest({ min: 16 })('should report ANR when event loop blocked', () => mechanism: { type: 'ANR' }, stacktrace: { frames: expect.arrayContaining([ - { + expect.objectContaining({ colno: expect.any(Number), lineno: expect.any(Number), filename: expect.stringMatching(/isolated.mjs$/), function: 'longWork', in_app: true, - }, + }), ]), }, }, diff --git a/packages/node/src/integrations/anr/common.ts b/packages/node/src/integrations/anr/common.ts index e2e50fae4179..0bdf712cae76 100644 --- a/packages/node/src/integrations/anr/common.ts +++ b/packages/node/src/integrations/anr/common.ts @@ -1,4 +1,4 @@ -import type { Contexts, DsnComponents, Primitive, SdkMetadata } from '@sentry/types'; +import type { Primitive } from '@sentry/types'; export interface AnrIntegrationOptions { /** @@ -13,14 +13,6 @@ export interface AnrIntegrationOptions { * Defaults to 5000ms. */ anrThreshold: number; - /** - * Whether to capture a stack trace when the ANR event is triggered. - * - * Defaults to `false`. - * - * This uses the node debugger which enables the inspector API and opens the required ports. - */ - captureStackTrace: boolean; /** * Tags to include with ANR events. */ @@ -35,11 +27,4 @@ export interface AnrIntegrationOptions { export interface WorkerStartData extends AnrIntegrationOptions { debug: boolean; - sdkMetadata: SdkMetadata; - dsn: DsnComponents; - tunnel: string | undefined; - release: string | undefined; - environment: string; - dist: string | undefined; - contexts: Contexts; } diff --git a/packages/node/src/integrations/anr/index.ts b/packages/node/src/integrations/anr/index.ts index 7dbe9e905cb4..bd6d19cf23ff 100644 --- a/packages/node/src/integrations/anr/index.ts +++ b/packages/node/src/integrations/anr/index.ts @@ -1,9 +1,9 @@ -import { defineIntegration, mergeScopeData } from '@sentry/core'; -import type { Contexts, Event, EventHint, Integration, IntegrationFn, ScopeData } from '@sentry/types'; -import { GLOBAL_OBJ, logger } from '@sentry/utils'; +import { captureEvent, captureSession, defineIntegration, getIsolationScope, updateSession } from '@sentry/core'; +import type { Event, Integration, IntegrationFn, StackFrame } from '@sentry/types'; +import { GLOBAL_OBJ, logger, normalizeUrlToBase, stripSentryFramesAndReverse } from '@sentry/utils'; import * as inspector from 'inspector'; import { Worker } from 'worker_threads'; -import { getCurrentScope, getGlobalScope, getIsolationScope } from '../..'; +import { getCurrentScope } from '../..'; import { NODE_VERSION } from '../../nodeVersion'; import type { NodeClient } from '../../sdk/client'; import type { AnrIntegrationOptions, WorkerStartData } from './common'; @@ -16,55 +16,87 @@ function log(message: string, ...args: unknown[]): void { logger.log(`[ANR] ${message}`, ...args); } -function globalWithScopeFetchFn(): typeof GLOBAL_OBJ & { __SENTRY_GET_SCOPES__?: () => ScopeData } { +function globalWithScopeFetchFn(): typeof GLOBAL_OBJ & { + __SENTRY_SEND_ANR__?: (frames: StackFrame[]) => void; +} { return GLOBAL_OBJ; } -/** Fetches merged scope data */ -function getScopeData(): ScopeData { - const scope = getGlobalScope().getScopeData(); - mergeScopeData(scope, getIsolationScope().getScopeData()); - mergeScopeData(scope, getCurrentScope().getScopeData()); - - // We remove attachments because they likely won't serialize well as json - scope.attachments = []; - // We can't serialize event processor functions - scope.eventProcessors = []; - - return scope; -} - -/** - * Gets contexts by calling all event processors. This shouldn't be called until all integrations are setup - */ -async function getContexts(client: NodeClient): Promise { - let event: Event | null = { message: 'ANR' }; - const eventHint: EventHint = {}; - - for (const processor of client.getEventProcessors()) { - if (event === null) break; - event = await processor(event, eventHint); - } - - return event?.contexts || {}; -} - const INTEGRATION_NAME = 'Anr'; type AnrInternal = { startWorker: () => void; stopWorker: () => void }; -const _anrIntegration = ((options: Partial = {}) => { +const _anrIntegration = ((integrationOptions: Partial = {}) => { if (NODE_VERSION.major < 16 || (NODE_VERSION.major === 16 && NODE_VERSION.minor < 17)) { throw new Error('ANR detection requires Node 16.17.0 or later'); } + const options: WorkerStartData = { + debug: logger.isEnabled(), + appRootPath: integrationOptions.appRootPath, + pollInterval: integrationOptions.pollInterval || DEFAULT_INTERVAL, + anrThreshold: integrationOptions.anrThreshold || DEFAULT_HANG_THRESHOLD, + staticTags: integrationOptions.staticTags || {}, + }; + let worker: Promise<() => void> | undefined; let client: NodeClient | undefined; + function prepareStackFrames(stackFrames: StackFrame[] | undefined): StackFrame[] | undefined { + if (!stackFrames) { + return undefined; + } + + // Strip Sentry frames and reverse the stack frames so they are in the correct order + const strippedFrames = stripSentryFramesAndReverse(stackFrames); + + // If we have an app root path, rewrite the filenames to be relative to the app root + if (options.appRootPath) { + for (const frame of strippedFrames) { + if (!frame.filename) { + continue; + } + + frame.filename = normalizeUrlToBase(frame.filename, options.appRootPath); + } + } + + return strippedFrames; + } + + function sendAnrEvent(frames?: StackFrame[]): void { + const session = getIsolationScope().getSession(); + if (session) { + log('Sending abnormal session'); + updateSession(session, { status: 'abnormal', abnormal_mechanism: 'anr_foreground' }); + captureSession(); + } + + log('Sending event'); + + const event: Event = { + level: 'error', + exception: { + values: [ + { + type: 'ApplicationNotResponding', + value: `Application Not Responding for at least ${options.anrThreshold} ms`, + stacktrace: { frames: prepareStackFrames(frames) }, + // This ensures the UI doesn't say 'Crashed in' for the stack trace + mechanism: { type: 'ANR' }, + }, + ], + }, + tags: options.staticTags, + }; + + captureEvent(event); + } + // Hookup the scope fetch function to the global object so that it can be called from the worker thread via the // debugger when it pauses const gbl = globalWithScopeFetchFn(); - gbl.__SENTRY_GET_SCOPES__ = getScopeData; + gbl.__SENTRY_SEND_ANR__ = sendAnrEvent; return { name: INTEGRATION_NAME, @@ -74,7 +106,7 @@ const _anrIntegration = ((options: Partial = {}) => { } if (client) { - worker = _startWorker(client, options); + worker = _startWorker(options); } }, stopWorker: () => { @@ -105,51 +137,9 @@ export const anrIntegration = defineIntegration(_anrIntegration) as AnrReturn; * * @returns A function to stop the worker */ -async function _startWorker( - client: NodeClient, - integrationOptions: Partial, -): Promise<() => void> { - const dsn = client.getDsn(); - - if (!dsn) { - return () => { - // - }; - } - - const contexts = await getContexts(client); - - // These will not be accurate if sent later from the worker thread - delete contexts.app?.app_memory; - delete contexts.device?.free_memory; - - const initOptions = client.getOptions(); - - const sdkMetadata = client.getSdkMetadata() || {}; - if (sdkMetadata.sdk) { - sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name); - } - - const options: WorkerStartData = { - debug: logger.isEnabled(), - dsn, - tunnel: initOptions.tunnel, - environment: initOptions.environment || 'production', - release: initOptions.release, - dist: initOptions.dist, - sdkMetadata, - appRootPath: integrationOptions.appRootPath, - pollInterval: integrationOptions.pollInterval || DEFAULT_INTERVAL, - anrThreshold: integrationOptions.anrThreshold || DEFAULT_HANG_THRESHOLD, - captureStackTrace: !!integrationOptions.captureStackTrace, - staticTags: integrationOptions.staticTags || {}, - contexts, - }; - - if (options.captureStackTrace) { - if (!inspector.url()) { - inspector.open(0); - } +async function _startWorker(options: WorkerStartData): Promise<() => void> { + if (!inspector.url()) { + inspector.open(0); } const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), { @@ -163,12 +153,7 @@ async function _startWorker( const timer = setInterval(() => { try { - const currentSession = getCurrentScope().getSession(); - // We need to copy the session object and remove the toJSON method so it can be sent to the worker - // serialized without making it a SerializedSession - const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined; - // message the worker to tell it the main event loop is still running - worker.postMessage({ session }); + worker.postMessage({}); } catch (_) { // } diff --git a/packages/node/src/integrations/anr/worker.ts b/packages/node/src/integrations/anr/worker.ts index 21bdcbbb0631..e46033a6edcb 100644 --- a/packages/node/src/integrations/anr/worker.ts +++ b/packages/node/src/integrations/anr/worker.ts @@ -1,31 +1,13 @@ -import { - applyScopeDataToEvent, - createEventEnvelope, - createSessionEnvelope, - getEnvelopeEndpointWithUrlEncodedAuth, - makeSession, - updateSession, -} from '@sentry/core'; -import type { Event, ScopeData, Session, StackFrame } from '@sentry/types'; -import { - callFrameToStackFrame, - normalizeUrlToBase, - stripSentryFramesAndReverse, - uuid4, - watchdogTimer, -} from '@sentry/utils'; +import { callFrameToStackFrame, watchdogTimer } from '@sentry/utils'; import { Session as InspectorSession } from 'inspector'; import { parentPort, workerData } from 'worker_threads'; -import { makeNodeTransport } from '../../transports'; import { createGetModuleFromFilename } from '../../utils/module'; import type { WorkerStartData } from './common'; type VoidFunction = () => void; const options: WorkerStartData = workerData; -let session: Session | undefined; -let hasSentAnrEvent = false; function log(msg: string): void { if (options.debug) { @@ -34,202 +16,70 @@ function log(msg: string): void { } } -const url = getEnvelopeEndpointWithUrlEncodedAuth(options.dsn, options.tunnel, options.sdkMetadata.sdk); -const transport = makeNodeTransport({ - url, - recordDroppedEvent: () => { - // - }, -}); +log('Started. Connecting to debugger'); -async function sendAbnormalSession(): Promise { - // of we have an existing session passed from the main thread, send it as abnormal - if (session) { - log('Sending abnormal session'); - updateSession(session, { status: 'abnormal', abnormal_mechanism: 'anr_foreground' }); +const session = new InspectorSession(); +session.connectToMainThread(); - const envelope = createSessionEnvelope(session, options.dsn, options.sdkMetadata, options.tunnel); - // Log the envelope so to aid in testing - log(JSON.stringify(envelope)); +log('Connected to debugger'); - await transport.send(envelope); +// Collect scriptId -> url map so we can look up the filenames later +const scripts = new Map(); - try { - // Notify the main process that the session has ended so the session can be cleared from the scope - parentPort?.postMessage('session-ended'); - } catch (_) { - // ignore - } - } -} - -log('Started'); - -function prepareStackFrames(stackFrames: StackFrame[] | undefined): StackFrame[] | undefined { - if (!stackFrames) { - return undefined; - } - - // Strip Sentry frames and reverse the stack frames so they are in the correct order - const strippedFrames = stripSentryFramesAndReverse(stackFrames); - - // If we have an app root path, rewrite the filenames to be relative to the app root - if (options.appRootPath) { - for (const frame of strippedFrames) { - if (!frame.filename) { - continue; - } +session.on('Debugger.scriptParsed', event => { + scripts.set(event.params.scriptId, event.params.url); +}); - frame.filename = normalizeUrlToBase(frame.filename, options.appRootPath); - } +session.on('Debugger.paused', event => { + if (event.params.reason !== 'other') { + return; } - return strippedFrames; -} - -function applyScopeToEvent(event: Event, scope: ScopeData): void { - applyScopeDataToEvent(event, scope); - - if (!event.contexts?.trace) { - const { traceId, spanId, parentSpanId } = scope.propagationContext; - event.contexts = { - trace: { - trace_id: traceId, - span_id: spanId, - parent_span_id: parentSpanId, + try { + log('Debugger paused'); + + // copy the frames + const callFrames = [...event.params.callFrames]; + + const getModuleName = options.appRootPath ? createGetModuleFromFilename(options.appRootPath) : () => undefined; + const stackFrames = callFrames.map(frame => + callFrameToStackFrame(frame, scripts.get(frame.location.scriptId), getModuleName), + ); + + // Evaluate a script in the currently paused context + session.post( + 'Runtime.evaluate', + { + // Send the stack frames to the main thread to be sent by the SDK + expression: `global.__SENTRY_SEND_ANR__(${JSON.stringify(stackFrames)});`, + // Don't re-trigger the debugger if this causes an error + silent: true, }, - ...event.contexts, - }; - } -} + err => { + if (err) { + log(`Error executing script: '${err.message}'`); + } -async function sendAnrEvent(frames?: StackFrame[], scope?: ScopeData): Promise { - if (hasSentAnrEvent) { - return; + session.post('Debugger.resume'); + session.post('Debugger.disable'); + }, + ); + } catch (e) { + session.post('Debugger.resume'); + session.post('Debugger.disable'); + throw e; } +}); - hasSentAnrEvent = true; - - await sendAbnormalSession(); - - log('Sending event'); - - const event: Event = { - event_id: uuid4(), - contexts: options.contexts, - release: options.release, - environment: options.environment, - dist: options.dist, - platform: 'node', - level: 'error', - exception: { - values: [ - { - type: 'ApplicationNotResponding', - value: `Application Not Responding for at least ${options.anrThreshold} ms`, - stacktrace: { frames: prepareStackFrames(frames) }, - // This ensures the UI doesn't say 'Crashed in' for the stack trace - mechanism: { type: 'ANR' }, - }, - ], - }, - tags: options.staticTags, - }; - - if (scope) { - applyScopeToEvent(event, scope); +const debuggerPause = (): void => { + try { + session.post('Debugger.enable', () => { + session.post('Debugger.pause'); + }); + } catch (_) { + // } - - const envelope = createEventEnvelope(event, options.dsn, options.sdkMetadata, options.tunnel); - // Log the envelope to aid in testing - log(JSON.stringify(envelope)); - - await transport.send(envelope); - await transport.flush(2000); - - // Delay for 5 seconds so that stdio can flush if the main event loop ever restarts. - // This is mainly for the benefit of logging or debugging. - setTimeout(() => { - process.exit(0); - }, 5_000); -} - -let debuggerPause: VoidFunction | undefined; - -if (options.captureStackTrace) { - log('Connecting to debugger'); - - const session = new InspectorSession(); - session.connectToMainThread(); - - log('Connected to debugger'); - - // Collect scriptId -> url map so we can look up the filenames later - const scripts = new Map(); - - session.on('Debugger.scriptParsed', event => { - scripts.set(event.params.scriptId, event.params.url); - }); - - session.on('Debugger.paused', event => { - if (event.params.reason !== 'other') { - return; - } - - try { - log('Debugger paused'); - - // copy the frames - const callFrames = [...event.params.callFrames]; - - const getModuleName = options.appRootPath ? createGetModuleFromFilename(options.appRootPath) : () => undefined; - const stackFrames = callFrames.map(frame => - callFrameToStackFrame(frame, scripts.get(frame.location.scriptId), getModuleName), - ); - - // Evaluate a script in the currently paused context - session.post( - 'Runtime.evaluate', - { - // Grab the trace context from the current scope - expression: 'global.__SENTRY_GET_SCOPES__();', - // Don't re-trigger the debugger if this causes an error - silent: true, - // Serialize the result to json otherwise only primitives are supported - returnByValue: true, - }, - (err, param) => { - if (err) { - log(`Error executing script: '${err.message}'`); - } - - const scopes = param && param.result ? (param.result.value as ScopeData) : undefined; - - session.post('Debugger.resume'); - session.post('Debugger.disable'); - - sendAnrEvent(stackFrames, scopes).then(null, () => { - log('Sending ANR event failed.'); - }); - }, - ); - } catch (e) { - session.post('Debugger.resume'); - session.post('Debugger.disable'); - throw e; - } - }); - - debuggerPause = () => { - try { - session.post('Debugger.enable', () => { - session.post('Debugger.pause'); - }); - } catch (_) { - // - } - }; -} +}; function createHrTimer(): { getTimeMs: () => number; reset: VoidFunction } { // TODO (v8): We can use process.hrtime.bigint() after we drop node v8 @@ -249,23 +99,12 @@ function createHrTimer(): { getTimeMs: () => number; reset: VoidFunction } { function watchdogTimeout(): void { log('Watchdog timeout'); - if (debuggerPause) { - log('Pausing debugger to capture stack trace'); - debuggerPause(); - } else { - log('Capturing event without a stack trace'); - sendAnrEvent().then(null, () => { - log('Sending ANR event failed on watchdog timeout.'); - }); - } + log('Pausing debugger to capture stack trace'); + debuggerPause(); } const { poll } = watchdogTimer(createHrTimer, options.pollInterval, options.anrThreshold, watchdogTimeout); -parentPort?.on('message', (msg: { session: Session | undefined }) => { - if (msg.session) { - session = makeSession(msg.session); - } - +parentPort?.on('message', () => { poll(); });