diff options
Diffstat (limited to 'devtools/client/performance/test/unit/test_jit-graph-data.js')
-rw-r--r-- | devtools/client/performance/test/unit/test_jit-graph-data.js | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/devtools/client/performance/test/unit/test_jit-graph-data.js b/devtools/client/performance/test/unit/test_jit-graph-data.js new file mode 100644 index 000000000..b298f4bcc --- /dev/null +++ b/devtools/client/performance/test/unit/test_jit-graph-data.js @@ -0,0 +1,209 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Unit test for `createTierGraphDataFromFrameNode` function. + */ + +function run_test() { + run_next_test(); +} + +const SAMPLE_COUNT = 1000; +const RESOLUTION = 50; +const TIME_PER_SAMPLE = 5; + +// Offset needed since ThreadNode requires the first sample to be strictly +// greater than its start time. This lets us still have pretty numbers +// in this test to keep it (more) simple, which it sorely needs. +const TIME_OFFSET = 5; + +add_task(function test() { + let { ThreadNode } = require("devtools/client/performance/modules/logic/tree-model"); + let { createTierGraphDataFromFrameNode } = require("devtools/client/performance/modules/logic/jit"); + + // Select the second half of the set of samples + let startTime = (SAMPLE_COUNT / 2 * TIME_PER_SAMPLE) - TIME_OFFSET; + let endTime = (SAMPLE_COUNT * TIME_PER_SAMPLE) - TIME_OFFSET; + let invertTree = true; + + let root = new ThreadNode(gThread, { invertTree, startTime, endTime }); + + equal(root.samples, SAMPLE_COUNT / 2, + "root has correct amount of samples"); + equal(root.sampleTimes.length, SAMPLE_COUNT / 2, + "root has correct amount of sample times"); + // Add time offset since the first sample begins TIME_OFFSET after startTime + equal(root.sampleTimes[0], startTime + TIME_OFFSET, + "root recorded first sample time in scope"); + equal(root.sampleTimes[root.sampleTimes.length - 1], endTime, + "root recorded last sample time in scope"); + + let frame = getFrameNodePath(root, "X"); + let data = createTierGraphDataFromFrameNode(frame, root.sampleTimes, + (endTime - startTime) / RESOLUTION); + + let TIME_PER_WINDOW = SAMPLE_COUNT / 2 / RESOLUTION * TIME_PER_SAMPLE; + + // Filter out the dupes created with the same delta so the graph + // can render correctly. + let filteredData = []; + for (let i = 0; i < data.length; i++) { + if (!i || data[i].delta !== data[i - 1].delta) { + filteredData.push(data[i]); + } + } + data = filteredData; + + for (let i = 0; i < 11; i++) { + equal(data[i].delta, startTime + TIME_OFFSET + (TIME_PER_WINDOW * i), + "first window has correct x"); + equal(data[i].values[0], 0.2, "first window has 2 frames in interpreter"); + equal(data[i].values[1], 0.2, "first window has 2 frames in baseline"); + equal(data[i].values[2], 0.2, "first window has 2 frames in ion"); + } + // Start on 11, since i===10 is where the values change, and the new value (0,0,0) + // is removed in `filteredData` + for (let i = 11; i < 20; i++) { + equal(data[i].delta, startTime + TIME_OFFSET + (TIME_PER_WINDOW * i), + "second window has correct x"); + equal(data[i].values[0], 0, "second window observed no optimizations"); + equal(data[i].values[1], 0, "second window observed no optimizations"); + equal(data[i].values[2], 0, "second window observed no optimizations"); + } + // Start on 21, since i===20 is where the values change, and the new value (0.3,0,0) + // is removed in `filteredData` + for (let i = 21; i < 30; i++) { + equal(data[i].delta, startTime + TIME_OFFSET + (TIME_PER_WINDOW * i), + "third window has correct x"); + equal(data[i].values[0], 0.3, "third window has 3 frames in interpreter"); + equal(data[i].values[1], 0, "third window has 0 frames in baseline"); + equal(data[i].values[2], 0, "third window has 0 frames in ion"); + } +}); + +var gUniqueStacks = new RecordingUtils.UniqueStacks(); + +function uniqStr(s) { + return gUniqueStacks.getOrAddStringIndex(s); +} + +const TIER_PATTERNS = [ + // 0-99 + ["X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0"], + // 100-199 + ["X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0"], + // 200-299 + ["X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0"], + // 300-399 + ["X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0"], + // 400-499 + ["X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0"], + + // 500-599 + // Test current frames in all opts + ["A", "A", "A", "A", "X_1", "X_2", "X_1", "X_2", "X_0", "X_0"], + + // 600-699 + // Nothing for current frame + ["A", "B", "A", "B", "A", "B", "A", "B", "A", "B"], + + // 700-799 + // A few frames where the frame is not the leaf node + ["X_2 -> Y", "X_2 -> Y", "X_2 -> Y", "X_0", "X_0", "X_0", "A", "A", "A", "A"], + + // 800-899 + ["X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0"], + // 900-999 + ["X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0", "X_0"], +]; + +function createSample(i, frames) { + let sample = {}; + sample.time = i * TIME_PER_SAMPLE; + sample.frames = [{ location: "(root)" }]; + if (i === 0) { + return sample; + } + if (frames) { + frames.split(" -> ").forEach(frame => sample.frames.push({ location: frame })); + } + return sample; +} + +var SAMPLES = (function () { + let samples = []; + + for (let i = 0; i < SAMPLE_COUNT;) { + let pattern = TIER_PATTERNS[Math.floor(i / 100)]; + for (let j = 0; j < pattern.length; j++) { + samples.push(createSample(i + j, pattern[j])); + } + i += 10; + } + + return samples; +})(); + +var gThread = RecordingUtils.deflateThread({ samples: SAMPLES, markers: [] }, + gUniqueStacks); + +var gRawSite1 = { + line: 12, + column: 2, + types: [{ + mirType: uniqStr("Object"), + site: uniqStr("B (http://foo/bar:10)"), + typeset: [{ + keyedBy: uniqStr("constructor"), + name: uniqStr("Foo"), + location: uniqStr("B (http://foo/bar:10)") + }, { + 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")] + ] + } +}; + +function serialize(x) { + return JSON.parse(JSON.stringify(x)); +} + +gThread.frameTable.data.forEach((frame) => { + const LOCATION_SLOT = gThread.frameTable.schema.location; + const OPTIMIZATIONS_SLOT = gThread.frameTable.schema.optimizations; + const IMPLEMENTATION_SLOT = gThread.frameTable.schema.implementation; + + let l = gThread.stringTable[frame[LOCATION_SLOT]]; + switch (l) { + // Rename some of the location sites so we can register different + // frames with different opt sites + case "X_0": + frame[LOCATION_SLOT] = uniqStr("X"); + frame[OPTIMIZATIONS_SLOT] = serialize(gRawSite1); + frame[IMPLEMENTATION_SLOT] = null; + break; + case "X_1": + frame[LOCATION_SLOT] = uniqStr("X"); + frame[OPTIMIZATIONS_SLOT] = serialize(gRawSite1); + frame[IMPLEMENTATION_SLOT] = uniqStr("baseline"); + break; + case "X_2": + frame[LOCATION_SLOT] = uniqStr("X"); + frame[OPTIMIZATIONS_SLOT] = serialize(gRawSite1); + frame[IMPLEMENTATION_SLOT] = uniqStr("ion"); + break; + } +}); |