mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-11 16:32:59 +00:00
Bug 1154115 - Fix devtools tests to use the new profiler JSON format. (r=jsantell)
This commit is contained in:
parent
2ad192eabb
commit
92f95889a0
@ -376,6 +376,7 @@ function deflateThread(thread, uniqueStacks) {
|
||||
stringTable: uniqueStacks.getStringTable()
|
||||
};
|
||||
}
|
||||
exports.RecordingUtils.deflateThread = deflateThread;
|
||||
|
||||
function stackTableWithSchema(data) {
|
||||
let slot = 0;
|
||||
@ -444,6 +445,8 @@ UniqueStrings.prototype.getOrAddStringIndex = function(s) {
|
||||
return index;
|
||||
};
|
||||
|
||||
exports.RecordingUtils.UniqueStrings = UniqueStrings;
|
||||
|
||||
/**
|
||||
* A helper class to deduplicate old-version profiles.
|
||||
*
|
||||
@ -564,3 +567,5 @@ UniqueStacks.prototype.getOrAddStackIndex = function(prefixIndex, frameIndex) {
|
||||
UniqueStacks.prototype.getOrAddStringIndex = function(s) {
|
||||
return this._uniqueStrings.getOrAddStringIndex(s);
|
||||
};
|
||||
|
||||
exports.RecordingUtils.UniqueStacks = UniqueStacks;
|
||||
|
@ -129,6 +129,7 @@ support-files =
|
||||
[browser_profiler_tree-view-06.js]
|
||||
[browser_profiler_tree-view-07.js]
|
||||
[browser_profiler_tree-view-08.js]
|
||||
[browser_profiler-frame-utils-01.js]
|
||||
[browser_timeline-blueprint.js]
|
||||
[browser_timeline-filters.js]
|
||||
[browser_timeline-waterfall-background.js]
|
||||
|
@ -10,7 +10,7 @@
|
||||
function test() {
|
||||
let { RecordingUtils } = devtools.require("devtools/performance/recording-utils");
|
||||
|
||||
let output = RecordingUtils.getSamplesFromAllocations(TEST_DATA);
|
||||
let output = RecordingUtils.getProfileThreadFromAllocations(TEST_DATA);
|
||||
is(output.toSource(), EXPECTED_OUTPUT.toSource(), "The output is correct.");
|
||||
|
||||
finish();
|
||||
@ -43,37 +43,60 @@ let TEST_DATA = {
|
||||
counts: [11, 22, 33, 44]
|
||||
};
|
||||
|
||||
let EXPECTED_OUTPUT = [{
|
||||
time: 50,
|
||||
frames: []
|
||||
}, {
|
||||
time: 100,
|
||||
frames: []
|
||||
}, {
|
||||
time: 150,
|
||||
frames: [{
|
||||
location: "x (A:1:2)",
|
||||
allocations: 22
|
||||
}]
|
||||
}, {
|
||||
time: 200,
|
||||
frames: [{
|
||||
location: "x (A:1:2)",
|
||||
allocations: 22
|
||||
}, {
|
||||
location: "y (B:3:4)",
|
||||
allocations: 33
|
||||
}]
|
||||
}, {
|
||||
time: 250,
|
||||
frames: [{
|
||||
location: "x (A:1:2)",
|
||||
allocations: 22
|
||||
}, {
|
||||
location: "y (B:3:4)",
|
||||
allocations: 33
|
||||
}, {
|
||||
location: "C:5:6",
|
||||
allocations: 44
|
||||
}]
|
||||
}];
|
||||
let EXPECTED_OUTPUT = {
|
||||
name: "allocations",
|
||||
samples: {
|
||||
"schema": {
|
||||
"stack": 0,
|
||||
"time": 1,
|
||||
"responsiveness": 2,
|
||||
"rss": 3,
|
||||
"uss": 4,
|
||||
"frameNumber": 5,
|
||||
"power": 6
|
||||
},
|
||||
data: [
|
||||
[ 1, 150 ],
|
||||
[ 2, 200 ],
|
||||
[ 3, 250 ]
|
||||
]
|
||||
},
|
||||
stackTable: {
|
||||
"schema": {
|
||||
"prefix": 0,
|
||||
"frame": 1
|
||||
},
|
||||
"data": [
|
||||
null,
|
||||
[ null, 1 ], // x (A:1:2)
|
||||
[ 1, 2 ], // x (A:1:2) > y (B:3:4)
|
||||
[ 2, 3 ] // x (A:1:2) > y (B:3:4) > C:5:6
|
||||
]
|
||||
},
|
||||
frameTable: {
|
||||
"schema": {
|
||||
"location": 0,
|
||||
"implementation": 1,
|
||||
"optimizations": 2,
|
||||
"line": 3,
|
||||
"category": 4
|
||||
},
|
||||
data: [
|
||||
null,
|
||||
[ 0 ],
|
||||
[ 1 ],
|
||||
[ 2 ]
|
||||
]
|
||||
},
|
||||
"stringTable": [
|
||||
"x (A:1:2)",
|
||||
"y (B:3:4)",
|
||||
"C:5:6"
|
||||
],
|
||||
"allocationsTable": [
|
||||
11,
|
||||
22,
|
||||
33,
|
||||
44
|
||||
]
|
||||
};
|
||||
|
@ -46,11 +46,12 @@ let test = Task.async(function*() {
|
||||
for (let thread of profile.threads) {
|
||||
info("Checking thread: " + thread.name);
|
||||
|
||||
for (let sample of thread.samples) {
|
||||
for (let sample of thread.samples.data) {
|
||||
sampleCount++;
|
||||
|
||||
if (sample.frames[0].location != "(root)") {
|
||||
ok(false, "The sample " + sample.toSource() + " doesn't have a root node.");
|
||||
let stack = getInflatedStackLocations(thread, sample);
|
||||
if (stack[0] != "(root)") {
|
||||
ok(false, "The sample " + stack.toSource() + " doesn't have a root node.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,11 +45,12 @@ let test = Task.async(function*() {
|
||||
for (let thread of profile.threads) {
|
||||
info("Checking thread: " + thread.name);
|
||||
|
||||
for (let sample of thread.samples) {
|
||||
for (let sample of thread.samples.data) {
|
||||
sampleCount++;
|
||||
|
||||
if (sample.frames[0].location != "(root)") {
|
||||
ok(false, "The sample " + sample.toSource() + " doesn't have a root node.");
|
||||
let stack = getInflatedStackLocations(thread, sample);
|
||||
if (stack[0] != "(root)") {
|
||||
ok(false, "The sample " + stack.toSource() + " doesn't have a root node.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,19 +52,20 @@ function spawnTest () {
|
||||
|
||||
yield front.stopRecording(secondRecording);
|
||||
let secondRecordingProfile = secondRecording.getProfile();
|
||||
let secondRecordingSamples = secondRecordingProfile.threads[0].samples;
|
||||
let secondRecordingSamples = secondRecordingProfile.threads[0].samples.data;
|
||||
|
||||
isnot(secondRecording._profilerStartTime, 0,
|
||||
"The profiling start time should not be 0 on the second recording.");
|
||||
ok(secondRecording.getDuration() >= WAIT_TIME,
|
||||
"The second recording duration is correct.");
|
||||
|
||||
info("Second profile's first sample time: " + secondRecordingSamples[0].time);
|
||||
ok(secondRecordingSamples[0].time < secondRecordingStartTime,
|
||||
const TIME_SLOT = secondRecordingProfile.threads[0].samples.schema.time;
|
||||
info("Second profile's first sample time: " + secondRecordingSamples[0][TIME_SLOT]);
|
||||
ok(secondRecordingSamples[0][TIME_SLOT] < secondRecordingStartTime,
|
||||
"The second recorded sample times were normalized.");
|
||||
ok(secondRecordingSamples[0].time > 0,
|
||||
ok(secondRecordingSamples[0][TIME_SLOT] > 0,
|
||||
"The second recorded sample times were normalized correctly.");
|
||||
ok(!secondRecordingSamples.find(e => e.time + secondRecordingStartTime <= firstRecording.getDuration()),
|
||||
ok(!secondRecordingSamples.find(e => e[TIME_SLOT] + secondRecordingStartTime <= firstRecording.getDuration()),
|
||||
"There should be no samples from the first recording in the second one, " +
|
||||
"even though the total number of frames did not overflow.");
|
||||
|
||||
|
@ -37,18 +37,19 @@ function spawnTest () {
|
||||
|
||||
yield front.stopRecording(secondRecording);
|
||||
let secondRecordingProfile = secondRecording.getProfile();
|
||||
let secondRecordingSamples = secondRecordingProfile.threads[0].samples;
|
||||
let secondRecordingSamples = secondRecordingProfile.threads[0].samples.data;
|
||||
|
||||
isnot(secondRecording._profilerStartTime, 0,
|
||||
"The profiling start time should not be 0 on the second recording.");
|
||||
ok(secondRecording.getDuration() >= WAIT_TIME,
|
||||
"The second recording duration is correct.");
|
||||
|
||||
ok(secondRecordingSamples[0].time < secondRecordingStartTime,
|
||||
const TIME_SLOT = secondRecordingProfile.threads[0].samples.schema.time;
|
||||
ok(secondRecordingSamples[0][TIME_SLOT] < secondRecordingStartTime,
|
||||
"The second recorded sample times were normalized.");
|
||||
ok(secondRecordingSamples[0].time > 0,
|
||||
ok(secondRecordingSamples[0][TIME_SLOT] > 0,
|
||||
"The second recorded sample times were normalized correctly.");
|
||||
ok(!secondRecordingSamples.find(e => e.time + secondRecordingStartTime <= firstRecording.getDuration()),
|
||||
ok(!secondRecordingSamples.find(e => e[TIME_SLOT] + secondRecordingStartTime <= firstRecording.getDuration()),
|
||||
"There should be no samples from the first recording in the second one, " +
|
||||
"even though the total number of frames did not overflow.");
|
||||
|
||||
|
@ -23,11 +23,12 @@ function spawnTest () {
|
||||
for (let thread of profile.threads) {
|
||||
info("Checking thread: " + thread.name);
|
||||
|
||||
for (let sample of thread.samples) {
|
||||
for (let sample of thread.samples.data) {
|
||||
sampleCount++;
|
||||
|
||||
if (sample.frames[0].location != "(root)") {
|
||||
ok(false, "The sample " + sample.toSource() + " doesn't have a root node.");
|
||||
let stack = getInflatedStackLocations(thread, sample);
|
||||
if (stack[0] != "(root)") {
|
||||
ok(false, "The sample " + stack.toSource() + " doesn't have a root node.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,8 @@
|
||||
/**
|
||||
* Tests that the call tree up/down events work for js calltree and memory calltree.
|
||||
*/
|
||||
let { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
const { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
const { RecordingUtils } = devtools.require("devtools/performance/recording-utils")
|
||||
function spawnTest () {
|
||||
let focus = 0;
|
||||
let focusEvent = () => focus++;
|
||||
@ -24,7 +25,7 @@ function spawnTest () {
|
||||
yield rendered;
|
||||
|
||||
// Mock the profile used so we can get a deterministic tree created
|
||||
let threadNode = new ThreadNode(gSamples);
|
||||
let threadNode = new ThreadNode(gProfile.threads[0]);
|
||||
JsCallTreeView._populateCallTree(threadNode);
|
||||
JsCallTreeView.emit(EVENTS.JS_CALL_TREE_RENDERED);
|
||||
|
||||
@ -44,36 +45,44 @@ function spawnTest () {
|
||||
finish();
|
||||
};
|
||||
|
||||
let gSamples = [{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
{ category: 8, location: "A (http://foo/bar/baz:12)" },
|
||||
{ category: 16, location: "B (http://foo/bar/baz:34)" },
|
||||
{ category: 32, location: "C (http://foo/bar/baz:56)" }
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
{ category: 8, location: "A (http://foo/bar/baz:12)" },
|
||||
{ category: 16, location: "B (http://foo/bar/baz:34)" },
|
||||
{ category: 64, location: "D (http://foo/bar/baz:78)" }
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1 + 2,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
{ category: 8, location: "A (http://foo/bar/baz:12)" },
|
||||
{ category: 16, location: "B (http://foo/bar/baz:34)" },
|
||||
{ category: 64, location: "D (http://foo/bar/baz:78)" }
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1 + 2 + 7,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
{ category: 8, location: "A (http://foo/bar/baz:12)" },
|
||||
{ category: 128, location: "E (http://foo/bar/baz:90)" },
|
||||
{ category: 256, location: "F (http://foo/bar/baz:99)" }
|
||||
]
|
||||
}];
|
||||
let gProfile = {
|
||||
meta: { version: 2 },
|
||||
threads: [{
|
||||
samples: [{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
{ category: 8, location: "A (http://foo/bar/baz:12)" },
|
||||
{ category: 16, location: "B (http://foo/bar/baz:34)" },
|
||||
{ category: 32, location: "C (http://foo/bar/baz:56)" }
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
{ category: 8, location: "A (http://foo/bar/baz:12)" },
|
||||
{ category: 16, location: "B (http://foo/bar/baz:34)" },
|
||||
{ category: 64, location: "D (http://foo/bar/baz:78)" }
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1 + 2,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
{ category: 8, location: "A (http://foo/bar/baz:12)" },
|
||||
{ category: 16, location: "B (http://foo/bar/baz:34)" },
|
||||
{ category: 64, location: "D (http://foo/bar/baz:78)" }
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1 + 2 + 7,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
{ category: 8, location: "A (http://foo/bar/baz:12)" },
|
||||
{ category: 128, location: "E (http://foo/bar/baz:90)" },
|
||||
{ category: 256, location: "F (http://foo/bar/baz:99)" }
|
||||
]
|
||||
}],
|
||||
markers: []
|
||||
}]
|
||||
};
|
||||
|
||||
RecordingUtils.deflateProfile(gProfile);
|
||||
|
@ -7,64 +7,113 @@
|
||||
* FrameNode, and the returning of that data is as expected.
|
||||
*/
|
||||
|
||||
const { RecordingUtils } = devtools.require("devtools/performance/recording-utils");
|
||||
|
||||
function test() {
|
||||
let { JITOptimizations } = devtools.require("devtools/shared/profiler/jit");
|
||||
|
||||
let jit = new JITOptimizations(gOpts);
|
||||
let rawSites = [];
|
||||
rawSites.push(gRawSite2);
|
||||
rawSites.push(gRawSite2);
|
||||
rawSites.push(gRawSite1);
|
||||
rawSites.push(gRawSite1);
|
||||
rawSites.push(gRawSite2);
|
||||
rawSites.push(gRawSite3);
|
||||
|
||||
jit.addOptimizationSite(1);
|
||||
jit.addOptimizationSite(1);
|
||||
jit.addOptimizationSite(0);
|
||||
jit.addOptimizationSite(0);
|
||||
jit.addOptimizationSite(1);
|
||||
jit.addOptimizationSite(2);
|
||||
|
||||
let sites = jit.getOptimizationSites();
|
||||
let jit = new JITOptimizations(rawSites, gStringTable.stringTable);
|
||||
let sites = jit.optimizationSites;
|
||||
|
||||
let [first, second, third] = sites;
|
||||
|
||||
is(first.id, 1, "Ordered by samples count, descending");
|
||||
is(first.id, 0, "site id is array index");
|
||||
is(first.samples, 3, "first OptimizationSiteProfile has correct sample count");
|
||||
is(first.data, gOpts[1], "includes OptimizationSite as reference under `data`");
|
||||
is(second.id, 0, "Ordered by samples count, descending");
|
||||
is(first.data.line, 34, "includes OptimizationSite as reference under `data`");
|
||||
is(second.id, 1, "site id is array index");
|
||||
is(second.samples, 2, "second OptimizationSiteProfile has correct sample count");
|
||||
is(second.data, gOpts[0], "includes OptimizationSite as reference under `data`");
|
||||
is(third.id, 2, "Ordered by samples count, descending");
|
||||
is(second.data.line, 12, "includes OptimizationSite as reference under `data`");
|
||||
is(third.id, 2, "site id is array index");
|
||||
is(third.samples, 1, "third OptimizationSiteProfile has correct sample count");
|
||||
is(third.data, gOpts[2], "includes OptimizationSite as reference under `data`");
|
||||
is(third.data.line, 78, "includes OptimizationSite as reference under `data`");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
let gOpts = [{
|
||||
let gStringTable = new RecordingUtils.UniqueStrings();
|
||||
|
||||
function uniqStr(s) {
|
||||
return gStringTable.getOrAddStringIndex(s);
|
||||
}
|
||||
|
||||
let gRawSite1 = {
|
||||
line: 12,
|
||||
column: 2,
|
||||
types: [{ mirType: "Object", site: "A (http://foo/bar/bar:12)", types: [
|
||||
{ keyedBy: "constructor", name: "Foo", location: "A (http://foo/bar/baz:12)" },
|
||||
{ keyedBy: "primitive", location: "self-hosted" }
|
||||
]}],
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "Inlined", strategy: "SomeGetter3" },
|
||||
]
|
||||
}, {
|
||||
types: [{
|
||||
mirType: uniqStr("Object"),
|
||||
site: uniqStr("A (http://foo/bar/bar:12)"),
|
||||
typeset: [{
|
||||
keyedBy: uniqStr("constructor"),
|
||||
name: uniqStr("Foo"),
|
||||
location: uniqStr("A (http://foo/bar/baz:12)")
|
||||
}, {
|
||||
keyedBy: uniqStr("primitive"),
|
||||
location: uniqStr("self-hosted")
|
||||
}]
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("Inlined"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let gRawSite2 = {
|
||||
line: 34,
|
||||
types: [{ mirType: "Int32", site: "Receiver" }], // use no types
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "Failure3", strategy: "SomeGetter3" },
|
||||
]
|
||||
}, {
|
||||
types: [{
|
||||
mirType: uniqStr("Int32"),
|
||||
site: uniqStr("Receiver")
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("Failure3"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let gRawSite3 = {
|
||||
line: 78,
|
||||
types: [{ mirType: "Object", site: "A (http://foo/bar/bar:12)", types: [
|
||||
{ keyedBy: "constructor", name: "Foo", location: "A (http://foo/bar/baz:12)" },
|
||||
{ keyedBy: "primitive", location: "self-hosted" }
|
||||
]}],
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "GenericSuccess", strategy: "SomeGetter3" },
|
||||
]
|
||||
}];
|
||||
types: [{
|
||||
mirType: uniqStr("Object"),
|
||||
site: uniqStr("A (http://foo/bar/bar:12)"),
|
||||
typeset: [{
|
||||
keyedBy: uniqStr("constructor"),
|
||||
name: uniqStr("Foo"),
|
||||
location: uniqStr("A (http://foo/bar/baz:12)")
|
||||
}, {
|
||||
keyedBy: uniqStr("primitive"),
|
||||
location: uniqStr("self-hosted")
|
||||
}]
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("GenericSuccess"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
@ -6,19 +6,21 @@
|
||||
* OptimizationSites methods work as expected.
|
||||
*/
|
||||
|
||||
const { RecordingUtils } = devtools.require("devtools/performance/recording-utils");
|
||||
|
||||
function test() {
|
||||
let { JITOptimizations, OptimizationSite } = devtools.require("devtools/shared/profiler/jit");
|
||||
|
||||
let jit = new JITOptimizations(gOpts);
|
||||
let rawSites = [];
|
||||
rawSites.push(gRawSite2);
|
||||
rawSites.push(gRawSite2);
|
||||
rawSites.push(gRawSite1);
|
||||
rawSites.push(gRawSite1);
|
||||
rawSites.push(gRawSite2);
|
||||
rawSites.push(gRawSite3);
|
||||
|
||||
jit.addOptimizationSite(1);
|
||||
jit.addOptimizationSite(1);
|
||||
jit.addOptimizationSite(0);
|
||||
jit.addOptimizationSite(0);
|
||||
jit.addOptimizationSite(1);
|
||||
jit.addOptimizationSite(2);
|
||||
|
||||
let sites = jit.getOptimizationSites();
|
||||
let jit = new JITOptimizations(rawSites, gStringTable.stringTable);
|
||||
let sites = jit.optimizationSites;
|
||||
|
||||
let [first, second, third] = sites;
|
||||
|
||||
@ -40,38 +42,91 @@ function test() {
|
||||
finish();
|
||||
}
|
||||
|
||||
let gOpts = [{
|
||||
|
||||
let gStringTable = new RecordingUtils.UniqueStrings();
|
||||
|
||||
function uniqStr(s) {
|
||||
return gStringTable.getOrAddStringIndex(s);
|
||||
}
|
||||
|
||||
let gRawSite1 = {
|
||||
line: 12,
|
||||
column: 2,
|
||||
types: [{ mirType: "Object", site: "A (http://foo/bar/bar:12)", types: [
|
||||
{ keyedBy: "constructor", name: "Foo", location: "A (http://foo/bar/baz:12)" },
|
||||
{ keyedBy: "constructor", location: "A (http://foo/bar/baz:12)" }
|
||||
]}, { mirType: "Int32", site: "A (http://foo/bar/bar:12)", types: [
|
||||
{ keyedBy: "primitive", location: "self-hosted" }
|
||||
]}],
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "Inlined", strategy: "SomeGetter3" },
|
||||
]
|
||||
}, {
|
||||
types: [{
|
||||
mirType: uniqStr("Object"),
|
||||
site: uniqStr("A (http://foo/bar/bar:12)"),
|
||||
typeset: [{
|
||||
keyedBy: uniqStr("constructor"),
|
||||
name: uniqStr("Foo"),
|
||||
location: uniqStr("A (http://foo/bar/baz:12)")
|
||||
}, {
|
||||
keyedBy: uniqStr("constructor"),
|
||||
location: uniqStr("A (http://foo/bar/baz:12)")
|
||||
}]
|
||||
}, {
|
||||
mirType: uniqStr("Int32"),
|
||||
site: uniqStr("A (http://foo/bar/bar:12)"),
|
||||
typeset: [{
|
||||
keyedBy: uniqStr("primitive"),
|
||||
location: uniqStr("self-hosted")
|
||||
}]
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("Inlined"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let gRawSite2 = {
|
||||
line: 34,
|
||||
types: [{ mirType: "Int32", site: "Receiver" }], // use no types
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
]
|
||||
}, {
|
||||
types: [{
|
||||
mirType: uniqStr("Int32"),
|
||||
site: uniqStr("Receiver")
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let gRawSite3 = {
|
||||
line: 78,
|
||||
types: [{ mirType: "Object", site: "A (http://foo/bar/bar:12)", types: [
|
||||
{ keyedBy: "constructor", name: "Foo", location: "A (http://foo/bar/baz:12)" },
|
||||
{ keyedBy: "primitive", location: "self-hosted" }
|
||||
]}],
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "GenericSuccess", strategy: "SomeGetter3" },
|
||||
]
|
||||
}];
|
||||
types: [{
|
||||
mirType: uniqStr("Object"),
|
||||
site: uniqStr("A (http://foo/bar/bar:12)"),
|
||||
typeset: [{
|
||||
keyedBy: uniqStr("constructor"),
|
||||
name: uniqStr("Foo"),
|
||||
location: uniqStr("A (http://foo/bar/baz:12)")
|
||||
}, {
|
||||
keyedBy: uniqStr("primitive"),
|
||||
location: uniqStr("self-hosted")
|
||||
}]
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("GenericSuccess"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
@ -6,6 +6,8 @@
|
||||
* if on, and displays selected frames on focus.
|
||||
*/
|
||||
|
||||
const { RecordingUtils } = devtools.require("devtools/performance/recording-utils");
|
||||
|
||||
Services.prefs.setBoolPref(INVERT_PREF, false);
|
||||
|
||||
function spawnTest () {
|
||||
@ -13,7 +15,7 @@ function spawnTest () {
|
||||
let { EVENTS, $, $$, window, PerformanceController } = panel.panelWin;
|
||||
let { OverviewView, DetailsView, JITOptimizationsView, JsCallTreeView, RecordingsView } = panel.panelWin;
|
||||
|
||||
let profilerData = { threads: [{samples: gSamples, optimizations: gOpts}] };
|
||||
let profilerData = { threads: [gThread] }
|
||||
|
||||
is(Services.prefs.getBoolPref(JIT_PREF), false, "show JIT Optimizations pref off by default");
|
||||
|
||||
@ -29,8 +31,12 @@ function spawnTest () {
|
||||
|
||||
yield injectAndRenderProfilerData();
|
||||
|
||||
yield checkFrame(1, [0, 1]);
|
||||
yield checkFrame(2, [2]);
|
||||
// gRawSite1 and gRawSite2 are both optimizations on A, so they'll have
|
||||
// indices in descending order of # of samples.
|
||||
yield checkFrame(1, [{ i: 0, opt: gRawSite1 }, { i: 1, opt: gRawSite2 }]);
|
||||
|
||||
// gRawSite3 is the only optimization on B, so it'll have index 0.
|
||||
yield checkFrame(2, [{ i: 0, opt: gRawSite3 }]);
|
||||
yield checkFrame(3);
|
||||
|
||||
let select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
|
||||
@ -64,7 +70,7 @@ function spawnTest () {
|
||||
Services.prefs.setBoolPref(JIT_PREF, false);
|
||||
}
|
||||
|
||||
function *checkFrame (frameIndex, expectedOptsIndex=[]) {
|
||||
function *checkFrame (frameIndex, expectedOpts=[]) {
|
||||
// Click the frame
|
||||
let rendered = once(JITOptimizationsView, EVENTS.OPTIMIZATIONS_RENDERED);
|
||||
mousedown(window, $$(".call-tree-item")[frameIndex]);
|
||||
@ -73,7 +79,7 @@ function spawnTest () {
|
||||
ok(true, "JITOptimizationsView rendered when enabling with the current frame node selected");
|
||||
|
||||
let isEmpty = $("#jit-optimizations-view").classList.contains("empty");
|
||||
if (expectedOptsIndex.length === 0) {
|
||||
if (expectedOpts.length === 0) {
|
||||
ok(isEmpty, "JIT Optimizations view has an empty message when selecting a frame without opt data.");
|
||||
return;
|
||||
} else {
|
||||
@ -82,7 +88,7 @@ function spawnTest () {
|
||||
|
||||
// Get the frame info for the first opt site, since all opt sites
|
||||
// share the same frame info
|
||||
let frameInfo = gOpts[expectedOptsIndex[0]]._testFrameInfo;
|
||||
let frameInfo = expectedOpts[0].opt._testFrameInfo;
|
||||
|
||||
let { $headerName, $headerLine, $headerFile } = JITOptimizationsView;
|
||||
ok(!$headerName.hidden, "header function name should be shown");
|
||||
@ -94,13 +100,12 @@ function spawnTest () {
|
||||
|
||||
// Need the value of the optimizations in its array, as its
|
||||
// an index used internally by the view to uniquely ID the opt
|
||||
for (let i of expectedOptsIndex) {
|
||||
let opt = gOpts[i];
|
||||
for (let { i, opt } of expectedOpts) {
|
||||
let { types: ionTypes, attempts } = opt;
|
||||
|
||||
// Check attempts
|
||||
is($$(`.tree-widget-container li[data-id='["${i}","${i}-attempts"]'] .tree-widget-children .tree-widget-item`).length, attempts.length,
|
||||
`found ${attempts.length} attempts`);
|
||||
is($$(`.tree-widget-container li[data-id='["${i}","${i}-attempts"]'] .tree-widget-children .tree-widget-item`).length, attempts.data.length,
|
||||
`found ${attempts.data.length} attempts`);
|
||||
|
||||
for (let j = 0; j < ionTypes.length; j++) {
|
||||
ok($(`.tree-widget-container li[data-id='["${i}","${i}-types","${i}-types-${j}"]']`),
|
||||
@ -109,7 +114,7 @@ function spawnTest () {
|
||||
|
||||
// The second and third optimization should display optimization failures.
|
||||
let warningIcon = $(`.tree-widget-container li[data-id='["${i}"]'] .opt-icon[severity=warning]`);
|
||||
if (i === 1 || i === 2) {
|
||||
if (opt === gRawSite2 || opt === gRawSite3) {
|
||||
ok(warningIcon, "did find a warning icon for all strategies failing.");
|
||||
} else {
|
||||
ok(!warningIcon, "did not find a warning icon for no successful strategies");
|
||||
@ -118,68 +123,142 @@ function spawnTest () {
|
||||
}
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A (http://foo/bar/baz:12)", optsIndex: 0 },
|
||||
{ location: "B (http://foo/bar/boo:34)", optsIndex: 2 },
|
||||
{ location: "C (http://foo/bar/baz:56)" }
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A (http://foo/bar/baz:12)" },
|
||||
{ location: "B (http://foo/bar/boo:34)" },
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1 + 2,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A (http://foo/bar/baz:12)", optsIndex: 1 },
|
||||
{ location: "B (http://foo/bar/boo:34)" },
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1 + 2 + 7,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A (http://foo/bar/baz:12)", optsIndex: 0 },
|
||||
{ location: "E (http://foo/bar/baz:90)" },
|
||||
{ location: "F (http://foo/bar/baz:99)" }
|
||||
]
|
||||
}];
|
||||
let gUniqueStacks = new RecordingUtils.UniqueStacks();
|
||||
|
||||
// Array of OptimizationSites
|
||||
let gOpts = [{
|
||||
function uniqStr(s) {
|
||||
return gUniqueStacks.getOrAddStringIndex(s);
|
||||
}
|
||||
|
||||
// Since deflateThread doesn't handle deflating optimization info, use
|
||||
// placeholder names A_O1, B_O3, and A_O2, which will be used to manually
|
||||
// splice deduped opts into the profile.
|
||||
let gThread = RecordingUtils.deflateThread({
|
||||
samples: [{
|
||||
time: 0,
|
||||
frames: [
|
||||
{ location: "(root)" }
|
||||
]
|
||||
}, {
|
||||
time: 5,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A_O1" },
|
||||
{ location: "B_O3" },
|
||||
{ location: "C (http://foo/bar/baz:56)" }
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A (http://foo/bar/baz:12)" },
|
||||
{ location: "B (http://foo/bar/boo:34)" },
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1 + 2,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A_O2" },
|
||||
{ location: "B (http://foo/bar/boo:34)" },
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1 + 2 + 7,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A_O1" },
|
||||
{ location: "E (http://foo/bar/baz:90)" },
|
||||
{ location: "F (http://foo/bar/baz:99)" }
|
||||
]
|
||||
}],
|
||||
markers: []
|
||||
}, gUniqueStacks);
|
||||
|
||||
// 3 RawOptimizationSites
|
||||
let gRawSite1 = {
|
||||
_testFrameInfo: { name: "A", line: "12", file: "@baz" },
|
||||
line: 12,
|
||||
column: 2,
|
||||
types: [{ mirType: "Object", site: "A (http://foo/bar/bar:12)", types: [
|
||||
{ keyedBy: "constructor", name: "Foo", location: "A (http://foo/bar/baz:12)" },
|
||||
{ keyedBy: "primitive", location: "self-hosted" }
|
||||
]}],
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "Inlined", strategy: "SomeGetter3" },
|
||||
]
|
||||
}, {
|
||||
types: [{
|
||||
mirType: uniqStr("Object"),
|
||||
site: uniqStr("A (http://foo/bar/bar:12)"),
|
||||
typeset: [{
|
||||
keyedBy: uniqStr("constructor"),
|
||||
name: uniqStr("Foo"),
|
||||
location: uniqStr("A (http://foo/bar/baz:12)")
|
||||
}, {
|
||||
keyedBy: uniqStr("primitive"),
|
||||
location: uniqStr("self-hosted")
|
||||
}]
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("Inlined"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let gRawSite2 = {
|
||||
_testFrameInfo: { name: "A", line: "12", file: "@baz" },
|
||||
line: 12,
|
||||
types: [{ mirType: "Int32", site: "Receiver" }], // use no types
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "Failure3", strategy: "SomeGetter3" },
|
||||
]
|
||||
}, {
|
||||
types: [{
|
||||
mirType: uniqStr("Int32"),
|
||||
site: uniqStr("Receiver")
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("Failure3"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let gRawSite3 = {
|
||||
_testFrameInfo: { name: "B", line: "34", file: "@boo" },
|
||||
line: 34,
|
||||
types: [{ mirType: "Int32", site: "Receiver" }], // use no types
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "Failure3", strategy: "SomeGetter3" },
|
||||
]
|
||||
}];
|
||||
types: [{
|
||||
mirType: uniqStr("Int32"),
|
||||
site: uniqStr("Receiver")
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("Failure3"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
gThread.frameTable.data.forEach((frame) => {
|
||||
const LOCATION_SLOT = gThread.frameTable.schema.location;
|
||||
const OPTIMIZATIONS_SLOT = gThread.frameTable.schema.optimizations;
|
||||
|
||||
let l = gThread.stringTable[frame[LOCATION_SLOT]];
|
||||
switch (l) {
|
||||
case "A_O1":
|
||||
frame[LOCATION_SLOT] = uniqStr("A (http://foo/bar/baz:12)");
|
||||
frame[OPTIMIZATIONS_SLOT] = gRawSite1;
|
||||
break;
|
||||
case "A_O2":
|
||||
frame[LOCATION_SLOT] = uniqStr("A (http://foo/bar/baz:12)");
|
||||
frame[OPTIMIZATIONS_SLOT] = gRawSite2;
|
||||
break;
|
||||
case "B_O3":
|
||||
frame[LOCATION_SLOT] = uniqStr("B (http://foo/bar/boo:34)");
|
||||
frame[OPTIMIZATIONS_SLOT] = gRawSite3;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
@ -6,7 +6,9 @@
|
||||
* for meta nodes when viewing "content only".
|
||||
*/
|
||||
|
||||
let { CATEGORY_MASK } = devtools.require("devtools/shared/profiler/global");
|
||||
const { CATEGORY_MASK } = devtools.require("devtools/shared/profiler/global");
|
||||
const { RecordingUtils } = devtools.require("devtools/performance/recording-utils");
|
||||
|
||||
Services.prefs.setBoolPref(INVERT_PREF, false);
|
||||
Services.prefs.setBoolPref(PLATFORM_DATA_PREF, false);
|
||||
|
||||
@ -15,7 +17,7 @@ function spawnTest () {
|
||||
let { EVENTS, $, $$, window, PerformanceController } = panel.panelWin;
|
||||
let { OverviewView, DetailsView, JITOptimizationsView, JsCallTreeView, RecordingsView } = panel.panelWin;
|
||||
|
||||
let profilerData = { threads: [{samples: gSamples, optimizations: gOpts}] };
|
||||
let profilerData = { threads: [gThread] };
|
||||
|
||||
is(Services.prefs.getBoolPref(JIT_PREF), false, "show JIT Optimizations pref off by default");
|
||||
|
||||
@ -70,40 +72,94 @@ function spawnTest () {
|
||||
}
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A (http://foo/bar/baz:12)", optsIndex: 0 }
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A (http://foo/bar/baz:12)", optsIndex: 0 },
|
||||
{ location: "JS", optsIndex: 1, category: CATEGORY_MASK("js") },
|
||||
]
|
||||
}];
|
||||
let gUniqueStacks = new RecordingUtils.UniqueStacks();
|
||||
|
||||
// Array of OptimizationSites
|
||||
let gOpts = [{
|
||||
function uniqStr(s) {
|
||||
return gUniqueStacks.getOrAddStringIndex(s);
|
||||
}
|
||||
|
||||
let gThread = RecordingUtils.deflateThread({
|
||||
samples: [{
|
||||
time: 0,
|
||||
frames: [
|
||||
{ location: "(root)" }
|
||||
]
|
||||
}, {
|
||||
time: 5,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A (http://foo/bar/baz:12)" }
|
||||
]
|
||||
}, {
|
||||
time: 5 + 1,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A (http://foo/bar/baz:12)" },
|
||||
{ location: "JS", category: CATEGORY_MASK("js") },
|
||||
]
|
||||
}],
|
||||
markers: []
|
||||
}, gUniqueStacks);
|
||||
|
||||
// 3 RawOptimizationSites
|
||||
let gRawSite1 = {
|
||||
line: 12,
|
||||
column: 2,
|
||||
types: [{ mirType: "Object", site: "A (http://foo/bar/bar:12)", types: [
|
||||
{ keyedBy: "constructor", name: "Foo", location: "A (http://foo/bar/baz:12)" },
|
||||
{ keyedBy: "primitive", location: "self-hosted" }
|
||||
]}],
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "Inlined", strategy: "SomeGetter3" },
|
||||
]
|
||||
}, {
|
||||
types: [{
|
||||
mirType: uniqStr("Object"),
|
||||
site: uniqStr("A (http://foo/bar/bar:12)"),
|
||||
typeset: [{
|
||||
keyedBy: uniqStr("constructor"),
|
||||
name: uniqStr("Foo"),
|
||||
location: uniqStr("A (http://foo/bar/baz:12)")
|
||||
}, {
|
||||
keyedBy: uniqStr("primitive"),
|
||||
location: uniqStr("self-hosted")
|
||||
}]
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("Inlined"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let gRawSite2 = {
|
||||
line: 22,
|
||||
types: [{ mirType: "Int32", site: "Receiver" }], // use no types
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "Failure3", strategy: "SomeGetter3" },
|
||||
]
|
||||
}];
|
||||
types: [{
|
||||
mirType: uniqStr("Int32"),
|
||||
site: uniqStr("Receiver")
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("Failure3"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
gThread.frameTable.data.forEach((frame) => {
|
||||
const LOCATION_SLOT = gThread.frameTable.schema.location;
|
||||
const OPTIMIZATIONS_SLOT = gThread.frameTable.schema.optimizations;
|
||||
|
||||
let l = gThread.stringTable[frame[LOCATION_SLOT]];
|
||||
switch (l) {
|
||||
case "A (http://foo/bar/baz:12)":
|
||||
frame[OPTIMIZATIONS_SLOT] = gRawSite1;
|
||||
break;
|
||||
case "JS":
|
||||
frame[OPTIMIZATIONS_SLOT] = gRawSite2;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
@ -18,10 +18,10 @@ function spawnTest () {
|
||||
yield DetailsView.selectView("js-flamegraph");
|
||||
yield rendered;
|
||||
|
||||
let samples1 = PerformanceController.getCurrentRecording().getProfile().threads[0].samples;
|
||||
let rendering1 = FlameGraphUtils._cache.get(samples1);
|
||||
let thread1 = PerformanceController.getCurrentRecording().getProfile().threads[0];
|
||||
let rendering1 = FlameGraphUtils._cache.get(thread1);
|
||||
|
||||
ok(samples1,
|
||||
ok(thread1,
|
||||
"The samples were retrieved from the controller.");
|
||||
ok(rendering1,
|
||||
"The rendering data was cached.");
|
||||
@ -32,10 +32,10 @@ function spawnTest () {
|
||||
|
||||
ok(true, "JsFlameGraphView rerendered when toggling flatten-tree-recursion.");
|
||||
|
||||
let samples2 = PerformanceController.getCurrentRecording().getProfile().threads[0].samples;
|
||||
let rendering2 = FlameGraphUtils._cache.get(samples2);
|
||||
let thread2 = PerformanceController.getCurrentRecording().getProfile().threads[0];
|
||||
let rendering2 = FlameGraphUtils._cache.get(thread2);
|
||||
|
||||
is(samples1, samples2,
|
||||
is(thread1, thread2,
|
||||
"The same samples data should be retrieved from the controller (1).");
|
||||
isnot(rendering1, rendering2,
|
||||
"The rendering data should be different because other options were used (1).");
|
||||
@ -46,10 +46,10 @@ function spawnTest () {
|
||||
|
||||
ok(true, "JsFlameGraphView rerendered when toggling back flatten-tree-recursion.");
|
||||
|
||||
let samples3 = PerformanceController.getCurrentRecording().getProfile().threads[0].samples;
|
||||
let rendering3 = FlameGraphUtils._cache.get(samples3);
|
||||
let thread3 = PerformanceController.getCurrentRecording().getProfile().threads[0];
|
||||
let rendering3 = FlameGraphUtils._cache.get(thread3);
|
||||
|
||||
is(samples2, samples3,
|
||||
is(thread2, thread3,
|
||||
"The same samples data should be retrieved from the controller (2).");
|
||||
isnot(rendering2, rendering3,
|
||||
"The rendering data should be different because other options were used (2).");
|
||||
|
@ -21,13 +21,13 @@ function spawnTest () {
|
||||
yield rendered;
|
||||
|
||||
let allocations1 = PerformanceController.getCurrentRecording().getAllocations();
|
||||
let samples1 = RecordingUtils.getSamplesFromAllocations(allocations1);
|
||||
let rendering1 = FlameGraphUtils._cache.get(samples1);
|
||||
let thread1 = RecordingUtils.getProfileThreadFromAllocations(allocations1);
|
||||
let rendering1 = FlameGraphUtils._cache.get(thread1);
|
||||
|
||||
ok(allocations1,
|
||||
"The allocations were retrieved from the controller.");
|
||||
ok(samples1,
|
||||
"The samples were retrieved from the utility funcs.");
|
||||
ok(thread1,
|
||||
"The allocations profile was synthesized by the utility funcs.");
|
||||
ok(rendering1,
|
||||
"The rendering data was cached.");
|
||||
|
||||
@ -38,13 +38,13 @@ function spawnTest () {
|
||||
ok(true, "MemoryFlameGraphView rerendered when toggling flatten-tree-recursion.");
|
||||
|
||||
let allocations2 = PerformanceController.getCurrentRecording().getAllocations();
|
||||
let samples2 = RecordingUtils.getSamplesFromAllocations(allocations2);
|
||||
let rendering2 = FlameGraphUtils._cache.get(samples2);
|
||||
let thread2 = RecordingUtils.getProfileThreadFromAllocations(allocations2);
|
||||
let rendering2 = FlameGraphUtils._cache.get(thread2);
|
||||
|
||||
is(allocations1, allocations2,
|
||||
"The same allocations data should be retrieved from the controller (1).");
|
||||
is(samples1, samples2,
|
||||
"The same samples data should be retrieved from the utility funcs. (1).");
|
||||
is(thread1, thread2,
|
||||
"The same allocations profile should be retrieved from the utility funcs. (1).");
|
||||
isnot(rendering1, rendering2,
|
||||
"The rendering data should be different because other options were used (1).");
|
||||
|
||||
@ -55,13 +55,13 @@ function spawnTest () {
|
||||
ok(true, "MemoryFlameGraphView rerendered when toggling back flatten-tree-recursion.");
|
||||
|
||||
let allocations3 = PerformanceController.getCurrentRecording().getAllocations();
|
||||
let samples3 = RecordingUtils.getSamplesFromAllocations(allocations3);
|
||||
let rendering3 = FlameGraphUtils._cache.get(samples3);
|
||||
let thread3 = RecordingUtils.getProfileThreadFromAllocations(allocations3);
|
||||
let rendering3 = FlameGraphUtils._cache.get(thread3);
|
||||
|
||||
is(allocations2, allocations3,
|
||||
"The same allocations data should be retrieved from the controller (2).");
|
||||
is(samples2, samples3,
|
||||
"The same samples data should be retrieved from the utility funcs. (2).");
|
||||
is(thread2, thread3,
|
||||
"The same allocations profile should be retrieved from the utility funcs. (2).");
|
||||
isnot(rendering2, rendering3,
|
||||
"The rendering data should be different because other options were used (2).");
|
||||
|
||||
|
@ -0,0 +1,95 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that frame-utils isContent and parseLocation work as intended
|
||||
* when parsing over frames from the profiler.
|
||||
*/
|
||||
|
||||
const CONTENT_LOCATIONS = [
|
||||
"hello/<.world (https://foo/bar.js:123:987)",
|
||||
"hello/<.world (http://foo/bar.js:123:987)",
|
||||
"hello/<.world (http://foo/bar.js:123)",
|
||||
"hello/<.world (http://foo/bar.js#baz:123:987)",
|
||||
"hello/<.world (http://foo/#bar:123:987)",
|
||||
"hello/<.world (http://foo/:123:987)",
|
||||
"hello/<.world (app://myfxosapp/file.js:100:1)",
|
||||
].map(argify);
|
||||
|
||||
const CHROME_LOCATIONS = [
|
||||
{ location: "Startup::XRE_InitChildProcess", line: 456, column: 123 },
|
||||
{ location: "chrome://browser/content/content.js", line: 456, column: 123 },
|
||||
"setTimeout_timer (resource://gre/foo.js:123:434)",
|
||||
"hello/<.world (jar:file://Users/mcurie/Dev/jetpacks.js)",
|
||||
"hello/<.world (resource://foo.js -> http://bar/baz.js:123:987)",
|
||||
"EnterJIT",
|
||||
].map(argify);
|
||||
|
||||
function test() {
|
||||
const { isContent, parseLocation } = devtools.require("devtools/shared/profiler/frame-utils");
|
||||
|
||||
for (let frame of CONTENT_LOCATIONS) {
|
||||
ok(isContent.apply(null, frameify(frame)), `${frame[0]} should be considered a content frame.`);
|
||||
}
|
||||
|
||||
for (let frame of CHROME_LOCATIONS) {
|
||||
ok(!isContent.apply(null, frameify(frame)), `${frame[0]} should not be considered a content frame.`);
|
||||
}
|
||||
|
||||
// functionName, fileName, hostName, url, line, column
|
||||
const FIELDS = ["functionName", "fileName", "hostName", "url", "line", "column"];
|
||||
const PARSED_CONTENT = [
|
||||
["hello/<.world", "bar.js", "foo", "https://foo/bar.js", 123, 987],
|
||||
["hello/<.world", "bar.js", "foo", "http://foo/bar.js", 123, 987],
|
||||
["hello/<.world", "bar.js", "foo", "http://foo/bar.js", 123, null],
|
||||
["hello/<.world", "bar.js#baz", "foo", "http://foo/bar.js#baz", 123, 987],
|
||||
["hello/<.world", "#bar", "foo", "http://foo/#bar", 123, 987],
|
||||
["hello/<.world", "/", "foo", "http://foo/", 123, 987],
|
||||
["hello/<.world", "file.js", "myfxosapp", "app://myfxosapp/file.js", 100, 1],
|
||||
];
|
||||
|
||||
for (let i = 0; i < PARSED_CONTENT.length; i++) {
|
||||
let parsed = parseLocation.apply(null, CONTENT_LOCATIONS[i]);
|
||||
for (let j = 0; j < FIELDS.length; j++) {
|
||||
is(parsed[FIELDS[j]], PARSED_CONTENT[i][j], `${CONTENT_LOCATIONS[i]} was parsed to correct ${FIELDS[j]}`);
|
||||
}
|
||||
}
|
||||
|
||||
const PARSED_CHROME = [
|
||||
["Startup::XRE_InitChildProcess", null, null, null, 456, 123],
|
||||
["chrome://browser/content/content.js", null, null, null, 456, 123],
|
||||
["setTimeout_timer", "foo.js", null, "resource://gre/foo.js", 123, 434],
|
||||
["hello/<.world (jar:file://Users/mcurie/Dev/jetpacks.js)", null, null, null, null, null],
|
||||
["hello/<.world", "baz.js", "bar", "http://bar/baz.js", 123, 987],
|
||||
["EnterJIT", null, null, null, null, null],
|
||||
];
|
||||
|
||||
for (let i = 0; i < PARSED_CHROME.length; i++) {
|
||||
let parsed = parseLocation.apply(null, CHROME_LOCATIONS[i]);
|
||||
for (let j = 0; j < FIELDS.length; j++) {
|
||||
is(parsed[FIELDS[j]], PARSED_CHROME[i][j], `${CHROME_LOCATIONS[i]} was parsed to correct ${FIELDS[j]}`);
|
||||
}
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes either a string or an object and turns it into an array that
|
||||
* parseLocation.apply expects.
|
||||
*/
|
||||
function argify (val) {
|
||||
if (typeof val === "string") {
|
||||
return [val];
|
||||
} else {
|
||||
return [val.location, val.line, val.column];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the result of argify and turns it into an array that can be passed to
|
||||
* isContent.apply.
|
||||
*/
|
||||
function frameify(val) {
|
||||
return [{ location: val[0] }];
|
||||
}
|
@ -9,10 +9,13 @@ function test() {
|
||||
let { FrameNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
let { CATEGORY_OTHER } = devtools.require("devtools/shared/profiler/global");
|
||||
|
||||
let frame1 = new FrameNode({
|
||||
let frame1 = new FrameNode("hello/<.world (http://foo/bar.js:123:987)", {
|
||||
location: "hello/<.world (http://foo/bar.js:123:987)",
|
||||
line: 456
|
||||
});
|
||||
line: 456,
|
||||
isContent: FrameNode.isContent({
|
||||
location: "hello/<.world (http://foo/bar.js:123:987)"
|
||||
})
|
||||
}, false);
|
||||
|
||||
is(frame1.getInfo().nodeType, "Frame",
|
||||
"The first frame node has the correct type.");
|
||||
@ -33,10 +36,13 @@ function test() {
|
||||
is(frame1.getInfo().isContent, true,
|
||||
"The first frame node has the correct content flag.");
|
||||
|
||||
let frame2 = new FrameNode({
|
||||
let frame2 = new FrameNode("hello/<.world (http://foo/bar.js#baz:123:987)", {
|
||||
location: "hello/<.world (http://foo/bar.js#baz:123:987)",
|
||||
line: 456
|
||||
});
|
||||
line: 456,
|
||||
isContent: FrameNode.isContent({
|
||||
location: "hello/<.world (http://foo/bar.js#baz:123:987)"
|
||||
})
|
||||
}, false);
|
||||
|
||||
is(frame2.getInfo().nodeType, "Frame",
|
||||
"The second frame node has the correct type.");
|
||||
@ -57,10 +63,13 @@ function test() {
|
||||
is(frame2.getInfo().isContent, true,
|
||||
"The second frame node has the correct content flag.");
|
||||
|
||||
let frame3 = new FrameNode({
|
||||
let frame3 = new FrameNode("hello/<.world (http://foo/#bar:123:987)", {
|
||||
location: "hello/<.world (http://foo/#bar:123:987)",
|
||||
line: 456
|
||||
});
|
||||
line: 456,
|
||||
isContent: FrameNode.isContent({
|
||||
location: "hello/<.world (http://foo/#bar:123:987)"
|
||||
})
|
||||
}, false);
|
||||
|
||||
is(frame3.getInfo().nodeType, "Frame",
|
||||
"The third frame node has the correct type.");
|
||||
@ -81,10 +90,13 @@ function test() {
|
||||
is(frame3.getInfo().isContent, true,
|
||||
"The third frame node has the correct content flag.");
|
||||
|
||||
let frame4 = new FrameNode({
|
||||
let frame4 = new FrameNode("hello/<.world (http://foo/:123:987)", {
|
||||
location: "hello/<.world (http://foo/:123:987)",
|
||||
line: 456
|
||||
});
|
||||
line: 456,
|
||||
isContent: FrameNode.isContent({
|
||||
location: "hello/<.world (http://foo/:123:987)"
|
||||
})
|
||||
}, false);
|
||||
|
||||
is(frame4.getInfo().nodeType, "Frame",
|
||||
"The fourth frame node has the correct type.");
|
||||
@ -105,10 +117,13 @@ function test() {
|
||||
is(frame4.getInfo().isContent, true,
|
||||
"The fourth frame node has the correct content flag.");
|
||||
|
||||
let frame5 = new FrameNode({
|
||||
let frame5 = new FrameNode("hello/<.world (resource://foo.js -> http://bar/baz.js:123:987)", {
|
||||
location: "hello/<.world (resource://foo.js -> http://bar/baz.js:123:987)",
|
||||
line: 456
|
||||
});
|
||||
line: 456,
|
||||
isContent: FrameNode.isContent({
|
||||
location: "hello/<.world (resource://foo.js -> http://bar/baz.js:123:987)"
|
||||
})
|
||||
}, false);
|
||||
|
||||
is(frame5.getInfo().nodeType, "Frame",
|
||||
"The fifth frame node has the correct type.");
|
||||
@ -129,12 +144,15 @@ function test() {
|
||||
is(frame5.getInfo().isContent, false,
|
||||
"The fifth frame node has the correct content flag.");
|
||||
|
||||
let frame6 = new FrameNode({
|
||||
let frame6 = new FrameNode("Foo::Bar::Baz", {
|
||||
location: "Foo::Bar::Baz",
|
||||
line: 456,
|
||||
column: 123,
|
||||
category: CATEGORY_OTHER
|
||||
});
|
||||
category: CATEGORY_OTHER,
|
||||
isContent: FrameNode.isContent({
|
||||
location: "Foo::Bar::Baz",
|
||||
category: CATEGORY_OTHER
|
||||
})
|
||||
}, false);
|
||||
|
||||
is(frame6.getInfo().nodeType, "Frame",
|
||||
"The sixth frame node has the correct type.");
|
||||
@ -148,16 +166,17 @@ function test() {
|
||||
"The sixth frame node has the correct url.");
|
||||
is(frame6.getInfo().line, 456,
|
||||
"The sixth frame node has the correct line.");
|
||||
is(frame6.getInfo().column, 123,
|
||||
"The sixth frame node has the correct column.");
|
||||
is(frame6.getInfo().categoryData.abbrev, "other",
|
||||
"The sixth frame node has the correct category data.");
|
||||
is(frame6.getInfo().isContent, false,
|
||||
"The sixth frame node has the correct content flag.");
|
||||
|
||||
let frame7 = new FrameNode({
|
||||
location: "EnterJIT"
|
||||
});
|
||||
let frame7 = new FrameNode("EnterJIT", {
|
||||
location: "EnterJIT",
|
||||
isContent: FrameNode.isContent({
|
||||
location: "EnterJIT"
|
||||
})
|
||||
}, false);
|
||||
|
||||
is(frame7.getInfo().nodeType, "Frame",
|
||||
"The seventh frame node has the correct type.");
|
||||
@ -178,19 +197,19 @@ function test() {
|
||||
is(frame7.getInfo().isContent, false,
|
||||
"The seventh frame node has the correct content flag.");
|
||||
|
||||
let frame8 = new FrameNode({
|
||||
let frame8 = new FrameNode("chrome://browser/content/content.js", {
|
||||
location: "chrome://browser/content/content.js",
|
||||
line: 456,
|
||||
column: 123
|
||||
});
|
||||
}, false);
|
||||
|
||||
is(frame8.getInfo().hostName, null,
|
||||
"The eighth frame node has the correct host name.");
|
||||
|
||||
let frame9 = new FrameNode({
|
||||
let frame9 = new FrameNode("hello/<.world (resource://gre/foo.js:123:434)", {
|
||||
location: "hello/<.world (resource://gre/foo.js:123:434)",
|
||||
line: 456
|
||||
});
|
||||
}, false);
|
||||
|
||||
is(frame9.getInfo().hostName, null,
|
||||
"The ninth frame node has the correct host name.");
|
||||
|
@ -6,170 +6,109 @@
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
const { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
|
||||
// Create a root node from a given samples array.
|
||||
|
||||
let root = new ThreadNode(gSamples);
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
let root = getFrameNodePath(threadNode, "(root)");
|
||||
|
||||
// Test the root node.
|
||||
|
||||
is(root.duration, 18,
|
||||
"The correct duration was calculated for the root node.");
|
||||
is(root.getInfo().nodeType, "Thread",
|
||||
is(threadNode.getInfo().nodeType, "Thread",
|
||||
"The correct node type was retrieved for the root node.");
|
||||
|
||||
is(root.duration, 20,
|
||||
"The correct duration was calculated for the root node.");
|
||||
is(root.getInfo().functionName, "(root)",
|
||||
"The correct function name was retrieved for the root node.");
|
||||
is(root.getInfo().categoryData.toSource(), "({})",
|
||||
"The correct empty category data was retrieved for the root node.");
|
||||
|
||||
is(Object.keys(root.calls).length, 1,
|
||||
is(root.calls.length, 1,
|
||||
"The correct number of child calls were calculated for the root node.");
|
||||
is(Object.keys(root.calls)[0], "A",
|
||||
ok(getFrameNodePath(root, "A"),
|
||||
"The root node's only child call is correct.");
|
||||
|
||||
// Test all the descendant nodes.
|
||||
|
||||
is(Object.keys(root.calls.A.calls).length, 2,
|
||||
"The correct number of child calls were calculated for the '.A' node.");
|
||||
is(Object.keys(root.calls.A.calls)[0], "B",
|
||||
"The '.A' node's first child call is correct.");
|
||||
is(Object.keys(root.calls.A.calls)[1], "E",
|
||||
"The '.A' node's second child call is correct.");
|
||||
is(getFrameNodePath(root, "A").calls.length, 2,
|
||||
"The correct number of child calls were calculated for the 'A' node.");
|
||||
ok(getFrameNodePath(root, "A > B"),
|
||||
"The 'A' node has a 'B' child call.");
|
||||
ok(getFrameNodePath(root, "A > E"),
|
||||
"The 'A' node has a 'E' child call.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.B.calls).length, 2,
|
||||
"The correct number of child calls were calculated for the '.A.B' node.");
|
||||
is(Object.keys(root.calls.A.calls.B.calls)[0], "C",
|
||||
"The '.A.B' node's first child call is correct.");
|
||||
is(Object.keys(root.calls.A.calls.B.calls)[1], "D",
|
||||
"The '.A.B' node's second child call is correct.");
|
||||
is(getFrameNodePath(root, "A > B").calls.length, 2,
|
||||
"The correct number of child calls were calculated for the 'A > B' node.");
|
||||
ok(getFrameNodePath(root, "A > B > C"),
|
||||
"The 'A > B' node has a 'C' child call.");
|
||||
ok(getFrameNodePath(root, "A > B > D"),
|
||||
"The 'A > B' node has a 'D' child call.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.E.calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A.E' node.");
|
||||
is(Object.keys(root.calls.A.calls.E.calls)[0], "F",
|
||||
"The '.A.E' node's only child call is correct.");
|
||||
is(getFrameNodePath(root, "A > E").calls.length, 1,
|
||||
"The correct number of child calls were calculated for the 'A > E' node.");
|
||||
ok(getFrameNodePath(root, "A > E > F"),
|
||||
"The 'A > E' node has a 'F' child call.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.B.calls.C.calls).length, 0,
|
||||
"The correct number of child calls were calculated for the '.A.B.C' node.");
|
||||
is(Object.keys(root.calls.A.calls.B.calls.D.calls).length, 0,
|
||||
"The correct number of child calls were calculated for the '.A.B.D' node.");
|
||||
is(Object.keys(root.calls.A.calls.E.calls.F.calls).length, 0,
|
||||
"The correct number of child calls were calculated for the '.A.E.F' node.");
|
||||
is(getFrameNodePath(root, "A > B > C").calls.length, 1,
|
||||
"The correct number of child calls were calculated for the 'A > B > C' node.");
|
||||
ok(getFrameNodePath(root, "A > B > C > D"),
|
||||
"The 'A > B > C' node has a 'D' child call.");
|
||||
|
||||
// Insert new nodes in the tree.
|
||||
is(getFrameNodePath(root, "A > B > C > D").calls.length, 1,
|
||||
"The correct number of child calls were calculated for the 'A > B > C > D' node.");
|
||||
ok(getFrameNodePath(root, "A > B > C > D > E"),
|
||||
"The 'A > B > C > D' node has a 'E' child call.");
|
||||
|
||||
root.insert({
|
||||
time: 20,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A" },
|
||||
{ location: "B" },
|
||||
{ location: "C" },
|
||||
{ location: "D" },
|
||||
{ location: "E" },
|
||||
{ location: "F" },
|
||||
{ location: "G" }
|
||||
]
|
||||
});
|
||||
is(getFrameNodePath(root, "A > B > C > D > E").calls.length, 1,
|
||||
"The correct number of child calls were calculated for the 'A > B > C > D > E' node.");
|
||||
ok(getFrameNodePath(root, "A > B > C > D > E > F"),
|
||||
"The 'A > B > C > D > E' node has a 'F' child call.");
|
||||
|
||||
// Retest the root node.
|
||||
is(getFrameNodePath(root, "A > B > C > D > E > F").calls.length, 1,
|
||||
"The correct number of child calls were calculated for the 'A > B > C > D > E > F' node.");
|
||||
ok(getFrameNodePath(root, "A > B > C > D > E > F > G"),
|
||||
"The 'A > B > C > D > E > F' node has a 'G' child call.");
|
||||
|
||||
is(root.duration, 20,
|
||||
"The correct duration was recalculated for the root node.");
|
||||
|
||||
is(Object.keys(root.calls).length, 1,
|
||||
"The correct number of child calls were calculated for the root node.");
|
||||
is(Object.keys(root.calls)[0], "A",
|
||||
"The root node's only child call is correct.");
|
||||
|
||||
// Retest all the descendant nodes.
|
||||
|
||||
is(Object.keys(root.calls.A.calls).length, 2,
|
||||
"The correct number of child calls were calculated for the '.A' node.");
|
||||
is(Object.keys(root.calls.A.calls)[0], "B",
|
||||
"The '.A' node's first child call is correct.");
|
||||
is(Object.keys(root.calls.A.calls)[1], "E",
|
||||
"The '.A' node's second child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.B.calls).length, 2,
|
||||
"The correct number of child calls were calculated for the '.A.B' node.");
|
||||
is(Object.keys(root.calls.A.calls.B.calls)[0], "C",
|
||||
"The '.A.B' node's first child call is correct.");
|
||||
is(Object.keys(root.calls.A.calls.B.calls)[1], "D",
|
||||
"The '.A.B' node's second child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.E.calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A.E' node.");
|
||||
is(Object.keys(root.calls.A.calls.E.calls)[0], "F",
|
||||
"The '.A.E' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.B.calls.C.calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A.B.C' node.");
|
||||
is(Object.keys(root.calls.A.calls.B.calls.C.calls)[0], "D",
|
||||
"The '.A.B.C' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.B.calls.C.calls.D.calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A.B.C.D' node.");
|
||||
is(Object.keys(root.calls.A.calls.B.calls.C.calls.D.calls)[0], "E",
|
||||
"The '.A.B.C.D' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.B.calls.C.calls.D.calls.E.calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A.B.C.D.E' node.");
|
||||
is(Object.keys(root.calls.A.calls.B.calls.C.calls.D.calls.E.calls)[0], "F",
|
||||
"The '.A.B.C.D.E' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.B.calls.C.calls.D.calls.E.calls.F.calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A.B.C.D.E.F' node.");
|
||||
is(Object.keys(root.calls.A.calls.B.calls.C.calls.D.calls.E.calls.F.calls)[0], "G",
|
||||
"The '.A.B.C.D.E.F' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.B.calls.C.calls.D.calls.E.calls.F.calls.G.calls).length, 0,
|
||||
"The correct number of child calls were calculated for the '.A.B.D.E.F.G' node.");
|
||||
is(Object.keys(root.calls.A.calls.B.calls.D.calls).length, 0,
|
||||
"The correct number of child calls were calculated for the '.A.B.D' node.");
|
||||
is(Object.keys(root.calls.A.calls.E.calls.F.calls).length, 0,
|
||||
"The correct number of child calls were calculated for the '.A.E.F' node.");
|
||||
is(getFrameNodePath(root, "A > B > C > D > E > F > G").calls.length, 0,
|
||||
"The correct number of child calls were calculated for the 'A > B > C > D > E > F > G' node.");
|
||||
is(getFrameNodePath(root, "A > B > D").calls.length, 0,
|
||||
"The correct number of child calls were calculated for the 'A > B > D' node.");
|
||||
is(getFrameNodePath(root, "A > E > F").calls.length, 0,
|
||||
"The correct number of child calls were calculated for the 'A > E > F' node.");
|
||||
|
||||
// Check the location, sample times, duration and samples of the root.
|
||||
|
||||
is(root.calls.A.location, "A",
|
||||
"The '.A' node has the correct location.");
|
||||
is(root.calls.A.sampleTimes.toSource(),
|
||||
"[{start:5, end:10}, {start:11, end:17}, {start:18, end:25}, {start:20, end:22}]",
|
||||
"The '.A' node has the correct sample times.");
|
||||
is(root.calls.A.duration, 20,
|
||||
"The '.A' node has the correct duration in milliseconds.");
|
||||
is(root.calls.A.samples, 4,
|
||||
"The '.A' node has the correct number of samples.");
|
||||
is(getFrameNodePath(root, "A").location, "A",
|
||||
"The 'A' node has the correct location.");
|
||||
is(getFrameNodePath(root, "A").duration, 20,
|
||||
"The 'A' node has the correct duration in milliseconds.");
|
||||
is(getFrameNodePath(root, "A").samples, 4,
|
||||
"The 'A' node has the correct number of samples.");
|
||||
|
||||
// ...and the rightmost leaf.
|
||||
|
||||
is(root.calls.A.calls.E.calls.F.location, "F",
|
||||
"The '.A.E.F' node has the correct location.");
|
||||
is(root.calls.A.calls.E.calls.F.sampleTimes.toSource(),
|
||||
"[{start:18, end:25}]",
|
||||
"The '.A.E.F' node has the correct sample times.");
|
||||
is(root.calls.A.calls.E.calls.F.duration, 7,
|
||||
"The '.A.E.F' node has the correct duration in milliseconds.");
|
||||
is(root.calls.A.calls.E.calls.F.samples, 1,
|
||||
"The '.A.E.F' node has the correct number of samples.");
|
||||
is(getFrameNodePath(root, "A > E > F").location, "F",
|
||||
"The 'A > E > F' node has the correct location.");
|
||||
is(getFrameNodePath(root, "A > E > F").duration, 7,
|
||||
"The 'A > E > F' node has the correct duration in milliseconds.");
|
||||
is(getFrameNodePath(root, "A > E > F").samples, 1,
|
||||
"The 'A > E > F' node has the correct number of samples.");
|
||||
|
||||
// ...and the leftmost leaf.
|
||||
|
||||
is(root.calls.A.calls.B.calls.C.calls.D.calls.E.calls.F.calls.G.location, "G",
|
||||
"The '.A.B.C.D.E.F.G' node has the correct location.");
|
||||
is(root.calls.A.calls.B.calls.C.calls.D.calls.E.calls.F.calls.G.sampleTimes.toSource(),
|
||||
"[{start:20, end:22}]",
|
||||
"The '.A.B.C.D.E.F.G' node has the correct sample times.");
|
||||
is(root.calls.A.calls.B.calls.C.calls.D.calls.E.calls.F.calls.G.duration, 2,
|
||||
"The '.A.B.C.D.E.F.G' node has the correct duration in milliseconds.");
|
||||
is(root.calls.A.calls.B.calls.C.calls.D.calls.E.calls.F.calls.G.samples, 1,
|
||||
"The '.A.B.C.D.E.F.G' node has the correct number of samples.");
|
||||
is(getFrameNodePath(root, "A > B > C > D > E > F > G").location, "G",
|
||||
"The 'A > B > C > D > E > F > G' node has the correct location.");
|
||||
is(getFrameNodePath(root, "A > B > C > D > E > F > G").duration, 2,
|
||||
"The 'A > B > C > D > E > F > G' node has the correct duration in milliseconds.");
|
||||
is(getFrameNodePath(root, "A > B > C > D > E > F > G").samples, 1,
|
||||
"The 'A > B > C > D > E > F > G' node has the correct number of samples.");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
@ -193,4 +132,16 @@ let gSamples = [{
|
||||
{ location: "E" },
|
||||
{ location: "F" }
|
||||
]
|
||||
}];
|
||||
}, {
|
||||
time: 20,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A" },
|
||||
{ location: "B" },
|
||||
{ location: "C" },
|
||||
{ location: "D" },
|
||||
{ location: "E" },
|
||||
{ location: "F" },
|
||||
{ location: "G" }
|
||||
]
|
||||
}]);
|
||||
|
@ -10,37 +10,37 @@ function test() {
|
||||
|
||||
// Create a root node from a given samples array.
|
||||
|
||||
let root = new ThreadNode(gSamples);
|
||||
let root = getFrameNodePath(new ThreadNode(gThread), "(root)");
|
||||
|
||||
// Test the root node.
|
||||
|
||||
is(root.duration, 5,
|
||||
"The correct duration was calculated for the root node.");
|
||||
|
||||
is(Object.keys(root.calls).length, 1,
|
||||
is(root.calls.length, 1,
|
||||
"The correct number of child calls were calculated for the root node.");
|
||||
is(Object.keys(root.calls)[0], "A",
|
||||
ok(getFrameNodePath(root, "A"),
|
||||
"The root node's only child call is correct.");
|
||||
|
||||
// Test all the descendant nodes.
|
||||
|
||||
is(Object.keys(root.calls.A.calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A' node.");
|
||||
is(Object.keys(root.calls.A.calls)[0], "B",
|
||||
"The '.A.B' node's only child call is correct.");
|
||||
is(getFrameNodePath(root, "A").calls.length, 1,
|
||||
"The correct number of child calls were calculated for the 'A' node.");
|
||||
ok(getFrameNodePath(root, "A > B"),
|
||||
"The 'A' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.B.calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A.B' node.");
|
||||
is(Object.keys(root.calls.A.calls.B.calls)[0], "C",
|
||||
"The '.A.B' node's only child call is correct.");
|
||||
is(getFrameNodePath(root, "A > B").calls.length, 1,
|
||||
"The correct number of child calls were calculated for the 'A > B' node.");
|
||||
ok(getFrameNodePath(root, "A > B > C"),
|
||||
"The 'A > B' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.B.calls.C.calls).length, 0,
|
||||
"The correct number of child calls were calculated for the '.A.B.C' node.");
|
||||
is(getFrameNodePath(root, "A > B > C").calls.length, 0,
|
||||
"The correct number of child calls were calculated for the 'A > B > C' node.");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
@ -56,4 +56,4 @@ let gSamples = [{
|
||||
{ location: "B" },
|
||||
{ location: "D" }
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
@ -10,47 +10,52 @@ function test() {
|
||||
let { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
|
||||
// Create a root node from a given samples array, filtering by time.
|
||||
|
||||
let root = new ThreadNode(gSamples, { startTime: 11, endTime: 18 });
|
||||
//
|
||||
// Filtering from 5 to 18 includes the 2nd and 3rd samples. The 2nd sample
|
||||
// starts exactly on 5 and ends at 11. The 3rd sample starts at 11 and ends
|
||||
// exactly at 18.
|
||||
let startTime = 5;
|
||||
let endTime = 18;
|
||||
let root = getFrameNodePath(new ThreadNode(gThread, { startTime, endTime }), "(root)");
|
||||
|
||||
// Test the root node.
|
||||
|
||||
is(root.duration, 18,
|
||||
is(root.duration, endTime - startTime,
|
||||
"The correct duration was calculated for the root node.");
|
||||
|
||||
is(Object.keys(root.calls).length, 1,
|
||||
is(root.calls.length, 1,
|
||||
"The correct number of child calls were calculated for the root node.");
|
||||
is(Object.keys(root.calls)[0], "A",
|
||||
ok(getFrameNodePath(root, "A"),
|
||||
"The root node's only child call is correct.");
|
||||
|
||||
// Test all the descendant nodes.
|
||||
|
||||
is(Object.keys(root.calls.A.calls).length, 2,
|
||||
"The correct number of child calls were calculated for the '.A' node.");
|
||||
is(Object.keys(root.calls.A.calls)[0], "B",
|
||||
"The '.A' node's first child call is correct.");
|
||||
is(Object.keys(root.calls.A.calls)[1], "E",
|
||||
"The '.A' node's second child call is correct.");
|
||||
is(getFrameNodePath(root, "A").calls.length, 2,
|
||||
"The correct number of child calls were calculated for the 'A' node.");
|
||||
ok(getFrameNodePath(root, "A > B"),
|
||||
"The 'A' node has a 'B' child call.");
|
||||
ok(getFrameNodePath(root, "A > E"),
|
||||
"The 'A' node has a 'E' child call.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.B.calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A.B' node.");
|
||||
is(Object.keys(root.calls.A.calls.B.calls)[0], "D",
|
||||
"The '.A.B' node's only child call is correct.");
|
||||
is(getFrameNodePath(root, "A > B").calls.length, 1,
|
||||
"The correct number of child calls were calculated for the 'A > B' node.");
|
||||
ok(getFrameNodePath(root, "A > B > D"),
|
||||
"The 'A > B' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.E.calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A.E' node.");
|
||||
is(Object.keys(root.calls.A.calls.E.calls)[0], "F",
|
||||
"The '.A.E' node's only child call is correct.");
|
||||
is(getFrameNodePath(root, "A > E").calls.length, 1,
|
||||
"The correct number of child calls were calculated for the 'A > E' node.");
|
||||
ok(getFrameNodePath(root, "A > E > F"),
|
||||
"The 'A > E' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls.A.calls.B.calls.D.calls).length, 0,
|
||||
"The correct number of child calls were calculated for the '.A.B.D' node.");
|
||||
is(Object.keys(root.calls.A.calls.E.calls.F.calls).length, 0,
|
||||
"The correct number of child calls were calculated for the '.A.E.F' node.");
|
||||
is(getFrameNodePath(root, "A > B > D").calls.length, 0,
|
||||
"The correct number of child calls were calculated for the 'A > B > D' node.");
|
||||
is(getFrameNodePath(root, "A > E > F").calls.length, 0,
|
||||
"The correct number of child calls were calculated for the 'A > E > F' node.");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
@ -83,4 +88,4 @@ let gSamples = [{
|
||||
{ location: "C" },
|
||||
{ location: "D" }
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
@ -11,44 +11,46 @@ function test() {
|
||||
|
||||
// Create a root node from a given samples array, filtering by time.
|
||||
|
||||
let root = new ThreadNode(gSamples, { startTime: 11, endTime: 18, contentOnly: true });
|
||||
let startTime = 5;
|
||||
let endTime = 18;
|
||||
let root = getFrameNodePath(new ThreadNode(gThread, { startTime: startTime, endTime: endTime, contentOnly: true }), "(root)");
|
||||
|
||||
// Test the root node.
|
||||
|
||||
is(root.duration, 18,
|
||||
is(root.duration, endTime - startTime,
|
||||
"The correct duration was calculated for the root node.");
|
||||
|
||||
is(Object.keys(root.calls).length, 2,
|
||||
is(root.calls.length, 2,
|
||||
"The correct number of child calls were calculated for the root node.");
|
||||
is(Object.keys(root.calls)[0], "http://D",
|
||||
"The root node's first child call is correct.");
|
||||
is(Object.keys(root.calls)[1], "http://A",
|
||||
"The root node's second child call is correct.");
|
||||
ok(getFrameNodePath(root, "http://D"),
|
||||
"The root has a 'http://D' child call.");
|
||||
ok(getFrameNodePath(root, "http://A"),
|
||||
"The root has a 'http://A' child call.");
|
||||
|
||||
// Test all the descendant nodes.
|
||||
|
||||
is(Object.keys(root.calls["http://A"].calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A' node.");
|
||||
is(Object.keys(root.calls["http://A"].calls)[0], "https://E",
|
||||
"The '.A' node's only child call is correct.");
|
||||
is(getFrameNodePath(root, "http://A").calls.length, 1,
|
||||
"The correct number of child calls were calculated for the 'http://A' node.");
|
||||
ok(getFrameNodePath(root, "http://A > https://E"),
|
||||
"The 'http://A' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls["http://A"].calls["https://E"].calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A.E' node.");
|
||||
is(Object.keys(root.calls["http://A"].calls["https://E"].calls)[0], "file://F",
|
||||
"The '.A.E' node's only child call is correct.");
|
||||
is(getFrameNodePath(root, "http://A > https://E").calls.length, 1,
|
||||
"The correct number of child calls were calculated for the 'http://A > http://E' node.");
|
||||
ok(getFrameNodePath(root, "http://A > https://E > file://F"),
|
||||
"The 'http://A > https://E' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls["http://A"].calls["https://E"].calls["file://F"].calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A.E.F' node.");
|
||||
is(Object.keys(root.calls["http://A"].calls["https://E"].calls["file://F"].calls)[0], "app://H",
|
||||
"The '.A.E.F' node's only child call is correct.");
|
||||
is(getFrameNodePath(root, "http://A > https://E > file://F").calls.length, 1,
|
||||
"The correct number of child calls were calculated for the 'http://A > https://E >> file://F' node.");
|
||||
ok(getFrameNodePath(root, "http://A > https://E > file://F > app://H"),
|
||||
"The 'http://A > https://E >> file://F' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls["http://D"].calls).length, 0,
|
||||
"The correct number of child calls were calculated for the '.D' node.");
|
||||
is(getFrameNodePath(root, "http://D").calls.length, 0,
|
||||
"The correct number of child calls were calculated for the 'http://D' node.");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
@ -83,4 +85,4 @@ let gSamples = [{
|
||||
{ location: "http://C" },
|
||||
{ location: "http://D" }
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
let time = 1;
|
||||
|
||||
let samples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: time++,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
@ -40,40 +40,40 @@ let samples = [{
|
||||
{ location: "B" },
|
||||
{ location: "F" }
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
||||
function test() {
|
||||
let { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
|
||||
let root = new ThreadNode(samples, { invertTree: true });
|
||||
let root = new ThreadNode(gThread, { invertTree: true });
|
||||
|
||||
is(Object.keys(root.calls).length, 2,
|
||||
is(root.calls.length, 2,
|
||||
"Should get the 2 youngest frames, not the 1 oldest frame");
|
||||
|
||||
let C = root.calls.C;
|
||||
let C = getFrameNodePath(root, "C");
|
||||
ok(C, "Should have C as a child of the root.");
|
||||
|
||||
is(Object.keys(C.calls).length, 3,
|
||||
is(C.calls.length, 3,
|
||||
"Should have 3 frames that called C.");
|
||||
ok(C.calls.B, "B called C.");
|
||||
ok(C.calls.D, "D called C.");
|
||||
ok(C.calls.E, "E called C.");
|
||||
ok(getFrameNodePath(C, "B"), "B called C.");
|
||||
ok(getFrameNodePath(C, "D"), "D called C.");
|
||||
ok(getFrameNodePath(C, "E"), "E called C.");
|
||||
|
||||
is(Object.keys(C.calls.B.calls).length, 1);
|
||||
ok(C.calls.B.calls.A, "A called B called C");
|
||||
is(Object.keys(C.calls.D.calls).length, 1);
|
||||
ok(C.calls.D.calls.A, "A called D called C");
|
||||
is(Object.keys(C.calls.E.calls).length, 1);
|
||||
ok(C.calls.E.calls.A, "A called E called C");
|
||||
is(getFrameNodePath(C, "B").calls.length, 1);
|
||||
ok(getFrameNodePath(C, "B > A"), "A called B called C");
|
||||
is(getFrameNodePath(C, "D").calls.length, 1);
|
||||
ok(getFrameNodePath(C, "D > A"), "A called D called C");
|
||||
is(getFrameNodePath(C, "E").calls.length, 1);
|
||||
ok(getFrameNodePath(C, "E > A"), "A called E called C");
|
||||
|
||||
let F = root.calls.F;
|
||||
let F = getFrameNodePath(root, "F");
|
||||
ok(F, "Should have F as a child of the root.");
|
||||
|
||||
is(Object.keys(F.calls).length, 1);
|
||||
ok(F.calls.B, "B called F");
|
||||
is(F.calls.length, 1);
|
||||
ok(getFrameNodePath(F, "B"), "B called F");
|
||||
|
||||
is(Object.keys(F.calls.B.calls).length, 1);
|
||||
ok(F.calls.B.calls.A, "A called B called F");
|
||||
is(getFrameNodePath(F, "B").calls.length, 1);
|
||||
ok(getFrameNodePath(F, "B > A"), "A called B called F");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
@ -6,96 +6,174 @@
|
||||
* the FrameNodes have the correct optimization data after iterating over samples.
|
||||
*/
|
||||
|
||||
const { RecordingUtils } = devtools.require("devtools/performance/recording-utils");
|
||||
|
||||
let gUniqueStacks = new RecordingUtils.UniqueStacks();
|
||||
|
||||
function uniqStr(s) {
|
||||
return gUniqueStacks.getOrAddStringIndex(s);
|
||||
}
|
||||
|
||||
let time = 1;
|
||||
|
||||
let samples = [{
|
||||
time: time++,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A", optsIndex: 0 },
|
||||
{ location: "B" },
|
||||
{ location: "C" }
|
||||
]
|
||||
}, {
|
||||
time: time++,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A", optsIndex: 0 },
|
||||
{ location: "D" },
|
||||
{ location: "C" }
|
||||
]
|
||||
}, {
|
||||
time: time++,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A", optsIndex: 1 },
|
||||
{ location: "E", optsIndex: 2 },
|
||||
{ location: "C" }
|
||||
],
|
||||
}, {
|
||||
time: time++,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A" },
|
||||
{ location: "B" },
|
||||
{ location: "F" }
|
||||
]
|
||||
}];
|
||||
let gThread = RecordingUtils.deflateThread({
|
||||
samples: [{
|
||||
time: 0,
|
||||
frames: [
|
||||
{ location: "(root)" }
|
||||
]
|
||||
}, {
|
||||
time: time++,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A_O1" },
|
||||
{ location: "B" },
|
||||
{ location: "C" }
|
||||
]
|
||||
}, {
|
||||
time: time++,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A_O1" },
|
||||
{ location: "D" },
|
||||
{ location: "C" }
|
||||
]
|
||||
}, {
|
||||
time: time++,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A_O2" },
|
||||
{ location: "E_O3" },
|
||||
{ location: "C" }
|
||||
],
|
||||
}, {
|
||||
time: time++,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A" },
|
||||
{ location: "B" },
|
||||
{ location: "F" }
|
||||
]
|
||||
}],
|
||||
markers: []
|
||||
}, gUniqueStacks);
|
||||
|
||||
// Array of OptimizationSites
|
||||
let gOpts = [{
|
||||
// 3 OptimizationSites
|
||||
let gRawSite1 = {
|
||||
line: 12,
|
||||
column: 2,
|
||||
types: [{ mirType: "Object", site: "A (http://foo/bar/bar:12)", types: [
|
||||
{ keyedBy: "constructor", name: "Foo", location: "A (http://foo/bar/baz:12)" },
|
||||
{ keyedBy: "primitive", location: "self-hosted" }
|
||||
]}],
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "Inlined", strategy: "SomeGetter3" },
|
||||
]
|
||||
}, {
|
||||
types: [{
|
||||
mirType: uniqStr("Object"),
|
||||
site: uniqStr("A (http://foo/bar/bar:12)"),
|
||||
typeset: [{
|
||||
keyedBy: uniqStr("constructor"),
|
||||
name: uniqStr("Foo"),
|
||||
location: uniqStr("A (http://foo/bar/baz:12)")
|
||||
}, {
|
||||
keyedBy: uniqStr("primitive"),
|
||||
location: uniqStr("self-hosted")
|
||||
}]
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("Inlined"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let gRawSite2 = {
|
||||
line: 34,
|
||||
types: [{ mirType: "Int32", site: "Receiver" }], // use no types
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "Failure3", strategy: "SomeGetter3" },
|
||||
]
|
||||
}, {
|
||||
types: [{
|
||||
mirType: uniqStr("Int32"),
|
||||
site: uniqStr("Receiver")
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("Failure3"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let gRawSite3 = {
|
||||
line: 78,
|
||||
types: [{ mirType: "Object", site: "A (http://foo/bar/bar:12)", types: [
|
||||
{ keyedBy: "constructor", name: "Foo", location: "A (http://foo/bar/baz:12)" },
|
||||
{ keyedBy: "primitive", location: "self-hosted" }
|
||||
]}],
|
||||
attempts: [
|
||||
{ outcome: "Failure1", strategy: "SomeGetter1" },
|
||||
{ outcome: "Failure2", strategy: "SomeGetter2" },
|
||||
{ outcome: "GenericSuccess", strategy: "SomeGetter3" },
|
||||
]
|
||||
}];
|
||||
types: [{
|
||||
mirType: uniqStr("Object"),
|
||||
site: uniqStr("A (http://foo/bar/bar:12)"),
|
||||
typeset: [{
|
||||
keyedBy: uniqStr("constructor"),
|
||||
name: uniqStr("Foo"),
|
||||
location: uniqStr("A (http://foo/bar/baz:12)")
|
||||
}, {
|
||||
keyedBy: uniqStr("primitive"),
|
||||
location: uniqStr("self-hosted")
|
||||
}]
|
||||
}],
|
||||
attempts: {
|
||||
schema: {
|
||||
outcome: 0,
|
||||
strategy: 1
|
||||
},
|
||||
data: [
|
||||
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
|
||||
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
|
||||
[uniqStr("GenericSuccess"), uniqStr("SomeGetter3")]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
gThread.frameTable.data.forEach((frame) => {
|
||||
const LOCATION_SLOT = gThread.frameTable.schema.location;
|
||||
const OPTIMIZATIONS_SLOT = gThread.frameTable.schema.optimizations;
|
||||
|
||||
let l = gThread.stringTable[frame[LOCATION_SLOT]];
|
||||
switch (l) {
|
||||
case "A_O1":
|
||||
frame[LOCATION_SLOT] = uniqStr("A");
|
||||
frame[OPTIMIZATIONS_SLOT] = gRawSite1;
|
||||
break;
|
||||
case "A_O2":
|
||||
frame[LOCATION_SLOT] = uniqStr("A");
|
||||
frame[OPTIMIZATIONS_SLOT] = gRawSite2;
|
||||
break;
|
||||
case "E_O3":
|
||||
frame[LOCATION_SLOT] = uniqStr("E");
|
||||
frame[OPTIMIZATIONS_SLOT] = gRawSite3;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
function test() {
|
||||
let { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
|
||||
let root = new ThreadNode(samples, { optimizations: gOpts });
|
||||
let root = new ThreadNode(gThread);
|
||||
|
||||
let A = root.calls.A;
|
||||
let A = getFrameNodePath(root, "(root) > A");
|
||||
|
||||
let opts = A.getOptimizations();
|
||||
let sites = opts.getOptimizationSites();
|
||||
let sites = opts.optimizationSites;
|
||||
is(sites.length, 2, "Frame A has two optimization sites.");
|
||||
is(sites[0].samples, 2, "first opt site has 2 samples.");
|
||||
is(sites[1].samples, 1, "second opt site has 1 sample.");
|
||||
|
||||
let E = A.calls.E;
|
||||
let E = getFrameNodePath(A, "E");
|
||||
opts = E.getOptimizations();
|
||||
sites = opts.getOptimizationSites();
|
||||
sites = opts.optimizationSites;
|
||||
is(sites.length, 1, "Frame E has one optimization site.");
|
||||
is(sites[0].samples, 1, "first opt site has 1 samples.");
|
||||
|
||||
let D = A.calls.D;
|
||||
let D = getFrameNodePath(A, "D");
|
||||
ok(!D.getOptimizations(),
|
||||
"frames that do not have any opts data do not have JITOptimizations instances.");
|
||||
|
||||
|
@ -13,7 +13,7 @@ function test() {
|
||||
|
||||
// Create a root node from a given samples array.
|
||||
|
||||
let root = new ThreadNode(gSamples, { contentOnly: true });
|
||||
let root = getFrameNodePath(new ThreadNode(gThread, { contentOnly: true }), "(root)");
|
||||
|
||||
/*
|
||||
* should have a tree like:
|
||||
@ -26,29 +26,31 @@ function test() {
|
||||
* - D
|
||||
* - E
|
||||
* - F
|
||||
* - (JS)
|
||||
* - (JS)
|
||||
*/
|
||||
|
||||
// Test the root node.
|
||||
|
||||
is(Object.keys(root.calls).length, 2, "root has 2 children");
|
||||
ok(root.calls[url("A")], "root has content child");
|
||||
ok(root.calls["64"], "root has platform generalized child");
|
||||
is(Object.keys(root.calls["64"].calls).length, 0, "platform generalized child is a leaf.");
|
||||
is(root.calls.length, 2, "root has 2 children");
|
||||
ok(getFrameNodePath(root, url("A")), "root has content child");
|
||||
ok(getFrameNodePath(root, "64"), "root has platform generalized child");
|
||||
is(getFrameNodePath(root, "64").calls.length, 0, "platform generalized child is a leaf.");
|
||||
|
||||
ok(root.calls[url("A")].calls["128"], "A has platform generalized child of another type");
|
||||
is(Object.keys(root.calls[url("A")].calls["128"].calls).length, 0, "second generalized type is a leaf.");
|
||||
ok(getFrameNodePath(root, `${url("A")} > 128`), "A has platform generalized child of another type");
|
||||
is(getFrameNodePath(root, `${url("A")} > 128`).calls.length, 0, "second generalized type is a leaf.");
|
||||
|
||||
ok(root.calls[url("A")].calls[url("E")].calls[url("F")].calls["64"],
|
||||
"a second leaf of the first generalized type exists deep in the tree.");
|
||||
ok(root.calls[url("A")].calls["128"], "A has platform generalized child of another type");
|
||||
ok(getFrameNodePath(root, `${url("A")} > ${url("E")} > ${url("F")} > 64`),
|
||||
"a second leaf of the first generalized type exists deep in the tree.");
|
||||
ok(getFrameNodePath(root, `${url("A")} > 128`), "A has platform generalized child of another type");
|
||||
|
||||
is(getFrameNodePath(root, "64").category,
|
||||
getFrameNodePath(root, `${url("A")} > ${url("E")} > ${url("F")} > 64`).category,
|
||||
"generalized frames of same type are duplicated in top-down view");
|
||||
|
||||
is(root.calls["64"].category, root.calls[url("A")].calls[url("E")].calls[url("F")].calls["64"].category,
|
||||
"generalized frames of same type are duplicated in top-down view");
|
||||
finish();
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
@ -88,4 +90,4 @@ let gSamples = [{
|
||||
{ location: "http://content/A" },
|
||||
{ location: "contentZ", category: CATEGORY_MASK("gc", 1) },
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
@ -10,7 +10,9 @@ function test() {
|
||||
let { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
let { CallView } = devtools.require("devtools/shared/profiler/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gSamples);
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
||||
let container = document.createElement("vbox");
|
||||
@ -60,7 +62,7 @@ function test() {
|
||||
finish();
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
@ -92,4 +94,4 @@ let gSamples = [{
|
||||
{ category: 128, location: "E (http://foo/bar/baz:90)" },
|
||||
{ category: 256, location: "F (http://foo/bar/baz:99)" }
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
@ -12,7 +12,9 @@ function test() {
|
||||
let { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
let { CallView } = devtools.require("devtools/shared/profiler/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gSamples);
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
||||
let container = document.createElement("vbox");
|
||||
@ -125,7 +127,7 @@ function test() {
|
||||
finish();
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ category: CATEGORY_MASK('other'), location: "(root)" },
|
||||
@ -157,4 +159,4 @@ let gSamples = [{
|
||||
{ category: CATEGORY_MASK('gc', 2), location: "E (http://foo/bar/baz:90)" },
|
||||
{ category: CATEGORY_MASK('network'), location: "F (http://foo/bar/baz:99)" }
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
@ -10,7 +10,9 @@ function test() {
|
||||
let { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
let { CallView } = devtools.require("devtools/shared/profiler/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gSamples);
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
||||
let container = document.createElement("vbox");
|
||||
@ -73,7 +75,7 @@ function test() {
|
||||
finish();
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
@ -105,5 +107,5 @@ let gSamples = [{
|
||||
{ category: 128, location: "E (http://foo/bar/baz:90)" },
|
||||
{ category: 256, location: "F (http://foo/bar/baz:99)" }
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
||||
|
@ -12,7 +12,9 @@ function test() {
|
||||
let { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
let { CallView } = devtools.require("devtools/shared/profiler/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gSamples);
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
||||
let container = document.createElement("vbox");
|
||||
@ -79,7 +81,7 @@ function test() {
|
||||
finish();
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ category: CATEGORY_MASK('other'), location: "(root)" },
|
||||
@ -111,4 +113,4 @@ let gSamples = [{
|
||||
{ category: CATEGORY_MASK('gc', 2), location: "E (http://foo/bar/baz:90)" },
|
||||
{ category: CATEGORY_MASK('network'), location: "F (http://foo/bar/baz:99)" }
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
@ -10,7 +10,9 @@ function test() {
|
||||
let { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
let { CallView } = devtools.require("devtools/shared/profiler/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gSamples);
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
||||
let container = document.createElement("vbox");
|
||||
@ -31,7 +33,7 @@ function test() {
|
||||
finish();
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
@ -63,4 +65,4 @@ let gSamples = [{
|
||||
{ category: 128, location: "E (http://foo/bar/baz:90)" },
|
||||
{ category: 256, location: "F (http://foo/bar/baz:99)" }
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
@ -10,7 +10,9 @@ function spawnTest () {
|
||||
let { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
let { CallView } = devtools.require("devtools/shared/profiler/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gSamples);
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
||||
let container = document.createElement("vbox");
|
||||
@ -29,7 +31,7 @@ function spawnTest () {
|
||||
finish();
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
@ -61,4 +63,4 @@ let gSamples = [{
|
||||
{ category: 128, location: "E (http://foo/bar/baz:90)" },
|
||||
{ category: 256, location: "F (http://foo/bar/baz:99)" }
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
@ -10,7 +10,9 @@ function spawnTest () {
|
||||
let { ThreadNode } = devtools.require("devtools/shared/profiler/tree-model");
|
||||
let { CallView } = devtools.require("devtools/shared/profiler/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gSamples);
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
||||
let container = document.createElement("vbox");
|
||||
@ -33,7 +35,7 @@ function spawnTest () {
|
||||
"The .A.B.D node has the correct container node.");
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ category: 8, location: "(root)" },
|
||||
@ -65,4 +67,4 @@ let gSamples = [{
|
||||
{ category: 128, location: "E (http://foo/bar/baz:90)" },
|
||||
{ category: 256, location: "F (http://foo/bar/baz:99)" }
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
@ -26,7 +26,9 @@ function test() {
|
||||
* - (JS)
|
||||
*/
|
||||
|
||||
let threadNode = new ThreadNode(gSamples, { contentOnly: true });
|
||||
let threadNode = new ThreadNode(gThread, { contentOnly: true });
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode, autoExpandDepth: 10 });
|
||||
|
||||
let container = document.createElement("vbox");
|
||||
@ -61,7 +63,7 @@ function test() {
|
||||
finish();
|
||||
}
|
||||
|
||||
let gSamples = [{
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
@ -101,4 +103,4 @@ let gSamples = [{
|
||||
{ location: "http://content/A" },
|
||||
{ location: "contentZ", category: CATEGORY_MASK("gc", 1) },
|
||||
]
|
||||
}];
|
||||
}]);
|
||||
|
@ -517,3 +517,66 @@ function forceCC () {
|
||||
SpecialPowers.DOMWindowUtils.garbageCollect();
|
||||
SpecialPowers.DOMWindowUtils.garbageCollect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflate a particular sample's stack and return an array of strings.
|
||||
*/
|
||||
function getInflatedStackLocations(thread, sample) {
|
||||
let stackTable = thread.stackTable;
|
||||
let frameTable = thread.frameTable;
|
||||
let stringTable = thread.stringTable;
|
||||
let SAMPLE_STACK_SLOT = thread.samples.schema.stack;
|
||||
let STACK_PREFIX_SLOT = stackTable.schema.prefix;
|
||||
let STACK_FRAME_SLOT = stackTable.schema.frame;
|
||||
let FRAME_LOCATION_SLOT = frameTable.schema.location;
|
||||
|
||||
// Build the stack from the raw data and accumulate the locations in
|
||||
// an array.
|
||||
let stackIndex = sample[SAMPLE_STACK_SLOT];
|
||||
let locations = [];
|
||||
while (stackIndex !== null) {
|
||||
let stackEntry = stackTable.data[stackIndex];
|
||||
let frame = frameTable.data[stackEntry[STACK_FRAME_SLOT]];
|
||||
locations.push(stringTable[frame[FRAME_LOCATION_SLOT]]);
|
||||
stackIndex = stackEntry[STACK_PREFIX_SLOT];
|
||||
}
|
||||
|
||||
// The profiler tree is inverted, so reverse the array.
|
||||
return locations.reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a path in a FrameNode call tree.
|
||||
*/
|
||||
function getFrameNodePath(root, path) {
|
||||
let calls = root.calls;
|
||||
let node;
|
||||
for (let key of path.split(" > ")) {
|
||||
node = calls.find((node) => node.key == key);
|
||||
if (!node) {
|
||||
break;
|
||||
}
|
||||
calls = node.calls;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a profile for testing.
|
||||
*/
|
||||
function synthesizeProfileForTest(samples) {
|
||||
const { RecordingUtils } = devtools.require("devtools/performance/recording-utils");
|
||||
|
||||
samples.unshift({
|
||||
time: 0,
|
||||
frames: [
|
||||
{ location: "(root)" }
|
||||
]
|
||||
});
|
||||
|
||||
let uniqueStacks = new RecordingUtils.UniqueStacks();
|
||||
return RecordingUtils.deflateThread({
|
||||
samples: samples,
|
||||
markers: []
|
||||
}, uniqueStacks);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
// widget work properly in the flame graph.
|
||||
|
||||
let {FlameGraphUtils, FLAME_GRAPH_BLOCK_HEIGHT} = devtools.require("devtools/shared/widgets/FlameGraph");
|
||||
let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
|
||||
|
||||
add_task(function*() {
|
||||
yield promiseTab("about:blank");
|
||||
@ -13,7 +14,7 @@ add_task(function*() {
|
||||
});
|
||||
|
||||
function* performTest() {
|
||||
let out = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA);
|
||||
let out = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA);
|
||||
|
||||
ok(out, "Some data was outputted properly");
|
||||
is(out.length, 10, "The outputted length is correct.");
|
||||
@ -42,7 +43,7 @@ function* performTest() {
|
||||
}
|
||||
}
|
||||
|
||||
let TEST_DATA = [{
|
||||
let TEST_DATA = synthesizeProfileForTest([{
|
||||
frames: [{
|
||||
location: "M"
|
||||
}, {
|
||||
@ -96,7 +97,7 @@ let TEST_DATA = [{
|
||||
location: "Z"
|
||||
}],
|
||||
time: 500
|
||||
}];
|
||||
}]);
|
||||
|
||||
let EXPECTED_OUTPUT = [{
|
||||
blocks: []
|
||||
|
@ -12,7 +12,7 @@ add_task(function*() {
|
||||
});
|
||||
|
||||
function* performTest() {
|
||||
let out = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA, {
|
||||
let out = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA, {
|
||||
flattenRecursion: true
|
||||
});
|
||||
|
||||
@ -43,7 +43,7 @@ function* performTest() {
|
||||
}
|
||||
}
|
||||
|
||||
let TEST_DATA = [{
|
||||
let TEST_DATA = synthesizeProfileForTest([{
|
||||
frames: [{
|
||||
location: "A"
|
||||
}, {
|
||||
@ -58,7 +58,7 @@ let TEST_DATA = [{
|
||||
location: "C"
|
||||
}],
|
||||
time: 50,
|
||||
}];
|
||||
}]);
|
||||
|
||||
let EXPECTED_OUTPUT = [{
|
||||
blocks: []
|
||||
@ -89,7 +89,17 @@ let EXPECTED_OUTPUT = [{
|
||||
text: "B"
|
||||
}]
|
||||
}, {
|
||||
blocks: []
|
||||
blocks: [{
|
||||
srcData: {
|
||||
startTime: 0,
|
||||
rawLocation: "C"
|
||||
},
|
||||
x: 0,
|
||||
y: FLAME_GRAPH_BLOCK_HEIGHT * 2,
|
||||
width: 50,
|
||||
height: FLAME_GRAPH_BLOCK_HEIGHT,
|
||||
text: "C"
|
||||
}]
|
||||
}, {
|
||||
blocks: []
|
||||
}, {
|
||||
|
@ -13,8 +13,8 @@ add_task(function*() {
|
||||
});
|
||||
|
||||
function* performTest() {
|
||||
let out = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA, {
|
||||
filterFrames: FrameNode.isContent
|
||||
let out = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA, {
|
||||
contentOnly: true
|
||||
});
|
||||
|
||||
ok(out, "Some data was outputted properly");
|
||||
@ -22,6 +22,8 @@ function* performTest() {
|
||||
|
||||
info("Got flame graph data:\n" + out.toSource() + "\n");
|
||||
|
||||
dump(JSON.stringify(out, undefined, 2));
|
||||
|
||||
for (let i = 0; i < out.length; i++) {
|
||||
let found = out[i];
|
||||
let expected = EXPECTED_OUTPUT[i];
|
||||
@ -44,7 +46,7 @@ function* performTest() {
|
||||
}
|
||||
}
|
||||
|
||||
let TEST_DATA = [{
|
||||
let TEST_DATA = synthesizeProfileForTest([{
|
||||
frames: [{
|
||||
location: "http://A"
|
||||
}, {
|
||||
@ -57,7 +59,7 @@ let TEST_DATA = [{
|
||||
location: "resource://E"
|
||||
}],
|
||||
time: 50,
|
||||
}];
|
||||
}]);
|
||||
|
||||
let EXPECTED_OUTPUT = [{
|
||||
blocks: []
|
||||
@ -92,7 +94,17 @@ let EXPECTED_OUTPUT = [{
|
||||
}, {
|
||||
blocks: []
|
||||
}, {
|
||||
blocks: []
|
||||
blocks: [{
|
||||
srcData: {
|
||||
startTime: 0,
|
||||
rawLocation: "Gecko"
|
||||
},
|
||||
x: 0,
|
||||
y: FLAME_GRAPH_BLOCK_HEIGHT * 3,
|
||||
width: 50,
|
||||
height: FLAME_GRAPH_BLOCK_HEIGHT,
|
||||
text: "Gecko"
|
||||
}]
|
||||
}, {
|
||||
blocks: []
|
||||
}, {
|
||||
|
@ -13,10 +13,10 @@ add_task(function*() {
|
||||
});
|
||||
|
||||
function* performTest() {
|
||||
let out = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA, {
|
||||
let out = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA, {
|
||||
flattenRecursion: true,
|
||||
filterFrames: FrameNode.isContent,
|
||||
showIdleBlocks: "\m/"
|
||||
contentOnly: true,
|
||||
showIdleBlocks: '\m/'
|
||||
});
|
||||
|
||||
ok(out, "Some data was outputted properly");
|
||||
@ -46,7 +46,7 @@ function* performTest() {
|
||||
}
|
||||
}
|
||||
|
||||
let TEST_DATA = [{
|
||||
let TEST_DATA = synthesizeProfileForTest([{
|
||||
frames: [{
|
||||
location: "http://A"
|
||||
}, {
|
||||
@ -66,11 +66,7 @@ let TEST_DATA = [{
|
||||
}],
|
||||
time: 50
|
||||
}, {
|
||||
frames: [{
|
||||
location: "chrome://D"
|
||||
}, {
|
||||
location: "resource://E"
|
||||
}],
|
||||
frames: [],
|
||||
time: 100
|
||||
}, {
|
||||
frames: [{
|
||||
@ -81,7 +77,7 @@ let TEST_DATA = [{
|
||||
location: "file://C",
|
||||
}],
|
||||
time: 150
|
||||
}];
|
||||
}]);
|
||||
|
||||
let EXPECTED_OUTPUT = [{
|
||||
blocks: []
|
||||
@ -118,6 +114,16 @@ let EXPECTED_OUTPUT = [{
|
||||
width: 50,
|
||||
height: FLAME_GRAPH_BLOCK_HEIGHT,
|
||||
text: "http://A"
|
||||
}, {
|
||||
srcData: {
|
||||
startTime: 0,
|
||||
rawLocation: "file://C"
|
||||
},
|
||||
x: 100,
|
||||
y: FLAME_GRAPH_BLOCK_HEIGHT * 2,
|
||||
width: 50,
|
||||
height: FLAME_GRAPH_BLOCK_HEIGHT,
|
||||
text: "file://C"
|
||||
}]
|
||||
}, {
|
||||
blocks: [{
|
||||
@ -136,7 +142,17 @@ let EXPECTED_OUTPUT = [{
|
||||
}, {
|
||||
blocks: []
|
||||
}, {
|
||||
blocks: []
|
||||
blocks: [{
|
||||
srcData: {
|
||||
startTime: 0,
|
||||
rawLocation: "Gecko"
|
||||
},
|
||||
x: 0,
|
||||
y: FLAME_GRAPH_BLOCK_HEIGHT * 3,
|
||||
width: 50,
|
||||
height: FLAME_GRAPH_BLOCK_HEIGHT,
|
||||
text: "Gecko"
|
||||
}]
|
||||
}, {
|
||||
blocks: []
|
||||
}, {
|
||||
|
@ -12,19 +12,19 @@ add_task(function*() {
|
||||
});
|
||||
|
||||
function* performTest() {
|
||||
let out1 = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA);
|
||||
let out2 = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA);
|
||||
let out1 = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA);
|
||||
let out2 = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA);
|
||||
is(out1, out2, "The outputted data is identical.")
|
||||
|
||||
let out3 = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA, { flattenRecursion: true });
|
||||
let out3 = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA, { flattenRecursion: true });
|
||||
is(out2, out3, "The outputted data is still identical.");
|
||||
|
||||
FlameGraphUtils.removeFromCache(TEST_DATA);
|
||||
let out4 = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA, { flattenRecursion: true });
|
||||
let out4 = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA, { flattenRecursion: true });
|
||||
isnot(out3, out4, "The outputted data is not identical anymore.");
|
||||
}
|
||||
|
||||
let TEST_DATA = [{
|
||||
let TEST_DATA = synthesizeProfileForTest([{
|
||||
frames: [{
|
||||
location: "A"
|
||||
}, {
|
||||
@ -39,4 +39,4 @@ let TEST_DATA = [{
|
||||
location: "C"
|
||||
}],
|
||||
time: 50,
|
||||
}];
|
||||
}]);
|
||||
|
@ -13,7 +13,7 @@ add_task(function*() {
|
||||
});
|
||||
|
||||
function* performTest() {
|
||||
let out = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA, {
|
||||
let out = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA, {
|
||||
flattenRecursion: true
|
||||
});
|
||||
|
||||
@ -44,14 +44,14 @@ function* performTest() {
|
||||
}
|
||||
}
|
||||
|
||||
let TEST_DATA = [{
|
||||
let TEST_DATA = synthesizeProfileForTest([{
|
||||
frames: [{
|
||||
location: "A (http://path/to/file.js:10:5"
|
||||
}, {
|
||||
location: "B (http://path/to/file.js:100:5"
|
||||
}],
|
||||
time: 50,
|
||||
}];
|
||||
}]);
|
||||
|
||||
let EXPECTED_OUTPUT = [{
|
||||
blocks: []
|
||||
@ -74,7 +74,17 @@ let EXPECTED_OUTPUT = [{
|
||||
text: "A (file.js:10)"
|
||||
}]
|
||||
}, {
|
||||
blocks: []
|
||||
blocks: [{
|
||||
srcData: {
|
||||
startTime: 0,
|
||||
rawLocation: "B (http://path/to/file.js:100:5)"
|
||||
},
|
||||
x: 0,
|
||||
y: FLAME_GRAPH_BLOCK_HEIGHT,
|
||||
width: 50,
|
||||
height: FLAME_GRAPH_BLOCK_HEIGHT,
|
||||
text: "B (file.js:100)"
|
||||
}]
|
||||
}, {
|
||||
blocks: []
|
||||
}, {
|
||||
|
@ -244,6 +244,24 @@ function* openAndCloseToolbox(nbOfTimes, usageTime, toolId) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a profile for testing.
|
||||
*/
|
||||
function synthesizeProfileForTest(samples) {
|
||||
const { RecordingUtils } = devtools.require("devtools/performance/recording-utils");
|
||||
|
||||
samples.unshift({
|
||||
time: 0,
|
||||
frames: []
|
||||
});
|
||||
|
||||
let uniqueStacks = new RecordingUtils.UniqueStacks();
|
||||
return RecordingUtils.deflateThread({
|
||||
samples: samples,
|
||||
markers: []
|
||||
}, uniqueStacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until a predicate returns true.
|
||||
*
|
||||
|
@ -713,3 +713,34 @@ function reload(tabClient) {
|
||||
tabClient._reload({}, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of stack location strings given a thread and a sample.
|
||||
*
|
||||
* @param object thread
|
||||
* @param object sample
|
||||
* @returns object
|
||||
*/
|
||||
function getInflatedStackLocations(thread, sample) {
|
||||
let stackTable = thread.stackTable;
|
||||
let frameTable = thread.frameTable;
|
||||
let stringTable = thread.stringTable;
|
||||
let SAMPLE_STACK_SLOT = thread.samples.schema.stack;
|
||||
let STACK_PREFIX_SLOT = stackTable.schema.prefix;
|
||||
let STACK_FRAME_SLOT = stackTable.schema.frame;
|
||||
let FRAME_LOCATION_SLOT = frameTable.schema.location;
|
||||
|
||||
// Build the stack from the raw data and accumulate the locations in
|
||||
// an array.
|
||||
let stackIndex = sample[SAMPLE_STACK_SLOT];
|
||||
let locations = [];
|
||||
while (stackIndex !== null) {
|
||||
let stackEntry = stackTable.data[stackIndex];
|
||||
let frame = frameTable.data[stackEntry[STACK_FRAME_SLOT]];
|
||||
locations.push(stringTable[frame[FRAME_LOCATION_SLOT]]);
|
||||
stackIndex = stackEntry[STACK_PREFIX_SLOT];
|
||||
}
|
||||
|
||||
// The profiler tree is inverted, so reverse the array.
|
||||
return locations.reverse();
|
||||
}
|
||||
|
@ -94,11 +94,11 @@ function test_data(client, actor, startTime, callback)
|
||||
// Now check the samples. At least one sample is expected to
|
||||
// have been in the busy wait above.
|
||||
let loc = stack.name + " (" + stack.filename + ":" + funcLine + ")";
|
||||
|
||||
do_check_true(response.profile.threads[0].samples.some(sample => {
|
||||
return typeof sample.frames == "object" &&
|
||||
sample.frames.length != 0 &&
|
||||
sample.frames.some(f => (f.location == loc));
|
||||
let thread0 = response.profile.threads[0];
|
||||
do_check_true(thread0.samples.data.some(sample => {
|
||||
let frames = getInflatedStackLocations(thread0, sample);
|
||||
return frames.length != 0 &&
|
||||
frames.some(location => (location == loc));
|
||||
}));
|
||||
|
||||
callback();
|
||||
|
Loading…
Reference in New Issue
Block a user