diff --git a/packages/browser/src/profiling/utils.ts b/packages/browser/src/profiling/utils.ts index 50272d9b5554..926ea6bbb9f5 100644 --- a/packages/browser/src/profiling/utils.ts +++ b/packages/browser/src/profiling/utils.ts @@ -1,21 +1,11 @@ /* eslint-disable max-lines */ import { DEFAULT_ENVIRONMENT, getClient, spanToJSON } from '@sentry/core'; -import type { - DebugImage, - Envelope, - Event, - EventEnvelope, - Profile, - Span, - StackFrame, - StackParser, - ThreadCpuProfile, -} from '@sentry/types'; +import type { DebugImage, Envelope, Event, EventEnvelope, Profile, Span, ThreadCpuProfile } from '@sentry/types'; import { - GLOBAL_OBJ, browserPerformanceTimeOrigin, forEachEnvelopeItem, + getDebugImagesForResources, logger, timestampInSeconds, uuid4, @@ -352,17 +342,10 @@ export function findProfiledTransactionsFromEnvelope(envelope: Envelope): Event[ return events; } -const debugIdStackParserCache = new WeakMap>(); /** * Applies debug meta data to an event from a list of paths to resources (sourcemaps) */ export function applyDebugMetadata(resource_paths: ReadonlyArray): DebugImage[] { - const debugIdMap = GLOBAL_OBJ._sentryDebugIds; - - if (!debugIdMap) { - return []; - } - const client = getClient(); const options = client && client.getOptions(); const stackParser = options && options.stackParser; @@ -371,51 +354,7 @@ export function applyDebugMetadata(resource_paths: ReadonlyArray): Debug return []; } - let debugIdStackFramesCache: Map; - const cachedDebugIdStackFrameCache = debugIdStackParserCache.get(stackParser); - if (cachedDebugIdStackFrameCache) { - debugIdStackFramesCache = cachedDebugIdStackFrameCache; - } else { - debugIdStackFramesCache = new Map(); - debugIdStackParserCache.set(stackParser, debugIdStackFramesCache); - } - - // Build a map of filename -> debug_id - const filenameDebugIdMap = Object.keys(debugIdMap).reduce>((acc, debugIdStackTrace) => { - let parsedStack: StackFrame[]; - - const cachedParsedStack = debugIdStackFramesCache.get(debugIdStackTrace); - if (cachedParsedStack) { - parsedStack = cachedParsedStack; - } else { - parsedStack = stackParser(debugIdStackTrace); - debugIdStackFramesCache.set(debugIdStackTrace, parsedStack); - } - - for (let i = parsedStack.length - 1; i >= 0; i--) { - const stackFrame = parsedStack[i]; - const file = stackFrame && stackFrame.filename; - - if (stackFrame && file) { - acc[file] = debugIdMap[debugIdStackTrace] as string; - break; - } - } - return acc; - }, {}); - - const images: DebugImage[] = []; - for (const path of resource_paths) { - if (path && filenameDebugIdMap[path]) { - images.push({ - type: 'sourcemap', - code_file: path, - debug_id: filenameDebugIdMap[path] as string, - }); - } - } - - return images; + return getDebugImagesForResources(stackParser, resource_paths); } /** diff --git a/packages/core/src/utils/prepareEvent.ts b/packages/core/src/utils/prepareEvent.ts index f7f209a49089..9ee0a0f1b1b6 100644 --- a/packages/core/src/utils/prepareEvent.ts +++ b/packages/core/src/utils/prepareEvent.ts @@ -6,10 +6,16 @@ import type { EventHint, Scope as ScopeInterface, ScopeContext, - StackFrame, StackParser, } from '@sentry/types'; -import { GLOBAL_OBJ, addExceptionMechanism, dateTimestampInSeconds, normalize, truncate, uuid4 } from '@sentry/utils'; +import { + addExceptionMechanism, + dateTimestampInSeconds, + getFilenameToDebugIdMap, + normalize, + truncate, + uuid4, +} from '@sentry/utils'; import { DEFAULT_ENVIRONMENT } from '../constants'; import { getGlobalScope } from '../currentScopes'; @@ -161,51 +167,12 @@ function applyClientOptions(event: Event, options: ClientOptions): void { } } -const debugIdStackParserCache = new WeakMap>(); - /** * Puts debug IDs into the stack frames of an error event. */ export function applyDebugIds(event: Event, stackParser: StackParser): void { - const debugIdMap = GLOBAL_OBJ._sentryDebugIds; - - if (!debugIdMap) { - return; - } - - let debugIdStackFramesCache: Map; - const cachedDebugIdStackFrameCache = debugIdStackParserCache.get(stackParser); - if (cachedDebugIdStackFrameCache) { - debugIdStackFramesCache = cachedDebugIdStackFrameCache; - } else { - debugIdStackFramesCache = new Map(); - debugIdStackParserCache.set(stackParser, debugIdStackFramesCache); - } - // Build a map of filename -> debug_id - const filenameDebugIdMap = Object.entries(debugIdMap).reduce>( - (acc, [debugIdStackTrace, debugIdValue]) => { - let parsedStack: StackFrame[]; - const cachedParsedStack = debugIdStackFramesCache.get(debugIdStackTrace); - if (cachedParsedStack) { - parsedStack = cachedParsedStack; - } else { - parsedStack = stackParser(debugIdStackTrace); - debugIdStackFramesCache.set(debugIdStackTrace, parsedStack); - } - - for (let i = parsedStack.length - 1; i >= 0; i--) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const stackFrame = parsedStack[i]!; - if (stackFrame.filename) { - acc[stackFrame.filename] = debugIdValue; - break; - } - } - return acc; - }, - {}, - ); + const filenameDebugIdMap = getFilenameToDebugIdMap(stackParser); try { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/packages/profiling-node/src/utils.ts b/packages/profiling-node/src/utils.ts index 1ec575aaaf3f..2693467d1f47 100644 --- a/packages/profiling-node/src/utils.ts +++ b/packages/profiling-node/src/utils.ts @@ -14,11 +14,16 @@ import type { ProfileChunkEnvelope, ProfileChunkItem, SdkInfo, - StackFrame, - StackParser, ThreadCpuProfile, } from '@sentry/types'; -import { GLOBAL_OBJ, createEnvelope, dsnToString, forEachEnvelopeItem, logger, uuid4 } from '@sentry/utils'; +import { + createEnvelope, + dsnToString, + forEachEnvelopeItem, + getDebugImagesForResources, + logger, + uuid4, +} from '@sentry/utils'; import { env, versions } from 'process'; import { isMainThread, threadId } from 'worker_threads'; @@ -415,69 +420,17 @@ export function makeProfileChunkEnvelope( ]); } -const debugIdStackParserCache = new WeakMap>(); - /** * Cross reference profile collected resources with debug_ids and return a list of debug images. * @param {string[]} resource_paths * @returns {DebugImage[]} */ export function applyDebugMetadata(client: Client, resource_paths: ReadonlyArray): DebugImage[] { - const debugIdMap = GLOBAL_OBJ._sentryDebugIds; - if (!debugIdMap) { - return []; - } - const options = client.getOptions(); if (!options || !options.stackParser) { return []; } - let debugIdStackFramesCache: Map; - const cachedDebugIdStackFrameCache = debugIdStackParserCache.get(options.stackParser); - if (cachedDebugIdStackFrameCache) { - debugIdStackFramesCache = cachedDebugIdStackFrameCache; - } else { - debugIdStackFramesCache = new Map(); - debugIdStackParserCache.set(options.stackParser, debugIdStackFramesCache); - } - - // Build a map of filename -> debug_id. - const filenameDebugIdMap = Object.keys(debugIdMap).reduce>((acc, debugIdStackTrace) => { - let parsedStack: StackFrame[]; - - const cachedParsedStack = debugIdStackFramesCache.get(debugIdStackTrace); - if (cachedParsedStack) { - parsedStack = cachedParsedStack; - } else { - parsedStack = options.stackParser(debugIdStackTrace); - debugIdStackFramesCache.set(debugIdStackTrace, parsedStack); - } - - for (let i = parsedStack.length - 1; i >= 0; i--) { - const stackFrame = parsedStack[i]; - const file = stackFrame && stackFrame.filename; - - if (stackFrame && file) { - acc[file] = debugIdMap[debugIdStackTrace] as string; - break; - } - } - return acc; - }, {}); - - const images: DebugImage[] = []; - - for (const resource of resource_paths) { - if (resource && filenameDebugIdMap[resource]) { - images.push({ - type: 'sourcemap', - code_file: resource, - debug_id: filenameDebugIdMap[resource] as string, - }); - } - } - - return images; + return getDebugImagesForResources(options.stackParser, resource_paths); } diff --git a/packages/utils/src/debug-ids.ts b/packages/utils/src/debug-ids.ts new file mode 100644 index 000000000000..4802b9356965 --- /dev/null +++ b/packages/utils/src/debug-ids.ts @@ -0,0 +1,70 @@ +import type { DebugImage, StackFrame, StackParser } from '@sentry/types'; +import { GLOBAL_OBJ } from './worldwide'; + +const debugIdStackParserCache = new WeakMap>(); + +/** + * Returns a map of filenames to debug identifiers. + */ +export function getFilenameToDebugIdMap(stackParser: StackParser): Record { + const debugIdMap = GLOBAL_OBJ._sentryDebugIds; + if (!debugIdMap) { + return {}; + } + + let debugIdStackFramesCache: Map; + const cachedDebugIdStackFrameCache = debugIdStackParserCache.get(stackParser); + if (cachedDebugIdStackFrameCache) { + debugIdStackFramesCache = cachedDebugIdStackFrameCache; + } else { + debugIdStackFramesCache = new Map(); + debugIdStackParserCache.set(stackParser, debugIdStackFramesCache); + } + + // Build a map of filename -> debug_id. + return Object.keys(debugIdMap).reduce>((acc, debugIdStackTrace) => { + let parsedStack: StackFrame[]; + + const cachedParsedStack = debugIdStackFramesCache.get(debugIdStackTrace); + if (cachedParsedStack) { + parsedStack = cachedParsedStack; + } else { + parsedStack = stackParser(debugIdStackTrace); + debugIdStackFramesCache.set(debugIdStackTrace, parsedStack); + } + + for (let i = parsedStack.length - 1; i >= 0; i--) { + const stackFrame = parsedStack[i]; + const file = stackFrame && stackFrame.filename; + + if (stackFrame && file) { + acc[file] = debugIdMap[debugIdStackTrace] as string; + break; + } + } + return acc; + }, {}); +} + +/** + * Returns a list of debug images for the given resources. + */ +export function getDebugImagesForResources( + stackParser: StackParser, + resource_paths: ReadonlyArray, +): DebugImage[] { + const filenameDebugIdMap = getFilenameToDebugIdMap(stackParser); + + const images: DebugImage[] = []; + for (const path of resource_paths) { + if (path && filenameDebugIdMap[path]) { + images.push({ + type: 'sourcemap', + code_file: path, + debug_id: filenameDebugIdMap[path] as string, + }); + } + } + + return images; +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 4a2d68ca0d8b..2a89826313e8 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -40,3 +40,4 @@ export * from './buildPolyfills'; export * from './propagationContext'; export * from './vercelWaitUntil'; export * from './version'; +export * from './debug-ids';