Skip to content

Commit 5ce6d8c

Browse files
author
Luca Forstner
committed
feat: Add source map images to debug_meta
1 parent a189b19 commit 5ce6d8c

File tree

9 files changed

+73
-94
lines changed

9 files changed

+73
-94
lines changed

packages/browser/test/unit/tracekit/firefox.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ describe('Tracekit - Firefox Tests', () => {
595595
{
596596
colno: 1018410,
597597
filename: 'https://www.random_website.com/main.4a4119c3cdfd10266d84.js',
598-
abs_path: 'https://www.random_website.com/vendor.d1cae9cfc9917df88de7.js',
598+
abs_path: 'https://www.random_website.com/main.4a4119c3cdfd10266d84.js',
599599
function: 'handleProfileResult',
600600
in_app: true,
601601
lineno: 146,

packages/browser/test/unit/tracekit/misc.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ describe('Tracekit - Misc Tests', () => {
3434
in_app: true,
3535
},
3636
{
37-
filename: 'http:///path/to/file.js',
38-
abs_path: 'http:///path/to/file.js',
37+
filename: 'file:///path/to/file.js',
38+
abs_path: 'file:///path/to/file.js',
3939
function: '?',
4040
lineno: 878,
4141
in_app: true,

packages/core/src/utils/prepareEvent.ts

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { ClientOptions, Event, EventHint } from '@sentry/types';
2-
import { dateTimestampInSeconds, normalize, resolvedSyncPromise, truncate, uuid4 } from '@sentry/utils';
1+
import type { ClientOptions, Event, EventHint, StackParser } from '@sentry/types';
2+
import { dateTimestampInSeconds, GLOBAL_OBJ, normalize, resolvedSyncPromise, truncate, uuid4 } from '@sentry/utils';
33

44
import { Scope } from '../scope';
55

@@ -36,6 +36,7 @@ export function prepareEvent(
3636

3737
applyClientOptions(prepared, options);
3838
applyIntegrationsMetadata(prepared, integrations);
39+
applyDebugMetadata(prepared, options.stackParser);
3940

4041
// If we have scope given to us, use it as the base for further modifications.
4142
// This allows us to prevent unnecessary copying of data if `captureContext` is not provided.
@@ -112,6 +113,54 @@ function applyClientOptions(event: Event, options: ClientOptions): void {
112113
}
113114
}
114115

116+
/**
117+
* Applies debug metadata images to the event in order to apply source maps by looking up their debug ID.
118+
*/
119+
function applyDebugMetadata(event: Event, stackParser: StackParser): void {
120+
const debugIdMap = GLOBAL_OBJ._sentryDebugIds;
121+
if (debugIdMap) {
122+
// Build a map of abs_path -> debug_id
123+
const absPathDebugIdMap: Record<string, string> = {};
124+
Object.keys(debugIdMap).forEach(debugIdStackTrace => {
125+
const parsedStack = stackParser(debugIdStackTrace);
126+
for (const stackFrame of parsedStack) {
127+
if (stackFrame.abs_path) {
128+
absPathDebugIdMap[stackFrame.abs_path] = debugIdMap[debugIdStackTrace];
129+
break;
130+
}
131+
}
132+
});
133+
134+
// Get a Set of abs_paths in the stack trace
135+
const errorAbsPaths = new Set<string>();
136+
if (event && event.exception && event.exception.values) {
137+
event.exception.values.forEach(exception => {
138+
if (exception.stacktrace && exception.stacktrace.frames) {
139+
exception.stacktrace.frames.forEach(frame => {
140+
if (frame.abs_path) {
141+
errorAbsPaths.add(frame.abs_path);
142+
}
143+
});
144+
}
145+
});
146+
}
147+
148+
// Fill debug_meta information
149+
event.debug_meta = event.debug_meta || {};
150+
event.debug_meta.images = event.debug_meta.images || [];
151+
const images = event.debug_meta.images;
152+
errorAbsPaths.forEach(absPath => {
153+
if (absPathDebugIdMap[absPath]) {
154+
images.push({
155+
type: 'sourcemap',
156+
source_filename: absPath,
157+
debug_id: absPathDebugIdMap[absPath],
158+
});
159+
}
160+
});
161+
}
162+
}
163+
115164
/**
116165
* This function adds all used integrations to the SDK info in the event.
117166
* @param event The event that will be filled with all integrations.

packages/types/src/debugMeta.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ export interface DebugMeta {
55
images?: Array<DebugImage>;
66
}
77

8-
/**
9-
* Possible choices for debug images.
10-
*/
11-
export type DebugImageType = 'wasm' | 'macho' | 'elf' | 'pe';
8+
export type DebugImage = WasmDebugImage | SourceMapDebugImage;
129

13-
/**
14-
* References to debug images.
15-
*/
16-
export interface DebugImage {
17-
type: DebugImageType;
10+
interface WasmDebugImage {
11+
type: 'wasm';
1812
debug_id: string;
1913
code_id?: string | null;
2014
code_file: string;
2115
debug_file?: string | null;
2216
}
17+
18+
interface SourceMapDebugImage {
19+
type: 'sourcemap';
20+
source_filename: string; // abs_path
21+
debug_id: string; // uuid
22+
}

packages/types/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export type { ClientReport, Outcome, EventDropReason } from './clientreport';
55
export type { Context, Contexts, DeviceContext, OsContext, AppContext, CultureContext, TraceContext } from './context';
66
export type { DataCategory } from './datacategory';
77
export type { DsnComponents, DsnLike, DsnProtocol } from './dsn';
8-
export type { DebugImage, DebugImageType, DebugMeta } from './debugMeta';
8+
export type { DebugImage, DebugMeta } from './debugMeta';
99
export type {
1010
AttachmentItem,
1111
BaseEnvelopeHeaders,

packages/utils/src/stacktrace.ts

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
import type { StackFrame, StackLineParser, StackLineParserFn, StackParser } from '@sentry/types';
22

3-
import { GLOBAL_OBJ } from './worldwide';
4-
53
const STACKTRACE_LIMIT = 50;
64

7-
type DebugIdFilename = string;
8-
type DebugId = string;
9-
10-
const debugIdParserCache = new Map<StackLineParserFn, Map<DebugIdFilename, DebugId>>();
11-
125
/**
136
* Creates a stack parser with the supplied line parsers
147
*
@@ -21,29 +14,6 @@ export function createStackParser(...parsers: StackLineParser[]): StackParser {
2114

2215
return (stack: string, skipFirst: number = 0): StackFrame[] => {
2316
const frames: StackFrame[] = [];
24-
25-
for (const parser of sortedParsers) {
26-
let debugIdCache = debugIdParserCache.get(parser);
27-
if (!debugIdCache) {
28-
debugIdCache = new Map();
29-
debugIdParserCache.set(parser, debugIdCache);
30-
}
31-
32-
const debugIdMap = GLOBAL_OBJ._sentryDebugIds;
33-
34-
if (debugIdMap) {
35-
Object.keys(debugIdMap).forEach(debugIdStackTrace => {
36-
debugIdStackTrace.split('\n').forEach(line => {
37-
const frame = parser(line);
38-
if (frame && frame.filename) {
39-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
40-
debugIdCache!.set(frame.filename, debugIdMap[debugIdStackTrace]);
41-
}
42-
});
43-
});
44-
}
45-
}
46-
4717
for (const line of stack.split('\n').slice(skipFirst)) {
4818
// Ignore lines over 1kb as they are unlikely to be stack frames.
4919
// Many of the regular expressions use backtracking which results in run time that increases exponentially with
@@ -61,14 +31,6 @@ export function createStackParser(...parsers: StackLineParser[]): StackParser {
6131
const frame = parser(cleanedLine);
6232

6333
if (frame) {
64-
const debugIdCache = debugIdParserCache.get(parser);
65-
if (debugIdCache && frame.filename) {
66-
const cachedDebugId = debugIdCache.get(frame.filename);
67-
if (cachedDebugId) {
68-
frame.debug_id = cachedDebugId;
69-
}
70-
}
71-
7234
frames.push(frame);
7335
break;
7436
}

packages/utils/test/stacktrace.test.ts

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -69,41 +69,3 @@ describe('Stacktrace', () => {
6969
});
7070
});
7171
});
72-
73-
describe('Stack parsers created with createStackParser', () => {
74-
afterEach(() => {
75-
GLOBAL_OBJ._sentryDebugIds = undefined;
76-
});
77-
78-
it('put debug ids onto individual frames', () => {
79-
GLOBAL_OBJ._sentryDebugIds = {
80-
'filename1.js\nfilename1.js': 'aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaa',
81-
'filename2.js\nfilename2.js': 'bbbbbbbb-bbbb-4bbb-bbbb-bbbbbbbbbb',
82-
};
83-
84-
const fakeErrorStack = 'filename1.js\nfilename2.js\nfilename1.js\nfilename3.js';
85-
const stackParser = createStackParser([0, line => ({ filename: line })]);
86-
87-
const result = stackParser(fakeErrorStack);
88-
89-
expect(result[0]).toStrictEqual({ filename: 'filename3.js', function: '?' });
90-
91-
expect(result[1]).toStrictEqual({
92-
filename: 'filename1.js',
93-
function: '?',
94-
debug_id: 'aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaa',
95-
});
96-
97-
expect(result[2]).toStrictEqual({
98-
filename: 'filename2.js',
99-
function: '?',
100-
debug_id: 'bbbbbbbb-bbbb-4bbb-bbbb-bbbbbbbbbb',
101-
});
102-
103-
expect(result[3]).toStrictEqual({
104-
filename: 'filename1.js',
105-
function: '?',
106-
debug_id: 'aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaa',
107-
});
108-
});
109-
});

packages/wasm/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class Wasm implements Integration {
6161

6262
if (haveWasm) {
6363
event.debug_meta = event.debug_meta || {};
64-
event.debug_meta.images = getImages();
64+
event.debug_meta.images = [...(event.debug_meta.images || []), ...getImages()];
6565
}
6666

6767
return event;

packages/wasm/src/registry.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export function getModuleInfo(module: WebAssembly.Module): ModuleInfo {
4141
export function registerModule(module: WebAssembly.Module, url: string): void {
4242
const { buildId, debugFile } = getModuleInfo(module);
4343
if (buildId) {
44-
const oldIdx = IMAGES.findIndex(img => img.code_file === url);
44+
const oldIdx = getImage(url);
4545
if (oldIdx >= 0) {
4646
IMAGES.splice(oldIdx, 1);
4747
}
@@ -68,5 +68,11 @@ export function getImages(): Array<DebugImage> {
6868
* @param url the URL of the WebAssembly module.
6969
*/
7070
export function getImage(url: string): number {
71-
return IMAGES.findIndex(img => img.code_file === url);
71+
return IMAGES.findIndex(image => {
72+
if (image.type === 'wasm') {
73+
return image.code_file === url;
74+
} else {
75+
return false;
76+
}
77+
});
7278
}

0 commit comments

Comments
 (0)