metro-symbolicate
Advanced tools
Comparing version 0.56.3 to 0.57.0
{ | ||
"name": "metro-symbolicate", | ||
"version": "0.56.3", | ||
"version": "0.57.0", | ||
"description": "A tool to find the source location from JS bundles and stack traces.", | ||
@@ -24,3 +24,3 @@ "license": "MIT", | ||
"invariant": "^2.2.4", | ||
"metro-source-map": "0.56.3", | ||
"metro-source-map": "0.57.0", | ||
"source-map": "^0.5.6", | ||
@@ -27,0 +27,0 @@ "through2": "^2.0.1", |
@@ -58,3 +58,62 @@ /** | ||
const TESTFILE_RAM_MAP = resolve('testfile.ram.js.map'); | ||
const HERMES_MAP = resolve('hermes.js.hbc.map'); | ||
const HERMES_MAP_CJS = resolve('hermescjs.js.hbc.map'); | ||
test('symbolicating a hermes stack trace', async () => { | ||
const output = JSON.parse( | ||
await execute( | ||
[HERMES_MAP, '--hermes-crash'], | ||
read('hermesStackTrace.json'), | ||
), | ||
); | ||
expect(output).toMatchSnapshot(); | ||
}); | ||
test('symbolicating a hermes stack trace with input-line-start and input-column-start', async () => { | ||
const output0Based = JSON.parse( | ||
await execute( | ||
[ | ||
HERMES_MAP, | ||
'--hermes-crash', | ||
'--input-line-start', | ||
'0', | ||
'--input-column-start', | ||
'0', | ||
], | ||
read('hermesStackTrace.json'), | ||
), | ||
); | ||
const output1Based = JSON.parse( | ||
await execute( | ||
[ | ||
HERMES_MAP, | ||
'--hermes-crash', | ||
'--input-line-start', | ||
'1', | ||
'--input-column-start', | ||
'1', | ||
], | ||
read('hermesStackTrace.json'), | ||
), | ||
); | ||
const outputNoFlag = JSON.parse( | ||
await execute( | ||
[HERMES_MAP, '--hermes-crash'], | ||
read('hermesStackTrace.json'), | ||
), | ||
); | ||
expect(outputNoFlag).toMatchObject(output0Based); | ||
expect(outputNoFlag).toMatchObject(output1Based); | ||
}); | ||
test('symbolicating a hermes stack trace CJS', async () => { | ||
const output = JSON.parse( | ||
await execute( | ||
[HERMES_MAP_CJS, '--hermes-crash'], | ||
read('hermesStackTraceCJS.json'), | ||
), | ||
); | ||
expect(output).toMatchSnapshot(); | ||
}); | ||
test('symbolicating a stack trace', async () => | ||
@@ -61,0 +120,0 @@ await expect( |
@@ -8,12 +8,2 @@ #!/usr/bin/env node | ||
* | ||
* Symbolicates a JavaScript stack trace using a source map. | ||
* In our first form, we read a stack trace from stdin and symbolicate it via | ||
* the provided source map. | ||
* In our second form, we symbolicate using an explicit line number, and | ||
* optionally a column. | ||
* In our third form, we symbolicate using a module ID, a line number, and | ||
* optionally a column. | ||
* | ||
* See https://our.intern.facebook.com/intern/dex/symbolicating-javascript-stack-traces-for-react-native/ | ||
* | ||
* @flow strict-local | ||
@@ -23,2 +13,10 @@ * @format | ||
// Symbolicates a JavaScript stack trace using a source map. | ||
// In our first form, we read a stack trace from stdin and symbolicate it via | ||
// the provided source map. | ||
// In our second form, we symbolicate using an explicit line number, and | ||
// optionally a column. | ||
// In our third form, we symbolicate using a module ID, a line number, and | ||
// optionally a column. | ||
'use strict'; | ||
@@ -45,3 +43,3 @@ | ||
} = process, | ||
) { | ||
): Promise<number> { | ||
const argv = argvInput.slice(); | ||
@@ -64,2 +62,3 @@ function checkAndRemoveArg(arg, valuesPerArg = 0) { | ||
const noFunctionNames = checkAndRemoveArg('--no-function-names'); | ||
const isHermesCrash = checkAndRemoveArg('--hermes-crash'); | ||
const inputLineStart = Number.parseInt( | ||
@@ -98,2 +97,3 @@ checkAndRemoveArgWithValue('--input-line-start') || '1', | ||
' --no-function-names', | ||
' --hermes-crash', | ||
' --input-line-start <line> (default: 1)', | ||
@@ -134,3 +134,11 @@ ' --input-column-start <column> (default: 0)', | ||
const stackTrace = await readAll(stdin); | ||
stdout.write(context.symbolicate(stackTrace)); | ||
if (isHermesCrash) { | ||
const stackTraceJSON = JSON.parse(stackTrace); | ||
const symbolicatedTrace = context.symbolicateHermesMinidumpTrace( | ||
stackTraceJSON, | ||
); | ||
stdout.write(JSON.stringify(symbolicatedTrace)); | ||
} else { | ||
stdout.write(context.symbolicate(stackTrace)); | ||
} | ||
} else if (argv[0].endsWith('.profmap')) { | ||
@@ -137,0 +145,0 @@ stdout.write(context.symbolicateProfilerMap(argv[0])); |
@@ -19,3 +19,3 @@ /** | ||
import type {MixedSourceMap} from 'metro-source-map'; | ||
import type {MixedSourceMap, HermesFunctionOffsets} from 'metro-source-map'; | ||
// flowlint-next-line untyped-type-import:off | ||
@@ -45,2 +45,32 @@ import {typeof SourceMapConsumer} from 'source-map'; | ||
type HermesMinidumpCrashInfo = { | ||
+callstack: $ReadOnlyArray<HermesMinidumpStackFrame | NativeCodeStackFrame>, | ||
}; | ||
type HermesMinidumpStackFrame = $ReadOnly<{| | ||
ByteCodeOffset: number, | ||
FunctionID: number, | ||
CJSModuleOffset: number, | ||
SourceURL: string, | ||
StackFrameRegOffs: string, | ||
SourceLocation?: string, | ||
|}>; | ||
type NativeCodeStackFrame = $ReadOnly<{| | ||
NativeCode: true, | ||
StackFrameRegOffs: string, | ||
|}>; | ||
type SymbolicatedStackTrace = $ReadOnlyArray< | ||
SymbolicatedStackFrame | NativeCodeStackFrame, | ||
>; | ||
type SymbolicatedStackFrame = $ReadOnly<{| | ||
line: ?number, | ||
column: ?number, | ||
source: ?string, | ||
functionName: ?string, | ||
name: ?string, | ||
|}>; | ||
const UNKNOWN_MODULE_IDS: SingleMapModuleIds = { | ||
@@ -319,2 +349,12 @@ segmentId: 0, | ||
/* | ||
* Symbolicates the JavaScript stack trace extracted from the minidump | ||
* produced by hermes | ||
*/ | ||
symbolicateHermesMinidumpTrace( | ||
crashInfo: HermesMinidumpCrashInfo, | ||
): SymbolicatedStackTrace { | ||
throw new Error('Not implemented'); | ||
} | ||
/* | ||
* An internal helper function similar to getOriginalPositionFor. This one | ||
@@ -328,9 +368,3 @@ * returns both `name` and `functionName` fields so callers can distinguish the | ||
moduleIds: ?ModuleIdsT, | ||
): {| | ||
line: ?number, | ||
column: ?number, | ||
source: ?string, | ||
name: ?string, | ||
functionName: ?string, | ||
|} { | ||
): SymbolicatedStackFrame { | ||
throw new Error('Not implemented'); | ||
@@ -350,4 +384,6 @@ } | ||
+sourceFunctionsConsumer: ?SourceMetadataMapConsumer, | ||
+hermesOffsets: ?HermesFunctionOffsets, | ||
|}, | ||
}; | ||
+_hasLegacySegments: boolean; | ||
@@ -365,2 +401,3 @@ constructor( | ||
: sourceMapContent; | ||
const {x_hermes_function_offsets} = sourceMapJson; | ||
const segments = { | ||
@@ -373,2 +410,3 @@ '0': { | ||
: null, | ||
hermesOffsets: x_hermes_function_offsets, | ||
}, | ||
@@ -385,8 +423,61 @@ }; | ||
: null, | ||
hermesOffsets: map.x_hermes_function_offsets, | ||
}; | ||
} | ||
} | ||
this._hasLegacySegments = sourceMapJson.x_facebook_segments != null; | ||
this._segments = segments; | ||
} | ||
symbolicateHermesMinidumpTrace( | ||
crashInfo: HermesMinidumpCrashInfo, | ||
): SymbolicatedStackTrace { | ||
const symbolicatedTrace = []; | ||
const {callstack} = crashInfo; | ||
if (callstack != null) { | ||
for (const stackItem of callstack) { | ||
if (stackItem.NativeCode) { | ||
symbolicatedTrace.push(stackItem); | ||
} else { | ||
const { | ||
CJSModuleOffset, | ||
SourceURL, | ||
FunctionID, | ||
ByteCodeOffset: localOffset, | ||
} = stackItem; | ||
const moduleInformation = this._hasLegacySegments | ||
? this.parseFileName(SourceURL) | ||
: UNKNOWN_MODULE_IDS; | ||
const generatedLine = CJSModuleOffset + this.options.inputLineStart; | ||
const segment = this._segments[ | ||
moduleInformation.segmentId.toString() | ||
]; | ||
const hermesOffsets = segment?.hermesOffsets; | ||
if (!hermesOffsets) { | ||
symbolicatedTrace.push({ | ||
line: null, | ||
column: null, | ||
source: null, | ||
functionName: null, | ||
name: null, | ||
}); | ||
} else { | ||
const segmentOffsets = hermesOffsets[Number(CJSModuleOffset)]; | ||
const generatedColumn = | ||
segmentOffsets[FunctionID] + | ||
localOffset + | ||
this.options.inputColumnStart; | ||
const originalPosition = this.getOriginalPositionDetailsFor( | ||
generatedLine, | ||
generatedColumn, | ||
moduleInformation, | ||
); | ||
symbolicatedTrace.push(originalPosition); | ||
} | ||
} | ||
} | ||
} | ||
return symbolicatedTrace; | ||
} | ||
/* | ||
@@ -401,9 +492,3 @@ * An internal helper function similar to getOriginalPositionFor. This one | ||
moduleIds: ?SingleMapModuleIds, | ||
): {| | ||
line: ?number, | ||
column: ?number, | ||
source: ?string, | ||
name: ?string, | ||
functionName: ?string, | ||
|} { | ||
): SymbolicatedStackFrame { | ||
// Adjust arguments to source-map's input coordinates | ||
@@ -509,9 +594,3 @@ lineNumber = | ||
filename: ?string, | ||
): {| | ||
line: ?number, | ||
column: ?number, | ||
source: ?string, | ||
name: ?string, | ||
functionName: ?string, | ||
|} { | ||
): SymbolicatedStackFrame { | ||
invariant( | ||
@@ -518,0 +597,0 @@ filename != null, |
@@ -9,15 +9,12 @@ #!/usr/bin/env node | ||
* | ||
* Symbolicates a JavaScript stack trace using a source map. | ||
* In our first form, we read a stack trace from stdin and symbolicate it via | ||
* the provided source map. | ||
* In our second form, we symbolicate using an explicit line number, and | ||
* optionally a column. | ||
* In our third form, we symbolicate using a module ID, a line number, and | ||
* optionally a column. | ||
* | ||
* See https://our.intern.facebook.com/intern/dex/symbolicating-javascript-stack-traces-for-react-native/ | ||
* | ||
* strict-local | ||
* @format | ||
*/ | ||
// Symbolicates a JavaScript stack trace using a source map. | ||
// In our first form, we read a stack trace from stdin and symbolicate it via | ||
// the provided source map. | ||
// In our second form, we symbolicate using an explicit line number, and | ||
// optionally a column. | ||
// In our third form, we symbolicate using a module ID, a line number, and | ||
// optionally a column. | ||
"use strict"; // flowlint-next-line untyped-import:off | ||
@@ -107,2 +104,3 @@ | ||
const noFunctionNames = checkAndRemoveArg("--no-function-names"); | ||
const isHermesCrash = checkAndRemoveArg("--hermes-crash"); | ||
const inputLineStart = Number.parseInt( | ||
@@ -140,2 +138,3 @@ checkAndRemoveArgWithValue("--input-line-start") || "1", | ||
" --no-function-names", | ||
" --hermes-crash", | ||
" --input-line-start <line> (default: 1)", | ||
@@ -177,3 +176,12 @@ " --input-column-start <column> (default: 0)", | ||
const stackTrace = yield readAll(stdin); | ||
stdout.write(context.symbolicate(stackTrace)); | ||
if (isHermesCrash) { | ||
const stackTraceJSON = JSON.parse(stackTrace); | ||
const symbolicatedTrace = context.symbolicateHermesMinidumpTrace( | ||
stackTraceJSON | ||
); | ||
stdout.write(JSON.stringify(symbolicatedTrace)); | ||
} else { | ||
stdout.write(context.symbolicate(stackTrace)); | ||
} | ||
} else if (argv[0].endsWith(".profmap")) { | ||
@@ -180,0 +188,0 @@ stdout.write(context.symbolicateProfilerMap(argv[0])); |
@@ -316,2 +316,10 @@ /** | ||
/* | ||
* Symbolicates the JavaScript stack trace extracted from the minidump | ||
* produced by hermes | ||
*/ | ||
symbolicateHermesMinidumpTrace(crashInfo) { | ||
throw new Error("Not implemented"); | ||
} | ||
/* | ||
* An internal helper function similar to getOriginalPositionFor. This one | ||
@@ -341,2 +349,3 @@ * returns both `name` and `functionName` fields so callers can distinguish the | ||
: sourceMapContent; | ||
const x_hermes_function_offsets = sourceMapJson.x_hermes_function_offsets; | ||
const segments = { | ||
@@ -348,3 +357,4 @@ "0": { | ||
? new SourceMetadataMapConsumer(sourceMapJson) | ||
: null | ||
: null, | ||
hermesOffsets: x_hermes_function_offsets | ||
} | ||
@@ -361,3 +371,4 @@ }; | ||
? new SourceMetadataMapConsumer(map) | ||
: null | ||
: null, | ||
hermesOffsets: map.x_hermes_function_offsets | ||
}; | ||
@@ -367,4 +378,60 @@ } | ||
this._hasLegacySegments = sourceMapJson.x_facebook_segments != null; | ||
this._segments = segments; | ||
} | ||
symbolicateHermesMinidumpTrace(crashInfo) { | ||
const symbolicatedTrace = []; | ||
const callstack = crashInfo.callstack; | ||
if (callstack != null) { | ||
for (const stackItem of callstack) { | ||
if (stackItem.NativeCode) { | ||
symbolicatedTrace.push(stackItem); | ||
} else { | ||
const CJSModuleOffset = stackItem.CJSModuleOffset, | ||
SourceURL = stackItem.SourceURL, | ||
FunctionID = stackItem.FunctionID, | ||
localOffset = stackItem.ByteCodeOffset; | ||
const moduleInformation = this._hasLegacySegments | ||
? this.parseFileName(SourceURL) | ||
: UNKNOWN_MODULE_IDS; | ||
const generatedLine = CJSModuleOffset + this.options.inputLineStart; | ||
const segment = this._segments[ | ||
moduleInformation.segmentId.toString() | ||
]; | ||
const hermesOffsets = | ||
segment === null || segment === void 0 | ||
? void 0 | ||
: segment.hermesOffsets; | ||
if (!hermesOffsets) { | ||
symbolicatedTrace.push({ | ||
line: null, | ||
column: null, | ||
source: null, | ||
functionName: null, | ||
name: null | ||
}); | ||
} else { | ||
const segmentOffsets = hermesOffsets[Number(CJSModuleOffset)]; | ||
const generatedColumn = | ||
segmentOffsets[FunctionID] + | ||
localOffset + | ||
this.options.inputColumnStart; | ||
const originalPosition = this.getOriginalPositionDetailsFor( | ||
generatedLine, | ||
generatedColumn, | ||
moduleInformation | ||
); | ||
symbolicatedTrace.push(originalPosition); | ||
} | ||
} | ||
} | ||
} | ||
return symbolicatedTrace; | ||
} | ||
/* | ||
@@ -371,0 +438,0 @@ * An internal helper function similar to getOriginalPositionFor. This one |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
326831
34
2728
+ Addedmetro-source-map@0.57.0(transitive)
+ Addedob1@0.57.0(transitive)
- Removedmetro-source-map@0.56.3(transitive)
- Removedob1@0.56.3(transitive)
Updatedmetro-source-map@0.57.0