From de65590b0e6fff42548be9b07b67df0067eb444d Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Wed, 27 Nov 2024 00:17:27 -0800 Subject: [PATCH] feat(core): Further optimize debug ID parsing (#14365) --- packages/core/src/utils-hoist/debug-ids.ts | 63 +++++++++++++-------- packages/core/src/utils/prepareEvent.ts | 2 +- packages/node/src/integrations/anr/index.ts | 16 +----- 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/packages/core/src/utils-hoist/debug-ids.ts b/packages/core/src/utils-hoist/debug-ids.ts index 4802b9356965..d407e6176e45 100644 --- a/packages/core/src/utils-hoist/debug-ids.ts +++ b/packages/core/src/utils-hoist/debug-ids.ts @@ -1,7 +1,12 @@ -import type { DebugImage, StackFrame, StackParser } from '@sentry/types'; +import type { DebugImage, StackParser } from '@sentry/types'; import { GLOBAL_OBJ } from './worldwide'; -const debugIdStackParserCache = new WeakMap>(); +type StackString = string; +type CachedResult = [string, string]; + +let parsedStackResults: Record | undefined; +let lastKeysCount: number | undefined; +let cachedFilenameDebugIds: Record | undefined; /** * Returns a map of filenames to debug identifiers. @@ -12,38 +17,46 @@ export function getFilenameToDebugIdMap(stackParser: StackParser): Record; - const cachedDebugIdStackFrameCache = debugIdStackParserCache.get(stackParser); - if (cachedDebugIdStackFrameCache) { - debugIdStackFramesCache = cachedDebugIdStackFrameCache; - } else { - debugIdStackFramesCache = new Map(); - debugIdStackParserCache.set(stackParser, debugIdStackFramesCache); + const debugIdKeys = Object.keys(debugIdMap); + + // If the count of registered globals hasn't changed since the last call, we + // can just return the cached result. + if (cachedFilenameDebugIds && debugIdKeys.length === lastKeysCount) { + return cachedFilenameDebugIds; } + lastKeysCount = debugIdKeys.length; + // Build a map of filename -> debug_id. - return Object.keys(debugIdMap).reduce>((acc, debugIdStackTrace) => { - let parsedStack: StackFrame[]; + cachedFilenameDebugIds = debugIdKeys.reduce>((acc, stackKey) => { + if (!parsedStackResults) { + parsedStackResults = {}; + } + + const result = parsedStackResults[stackKey]; - const cachedParsedStack = debugIdStackFramesCache.get(debugIdStackTrace); - if (cachedParsedStack) { - parsedStack = cachedParsedStack; + if (result) { + acc[result[0]] = result[1]; } else { - parsedStack = stackParser(debugIdStackTrace); - debugIdStackFramesCache.set(debugIdStackTrace, parsedStack); - } + const parsedStack = stackParser(stackKey); - for (let i = parsedStack.length - 1; i >= 0; i--) { - const stackFrame = parsedStack[i]; - const file = stackFrame && stackFrame.filename; + for (let i = parsedStack.length - 1; i >= 0; i--) { + const stackFrame = parsedStack[i]; + const filename = stackFrame && stackFrame.filename; + const debugId = debugIdMap[stackKey]; - if (stackFrame && file) { - acc[file] = debugIdMap[debugIdStackTrace] as string; - break; + if (filename && debugId) { + acc[filename] = debugId; + parsedStackResults[stackKey] = [filename, debugId]; + break; + } } } + return acc; }, {}); + + return cachedFilenameDebugIds; } /** @@ -55,6 +68,10 @@ export function getDebugImagesForResources( ): DebugImage[] { const filenameDebugIdMap = getFilenameToDebugIdMap(stackParser); + if (!filenameDebugIdMap) { + return []; + } + const images: DebugImage[] = []; for (const path of resource_paths) { if (path && filenameDebugIdMap[path]) { diff --git a/packages/core/src/utils/prepareEvent.ts b/packages/core/src/utils/prepareEvent.ts index 98f64adbdd62..14358d4e3101 100644 --- a/packages/core/src/utils/prepareEvent.ts +++ b/packages/core/src/utils/prepareEvent.ts @@ -179,7 +179,7 @@ export function applyDebugIds(event: Event, stackParser: StackParser): void { event!.exception!.values!.forEach(exception => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion exception.stacktrace!.frames!.forEach(frame => { - if (frame.filename) { + if (filenameDebugIdMap && frame.filename) { frame.debug_id = filenameDebugIdMap[frame.filename]; } }); diff --git a/packages/node/src/integrations/anr/index.ts b/packages/node/src/integrations/anr/index.ts index 1fa2218d5879..0777940cc211 100644 --- a/packages/node/src/integrations/anr/index.ts +++ b/packages/node/src/integrations/anr/index.ts @@ -1,4 +1,3 @@ -import * as diagnosticsChannel from 'node:diagnostics_channel'; import { Worker } from 'node:worker_threads'; import { defineIntegration, getCurrentScope, getGlobalScope, getIsolationScope, mergeScopeData } from '@sentry/core'; import { GLOBAL_OBJ, getFilenameToDebugIdMap, logger } from '@sentry/core'; @@ -101,13 +100,6 @@ type AnrReturn = (options?: Partial) => Integration & Anr export const anrIntegration = defineIntegration(_anrIntegration) as AnrReturn; -function onModuleLoad(callback: () => void): void { - // eslint-disable-next-line deprecation/deprecation - diagnosticsChannel.channel('module.require.end').subscribe(() => callback()); - // eslint-disable-next-line deprecation/deprecation - diagnosticsChannel.channel('module.import.asyncEnd').subscribe(() => callback()); -} - /** * Starts the ANR worker thread * @@ -161,12 +153,6 @@ async function _startWorker( } } - let debugImages: Record = getFilenameToDebugIdMap(initOptions.stackParser); - - onModuleLoad(() => { - debugImages = getFilenameToDebugIdMap(initOptions.stackParser); - }); - const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), { workerData: options, // We don't want any Node args to be passed to the worker @@ -185,7 +171,7 @@ async function _startWorker( // 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, debugImages }); + worker.postMessage({ session, debugImages: getFilenameToDebugIdMap(initOptions.stackParser) }); } catch (_) { // }