Skip to content

Commit

Permalink
Use more than one virtual offset per function for size attribution
Browse files Browse the repository at this point in the history
Reviewed By: zlern2k

Differential Revision: D15062973

fbshipit-source-id: b96df9a67abd5c3e3c17cab900b5aaa0c8ec088f
  • Loading branch information
motiz88 authored and facebook-github-bot committed Apr 25, 2019
1 parent 35afd38 commit b8c5243
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 0 deletions.
22 changes: 22 additions & 0 deletions packages/metro-symbolicate/src/Symbolication.js
Expand Up @@ -194,6 +194,28 @@ function symbolicateAttribution(obj, context) {
var column = loc.column || loc.virtualOffset;
var file = loc.filename ? parseFileName(loc.filename) : UNKNOWN_MODULE_IDS;
var original = getOriginalPositionFor(line, column, file, context);

const isBytecodeRange =
loc.bytecodeSize != null &&
loc.virtualOffset != null &&
!loc.column != null;

// Functions compiled from Metro-bundled modules will often have a little bit
// of unmapped wrapper code right at the beginning - which is where we query.
// Let's attribute them to where the inner module code originates instead.
// This loop is O(n*log(n)) in the size of the function, but we will generally
// either:
// 1. Find a non-null mapping within one or two iterations; or
// 2. Reach the end of the function without encountering mappings - this might
// happen for function bodies that never throw (generally very short).
while (
isBytecodeRange &&
original.source == null &&
++column < loc.virtualOffset + loc.bytecodeSize
) {
original = getOriginalPositionFor(line, column, file, context);
}

obj.location = {
file: original.source,
line: original.line,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

116 changes: 116 additions & 0 deletions packages/metro-symbolicate/src/__tests__/symbolicate-test.js
Expand Up @@ -95,6 +95,122 @@ test('symbolicating an attribution file', async () =>
),
).resolves.toMatchSnapshot());

describe('symbolicating an attribution file specifying unmapped offsets', () => {
const attribute = async obj =>
(await execute(
[resolve('testfile.partial.js.map'), '--attribution'],
JSON.stringify(obj) + '\n',
))
.split('\n')
.filter(Boolean)
.map(line => JSON.parse(line));

test('Lookup falls before all mappings with no non-null mapping in range', async () =>
await expect(
attribute({
functionId: 0,
location: {virtualOffset: 0, bytecodeSize: 5},
usage: [],
}),
).resolves.toMatchInlineSnapshot(`
Array [
Object {
"functionId": 0,
"location": Object {
"column": null,
"file": null,
"line": null,
},
"usage": Array [],
},
]
`));

test('Lookup finds a null mapping and falls back to a non-null mapping in range', async () =>
await expect(
attribute({
functionId: 1,
location: {virtualOffset: 5, bytecodeSize: 2},
usage: [],
}),
).resolves.toMatchInlineSnapshot(`
Array [
Object {
"functionId": 1,
"location": Object {
"column": 1,
"file": "foo.js",
"line": 2,
},
"usage": Array [],
},
]
`));

test('Lookup finds a null mapping with no bytecodeSize specified', async () =>
await expect(
attribute({
functionId: 1,
location: {virtualOffset: 5},
usage: [],
}),
).resolves.toMatchInlineSnapshot(`
Array [
Object {
"functionId": 1,
"location": Object {
"column": null,
"file": null,
"line": null,
},
"usage": Array [],
},
]
`));

test('Lookup finds a null mapping with no non-null mapping in range', async () =>
await expect(
attribute({
functionId: 2,
location: {virtualOffset: 11, bytecodeSize: 1},
usage: [],
}),
).resolves.toMatchInlineSnapshot(`
Array [
Object {
"functionId": 2,
"location": Object {
"column": null,
"file": null,
"line": null,
},
"usage": Array [],
},
]
`));

test('Lookup finds the last mapping and it is null', async () =>
await expect(
attribute({
functionId: 3,
location: {virtualOffset: 17, bytecodeSize: 1},
usage: [],
}),
).resolves.toMatchInlineSnapshot(`
Array [
Object {
"functionId": 3,
"location": Object {
"column": null,
"file": null,
"line": null,
},
"usage": Array [],
},
]
`));
});

test('symbolicating with a cpuprofile', async () => {
fs.copyFileSync(
resolve('testfile.cpuprofile'),
Expand Down

0 comments on commit b8c5243

Please sign in to comment.