summaryrefslogtreecommitdiffstats
path: root/devtools/client/performance/test/unit/test_jit-graph-data.js
diff options
context:
space:
mode:
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.js209
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;
+ }
+});