Compare commits

..
10 Commits
Author SHA1 Message Date
Jamie Wong 30ca6291ca 1.5.0 2019-02-17 18:38:17 -08:00
Jamie Wong 66a9e5d1cf Support importing unterminated JSON in simple cases (#208)
This PR introduces support for importing JSON based profiles that are missing a terminating `]` (and possibly have an extraneous `,`).

This is similar to #202, but takes a much more targeted and simple approach.

I'm confident that this approach is sufficient because this is exactly what `chrome://tracing` does: https://github.com/catapult-project/catapult/blob/27e047e0494df162022be6aa8a8862742a270232/tracing/tracing/extras/importer/trace_event_importer.html#L197-L208

Fixes #204
2019-02-17 18:32:26 -08:00
Jamie Wong a2022a07a2 Fix crash when importing from stackprof without raw_timestamp_deltas (#207)
Fixes #200
2019-02-17 18:30:02 -08:00
Jamie Wong b7806a1c5f Alert instead of crash when successfully importing a file containing no profiles (#205)
Fixes #169
2019-02-17 17:46:24 -08:00
Jamie Wong 7f19a13012 Support importing multithreaded profiles from Chrome 66 (#206)
In #194, I added code to support import of multithreaded profiles from Chrome 70. I'm now doing some profiling work on an older version of Android chrome, and it seems like the profile objects don't yet have `id` properties. Instead, we should try using the `pid/tid` pair to identify profiles when the `id` field is absent.

This was tested against a profile import from Android Chrome 66.
2019-02-17 17:46:09 -08:00
Archerlly abd74be9fa add default instruments selected run number (#203)
this's will lack `com.apple.xray.owner.template` in instruments archive data where run instruments with command line.
like:
1. run`instruments -t Template.tracetemplate -D demo.trace -l 10000 -w  test.app`
2. drag `demo.trace` into `https://www.speedscope.app`
3. alert `Unrecognized format! See documentation about supported formats`
2019-02-17 17:45:51 -08:00
Jamie Wong c706bdfe04 Revert "Support importing partial JSON files (#202)"
This reverts commit cfc8fe8f6e.
2019-02-08 18:33:30 -08:00
Marcin Kolny cfc8fe8f6e Support importing partial JSON files (#202)
Partial files are allowed in many specs, e.g. Trace Event Format,
so the viewer should be able to load partial files as well.
2019-02-08 18:08:51 -08:00
Jamie Wong ddc61302e8 1.4.1 2019-01-22 21:53:09 -08:00
Jamie Wong 864c065053 Fix importing of Trace Event Format files with no ts field on M events (#198)
The spec for the Trace Event Format technically requires that all entries have "ts" values, and they do in the profiles recorded using chrome://tracing. We don't actually use those values in the case of "M" (metadata) events, however, and they're semantically meaningless as far as I can tell, so let's stop requiring them.

This allows the files that @aras-p provided in #77 to import successfully.

Fixes #77
2019-01-22 21:51:23 -08:00
20 changed files with 10174 additions and 32 deletions
+19
View File
@@ -1,5 +1,24 @@
## Unreleased
## [1.5.0] - 2019-02-17
### Added
* Support importing unterminated JSON in simple cases (#208) (by @jlfwong)
### Fixed
* Fix crash when importing from stackpro without raw_timestamp_deltas (#207) (by @jlfwong)
* Alert instead of crash when importing a file containing no profiles (#205) (by @jlfwong)
* Fixed import of multithreaded profiles from Chrome 66 (#206) (by @jlfwong)
* Fixed import of instruments trace files with missing run number (#203) (by @Archerlly)
## [1.4.1] - 2019-01-22
### Fixed
* Fix importing of Trace Event Format files with no ts field on M events [#198] (by @jlfwong)
## [1.4.0] - 2019-01-22
### Added
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "speedscope",
"version": "1.4.0",
"version": "1.5.0",
"description": "",
"repository": "jlfwong/speedscope",
"main": "index.js",
File diff suppressed because one or more lines are too long
@@ -1,22 +1,22 @@
[
{"pid": 0, "tid": 0, "ph": "M", "name": "process_name", "args": {"name": "p0"}, "ts": 0},
{"pid": 0, "tid": 0, "ph": "M", "name": "thread_name", "args": {"name": "p0t0"}, "ts": 0},
{"pid": 0, "tid": 0, "ph": "M", "name": "process_name", "args": {"name": "p0"}},
{"pid": 0, "tid": 0, "ph": "M", "name": "thread_name", "args": {"name": "p0t0"}},
{"pid": 0, "tid": 0, "ph": "X", "name": "alpha", "ts": 0, "dur": 1},
{"pid": 0, "tid": 1, "ph": "M", "name": "thread_name", "args": {"name": "p0t1"}, "ts": 0},
{"pid": 0, "tid": 1, "ph": "M", "name": "thread_name", "args": {"name": "p0t1"}},
{"pid": 0, "tid": 1, "ph": "X", "name": "beta", "ts": 0, "dur": 1},
{"pid": 1, "tid": 0, "ph": "M", "name": "process_name", "args": {"name": "p1"}, "ts": 0},
{"pid": 1, "tid": 0, "ph": "M", "name": "thread_name", "args": {"name": "p1t0"}, "ts": 0},
{"pid": 1, "tid": 0, "ph": "M", "name": "process_name", "args": {"name": "p1"}},
{"pid": 1, "tid": 0, "ph": "M", "name": "thread_name", "args": {"name": "p1t0"}},
{"pid": 1, "tid": 0, "ph": "X", "name": "gamma", "ts": 0, "dur": 1},
{"pid": 1, "tid": 1, "ph": "M", "name": "thread_name", "args": {"name": "p1t1"}, "ts": 0},
{"pid": 1, "tid": 1, "ph": "M", "name": "thread_name", "args": {"name": "p1t1"}},
{"pid": 1, "tid": 1, "ph": "X", "name": "delta", "ts": 0, "dur": 1},
{"pid": 2, "tid": 0, "ph": "M", "name": "thread_name", "args": {"name": "p2t0"}, "ts": 0},
{"pid": 2, "tid": 0, "ph": "M", "name": "thread_name", "args": {"name": "p2t0"}},
{"pid": 2, "tid": 0, "ph": "X", "name": "epsilon", "ts": 0, "dur": 1},
{"pid": 2, "tid": 1, "ph": "M", "name": "thread_name", "args": {"name": "p2t1"}, "ts": 0},
{"pid": 2, "tid": 1, "ph": "M", "name": "thread_name", "args": {"name": "p2t1"}},
{"pid": 2, "tid": 1, "ph": "X", "name": "phi", "ts": 0, "dur": 1},
{"pid": 3, "tid": 0, "ph": "M", "name": "process_name", "args": {"name": "p3"}, "ts": 0},
{"pid": 3, "tid": 0, "ph": "M", "name": "process_name", "args": {"name": "p3"}},
{"pid": 3, "tid": 0, "ph": "X", "name": "zeta", "ts": 0, "dur": 1},
{"pid": 3, "tid": 1, "ph": "X", "name": "eta", "ts": 0, "dur": 1}
]
@@ -0,0 +1,7 @@
[
{"pid": 0, "tid": 0, "ph": "B", "name": "alpha", "ts": 0},
{"pid": 0, "tid": 0, "ph": "B", "name": "beta", "ts": 1},
{"pid": 0, "tid": 0, "ph": "X", "name": "gamma", "ts": 2, "dur": 5, "args": {"detail": "foobar"}},
{"pid": 0, "tid": 0, "ph": "X", "name": "epsilon", "ts": 7, "tdur": 4},
{"pid": 0, "tid": 0, "ph": "E", "name": "beta", "ts": 13},
{"pid": 0, "tid": 0, "ph": "E", "name": "alpha", "ts": 14},
@@ -0,0 +1,13 @@
[
{"pid": 0, "tid": 0, "ph": "B", "name": "alpha", "ts": 0},
{"pid": 0, "tid": 0, "ph": "B", "name": "beta", "ts": 1},
{"pid": 0, "tid": 0, "ph": "X", "name": "gamma", "ts": 2, "dur": 5, "args": {"detail": "foobar"}},
{"pid": 0, "tid": 0, "ph": "X", "name": "epsilon", "ts": 7, "tdur": 4},
{"pid": 0, "tid": 0, "ph": "E", "name": "beta", "ts": 13},
{"pid": 0, "tid": 0, "ph": "E", "name": "alpha", "ts": 14},
@@ -0,0 +1,7 @@
[
{"pid": 0, "tid": 0, "ph": "B", "name": "alpha", "ts": 0},
{"pid": 0, "tid": 0, "ph": "B", "name": "beta", "ts": 1},
{"pid": 0, "tid": 0, "ph": "X", "name": "gamma", "ts": 2, "dur": 5, "args": {"detail": "foobar"}},
{"pid": 0, "tid": 0, "ph": "X", "name": "epsilon", "ts": 7, "tdur": 4},
{"pid": 0, "tid": 0, "ph": "E", "name": "beta", "ts": 13},
{"pid": 0, "tid": 0, "ph": "E", "name": "alpha", "ts": 14}
@@ -515,6 +515,408 @@ exports[`importFromChromeTimeline Chrome 69: indexToView 1`] = `0`;
exports[`importFromChromeTimeline Chrome 69: profileGroup.name 1`] = `"simple.json"`;
exports[`importFromChromeTimeline Workers Chrome 66 1`] = `
Object {
"frames": Array [
Frame {
"col": -1,
"file": "",
"key": "(program)::-1:-1",
"line": -1,
"name": "(program)",
"selfWeight": 819120,
"totalWeight": 819120,
},
Frame {
"col": 20,
"file": "http://172.30.3.176:7777/worker.js",
"key": "onmessage:http://172.30.3.176:7777/worker.js:14:20",
"line": 14,
"name": "onmessage",
"selfWeight": 639,
"totalWeight": 857719,
},
Frame {
"col": 14,
"file": "http://172.30.3.176:7777/worker.js",
"key": "alpha:http://172.30.3.176:7777/worker.js:8:14",
"line": 8,
"name": "alpha",
"selfWeight": 19976,
"totalWeight": 835149,
},
Frame {
"col": 14,
"file": "http://172.30.3.176:7777/worker.js",
"key": "gamma:http://172.30.3.176:7777/worker.js:0:14",
"line": 0,
"name": "gamma",
"selfWeight": 22197,
"totalWeight": 30000,
},
Frame {
"col": -1,
"file": "",
"key": "postMessage::-1:-1",
"line": -1,
"name": "postMessage",
"selfWeight": 1278,
"totalWeight": 21931,
},
],
"name": "worker.json - DedicatedWorker thread",
"stacks": Array [
" 9.07ms",
"(program) 5.49ms",
"onmessage;alpha;gamma 3.69ms",
"onmessage;postMessage 212.00µs",
"onmessage;postMessage;(program) 3.97ms",
"onmessage;alpha;gamma 3.82ms",
"onmessage;postMessage 414.00µs",
"onmessage;postMessage;(program) 3.71ms",
"onmessage;alpha;gamma 1.13ms",
"onmessage 188.00µs",
"onmessage;alpha;gamma 2.48ms",
"onmessage;alpha;gamma;(program) 3.89ms",
"onmessage;alpha;gamma 373.00µs",
"onmessage;alpha 184.00µs",
"onmessage;alpha;gamma 3.36ms",
"onmessage;alpha;gamma;(program) 3.91ms",
"onmessage;alpha;gamma 3.94ms",
"onmessage;postMessage 205.00µs",
"onmessage;postMessage;(program) 3.91ms",
"onmessage 451.00µs",
"onmessage;alpha;gamma 566.00µs",
"onmessage;alpha 194.00µs",
"onmessage;alpha;gamma 2.09ms",
"onmessage;alpha 189.00µs",
"onmessage;alpha;gamma 746.00µs",
"onmessage;postMessage 211.00µs",
"onmessage;postMessage;(program) 4.00ms",
"onmessage;alpha 4.80ms",
"onmessage;alpha;(program) 5.28ms",
"onmessage;alpha 4.70ms",
"onmessage;postMessage 236.00µs",
"onmessage;postMessage;(program) 5.06ms",
"onmessage;alpha 5.07ms",
"onmessage;alpha;(program) 4.81ms",
"onmessage;alpha 4.84ms",
"onmessage;alpha;(program) 775.08ms",
],
}
`;
exports[`importFromChromeTimeline Workers Chrome 66 2`] = `
Object {
"frames": Array [
Frame {
"col": -1,
"file": "",
"key": "(program)::-1:-1",
"line": -1,
"name": "(program)",
"selfWeight": 106601,
"totalWeight": 108131,
},
Frame {
"col": 8,
"file": "",
"key": "(anonymous)::247:8",
"line": 247,
"name": "(anonymous)",
"selfWeight": 551,
"totalWeight": 2766,
},
Frame {
"col": -1,
"file": "",
"key": "addEventListener::-1:-1",
"line": -1,
"name": "addEventListener",
"selfWeight": 178,
"totalWeight": 2215,
},
Frame {
"col": 17,
"file": "",
"key": "onLoaded::866:17",
"line": 866,
"name": "onLoaded",
"selfWeight": 372,
"totalWeight": 5158,
},
Frame {
"col": 0,
"file": "",
"key": "(anonymous)::0:0",
"line": 0,
"name": "(anonymous)",
"selfWeight": 0,
"totalWeight": 25649,
},
Frame {
"col": 17,
"file": "",
"key": "dispatch::845:17",
"line": 845,
"name": "dispatch",
"selfWeight": 1103,
"totalWeight": 25649,
},
Frame {
"col": 20,
"file": "",
"key": "setPlatform::839:20",
"line": 839,
"name": "setPlatform",
"selfWeight": 0,
"totalWeight": 902,
},
Frame {
"col": -1,
"file": "",
"key": "add::-1:-1",
"line": -1,
"name": "add",
"selfWeight": 521,
"totalWeight": 902,
},
Frame {
"col": 14,
"file": "",
"key": "reset::433:14",
"line": 433,
"name": "reset",
"selfWeight": 911,
"totalWeight": 2438,
},
Frame {
"col": -1,
"file": "",
"key": "getContext::-1:-1",
"line": -1,
"name": "getContext",
"selfWeight": 598,
"totalWeight": 598,
},
Frame {
"col": 20,
"file": "",
"key": "resetCanvas::423:20",
"line": 423,
"name": "resetCanvas",
"selfWeight": 370,
"totalWeight": 553,
},
Frame {
"col": -1,
"file": "",
"key": "scale::-1:-1",
"line": -1,
"name": "scale",
"selfWeight": 183,
"totalWeight": 183,
},
Frame {
"col": 8,
"file": "http://172.30.3.176:7777/worker.html",
"key": "(anonymous):http://172.30.3.176:7777/worker.html:0:8",
"line": 0,
"name": "(anonymous)",
"selfWeight": 0,
"totalWeight": 2591,
},
Frame {
"col": -1,
"file": "",
"key": "Worker::-1:-1",
"line": -1,
"name": "Worker",
"selfWeight": 873,
"totalWeight": 2591,
},
Frame {
"col": 29,
"file": "http://172.30.3.176:7777/worker.html",
"key": "worker.onmessage:http://172.30.3.176:7777/worker.html:19:29",
"line": 19,
"name": "worker.onmessage",
"selfWeight": 1221,
"totalWeight": 60859,
},
Frame {
"col": 16,
"file": "http://172.30.3.176:7777/worker.html",
"key": "apple:http://172.30.3.176:7777/worker.html:9:16",
"line": 9,
"name": "apple",
"selfWeight": 5474,
"totalWeight": 58863,
},
Frame {
"col": 17,
"file": "http://172.30.3.176:7777/worker.html",
"key": "banana:http://172.30.3.176:7777/worker.html:1:17",
"line": 1,
"name": "banana",
"selfWeight": 50635,
"totalWeight": 53816,
},
Frame {
"col": -1,
"file": "",
"key": "postMessage::-1:-1",
"line": -1,
"name": "postMessage",
"selfWeight": 348,
"totalWeight": 348,
},
Frame {
"col": -1,
"file": "",
"key": "(garbage collector)::-1:-1",
"line": -1,
"name": "(garbage collector)",
"selfWeight": 1530,
"totalWeight": 1530,
},
],
"name": "worker.json - CrRendererMain",
"stacks": Array [
" 28.66ms",
"(program) 315.00µs",
" 108.00µs",
"(program) 433.00µs",
" 116.00µs",
"(program) 436.00µs",
" 368.00µs",
"(program) 108.00µs",
" 30.76ms",
"(program) 1.48ms",
" 167.00µs",
"(program) 1.68ms",
" 6.96ms",
"(program) 2.03ms",
" 4.64ms",
"(program) 3.86ms",
" 14.05ms",
"(program) 15.35ms",
"(anonymous) 551.00µs",
"(anonymous);addEventListener 178.00µs",
"(anonymous);addEventListener;(program) 2.04ms",
"onLoaded 372.00µs",
"onLoaded;(program) 4.79ms",
"(anonymous);dispatch;setPlatform;add 521.00µs",
"(anonymous);dispatch;setPlatform;add;(program) 381.00µs",
"(anonymous);dispatch 927.00µs",
"(anonymous);dispatch;reset 185.00µs",
"(anonymous);dispatch;reset;getContext 598.00µs",
"(anonymous);dispatch;reset 131.00µs",
"(anonymous);dispatch;reset;resetCanvas 370.00µs",
"(anonymous);dispatch;reset;resetCanvas;scale 183.00µs",
"(anonymous);dispatch;reset 595.00µs",
"(anonymous);dispatch;reset;(program) 376.00µs",
"(anonymous);dispatch 176.00µs",
"(anonymous);dispatch;(program) 21.21ms",
" 606.00µs",
"(program) 11.20ms",
" 296.00µs",
"(program) 1.90ms",
"(anonymous);Worker 873.00µs",
"(anonymous);Worker;(program) 1.72ms",
" 670.00µs",
"(program) 28.45ms",
" 353.00µs",
"(program) 187.00µs",
" 185.00µs",
"(program) 4.29ms",
" 607.00µs",
"(program) 192.00µs",
" 407.00µs",
"(program) 215.00µs",
" 28.98ms",
"(program) 770.00µs",
"worker.onmessage;apple 185.00µs",
"worker.onmessage;apple;banana 180.00µs",
"worker.onmessage;apple 367.00µs",
"worker.onmessage;apple;banana 182.00µs",
"worker.onmessage;apple 183.00µs",
"worker.onmessage;apple;banana 546.00µs",
"worker.onmessage;apple 365.00µs",
"worker.onmessage;apple;banana 182.00µs",
"worker.onmessage;apple 365.00µs",
"worker.onmessage;apple;banana 188.00µs",
"worker.onmessage;apple 912.00µs",
"worker.onmessage;apple;banana 189.00µs",
"worker.onmessage;apple 2.51ms",
"worker.onmessage;apple;banana 8.48ms",
"worker.onmessage;postMessage 170.00µs",
" 3.95ms",
"worker.onmessage;apple;banana 433.00µs",
"worker.onmessage 187.00µs",
"worker.onmessage;apple;banana 3.30ms",
" 3.90ms",
"(program) 187.00µs",
"worker.onmessage;apple;banana 1.47ms",
"worker.onmessage;apple 187.00µs",
"worker.onmessage;apple;banana 2.05ms",
" 4.01ms",
"worker.onmessage;apple;banana 3.77ms",
"worker.onmessage;postMessage 178.00µs",
" 3.87ms",
"worker.onmessage;apple;banana 3.74ms",
"worker.onmessage 233.00µs",
" 3.88ms",
"(program) 187.00µs",
"worker.onmessage;apple;banana 744.00µs",
"worker.onmessage;apple 196.00µs",
"worker.onmessage;apple;banana 2.08ms",
"worker.onmessage 188.00µs",
"worker.onmessage;apple;banana 582.00µs",
" 4.46ms",
"(program) 187.00µs",
"worker.onmessage;apple;banana 3.84ms",
"worker.onmessage;apple;banana;(program) 278.00µs",
" 4.76ms",
"worker.onmessage;apple;banana 5.01ms",
"worker.onmessage;apple;banana;(program) 214.00µs",
" 4.67ms",
"(program) 193.00µs",
"worker.onmessage;apple;banana 2.44ms",
"worker.onmessage 186.00µs",
"worker.onmessage;apple;banana 1.02ms",
"worker.onmessage;banana 209.00µs",
"worker.onmessage;apple;banana 207.00µs",
"worker.onmessage;apple 207.00µs",
"worker.onmessage;apple;banana 634.00µs",
"worker.onmessage;apple;banana;(program) 237.00µs",
" 4.92ms",
"worker.onmessage;apple;banana 3.05ms",
"worker.onmessage;banana 218.00µs",
"worker.onmessage;apple;banana 876.00µs",
"worker.onmessage 219.00µs",
"worker.onmessage;apple;banana 219.00µs",
"worker.onmessage;apple;banana;(program) 706.00µs",
" 4.40ms",
"worker.onmessage;apple;banana 2.71ms",
"worker.onmessage 208.00µs",
"worker.onmessage;apple;banana 2.08ms",
"worker.onmessage;apple;banana;(program) 216.00µs",
"worker.onmessage;apple;banana;(program);(garbage collector) 1.53ms",
" 469.53ms",
"(program) 382.00µs",
" 262.13ms",
"(program) 200.00µs",
" 4.51ms",
"(program) 221.00µs",
],
}
`;
exports[`importFromChromeTimeline Workers Chrome 66: indexToView 1`] = `1`;
exports[`importFromChromeTimeline Workers Chrome 66: profileGroup.name 1`] = `"worker.json"`;
exports[`importFromChromeTimeline Workers Chrome 70 1`] = `
Object {
"frames": Array [
File diff suppressed because it is too large Load Diff
@@ -164,6 +164,174 @@ exports[`importTraceEvents multiprocess: indexToView 1`] = `0`;
exports[`importTraceEvents multiprocess: profileGroup.name 1`] = `"multiprocess.json"`;
exports[`importTraceEvents partial json import 1`] = `
Object {
"frames": Array [
Frame {
"col": undefined,
"file": undefined,
"key": "alpha",
"line": undefined,
"name": "alpha",
"selfWeight": 2,
"totalWeight": 14,
},
Frame {
"col": undefined,
"file": undefined,
"key": "beta",
"line": undefined,
"name": "beta",
"selfWeight": 3,
"totalWeight": 12,
},
Frame {
"col": undefined,
"file": undefined,
"key": "gamma {\\"detail\\":\\"foobar\\"}",
"line": undefined,
"name": "gamma {\\"detail\\":\\"foobar\\"}",
"selfWeight": 5,
"totalWeight": 5,
},
Frame {
"col": undefined,
"file": undefined,
"key": "epsilon",
"line": undefined,
"name": "epsilon",
"selfWeight": 4,
"totalWeight": 4,
},
],
"name": "pid 0, tid 0",
"stacks": Array [
"alpha 1.00µs",
"alpha;beta 1.00µs",
"alpha;beta;gamma {\\"detail\\":\\"foobar\\"} 5.00µs",
"alpha;beta;epsilon 4.00µs",
"alpha;beta 2.00µs",
"alpha 1.00µs",
],
}
`;
exports[`importTraceEvents partial json import trailing comma 1`] = `
Object {
"frames": Array [
Frame {
"col": undefined,
"file": undefined,
"key": "alpha",
"line": undefined,
"name": "alpha",
"selfWeight": 2,
"totalWeight": 14,
},
Frame {
"col": undefined,
"file": undefined,
"key": "beta",
"line": undefined,
"name": "beta",
"selfWeight": 3,
"totalWeight": 12,
},
Frame {
"col": undefined,
"file": undefined,
"key": "gamma {\\"detail\\":\\"foobar\\"}",
"line": undefined,
"name": "gamma {\\"detail\\":\\"foobar\\"}",
"selfWeight": 5,
"totalWeight": 5,
},
Frame {
"col": undefined,
"file": undefined,
"key": "epsilon",
"line": undefined,
"name": "epsilon",
"selfWeight": 4,
"totalWeight": 4,
},
],
"name": "pid 0, tid 0",
"stacks": Array [
"alpha 1.00µs",
"alpha;beta 1.00µs",
"alpha;beta;gamma {\\"detail\\":\\"foobar\\"} 5.00µs",
"alpha;beta;epsilon 4.00µs",
"alpha;beta 2.00µs",
"alpha 1.00µs",
],
}
`;
exports[`importTraceEvents partial json import trailing comma: indexToView 1`] = `0`;
exports[`importTraceEvents partial json import trailing comma: profileGroup.name 1`] = `"simple-partial-trailing-comma.json"`;
exports[`importTraceEvents partial json import whitespace padding 1`] = `
Object {
"frames": Array [
Frame {
"col": undefined,
"file": undefined,
"key": "alpha",
"line": undefined,
"name": "alpha",
"selfWeight": 2,
"totalWeight": 14,
},
Frame {
"col": undefined,
"file": undefined,
"key": "beta",
"line": undefined,
"name": "beta",
"selfWeight": 3,
"totalWeight": 12,
},
Frame {
"col": undefined,
"file": undefined,
"key": "gamma {\\"detail\\":\\"foobar\\"}",
"line": undefined,
"name": "gamma {\\"detail\\":\\"foobar\\"}",
"selfWeight": 5,
"totalWeight": 5,
},
Frame {
"col": undefined,
"file": undefined,
"key": "epsilon",
"line": undefined,
"name": "epsilon",
"selfWeight": 4,
"totalWeight": 4,
},
],
"name": "pid 0, tid 0",
"stacks": Array [
"alpha 1.00µs",
"alpha;beta 1.00µs",
"alpha;beta;gamma {\\"detail\\":\\"foobar\\"} 5.00µs",
"alpha;beta;epsilon 4.00µs",
"alpha;beta 2.00µs",
"alpha 1.00µs",
],
}
`;
exports[`importTraceEvents partial json import whitespace padding: indexToView 1`] = `0`;
exports[`importTraceEvents partial json import whitespace padding: profileGroup.name 1`] = `"simple-partial-whitespace.json"`;
exports[`importTraceEvents partial json import: indexToView 1`] = `0`;
exports[`importTraceEvents partial json import: profileGroup.name 1`] = `"simple-partial.json"`;
exports[`importTraceEvents simple 1`] = `
Object {
"frames": Array [
+4
View File
@@ -16,6 +16,10 @@ test('importFromV8Profiler Node 10', async () => {
await checkProfileSnapshot('./sample/profiles/node/10.11.0/example.cpuprofile')
})
test('importFromChromeTimeline Workers Chrome 66', async () => {
await checkProfileSnapshot('./sample/profiles/Chrome/66/worker.json')
})
test('importFromChromeTimeline Workers Chrome 70', async () => {
await checkProfileSnapshot('./sample/profiles/Chrome/70/worker.json')
})
+9 -12
View File
@@ -79,19 +79,17 @@ export function importFromChromeTimeline(events: TimelineEvent[], fileName: stri
// that they are.
sortBy(events, e => e.ts)
const DEFAULT_ID = '(default id)'
for (let event of events) {
if (event.name === 'CpuProfile') {
cpuProfileByID.set(event.id || DEFAULT_ID, event.args.data.cpuProfile as CPUProfile)
if (event.id) {
pidTidById.set(event.id, `${event.pid}:${event.tid}`)
}
const pidTid = `${event.pid}:${event.tid}`
const id = event.id || pidTid
cpuProfileByID.set(id, event.args.data.cpuProfile as CPUProfile)
pidTidById.set(id, pidTid)
}
if (event.name === 'Profile') {
cpuProfileByID.set(event.id || DEFAULT_ID, {
const pidTid = `${event.pid}:${event.tid}`
cpuProfileByID.set(event.id || pidTid, {
startTime: 0,
endTime: 0,
nodes: [],
@@ -110,7 +108,8 @@ export function importFromChromeTimeline(events: TimelineEvent[], fileName: stri
}
if (event.name === 'ProfileChunk') {
const cpuProfile = cpuProfileByID.get(event.id || DEFAULT_ID)
const pidTid = `${event.pid}:${event.tid}`
const cpuProfile = cpuProfileByID.get(event.id || pidTid)
if (cpuProfile) {
const chunk = event.args.data
if (chunk.cpuProfile) {
@@ -131,9 +130,7 @@ export function importFromChromeTimeline(events: TimelineEvent[], fileName: stri
cpuProfile.endTime = chunk.endTime
}
} else {
console.warn(
`Ignoring ProfileChunk for undeclared Profile with id ${event.id || DEFAULT_ID}`,
)
console.warn(`Ignoring ProfileChunk for undeclared Profile with id ${event.id || pidTid}`)
}
}
}
+4
View File
@@ -5,4 +5,8 @@ test('importProfileGroup', async () => {
expect(await importProfileGroupFromText('unknown', '')).toBe(null)
expect(await importProfileGroupFromText('unknown', 'Hello world')).toBe(null)
expect(await importProfileGroupFromText('unknown', 'Hello\n\nWorld')).toBe(null)
// Importing from a version of stackprof which was missing raw_timestamp_deltas should return null
const oldStackprof = `{"version":1.2,"mode":"wall","interval":1000,"samples":0,"gc_samples":0,"missed_samples":0,"frames":{}}`
expect(await importProfileGroupFromText('unknown', oldStackprof)).toBe(null)
})
+22 -2
View File
@@ -71,6 +71,26 @@ function toGroup(profile: Profile | null): ProfileGroup | null {
return {name: profile.getName(), indexToView: 0, profiles: [profile]}
}
function fixUpJSON(content: string): string {
// This code is similar to the code from here:
// https://github.com/catapult-project/catapult/blob/27e047e0494df162022be6aa8a8862742a270232/tracing/tracing/extras/importer/trace_event_importer.html#L197-L208
//
// If the event data begins with a [, then we know it should end with a ]. The
// reason we check for this is because some tracing implementations cannot
// guarantee that a ']' gets written to the trace file. So, we are forgiving
// and if this is obviously the case, we fix it up before throwing the string
// at JSON.parse.
//
content = content.trim()
if (content[0] === '[') {
content = content.replace(/,\s*$/, '')
if (content[content.length - 1] !== ']') {
content += ']'
}
}
return content
}
async function _importProfileGroup(dataSource: ProfileDataSource): Promise<ProfileGroup | null> {
const fileName = await dataSource.name()
@@ -116,7 +136,7 @@ async function _importProfileGroup(dataSource: ProfileDataSource): Promise<Profi
// Second pass: Try to guess what file format it is based on structure
let parsed: any
try {
parsed = JSON.parse(contents)
parsed = JSON.parse(fixUpJSON(contents))
} catch (e) {}
if (parsed) {
if (parsed['$schema'] === 'https://www.speedscope.app/file-format-schema.json') {
@@ -137,7 +157,7 @@ async function _importProfileGroup(dataSource: ProfileDataSource): Promise<Profi
} else if ('head' in parsed && 'samples' in parsed && 'timestamps' in parsed) {
console.log('Importing as Chrome CPU Profile (old format)')
return toGroup(importFromOldV8CPUProfile(parsed))
} else if ('mode' in parsed && 'frames' in parsed) {
} else if ('mode' in parsed && 'frames' in parsed && 'raw_timestamp_deltas' in parsed) {
console.log('Importing as stackprof profile')
return toGroup(importFromStackprof(parsed))
} else if ('code' in parsed && 'functions' in parsed && 'ticks' in parsed) {
+3
View File
@@ -98,4 +98,7 @@ describe('importFromInstrumentsTrace', () => {
test('Instruments 9.3.1', async () => {
await importFromTrace('./sample/profiles/Instruments/9.3.1/simple-time-profile.trace.zip')
})
test('Instruments 10.0', async () => {
await importFromTrace('./sample/profiles/Instruments/10.0/simple-time-profile.trace.zip')
})
})
+4 -1
View File
@@ -361,7 +361,10 @@ async function readFormTemplate(tree: TraceDirectoryTree): Promise<FormTemplateD
const archive = readInstrumentsKeyedArchive(await readAsArrayBuffer(formTemplate))
const version = archive['com.apple.xray.owner.template.version']
const selectedRunNumber = archive['com.apple.xray.owner.template'].get('_selectedRunNumber')
let selectedRunNumber = 1
if ('com.apple.xray.owner.template' in archive) {
selectedRunNumber = archive['com.apple.xray.owner.template'].get('_selectedRunNumber')
}
let instrument = archive['$1']
if ('stubInfoByUUID' in archive) {
instrument = Array.from(archive['stubInfoByUUID'].keys())[0]
+12
View File
@@ -11,3 +11,15 @@ test('importTraceEvents simple object', async () => {
test('importTraceEvents multiprocess', async () => {
await checkProfileSnapshot('./sample/profiles/trace-event/multiprocess.json')
})
test('importTraceEvents partial json import', async () => {
await checkProfileSnapshot('./sample/profiles/trace-event/simple-partial.json')
})
test('importTraceEvents partial json import trailing comma', async () => {
await checkProfileSnapshot('./sample/profiles/trace-event/simple-partial-trailing-comma.json')
})
test('importTraceEvents partial json import whitespace padding', async () => {
await checkProfileSnapshot('./sample/profiles/trace-event/simple-partial-whitespace.json')
})
+20 -6
View File
@@ -251,7 +251,25 @@ function isTraceEventList(maybeEventList: any): maybeEventList is TraceEvent[] {
// fields are mandatory, but without these fields, we won't usefully be able
// to import the data, so we'll rely upon these.
for (let el of maybeEventList) {
if (!('ph' in el) || !('ts' in el)) return false
if (!('ph' in el)) {
return false
}
switch (el.ph) {
case 'B':
case 'E':
case 'X':
// All B, E, and X events must have a timestamp specified, otherwise we
// won't be able to import correctly.
if (!('ts' in el)) {
return false
}
case 'M':
// It's explicitly okay for "M" (metadata) events not to specify a "ts"
// field, since usually there is no logical timestamp for them to have
break
}
}
return true
@@ -267,13 +285,9 @@ function isTraceEventObject(
export function isTraceEventFormatted(
rawProfile: any,
): rawProfile is {traceEvents: TraceEvent[]} | TraceEvent[] {
// We're only going to suppor the JSON formatted profiles for now.
// We're only going to support the JSON formatted profiles for now.
// The spec also discusses support for data embedded in ftrace supported data: https://lwn.net/Articles/365835/.
// TODO(jlfwong): The spec also specifies that it's valid for the trace to not contain a terminating `]`.
// That complicates things a bit for us, so let's just ignore that for now until someone writes in with a
// bug report from real data.
return isTraceEventObject(rawProfile) || isTraceEventList(rawProfile)
}
+5 -1
View File
@@ -310,11 +310,15 @@ export class Application extends StatelessComponent<ApplicationProps> {
return
}
// TODO(jlfwong): Make these into nicer overlays
if (profileGroup == null) {
// TODO(jlfwong): Make this a nicer overlay
alert('Unrecognized format! See documentation about supported formats.')
this.props.setLoading(false)
return
} else if (profileGroup.profiles.length === 0) {
alert("Successfully imported profile, but it's empty!")
this.props.setLoading(false)
return
}
if (this.props.hashParams.title) {