Compare commits

...
4 Commits
Author SHA1 Message Date
Jamie Wong c9ba143eb6 1.0.3 2018-09-10 13:54:32 -07:00
Jamie Wong 802fc2d358 Add test for Chrome 69 import 2018-09-09 18:03:49 -07:00
Jamie Wong b910a2069b Fix import for Chrome 69, support leading idle time before first call (#160)
This PR fixes #159, and also fixes various small things about how profiles were imported for previous versions of Chrome & for Firefox.

The Chrome 69 format splits profiles across several [Trace Event Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview) events. There are two relevant events: "Profile" and "ProfileChunk". At first read through a profile, it seems like profiles are incorrectly terminated, but it seems like the cause of that is that, for whatever reason, events in the event log are not always sorted in chronological order. If sorted chronologically, then the event sequence can be parsed sensibly.

In the process of looking at this information, I also discovered that speedscope's chrome importer was incorrectly interpreting the value of the first element in `timeDeltas` array. It's intended to be the elapsed time since the start of the profile, not the time between the first pair of samples. This changes the weight attributed to the first sample.
2018-09-09 18:00:30 -07:00
William Martin Stewart a1f9755f9c Pretty print JSON (#158) 2018-09-05 09:21:26 -07:00
11 changed files with 1977 additions and 195 deletions
+4
View File
@@ -1,5 +1,9 @@
## Unreleased
## [1.0.3] - 2018-09-10
### Fixed
## [1.0.2] - 2018-09-04
### Fixed
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "speedscope",
"version": "1.0.2",
"version": "1.0.3",
"description": "",
"repository": "jlfwong/speedscope",
"main": "index.js",
File diff suppressed because it is too large Load Diff
@@ -1 +1,81 @@
{"version":"0.0.1","$schema":"https://www.speedscope.app/file-format-schema.json","shared":{"frames":[{"name":"a"},{"name":"b"},{"name":"c"},{"name":"d"}]},"profiles":[{"type":"evented","name":"simple.txt","unit":"none","startValue":0,"endValue":14,"events":[{"type":"O","frame":0,"at":0},{"type":"O","frame":1,"at":0},{"type":"O","frame":2,"at":0},{"type":"C","frame":2,"at":2},{"type":"O","frame":3,"at":2},{"type":"C","frame":3,"at":6},{"type":"O","frame":2,"at":6},{"type":"C","frame":2,"at":9},{"type":"C","frame":1,"at":14},{"type":"C","frame":0,"at":14}]}]}
{
"$schema": "https://www.speedscope.app/file-format-schema.json",
"profiles": [
{
"endValue": 14,
"events": [
{
"at": 0,
"frame": 0,
"type": "O"
},
{
"at": 0,
"frame": 1,
"type": "O"
},
{
"at": 0,
"frame": 2,
"type": "O"
},
{
"at": 2,
"frame": 2,
"type": "C"
},
{
"at": 2,
"frame": 3,
"type": "O"
},
{
"at": 6,
"frame": 3,
"type": "C"
},
{
"at": 6,
"frame": 2,
"type": "O"
},
{
"at": 9,
"frame": 2,
"type": "C"
},
{
"at": 14,
"frame": 1,
"type": "C"
},
{
"at": 14,
"frame": 0,
"type": "C"
}
],
"name": "simple.txt",
"startValue": 0,
"type": "evented",
"unit": "none"
}
],
"shared": {
"frames": [
{
"name": "a"
},
{
"name": "b"
},
{
"name": "c"
},
{
"name": "d"
}
]
},
"version": "0.0.1"
}
-4
View File
@@ -25,8 +25,4 @@ function gamma() {
return prod
}
console.profile('simple')
alpha()
setTimeout(() => {
console.profileEnd('simple')
}, 0)
+407 -154
View File
@@ -9,8 +9,8 @@ Object {
"key": "(anonymous)::0:0",
"line": 0,
"name": "(anonymous)",
"selfWeight": 11575,
"totalWeight": 45920,
"selfWeight": 1060,
"totalWeight": 34345,
},
Frame {
"col": 10,
@@ -19,7 +19,7 @@ Object {
"line": 0,
"name": "a",
"selfWeight": 0,
"totalWeight": 34345,
"totalWeight": 33285,
},
Frame {
"col": 10,
@@ -28,7 +28,7 @@ Object {
"line": 5,
"name": "b",
"selfWeight": 0,
"totalWeight": 16788,
"totalWeight": 17002,
},
Frame {
"col": 10,
@@ -36,8 +36,8 @@ Object {
"key": "d::13:10",
"line": 13,
"name": "d",
"selfWeight": 34345,
"totalWeight": 34345,
"selfWeight": 33285,
"totalWeight": 33285,
},
Frame {
"col": 10,
@@ -46,14 +46,15 @@ Object {
"line": 9,
"name": "c",
"selfWeight": 0,
"totalWeight": 17557,
"totalWeight": 16283,
},
],
"name": "simple.cpuprofile",
"stacks": Array [
"(anonymous) 11.57ms",
"(anonymous);a;b;d 16.79ms",
"(anonymous);a;c;d 17.56ms",
" 11.57ms",
"(anonymous) 1.06ms",
"(anonymous);a;b;d 17.00ms",
"(anonymous);a;c;d 16.28ms",
],
}
`;
@@ -71,8 +72,8 @@ Object {
"key": "(program)::-1:-1",
"line": -1,
"name": "(program)",
"selfWeight": 12244,
"totalWeight": 12244,
"selfWeight": 2299,
"totalWeight": 2299,
},
Frame {
"col": 10,
@@ -81,7 +82,7 @@ Object {
"line": 0,
"name": "a",
"selfWeight": 0,
"totalWeight": 33184,
"totalWeight": 33186,
},
Frame {
"col": 10,
@@ -89,8 +90,8 @@ Object {
"key": "c:file:///Users/jlfwong/code/speedscope/sample/programs/javascript/simple.js:13:10",
"line": 13,
"name": "c",
"selfWeight": 16197,
"totalWeight": 16197,
"selfWeight": 16275,
"totalWeight": 16275,
},
Frame {
"col": 10,
@@ -98,170 +99,422 @@ Object {
"key": "b:file:///Users/jlfwong/code/speedscope/sample/programs/javascript/simple.js:7:10",
"line": 7,
"name": "b",
"selfWeight": 16987,
"totalWeight": 16987,
"selfWeight": 16911,
"totalWeight": 16911,
},
],
"name": "simple-timeline.json",
"stacks": Array [
"(program) 10.16ms",
" 1.99ms",
"(program) 926.00µs",
" 85.95ms",
"(program) 133.00µs",
" 10.06ms",
"(program) 202.00µs",
" 1.97ms",
"(program) 1.04ms",
" 85.89ms",
"(program) 132.00µs",
" 8.40ms",
"(program) 134.00µs",
"a;c 257.00µs",
"a;b 534.00µs",
"a;c 513.00µs",
"a;c 273.00µs",
"a;b 513.00µs",
"a;c 257.00µs",
"a;b 518.00µs",
"a;c 514.00µs",
"a;b 261.00µs",
"a;c 512.00µs",
"a;b 513.00µs",
"a;c 262.00µs",
"a;b 514.00µs",
"a;c 517.00µs",
"a;b 258.00µs",
"a;b 261.00µs",
"a;c 515.00µs",
"a;b 273.00µs",
"a;c 134.00µs",
"a;b 146.00µs",
"a;c 291.00µs",
"a;b 256.00µs",
"a;c 145.00µs",
"a;b 134.00µs",
"a;c 292.00µs",
"a;b 272.00µs",
"a;c 129.00µs",
"a;b 145.00µs",
"a;c 265.00µs",
"a;b 142.00µs",
"a;c 141.00µs",
"a;b 139.00µs",
"a;c 397.00µs",
"a;b 126.00µs",
"a;c 131.00µs",
"a;b 126.00µs",
"a;c 521.00µs",
"a;b 128.00µs",
"a;c 261.00µs",
"a;b 127.00µs",
"a;c 130.00µs",
"a;b 128.00µs",
"a;c 136.00µs",
"a;b 272.00µs",
"a;c 146.00µs",
"a;b 661.00µs",
"a;c 113.00µs",
"a;b 135.00µs",
"a;c 585.00µs",
"a;b 294.00µs",
"a;c 274.00µs",
"a;b 129.00µs",
"a;c 127.00µs",
"a;b 128.00µs",
"a;c 247.00µs",
"a;b 526.00µs",
"a;c 125.00µs",
"a;b 129.00µs",
"a;c 131.00µs",
"a;b 253.00µs",
"a;c 135.00µs",
"a;b 123.00µs",
"a;c 128.00µs",
"a;b 257.00µs",
"a;c 137.00µs",
"a;b 256.00µs",
"a;c 411.00µs",
"a;b 892.00µs",
"a;c 146.00µs",
"a;b 148.00µs",
"a;c 309.00µs",
"a;b 140.00µs",
"a;c 151.00µs",
"a;b 458.00µs",
"a;c 156.00µs",
"a;b 170.00µs",
"a;c 273.00µs",
"a;b 141.00µs",
"a;c 139.00µs",
"a;b 290.00µs",
"a;c 115.00µs",
"a;b 281.00µs",
"a;c 270.00µs",
"a;b 283.00µs",
"a;c 144.00µs",
"a;b 146.00µs",
"a;c 410.00µs",
"a;b 146.00µs",
"a;c 147.00µs",
"a;b 312.00µs",
"a;c 146.00µs",
"a;b 907.00µs",
"a;c 231.00µs",
"a;b 138.00µs",
"a;c 280.00µs",
"a;b 255.00µs",
"a;c 257.00µs",
"a;b 130.00µs",
"a;c 255.00µs",
"a;b 128.00µs",
"a;c 526.00µs",
"a;b 128.00µs",
"a;c 384.00µs",
"a;b 398.00µs",
"a;c 260.00µs",
"a;b 664.00µs",
"a;c 393.00µs",
"a;b 131.00µs",
"a;c 126.00µs",
"a;b 129.00µs",
"a;c 520.00µs",
"a;b 129.00µs",
"a;c 259.00µs",
"a;b 130.00µs",
"a;c 128.00µs",
"a;b 136.00µs",
"a;c 137.00µs",
"a;b 281.00µs",
"a;c 150.00µs",
"a;b 624.00µs",
"a;c 135.00µs",
"a;b 129.00µs",
"a;c 604.00µs",
"a;b 292.00µs",
"a;c 257.00µs",
"a;b 127.00µs",
"a;c 128.00µs",
"a;b 118.00µs",
"a;c 256.00µs",
"a;b 524.00µs",
"a;c 129.00µs",
"a;b 131.00µs",
"a;c 125.00µs",
"a;b 263.00µs",
"a;c 123.00µs",
"a;b 128.00µs",
"a;c 129.00µs",
"a;b 265.00µs",
"a;c 128.00µs",
"a;b 280.00µs",
"a;c 366.00µs",
"a;b 931.00µs",
"a;c 148.00µs",
"a;b 147.00µs",
"a;c 156.00µs",
"a;b 137.00µs",
"a;c 302.00µs",
"a;b 151.00µs",
"a;c 158.00µs",
"a;b 456.00µs",
"a;c 170.00µs",
"a;b 139.00µs",
"a;c 145.00µs",
"a;b 260.00µs",
"a;c 141.00µs",
"a;b 280.00µs",
"a;c 268.00µs",
"a;b 289.00µs",
"a;c 146.00µs",
"a;b 120.00µs",
"a;c 436.00µs",
"a;b 147.00µs",
"a;c 164.00µs",
"a;b 294.00µs",
"a;c 147.00µs",
"a;b 856.00µs",
"a;c 273.00µs",
"a;b 139.00µs",
"a;c 269.00µs",
"a;b 256.00µs",
"a;c 258.00µs",
"a;b 128.00µs",
"a;c 255.00µs",
"a;b 140.00µs",
"a;c 141.00µs",
"a;c 514.00µs",
"a;b 129.00µs",
"a;c 395.00µs",
"a;b 389.00µs",
"a;c 257.00µs",
"a;b 686.00µs",
"a;c 147.00µs",
"a;b 156.00µs",
"a;c 137.00µs",
"a;b 141.00µs",
"a;c 140.00µs",
"a;b 140.00µs",
"a;c 139.00µs",
"a;b 129.00µs",
"a;c 269.00µs",
"a;b 293.00µs",
"a;c 148.00µs",
"a;b 293.00µs",
"a;c 292.00µs",
"a;b 296.00µs",
"a;c 147.00µs",
"a;b 140.00µs",
"a;c 139.00µs",
"a;b 139.00µs",
"a;c 511.00µs",
"a;b 515.00µs",
"a;c 386.00µs",
"a;b 514.00µs",
"a;c 272.00µs",
"a;b 242.00µs",
"a;c 128.00µs",
"a;b 134.00µs",
"a;c 112.00µs",
"a;b 288.00µs",
"a;c 451.00µs",
"a;b 138.00µs",
"a;c 292.00µs",
"a;b 145.00µs",
"a;c 259.00µs",
"a;b 148.00µs",
"a;c 293.00µs",
"a;b 141.00µs",
"a;c 141.00µs",
"a;b 140.00µs",
"a;c 140.00µs",
" 61.61ms",
"(program) 118.00µs",
" 134.00µs",
"(program) 128.00µs",
" 16.40ms",
"(program) 125.00µs",
" 16.64ms",
"(program) 129.00µs",
" 16.72ms",
"(program) 130.00µs",
" 294.15ms",
"(program) 129.00µs",
" 7.68ms",
"a;b 139.00µs",
"a;c 129.00µs",
"a;b 149.00µs",
"a;c 267.00µs",
"a;b 294.00µs",
"a;c 145.00µs",
"a;b 294.00µs",
"a;c 294.00µs",
"a;b 295.00µs",
"a;c 140.00µs",
"a;b 139.00µs",
"a;c 139.00µs",
"a;b 127.00µs",
"a;c 513.00µs",
"a;b 516.00µs",
"a;c 385.00µs",
"a;b 520.00µs",
"a;c 252.00µs",
"a;b 255.00µs",
"a;c 134.00µs",
"a;b 112.00µs",
"a;c 140.00µs",
"a;b 296.00µs",
"a;c 441.00µs",
"a;b 145.00µs",
"a;c 292.00µs",
"a;b 130.00µs",
"a;c 277.00µs",
"a;b 147.00µs",
"a;c 286.00µs",
"a;b 140.00µs",
"a;c 136.00µs",
" 61.59ms",
"(program) 134.00µs",
" 128.00µs",
"(program) 141.00µs",
" 16.39ms",
"(program) 145.00µs",
" 16.63ms",
"(program) 131.00µs",
" 16.71ms",
"(program) 131.00µs",
" 294.15ms",
"(program) 109.00µs",
],
}
`;
exports[`importFromChromeTimeline Chrome 69 1`] = `
Object {
"frames": Array [
Frame {
"col": 0,
"file": "extensions::SafeBuiltins",
"key": "(anonymous):extensions::SafeBuiltins:0:0",
"line": 0,
"name": "(anonymous)",
"selfWeight": 0,
"totalWeight": 3188,
},
Frame {
"col": 9,
"file": "extensions::SafeBuiltins",
"key": "(anonymous):extensions::SafeBuiltins:0:9",
"line": 0,
"name": "(anonymous)",
"selfWeight": 0,
"totalWeight": 3188,
},
Frame {
"col": 20,
"file": "extensions::SafeBuiltins",
"key": "saveBuiltin:extensions::SafeBuiltins:24:20",
"line": 24,
"name": "saveBuiltin",
"selfWeight": 302,
"totalWeight": 3188,
},
Frame {
"col": undefined,
"file": undefined,
"key": "(program):undefined:undefined:undefined",
"line": undefined,
"name": "(program)",
"selfWeight": 6862,
"totalWeight": 6862,
},
Frame {
"col": 0,
"file": "file:///Users/jlfwong/code/speedscope/sample/programs/javascript/simple.js",
"key": "(anonymous):file:///Users/jlfwong/code/speedscope/sample/programs/javascript/simple.js:0:0",
"line": 0,
"name": "(anonymous)",
"selfWeight": 136,
"totalWeight": 37217,
},
Frame {
"col": 14,
"file": "file:///Users/jlfwong/code/speedscope/sample/programs/javascript/simple.js",
"key": "alpha:file:///Users/jlfwong/code/speedscope/sample/programs/javascript/simple.js:0:14",
"line": 0,
"name": "alpha",
"selfWeight": 873,
"totalWeight": 37081,
},
Frame {
"col": 13,
"file": "file:///Users/jlfwong/code/speedscope/sample/programs/javascript/simple.js",
"key": "beta:file:///Users/jlfwong/code/speedscope/sample/programs/javascript/simple.js:7:13",
"line": 7,
"name": "beta",
"selfWeight": 7552,
"totalWeight": 17562,
},
Frame {
"col": 14,
"file": "file:///Users/jlfwong/code/speedscope/sample/programs/javascript/simple.js",
"key": "gamma:file:///Users/jlfwong/code/speedscope/sample/programs/javascript/simple.js:19:14",
"line": 19,
"name": "gamma",
"selfWeight": 20018,
"totalWeight": 20018,
},
Frame {
"col": 14,
"file": "file:///Users/jlfwong/code/speedscope/sample/programs/javascript/simple.js",
"key": "delta:file:///Users/jlfwong/code/speedscope/sample/programs/javascript/simple.js:13:14",
"line": 13,
"name": "delta",
"selfWeight": 7179,
"totalWeight": 18646,
},
Frame {
"col": undefined,
"file": undefined,
"key": "(garbage collector):undefined:undefined:undefined",
"line": undefined,
"name": "(garbage collector)",
"selfWeight": 646,
"totalWeight": 778,
},
],
"name": "simple.json",
"stacks": Array [
" 6.85ms",
"(anonymous);(anonymous);saveBuiltin 302.00µs",
"(anonymous);(anonymous);saveBuiltin;(program) 2.89ms",
" 230.00µs",
"(program) 828.00µs",
" 110.00µs",
"(program) 118.00µs",
" 323.00µs",
"(program) 123.00µs",
" 139.00µs",
"(program) 400.00µs",
"(anonymous) 136.00µs",
"(anonymous);alpha;beta 108.00µs",
"(anonymous);alpha;beta;gamma 268.00µs",
"(anonymous);alpha 137.00µs",
"(anonymous);alpha;delta 139.00µs",
"(anonymous);alpha;delta;gamma 261.00µs",
"(anonymous);alpha;beta;gamma 964.00µs",
"(anonymous);alpha;delta;gamma 268.00µs",
"(anonymous);alpha;beta;gamma 265.00µs",
"(anonymous);alpha;delta;gamma 582.00µs",
"(anonymous);alpha;beta;gamma 135.00µs",
"(anonymous);alpha;delta;gamma 147.00µs",
"(anonymous);alpha;beta;gamma 163.00µs",
"(anonymous);alpha;delta;gamma 133.00µs",
"(anonymous);alpha;beta;gamma 144.00µs",
"(anonymous);alpha;delta;gamma 276.00µs",
"(anonymous);alpha;delta 136.00µs",
"(anonymous);alpha;beta;gamma 144.00µs",
"(anonymous);alpha;delta;gamma 431.00µs",
"(anonymous);alpha;beta;gamma 419.00µs",
"(anonymous);alpha;delta;gamma 134.00µs",
"(anonymous);alpha;beta;gamma 136.00µs",
"(anonymous);alpha;delta;gamma 144.00µs",
"(anonymous);alpha;beta;gamma 269.00µs",
"(anonymous);alpha;delta;gamma 160.00µs",
"(anonymous);alpha;beta;gamma 141.00µs",
"(anonymous);alpha;delta;gamma 292.00µs",
"(anonymous);alpha;beta;gamma 583.00µs",
"(anonymous);alpha;delta;gamma 149.00µs",
"(anonymous);alpha;beta;gamma 139.00µs",
"(anonymous);alpha;delta;gamma 133.00µs",
"(anonymous);alpha;beta;gamma 386.00µs",
"(anonymous);alpha;delta;gamma 385.00µs",
"(anonymous);alpha;beta;gamma 391.00µs",
"(anonymous);alpha;delta;gamma 261.00µs",
"(anonymous);alpha;beta;gamma 131.00µs",
"(anonymous);alpha;delta;gamma 132.00µs",
"(anonymous);alpha 144.00µs",
"(anonymous);alpha;delta;gamma 134.00µs",
"(anonymous);alpha;beta;gamma 146.00µs",
"(anonymous);alpha;delta;gamma 119.00µs",
"(anonymous);alpha;beta;gamma 120.00µs",
"(anonymous);alpha;delta;gamma 134.00µs",
"(anonymous);alpha;beta;gamma 1.47ms",
"(anonymous);alpha;delta;gamma 148.00µs",
"(anonymous);alpha;beta;gamma 147.00µs",
"(anonymous);alpha;delta;gamma 146.00µs",
"(anonymous);alpha;beta;gamma 269.00µs",
"(anonymous);alpha;delta;gamma 138.00µs",
"(anonymous);alpha;beta;gamma 509.00µs",
"(anonymous);alpha;delta;gamma 1.63ms",
"(anonymous);alpha;beta;gamma 432.00µs",
"(anonymous);alpha;delta;gamma 129.00µs",
"(anonymous);alpha;beta;gamma 139.00µs",
"(anonymous);alpha;delta;gamma 389.00µs",
"(anonymous);alpha;beta 133.00µs",
"(anonymous);alpha;beta;gamma 133.00µs",
"(anonymous);alpha;delta;gamma 803.00µs",
"(anonymous);alpha;beta;gamma 283.00µs",
"(anonymous);alpha;delta;gamma 152.00µs",
"(anonymous);alpha;beta;gamma 135.00µs",
"(anonymous);alpha;delta;gamma 132.00µs",
"(anonymous);alpha;beta;gamma 134.00µs",
"(anonymous);alpha;delta;gamma 154.00µs",
"(anonymous);alpha;beta;gamma 145.00µs",
"(anonymous);alpha;delta;gamma 516.00µs",
"(anonymous);alpha;delta 128.00µs",
"(anonymous);alpha;delta;gamma 138.00µs",
"(anonymous);alpha;beta;gamma 139.00µs",
"(anonymous);alpha;delta;gamma 253.00µs",
"(anonymous);alpha;beta;gamma 145.00µs",
"(anonymous);alpha;delta;gamma 450.00µs",
"(anonymous);alpha;beta;gamma 142.00µs",
"(anonymous);alpha;delta;gamma 125.00µs",
"(anonymous);alpha;beta;gamma 289.00µs",
"(anonymous);alpha;delta;gamma 293.00µs",
"(anonymous);alpha;beta;gamma 147.00µs",
"(anonymous);alpha 443.00µs",
"(anonymous);alpha;delta;gamma 133.00µs",
"(anonymous);alpha;beta;gamma 409.00µs",
"(anonymous);alpha;beta 385.00µs",
"(anonymous);alpha;delta 659.00µs",
"(anonymous);alpha;beta 253.00µs",
"(anonymous);alpha;delta 257.00µs",
"(anonymous);alpha;beta 256.00µs",
"(anonymous);alpha;delta 129.00µs",
"(anonymous);alpha;beta 260.00µs",
"(anonymous);alpha;delta 255.00µs",
"(anonymous);alpha;beta 129.00µs",
"(anonymous);alpha;delta 140.00µs",
"(anonymous);alpha;beta 136.00µs",
"(anonymous);alpha;delta 134.00µs",
"(anonymous);alpha;beta 283.00µs",
"(anonymous);alpha;delta 253.00µs",
"(anonymous);alpha;beta 397.00µs",
"(anonymous);alpha;delta 401.00µs",
"(anonymous);alpha;beta 267.00µs",
"(anonymous);alpha;delta 283.00µs",
"(anonymous);alpha;beta 137.00µs",
"(anonymous);alpha;delta 134.00µs",
"(anonymous);alpha;beta 403.00µs",
"(anonymous);alpha;delta 135.00µs",
"(anonymous);alpha;beta 658.00µs",
"(anonymous);alpha;delta 117.00µs",
"(anonymous);alpha;beta 257.00µs",
"(anonymous);alpha;delta 134.00µs",
"(anonymous);alpha;beta 143.00µs",
"(anonymous);alpha;delta 260.00µs",
"(anonymous);alpha 149.00µs",
"(anonymous);alpha;delta 146.00µs",
"(anonymous);alpha;beta 129.00µs",
"(anonymous);alpha;delta 131.00µs",
"(anonymous);alpha;beta 130.00µs",
"(anonymous);alpha;delta 665.00µs",
"(anonymous);alpha;beta 415.00µs",
"(anonymous);alpha;delta 264.00µs",
"(anonymous);alpha;beta 527.00µs",
"(anonymous);alpha;delta 140.00µs",
"(anonymous);alpha;beta 269.00µs",
"(anonymous);alpha;delta 135.00µs",
"(anonymous);alpha;beta 386.00µs",
"(anonymous);alpha;delta 559.00µs",
"(anonymous);alpha;beta 131.00µs",
"(anonymous);alpha;delta 140.00µs",
"(anonymous);alpha;beta 129.00µs",
"(anonymous);alpha;delta 400.00µs",
"(anonymous);alpha;beta 134.00µs",
"(anonymous);alpha;delta 634.00µs",
"(anonymous);alpha;beta 1.10ms",
"(anonymous);alpha;delta 271.00µs",
"(anonymous);alpha;delta;(program) 1.46ms",
" 430.00µs",
"(program) 781.00µs",
" 273.00µs",
"(garbage collector) 646.00µs",
"(garbage collector);(program) 132.00µs",
" 26.36ms",
"(program) 135.00µs",
],
}
`;
exports[`importFromChromeTimeline Chrome 69: indexToView 1`] = `0`;
exports[`importFromChromeTimeline Chrome 69: profileGroup.name 1`] = `"simple.json"`;
exports[`importFromChromeTimeline: indexToView 1`] = `0`;
exports[`importFromChromeTimeline: profileGroup.name 1`] = `"simple-timeline.json"`;
@@ -42,6 +42,7 @@ Object {
],
"name": "simple-firefox.json",
"stacks": Array [
" 2.04ms",
"a;b;d 989.53µs",
"a;c;d 1.02ms",
"a;b;d 982.04µs",
@@ -101,6 +102,7 @@ Object {
],
"name": "simple-firefox.json",
"stacks": Array [
" 4.61ms",
"alpha;delta;gamma 999.57µs",
"alpha;beta;gamma 2.01ms",
"alpha;delta;gamma 1.00ms",
@@ -174,6 +176,7 @@ Object {
],
"name": "recursion.json",
"stacks": Array [
" 1.71ms",
"main;alpha;beta;alpha;beta;alpha;beta;alpha;delta;gamma 998.89µs",
"main 1.19ms",
"main;alpha;beta;alpha;delta;gamma 1.83ms",
+4
View File
@@ -7,3 +7,7 @@ test('importFromChromeCPUProfile', async () => {
test('importFromChromeTimeline', async () => {
await checkProfileSnapshot('./sample/profiles/Chrome/65/simple-timeline.json')
})
test('importFromChromeTimeline Chrome 69', async () => {
await checkProfileSnapshot('./sample/profiles/Chrome/69/simple.json')
})
+103 -25
View File
@@ -1,7 +1,9 @@
import {Profile, FrameInfo, CallTreeProfileBuilder} from '../lib/profile'
import {getOrInsert, lastOf} from '../lib/utils'
import {getOrInsert, lastOf, sortBy} from '../lib/utils'
import {TimeFormatter} from '../lib/value-formatters'
// See: https://github.com/v8/v8/blob/master/src/inspector/js_protocol.json
interface TimelineEvent {
pid: number
tid: number
@@ -45,16 +47,78 @@ interface CPUProfile {
timeDeltas: number[]
}
export function isChromeTimeline(rawProfile: any): boolean {
if (!Array.isArray(rawProfile)) return false
if (rawProfile.length < 1) return false
const first = rawProfile[0]
if (!('pid' in first && 'tid' in first && 'ph' in first && 'cat' in first)) return false
if (
!rawProfile.find(
e => e.name === 'CpuProfile' || e.name === 'Profile' || e.name === 'ProfileChunk',
)
)
return false
return true
}
export function importFromChromeTimeline(events: TimelineEvent[]): Profile {
// It seems like sometimes Chrome timeline files contain multiple CpuProfiles?
// For now, choose the first one in the list.
let cpuProfile: CPUProfile | null = null
// The events aren't necessarily recorded in chronological order. Sort them so
// that they are.
sortBy(events, e => e.ts)
for (let event of events) {
if (event.name == 'CpuProfile') {
const chromeProfile = event.args.data.cpuProfile as CPUProfile
return importFromChromeCPUProfile(chromeProfile)
cpuProfile = event.args.data.cpuProfile as CPUProfile
break
}
if (event.name == 'Profile') {
cpuProfile = {
startTime: 0,
endTime: 0,
nodes: [],
samples: [],
timeDeltas: [],
...event.args.data,
}
}
if (event.name == 'ProfileChunk') {
if (cpuProfile) {
const chunk = event.args.data
if (chunk.cpuProfile) {
if (chunk.cpuProfile.nodes) {
cpuProfile.nodes = cpuProfile.nodes.concat(chunk.cpuProfile.nodes)
}
if (chunk.cpuProfile.samples) {
cpuProfile.samples = cpuProfile.samples.concat(chunk.cpuProfile.samples)
}
}
if (chunk.timeDeltas) {
cpuProfile.timeDeltas = cpuProfile.timeDeltas.concat(chunk.timeDeltas)
}
if (chunk.startTime != null) {
cpuProfile.startTime = chunk.startTime
}
if (chunk.endTime != null) {
cpuProfile.endTime = chunk.endTime
}
} else {
console.log('Ignoring ProfileChunk when no Profile is active')
}
}
}
throw new Error('Could not find CPU profile in Timeline')
if (cpuProfile) {
return importFromChromeCPUProfile(cpuProfile as CPUProfile)
} else {
throw new Error('Could not find CPU profile in Timeline')
}
}
const callFrameToFrameInfo = new Map<CPUProfileCallFrame, FrameInfo>()
@@ -74,7 +138,14 @@ function frameInfoForCallFrame(callFrame: CPUProfileCallFrame) {
})
}
function shouldIgnoreFunction(functionName: string) {
function shouldIgnoreFunction(callFrame: CPUProfileCallFrame) {
const {functionName, url} = callFrame
if (url === 'native dummy.js') {
// I'm not really sure what this is about, but this seems to be used
// as a way of avoiding edge cases in V8's implementation.
// See: https://github.com/v8/v8/blob/b8626ca4/tools/js2c.py#L419-L424
return true
}
return functionName === '(root)' || functionName === '(idle)'
}
@@ -90,6 +161,10 @@ export function importFromChromeCPUProfile(chromeProfile: CPUProfile): Profile {
nodeById.set(node.id, node)
}
for (let node of chromeProfile.nodes) {
if (typeof node.parent === 'number') {
node.parent = nodeById.get(node.parent)
}
if (!node.children) continue
for (let childId of node.children) {
const child = nodeById.get(childId)
@@ -99,9 +174,12 @@ export function importFromChromeCPUProfile(chromeProfile: CPUProfile): Profile {
}
const samples: number[] = []
const timeDeltas: number[] = []
const sampleTimes: number[] = []
// The first delta is relative to the profile startTime.
// Ref: https://github.com/v8/v8/blob/44bd8fd7/src/inspector/js_protocol.json#L1485
let elapsed = chromeProfile.timeDeltas[0]
let elapsed = 0
let lastNodeId = NaN
// The chrome CPU profile format doesn't collapse identical samples. We'll do that
@@ -110,29 +188,30 @@ export function importFromChromeCPUProfile(chromeProfile: CPUProfile): Profile {
const nodeId = chromeProfile.samples[i]
if (nodeId != lastNodeId) {
samples.push(nodeId)
timeDeltas.push(elapsed)
elapsed = 0
sampleTimes.push(elapsed)
}
let timeDelta = chromeProfile.timeDeltas[i]
if (timeDelta < 0) {
console.warn('Substituting zero for unexpected time delta:', timeDelta, 'at index', i)
timeDelta = 0
}
if (i === chromeProfile.samples.length - 1) {
if (!isNaN(lastNodeId)) {
samples.push(lastNodeId)
sampleTimes.push(elapsed)
}
} else {
let timeDelta = chromeProfile.timeDeltas[i + 1]
if (timeDelta < 0) {
console.warn('Substituting zero for unexpected time delta:', timeDelta, 'at index', i)
timeDelta = 0
}
elapsed += timeDelta
lastNodeId = nodeId
}
if (!isNaN(lastNodeId)) {
samples.push(lastNodeId)
timeDeltas.push(elapsed)
elapsed += timeDelta
lastNodeId = nodeId
}
}
let prevStack: CPUProfileNode[] = []
let value = 0
for (let i = 0; i < samples.length; i++) {
const timeDelta = timeDeltas[i + 1] || 0
const value = sampleTimes[i]
const nodeId = samples[i]
let stackTop = nodeById.get(nodeId)
if (!stackTop) continue
@@ -161,7 +240,7 @@ export function importFromChromeCPUProfile(chromeProfile: CPUProfile): Profile {
const toOpen: CPUProfileNode[] = []
for (
let node: CPUProfileNode | null = stackTop;
node && node != lca && !shouldIgnoreFunction(node.callFrame.functionName);
node && node != lca && !shouldIgnoreFunction(node.callFrame);
// Place Chrome internal functions on top of the previous call stack
node = shouldPlaceOnTopOfPreviousStack(node.callFrame.functionName)
? lastOf(prevStack)
@@ -176,12 +255,11 @@ export function importFromChromeCPUProfile(chromeProfile: CPUProfile): Profile {
}
prevStack = prevStack.concat(toOpen)
value += timeDelta
}
// Close frames that are open at the end of the trace
for (let i = prevStack.length - 1; i >= 0; i--) {
profile.leaveFrame(frameInfoForCallFrame(prevStack[i].callFrame), value)
profile.leaveFrame(frameInfoForCallFrame(prevStack[i].callFrame), lastOf(sampleTimes)!)
}
profile.setValueFormatter(new TimeFormatter('microseconds'))
+2 -2
View File
@@ -1,7 +1,7 @@
import {Profile, ProfileGroup} from '../lib/profile'
import {FileSystemDirectoryEntry} from './file-system-entry'
import {importFromChromeCPUProfile, importFromChromeTimeline} from './chrome'
import {importFromChromeCPUProfile, importFromChromeTimeline, isChromeTimeline} from './chrome'
import {importFromStackprof} from './stackprof'
import {importFromInstrumentsDeepCopy, importFromInstrumentsTrace} from './instruments'
import {importFromBGFlameGraph} from './bg-flamegraph'
@@ -77,7 +77,7 @@ async function _importProfileGroup(
} else if (parsed['systemHost'] && parsed['systemHost']['name'] == 'Firefox') {
console.log('Importing as Firefox profile')
return toGroup(importFromFirefox(parsed))
} else if (Array.isArray(parsed) && parsed[parsed.length - 1].name === 'CpuProfile') {
} else if (isChromeTimeline(parsed)) {
console.log('Importing as Chrome CPU Profile')
return toGroup(importFromChromeTimeline(parsed))
} else if ('nodes' in parsed && 'samples' in parsed && 'timeDeltas' in parsed) {
+8 -8
View File
@@ -521,10 +521,9 @@ export class CallTreeProfileBuilder extends Profile {
private framesInStack = new Map<Frame, number>()
private stack: Frame[] = []
private lastValue: number | null = null
private lastValue: number = 0
private addWeightsToFrames(value: number) {
if (this.lastValue == null) this.lastValue = value
const delta = value - this.lastValue!
const delta = value - this.lastValue
for (let frame of this.framesInStack.keys()) {
frame.addToTotalWeight(delta)
}
@@ -534,7 +533,7 @@ export class CallTreeProfileBuilder extends Profile {
}
}
private addWeightsToNodes(value: number, stack: CallTreeNode[]) {
const delta = value - this.lastValue!
const delta = value - this.lastValue
for (let node of stack) {
node.addToTotalWeight(delta)
}
@@ -552,14 +551,15 @@ export class CallTreeProfileBuilder extends Profile {
if (prevTop) {
if (useAppendOrder) {
const delta = value - this.lastValue!
const delta = value - this.lastValue
if (delta > 0) {
this.samples.push(prevTop)
this.weights.push(value - this.lastValue!)
this.weights.push(value - this.lastValue)
} else if (delta < 0) {
throw new Error(
`Samples must be provided in increasing order of cumulative value. Last sample was ${this
.lastValue!}, this sample was ${value}`,
`Samples must be provided in increasing order of cumulative value. Last sample was ${
this.lastValue
}, this sample was ${value}`,
)
}
}