Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7dff905
feat(profiling): Send debug information with Profiles
krystofwoldrich Oct 11, 2023
f147e93
fix white space
krystofwoldrich Oct 11, 2023
5561dc7
Add support for Hermes bytecode frames
krystofwoldrich Oct 13, 2023
fd0876e
Add changelog
krystofwoldrich Oct 13, 2023
7e964ce
Use bundle filename to lookup debugid
krystofwoldrich Oct 13, 2023
073450f
Remove unnecessary lookup logic as only one bundle is supported
krystofwoldrich Oct 13, 2023
22eabf0
Merge branch 'kw-add-hermes-profile-bytecode-frames-parsing' into kw-…
krystofwoldrich Oct 13, 2023
affcee8
Fix build
krystofwoldrich Oct 13, 2023
143d6e2
Merge branch 'kw-add-hermes-profile-bytecode-frames-parsing' into kw-…
krystofwoldrich Oct 13, 2023
5dd4ea7
Fix build
krystofwoldrich Oct 13, 2023
81c2acb
fix lint
krystofwoldrich Oct 13, 2023
c3683f4
Merge branch 'kw-add-hermes-profile-bytecode-frames-parsing' into kw-…
krystofwoldrich Oct 13, 2023
729876e
fix lint
krystofwoldrich Oct 13, 2023
7d608fb
change profile platform to javascript
krystofwoldrich Oct 13, 2023
102f1f1
Mark root as in app false
krystofwoldrich Oct 13, 2023
7e33cfa
Use abs_path like error stacktrace frames
krystofwoldrich Oct 18, 2023
66c2d58
Merge remote-tracking branch 'origin/main' into kw-symbolicated-profiles
krystofwoldrich Oct 18, 2023
4b1c004
Fix tests
krystofwoldrich Oct 18, 2023
facf3ed
remove duplicate types
krystofwoldrich Oct 19, 2023
fff24a0
Merge remote-tracking branch 'origin/main' into kw-symbolicated-profiles
krystofwoldrich Oct 27, 2023
0dcaa07
Update changelog
krystofwoldrich Oct 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- Send Source Maps Debug ID for symbolicated Profiles ([#3343](https://github.com/getsentry/sentry-react-native/pull/3343))

### Fixes

- Add actual `activeThreadId` to Profiles ([#3338](https://github.com/getsentry/sentry-react-native/pull/3338))
Expand Down
38 changes: 37 additions & 1 deletion src/js/profiling/convertHermesProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { FrameId, StackId, ThreadCpuFrame, ThreadCpuSample, ThreadCpuStack,
import { logger } from '@sentry/utils';

import type * as Hermes from './hermes';
import { parseHermesJSStackFrame } from './hermes';
import { DEFAULT_BUNDLE_NAME } from './hermes';
import { MAX_PROFILE_DURATION_MS } from './integration';
import type { RawThreadCpuProfile } from './types';

Expand Down Expand Up @@ -173,3 +173,39 @@ function mapStacks(
hermesStackToSentryStackMap,
};
}

/**
* Parses Hermes StackFrame to Sentry StackFrame.
* For native frames only function name is returned, for Hermes bytecode the line and column are calculated.
*/
export function parseHermesJSStackFrame(frame: Hermes.StackFrame): ThreadCpuFrame {
if (frame.category !== 'JavaScript') {
// Native
if (frame.name === '[root]') {
return { function: frame.name, in_app: false };
}
return { function: frame.name };
}

if (frame.funcVirtAddr !== undefined && frame.offset !== undefined) {
// Hermes Bytecode
return {
function: frame.name,
abs_path: DEFAULT_BUNDLE_NAME,
// https://github.com/krystofwoldrich/metro/blob/417e6f276ff9422af6039fc4d1bce41fcf7d9f46/packages/metro-symbolicate/src/Symbolication.js#L298-L301
// Hermes lineno is hardcoded 1, currently only one bundle symbolication is supported by metro-symbolicate and thus by us.
lineno: 1,
// Hermes colno is 0-based, while Sentry is 1-based
colno: Number(frame.funcVirtAddr) + Number(frame.offset) + 1,
};
}

// JavaScript
const indexOfLeftParenthesis = frame.name.indexOf('(');
return {
function: indexOfLeftParenthesis !== -1 ? frame.name.substring(0, indexOfLeftParenthesis) || undefined : frame.name,
abs_path: DEFAULT_BUNDLE_NAME,
lineno: frame.line !== undefined ? Number(frame.line) : undefined,
colno: frame.column !== undefined ? Number(frame.column) : undefined,
};
}
45 changes: 1 addition & 44 deletions src/js/profiling/hermes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,51 +53,8 @@ export interface Profile {
stackFrames: Record<string, StackFrame>;
}

export interface ParsedHermesStackFrame {
function: string;
file?: string;
lineno?: number;
colno?: number;
}

const DEFAULT_BUNDLE_NAME =
export const DEFAULT_BUNDLE_NAME =
Platform.OS === 'android' ? ANDROID_DEFAULT_BUNDLE_NAME : Platform.OS === 'ios' ? IOS_DEFAULT_BUNDLE_NAME : undefined;
const ANONYMOUS_FUNCTION_NAME = 'anonymous';

/**
* Parses Hermes StackFrame to Sentry StackFrame.
* For native frames only function name is returned, for Hermes bytecode the line and column are calculated.
*/
export function parseHermesJSStackFrame(frame: StackFrame): ParsedHermesStackFrame {
if (frame.category !== 'JavaScript') {
// Native
return { function: frame.name };
}

if (frame.funcVirtAddr !== undefined && frame.offset !== undefined) {
// Hermes Bytecode
return {
function: frame.name || ANONYMOUS_FUNCTION_NAME,
file: DEFAULT_BUNDLE_NAME,
// https://github.com/krystofwoldrich/metro/blob/417e6f276ff9422af6039fc4d1bce41fcf7d9f46/packages/metro-symbolicate/src/Symbolication.js#L298-L301
// Hermes lineno is hardcoded 1, currently only one bundle symbolication is supported by metro-symbolicate and thus by us.
lineno: 1,
// Hermes colno is 0-based, while Sentry is 1-based
colno: Number(frame.funcVirtAddr) + Number(frame.offset) + 1,
};
}

// JavaScript
const indexOfLeftParenthesis = frame.name.indexOf('(');
return {
function:
(indexOfLeftParenthesis !== -1 && (frame.name.substring(0, indexOfLeftParenthesis) || ANONYMOUS_FUNCTION_NAME)) ||
frame.name,
file: DEFAULT_BUNDLE_NAME,
lineno: frame.line !== undefined ? Number(frame.line) : undefined,
colno: frame.column !== undefined ? Number(frame.column) : undefined,
};
}

const MS_TO_NS: number = 1e6;

Expand Down
3 changes: 2 additions & 1 deletion src/js/profiling/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ThreadCpuProfile } from '@sentry/types';
import type { ThreadCpuFrame, ThreadCpuProfile } from '@sentry/types';

export interface RawThreadCpuProfile extends ThreadCpuProfile {
frames: ThreadCpuFrame[];
profile_id?: string;
active_thread_id: string;
}
43 changes: 40 additions & 3 deletions src/js/profiling/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Envelope, Event, Profile } from '@sentry/types';
import { forEachEnvelopeItem, logger } from '@sentry/utils';
import type { DebugImage, Envelope, Event, Profile } from '@sentry/types';
import { forEachEnvelopeItem, GLOBAL_OBJ, logger } from '@sentry/utils';

import { DEFAULT_BUNDLE_NAME } from './hermes';
import type { RawThreadCpuProfile } from './types';

/**
Expand Down Expand Up @@ -136,7 +137,7 @@ function createProfilePayload(
const profile: Profile = {
event_id: profile_id,
timestamp: new Date(start_timestamp).toISOString(),
platform: 'node',
platform: 'javascript',
version: '1',
release: release,
environment: environment,
Expand All @@ -163,11 +164,47 @@ function createProfilePayload(
trace_id: trace_id || '',
active_thread_id: cpuProfile.active_thread_id,
},
debug_meta: {
images: getDebugMetadata(),
},
};

return profile;
}

/**
* Returns debug meta images of the loaded bundle.
*/
export function getDebugMetadata(): DebugImage[] {
if (!DEFAULT_BUNDLE_NAME) {
return [];
}

const debugIdMap = GLOBAL_OBJ._sentryDebugIds;
if (!debugIdMap) {
return [];
}

const debugIdsKeys = Object.keys(debugIdMap);
if (!debugIdsKeys.length) {
return [];
}

if (debugIdsKeys.length > 1) {
logger.warn(
'[Profiling] Multiple debug images found, but only one one bundle is supported. Using the first one...',
);
}

return [
{
code_file: DEFAULT_BUNDLE_NAME,
debug_id: debugIdMap[debugIdsKeys[0]],
type: 'sourcemap',
},
];
}

/**
* Adds items to envelope if they are not already present - mutates the envelope.
* @param envelope
Expand Down
9 changes: 5 additions & 4 deletions test/profiling/convertHermesProfile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,24 @@ describe('convert hermes profile to sentry profile', () => {
frames: [
{
function: '[root]',
in_app: false,
},
{
colno: 33,
file: 'app:///main.jsbundle',
abs_path: 'app:///main.jsbundle',
function: 'fooA',
lineno: 1610,
},
{
colno: 21,
file: 'app:///main.jsbundle',
abs_path: 'app:///main.jsbundle',
function: 'fooB',
lineno: 1616,
},
{
colno: 18,
file: 'app:///main.jsbundle',
function: 'anonymous',
abs_path: 'app:///main.jsbundle',
function: undefined,
lineno: 1627,
},
],
Expand Down
20 changes: 10 additions & 10 deletions test/profiling/hermes.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ParsedHermesStackFrame } from '../../src/js/profiling/hermes';
import { parseHermesJSStackFrame } from '../../src/js/profiling/hermes';
import type { ThreadCpuFrame } from '@sentry/types';

import { parseHermesJSStackFrame } from '../../src/js/profiling/convertHermesProfile';

describe('hermes', () => {
describe('parseHermesStackFrameName', () => {
Expand All @@ -11,9 +12,9 @@ describe('hermes', () => {
column: '33',
category: 'JavaScript',
}),
).toEqual(<ParsedHermesStackFrame>{
).toEqual(<ThreadCpuFrame>{
function: 'fooA',
file: 'app:///main.jsbundle',
abs_path: 'app:///main.jsbundle',
lineno: 1610,
colno: 33,
});
Expand All @@ -25,7 +26,7 @@ describe('hermes', () => {
category: 'root',
}),
).toEqual(
expect.objectContaining(<ParsedHermesStackFrame>{
expect.objectContaining(<ThreadCpuFrame>{
function: '[root]',
}),
);
Expand All @@ -38,9 +39,8 @@ describe('hermes', () => {
column: '33',
category: 'JavaScript',
}),
).toEqual(<ParsedHermesStackFrame>{
function: 'anonymous',
file: 'app:///main.jsbundle',
).toEqual(<ThreadCpuFrame>{
abs_path: 'app:///main.jsbundle',
lineno: 1610,
colno: 33,
});
Expand All @@ -52,9 +52,9 @@ describe('hermes', () => {
category: 'JavaScript',
}),
).toEqual(
expect.objectContaining(<ParsedHermesStackFrame>{
expect.objectContaining(<ThreadCpuFrame>{
function: 'fooA',
file: 'app:///main.jsbundle',
abs_path: 'app:///main.jsbundle',
}),
);
});
Expand Down