зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1194458 - Calculate bytesize costs per frame for allocations in the performance tools. r=vp,shu
--HG-- rename : browser/devtools/performance/test/browser_perf-allocations-to-samples.js => browser/devtools/performance/test/unit/test_perf-utils-allocations-to-samples.js
This commit is contained in:
Родитель
5de13ebb49
Коммит
935672ee36
|
@ -488,13 +488,17 @@ function getFrameInfo (node, options) {
|
|||
gFrameData.set(node, data);
|
||||
}
|
||||
|
||||
// If no options specified, we can't calculate relative values, abort here
|
||||
if (!options) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// If a root specified, calculate the relative costs in the context of
|
||||
// this call tree. The cached store may already have this, but generate
|
||||
// if it does not.
|
||||
let totalSamples = options.root.samples;
|
||||
let totalDuration = options.root.duration;
|
||||
if (options && options.root && !data.COSTS_CALCULATED) {
|
||||
let totalSamples = options.root.samples;
|
||||
let totalDuration = options.root.duration;
|
||||
|
||||
data.selfDuration = node.youngestFrameSamples / totalSamples * totalDuration;
|
||||
data.selfPercentage = node.youngestFrameSamples / totalSamples * 100;
|
||||
data.totalDuration = node.samples / totalSamples * totalDuration;
|
||||
|
@ -503,8 +507,15 @@ function getFrameInfo (node, options) {
|
|||
}
|
||||
|
||||
if (options && options.allocations && !data.ALLOCATION_DATA_CALCULATED) {
|
||||
let totalBytes = options.root.byteSize;
|
||||
data.selfCount = node.youngestFrameSamples;
|
||||
data.totalCount = node.samples;
|
||||
data.selfCountPercentage = node.youngestFrameSamples / totalSamples * 100;
|
||||
data.totalCountPercentage = node.samples / totalSamples * 100;
|
||||
data.selfSize = node.youngestFrameByteSize;
|
||||
data.totalSize = node.byteSize;
|
||||
data.selfSizePercentage = node.youngestFrameByteSize / totalBytes * 100;
|
||||
data.totalSizePercentage = node.byteSize / totalBytes * 100;
|
||||
data.ALLOCATION_DATA_CALCULATED = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,9 @@ function ThreadNode(thread, options = {}) {
|
|||
this.calls = [];
|
||||
this.duration = options.endTime - options.startTime;
|
||||
this.nodeType = "Thread";
|
||||
// Total bytesize of all allocations if enabled
|
||||
this.byteSize = 0;
|
||||
this.youngestFrameByteSize = 0;
|
||||
|
||||
let { samples, stackTable, frameTable, stringTable } = thread;
|
||||
|
||||
|
@ -108,6 +111,7 @@ ThreadNode.prototype = {
|
|||
|
||||
const SAMPLE_STACK_SLOT = samples.schema.stack;
|
||||
const SAMPLE_TIME_SLOT = samples.schema.time;
|
||||
const SAMPLE_BYTESIZE_SLOT = samples.schema.size;
|
||||
|
||||
const STACK_PREFIX_SLOT = stackTable.schema.prefix;
|
||||
const STACK_FRAME_SLOT = stackTable.schema.frame;
|
||||
|
@ -134,10 +138,15 @@ ThreadNode.prototype = {
|
|||
isMetaCategoryOut: false
|
||||
};
|
||||
|
||||
let byteSize = 0;
|
||||
for (let i = 0; i < samplesData.length; i++) {
|
||||
let sample = samplesData[i];
|
||||
let sampleTime = sample[SAMPLE_TIME_SLOT];
|
||||
|
||||
if (SAMPLE_BYTESIZE_SLOT !== void 0) {
|
||||
byteSize = sample[SAMPLE_BYTESIZE_SLOT];
|
||||
}
|
||||
|
||||
// A sample's end time is considered to be its time of sampling. Its
|
||||
// start time is the sampling time of the previous sample.
|
||||
//
|
||||
|
@ -227,11 +236,18 @@ ThreadNode.prototype = {
|
|||
frameNode._addOptimizations(inflatedFrame.optimizations, inflatedFrame.implementation,
|
||||
sampleTime, stringTable);
|
||||
}
|
||||
|
||||
if (byteSize) {
|
||||
frameNode.youngestFrameByteSize += byteSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't overcount flattened recursive frames.
|
||||
if (!shouldFlatten) {
|
||||
frameNode.samples++;
|
||||
if (byteSize) {
|
||||
frameNode.byteSize += byteSize;
|
||||
}
|
||||
}
|
||||
|
||||
prevFrameKey = frameKey;
|
||||
|
@ -241,6 +257,9 @@ ThreadNode.prototype = {
|
|||
|
||||
this.samples++;
|
||||
this.sampleTimes.push(sampleTime);
|
||||
if (byteSize) {
|
||||
this.byteSize += byteSize;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -248,18 +267,18 @@ ThreadNode.prototype = {
|
|||
* Uninverts the call tree after its having been built.
|
||||
*/
|
||||
_uninvert: function uninvert() {
|
||||
function mergeOrAddFrameNode(calls, node, samples) {
|
||||
function mergeOrAddFrameNode(calls, node, samples, size) {
|
||||
// Unlike the inverted call tree, we don't use a root table for the top
|
||||
// level, as in general, there are many fewer entry points than
|
||||
// leaves. Instead, linear search is used regardless of level.
|
||||
for (let i = 0; i < calls.length; i++) {
|
||||
if (calls[i].key === node.key) {
|
||||
let foundNode = calls[i];
|
||||
foundNode._merge(node, samples);
|
||||
foundNode._merge(node, samples, size);
|
||||
return foundNode.calls;
|
||||
}
|
||||
}
|
||||
let copy = node._clone(samples);
|
||||
let copy = node._clone(samples, size);
|
||||
calls.push(copy);
|
||||
return copy.calls;
|
||||
}
|
||||
|
@ -278,11 +297,13 @@ ThreadNode.prototype = {
|
|||
let node = entry.node;
|
||||
let calls = node.calls;
|
||||
let callSamples = 0;
|
||||
let callByteSize = 0;
|
||||
|
||||
// Continue the depth-first walk.
|
||||
for (let i = 0; i < calls.length; i++) {
|
||||
workstack.push({ node: calls[i], level: entry.level + 1 });
|
||||
callSamples += calls[i].samples;
|
||||
callByteSize += calls[i].byteSize;
|
||||
}
|
||||
|
||||
// The sample delta is used to distinguish stacks.
|
||||
|
@ -307,12 +328,13 @@ ThreadNode.prototype = {
|
|||
// Note that bottoming out is a degenerate where callSamples = 0.
|
||||
|
||||
let samplesDelta = node.samples - callSamples;
|
||||
let byteSizeDelta = node.byteSize - callByteSize;
|
||||
if (samplesDelta > 0) {
|
||||
// Reverse the spine and add them to the uninverted call tree.
|
||||
let uninvertedCalls = rootCalls;
|
||||
for (let level = entry.level; level > 0; level--) {
|
||||
let callee = spine[level];
|
||||
uninvertedCalls = mergeOrAddFrameNode(uninvertedCalls, callee.node, samplesDelta);
|
||||
uninvertedCalls = mergeOrAddFrameNode(uninvertedCalls, callee.node, samplesDelta, byteSizeDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -393,6 +415,8 @@ function FrameNode(frameKey, { location, line, category, isContent }, isMetaCate
|
|||
this.isMetaCategory = !!isMetaCategory;
|
||||
this.category = category;
|
||||
this.nodeType = "Frame";
|
||||
this.byteSize = 0;
|
||||
this.youngestFrameByteSize = 0;
|
||||
}
|
||||
|
||||
FrameNode.prototype = {
|
||||
|
@ -429,22 +453,27 @@ FrameNode.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_clone: function (samples) {
|
||||
_clone: function (samples, size) {
|
||||
let newNode = new FrameNode(this.key, this, this.isMetaCategory);
|
||||
newNode._merge(this, samples);
|
||||
newNode._merge(this, samples, size);
|
||||
return newNode;
|
||||
},
|
||||
|
||||
_merge: function (otherNode, samples) {
|
||||
_merge: function (otherNode, samples, size) {
|
||||
if (this === otherNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.samples += samples;
|
||||
this.byteSize += size;
|
||||
if (otherNode.youngestFrameSamples > 0) {
|
||||
this.youngestFrameSamples += samples;
|
||||
}
|
||||
|
||||
if (otherNode.youngestFrameByteSize > 0) {
|
||||
this.youngestFrameByteSize += otherNode.youngestFrameByteSize;
|
||||
}
|
||||
|
||||
if (otherNode._optimizations) {
|
||||
let opts = this._optimizations;
|
||||
if (opts === null) {
|
||||
|
|
|
@ -12,7 +12,6 @@ support-files =
|
|||
# that need to be moved over to performance tool
|
||||
|
||||
[browser_aaa-run-first-leaktest.js]
|
||||
[browser_perf-allocations-to-samples.js]
|
||||
[browser_perf-categories-js-calltree.js]
|
||||
[browser_perf-clear-01.js]
|
||||
[browser_perf-clear-02.js]
|
||||
|
|
|
@ -12,7 +12,7 @@ function* spawnTest() {
|
|||
Services.prefs.setBoolPref(ALLOCATIONS_PREF, true);
|
||||
|
||||
yield startRecording(panel);
|
||||
yield waitUntil(() => PerformanceController.getCurrentRecording().getAllocations().timestamps.length);
|
||||
yield waitUntil(() => PerformanceController.getCurrentRecording().getAllocations().sizes.length);
|
||||
yield stopRecording(panel);
|
||||
|
||||
let rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);
|
||||
|
|
|
@ -55,6 +55,7 @@ function *testMockMemory () {
|
|||
isEmptyArray(allocations.sites, "allocations.sites");
|
||||
isEmptyArray(allocations.timestamps, "allocations.timestamps");
|
||||
isEmptyArray(allocations.frames, "allocations.frames");
|
||||
isEmptyArray(allocations.sizes, "allocations.sizes");
|
||||
|
||||
is(isVisible($("#overview-pane")), true,
|
||||
"overview pane not hidden when server not supporting memory actors, yet UI prefs request them.");
|
||||
|
@ -110,6 +111,7 @@ function *testMockMemoryAndTimeline() {
|
|||
isEmptyArray(allocations.sites, "allocations.sites");
|
||||
isEmptyArray(allocations.timestamps, "allocations.timestamps");
|
||||
isEmptyArray(allocations.frames, "allocations.frames");
|
||||
isEmptyArray(allocations.sizes, "allocations.sizes");
|
||||
|
||||
is(isVisible($("#overview-pane")), false,
|
||||
"overview pane hidden when server not supporting memory/timeline actors, yet UI prefs request them.");
|
||||
|
|
|
@ -41,6 +41,7 @@ let test = Task.async(function*() {
|
|||
isEmptyArray(allocations.sites, "allocations.sites");
|
||||
isEmptyArray(allocations.timestamps, "allocations.timestamps");
|
||||
isEmptyArray(allocations.frames, "allocations.frames");
|
||||
isEmptyArray(allocations.sizes, "allocations.sizes");
|
||||
|
||||
let sampleCount = 0;
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ let test = Task.async(function*() {
|
|||
isEmptyArray(allocations.sites, "allocations.sites");
|
||||
isEmptyArray(allocations.timestamps, "allocations.timestamps");
|
||||
isEmptyArray(allocations.frames, "allocations.frames");
|
||||
isEmptyArray(allocations.sizes, "allocations.sizes");
|
||||
|
||||
let sampleCount = 0;
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ let test = Task.async(function*() {
|
|||
memory: [].toSource(),
|
||||
ticks: TICKS_DATA.toSource(),
|
||||
profile: RecordingUtils.deflateProfile(JSON.parse(JSON.stringify(PROFILER_DATA))).toSource(),
|
||||
allocations: ({sites:[], timestamps:[], frames:[]}).toSource(),
|
||||
allocations: ({sites:[], timestamps:[], frames:[], sizes: []}).toSource(),
|
||||
withTicks: true,
|
||||
withMemory: false,
|
||||
sampleFrequency: void 0
|
||||
|
|
|
@ -2,21 +2,25 @@
|
|||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if allocations data received from the memory actor is properly
|
||||
* Tests if allocations data received from the performance actor is properly
|
||||
* converted to something that follows the same structure as the samples data
|
||||
* received from the profiler.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let output = RecordingUtils.getProfileThreadFromAllocations(TEST_DATA);
|
||||
is(output.toSource(), EXPECTED_OUTPUT.toSource(), "The output is correct.");
|
||||
|
||||
finish();
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function () {
|
||||
const { getProfileThreadFromAllocations } = require("devtools/toolkit/performance/utils");
|
||||
let output = getProfileThreadFromAllocations(TEST_DATA);
|
||||
equal(output.toSource(), EXPECTED_OUTPUT.toSource(), "The output is correct.");
|
||||
});
|
||||
|
||||
let TEST_DATA = {
|
||||
sites: [0, 0, 1, 2, 3],
|
||||
timestamps: [50, 100, 150, 200, 250],
|
||||
sizes: [0, 0, 100, 200, 300],
|
||||
frames: [
|
||||
null, {
|
||||
source: "A",
|
||||
|
@ -46,11 +50,12 @@ let EXPECTED_OUTPUT = {
|
|||
"schema": {
|
||||
"stack": 0,
|
||||
"time": 1,
|
||||
"size": 2,
|
||||
},
|
||||
data: [
|
||||
[ 1, 150 ],
|
||||
[ 2, 200 ],
|
||||
[ 3, 250 ]
|
||||
[ 1, 150, 100 ],
|
||||
[ 2, 200, 200 ],
|
||||
[ 3, 250, 300 ]
|
||||
]
|
||||
},
|
||||
stackTable: {
|
|
@ -0,0 +1,93 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the tree model calculates correct costs/percentages for
|
||||
* allocation frame nodes.
|
||||
*/
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function () {
|
||||
let { ThreadNode } = require("devtools/performance/tree-model");
|
||||
const { getProfileThreadFromAllocations } = require("devtools/toolkit/performance/utils");
|
||||
let allocationData = getProfileThreadFromAllocations(TEST_DATA);
|
||||
let thread = new ThreadNode(allocationData, { startTime: 0, endTime: 1000 });
|
||||
|
||||
/**
|
||||
* Values are in order according to:
|
||||
* +-------------+------------+-------------+-------------+------------------------------+
|
||||
* | Self Bytes | Self Count | Total Bytes | Total Count | Function |
|
||||
* +-------------+------------+-------------+-------------+------------------------------+
|
||||
* | 1790272 41% | 8307 17% | 1790372 42% | 8317 18% | V someFunc @ a.j:345:6 |
|
||||
* | 100 1% | 10 1% | 100 1% | 10 1% | > callerFunc @ b.j:765:34 |
|
||||
* +-------------+------------+-------------+-------------+------------------------------+
|
||||
*/
|
||||
[
|
||||
[100, 10, 1, 33, 1000, 100, 3, 100, "x (A:1:2)", [
|
||||
[200, 20, 1, 33, 900, 90, 2, 66, "y (B:3:4)", [
|
||||
[700, 70, 1, 33, 700, 70, 1, 33, "z (C:5:6)"]
|
||||
]]
|
||||
]]
|
||||
].forEach(compareFrameInfo(thread));
|
||||
});
|
||||
|
||||
function compareFrameInfo (root, parent) {
|
||||
parent = parent || root;
|
||||
let fields = [
|
||||
"selfSize", "selfSizePercentage", "selfCount", "selfCountPercentage",
|
||||
"totalSize", "totalSizePercentage", "totalCount", "totalCountPercentage"
|
||||
];
|
||||
return function (def) {
|
||||
let children;
|
||||
if (Array.isArray(def[def.length - 1])) {
|
||||
children = def.pop();
|
||||
}
|
||||
let name = def.pop();
|
||||
let expected = def;
|
||||
|
||||
let node = getFrameNodePath(parent, name);
|
||||
let data = node.getInfo({ root, allocations: true });
|
||||
|
||||
fields.forEach((field, i) => {
|
||||
let actual = data[field];
|
||||
if (/percentage/i.test(field)) {
|
||||
actual = Number.parseInt(actual, 10);
|
||||
}
|
||||
equal(actual, expected[i], `${name} has correct ${field}: ${expected[i]}`);
|
||||
});
|
||||
|
||||
if (children) {
|
||||
children.forEach(compareFrameInfo(root, node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let TEST_DATA = {
|
||||
sites: [1, 2, 3],
|
||||
timestamps: [150, 200, 250],
|
||||
sizes: [100, 200, 700],
|
||||
frames: [
|
||||
null, {
|
||||
source: "A",
|
||||
line: 1,
|
||||
column: 2,
|
||||
functionDisplayName: "x",
|
||||
parent: 0
|
||||
}, {
|
||||
source: "B",
|
||||
line: 3,
|
||||
column: 4,
|
||||
functionDisplayName: "y",
|
||||
parent: 1
|
||||
}, {
|
||||
source: "C",
|
||||
line: 5,
|
||||
column: 6,
|
||||
functionDisplayName: "z",
|
||||
parent: 2
|
||||
}
|
||||
]
|
||||
};
|
|
@ -0,0 +1,101 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the tree model calculates correct costs/percentages for
|
||||
* allocation frame nodes. Inverted version of test_tree-model-allocations-01.js
|
||||
*/
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function () {
|
||||
let { ThreadNode } = require("devtools/performance/tree-model");
|
||||
const { getProfileThreadFromAllocations } = require("devtools/toolkit/performance/utils");
|
||||
let allocationData = getProfileThreadFromAllocations(TEST_DATA);
|
||||
let thread = new ThreadNode(allocationData, { invertTree: true, startTime: 0, endTime: 1000 });
|
||||
|
||||
/**
|
||||
* Values are in order according to:
|
||||
* +-------------+------------+-------------+-------------+------------------------------+
|
||||
* | Self Bytes | Self Count | Total Bytes | Total Count | Function |
|
||||
* +-------------+------------+-------------+-------------+------------------------------+
|
||||
* | 1790272 41% | 8307 17% | 1790372 42% | 8317 18% | V someFunc @ a.j:345:6 |
|
||||
* | 100 1% | 10 1% | 100 1% | 10 1% | > callerFunc @ b.j:765:34 |
|
||||
* +-------------+------------+-------------+-------------+------------------------------+
|
||||
*/
|
||||
[
|
||||
[700, 70, 1, 33, 700, 70, 1, 33, "z (C:5:6)", [
|
||||
[0, 0, 0, 0, 700, 70, 1, 33, "y (B:3:4)", [
|
||||
[0, 0, 0, 0, 700, 70, 1, 33, "x (A:1:2)"]
|
||||
]]
|
||||
]],
|
||||
[200, 20, 1, 33, 200, 20, 1, 33, "y (B:3:4)", [
|
||||
[0, 0, 0, 0, 200, 20, 1, 33, "x (A:1:2)"]
|
||||
]],
|
||||
[100, 10, 1, 33, 100, 10, 1, 33, "x (A:1:2)"]
|
||||
].forEach(compareFrameInfo(thread));
|
||||
});
|
||||
|
||||
function compareFrameInfo (root, parent) {
|
||||
parent = parent || root;
|
||||
let fields = [
|
||||
"selfSize", "selfSizePercentage", "selfCount", "selfCountPercentage",
|
||||
"totalSize", "totalSizePercentage", "totalCount", "totalCountPercentage"
|
||||
];
|
||||
|
||||
return function (def) {
|
||||
let children;
|
||||
|
||||
if (Array.isArray(def[def.length - 1])) {
|
||||
children = def.pop();
|
||||
}
|
||||
|
||||
let name = def.pop();
|
||||
let expected = def;
|
||||
|
||||
let node = getFrameNodePath(parent, name);
|
||||
let data = node.getInfo({ root, allocations: true });
|
||||
|
||||
fields.forEach((field, i) => {
|
||||
let actual = data[field];
|
||||
if (/percentage/i.test(field)) {
|
||||
actual = Number.parseInt(actual, 10);
|
||||
}
|
||||
equal(actual, expected[i], `${name} has correct ${field}: ${expected[i]}`);
|
||||
});
|
||||
|
||||
if (children) {
|
||||
children.forEach(compareFrameInfo(root, node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let TEST_DATA = {
|
||||
sites: [0, 1, 2, 3],
|
||||
timestamps: [0, 150, 200, 250],
|
||||
sizes: [0, 100, 200, 700],
|
||||
frames: [{
|
||||
source: "(root)"
|
||||
}, {
|
||||
source: "A",
|
||||
line: 1,
|
||||
column: 2,
|
||||
functionDisplayName: "x",
|
||||
parent: 0
|
||||
}, {
|
||||
source: "B",
|
||||
line: 3,
|
||||
column: 4,
|
||||
functionDisplayName: "y",
|
||||
parent: 1
|
||||
}, {
|
||||
source: "C",
|
||||
line: 5,
|
||||
column: 6,
|
||||
functionDisplayName: "z",
|
||||
parent: 2
|
||||
}
|
||||
]
|
||||
};
|
|
@ -13,6 +13,7 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
|
|||
[test_jit-graph-data.js]
|
||||
[test_jit-model-01.js]
|
||||
[test_jit-model-02.js]
|
||||
[test_perf-utils-allocations-to-samples.js]
|
||||
[test_tree-model-01.js]
|
||||
[test_tree-model-02.js]
|
||||
[test_tree-model-03.js]
|
||||
|
@ -26,6 +27,8 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
|
|||
[test_tree-model-11.js]
|
||||
[test_tree-model-12.js]
|
||||
[test_tree-model-13.js]
|
||||
[test_tree-model-allocations-01.js]
|
||||
[test_tree-model-allocations-02.js]
|
||||
[test_waterfall-utils-collapse-01.js]
|
||||
[test_waterfall-utils-collapse-02.js]
|
||||
[test_waterfall-utils-collapse-03.js]
|
||||
|
|
|
@ -144,7 +144,7 @@ function convertLegacyData (legacyData) {
|
|||
frames: [],
|
||||
memory: [],
|
||||
ticks: ticksData,
|
||||
allocations: { sites: [], timestamps: [], frames: [] },
|
||||
allocations: { sites: [], timestamps: [], frames: [], sizes: [] },
|
||||
profile: profilerData.profile,
|
||||
// Fake a configuration object here if there's tick data,
|
||||
// so that it can be rendered
|
||||
|
|
|
@ -116,7 +116,7 @@ LegacyPerformanceRecording.prototype = {
|
|||
this._frames = [];
|
||||
this._memory = [];
|
||||
this._ticks = [];
|
||||
this._allocations = { sites: [], timestamps: [], frames: [] };
|
||||
this._allocations = { sites: [], timestamps: [], frames: [], sizes: [] };
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell.ini']
|
||||
|
||||
EXTRA_JS_MODULES.devtools.performance += [
|
||||
'io.js',
|
||||
'process-communication.js',
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
let { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
|
@ -0,0 +1,93 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if allocations data received from the performance actor is properly
|
||||
* converted to something that follows the same structure as the samples data
|
||||
* received from the profiler.
|
||||
*/
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function () {
|
||||
const { getProfileThreadFromAllocations } = require("devtools/toolkit/performance/utils");
|
||||
let output = getProfileThreadFromAllocations(TEST_DATA);
|
||||
equal(output.toSource(), EXPECTED_OUTPUT.toSource(), "The output is correct.");
|
||||
});
|
||||
|
||||
let TEST_DATA = {
|
||||
sites: [0, 0, 1, 2, 3],
|
||||
timestamps: [50, 100, 150, 200, 250],
|
||||
sizes: [0, 0, 100, 200, 300],
|
||||
frames: [
|
||||
null, {
|
||||
source: "A",
|
||||
line: 1,
|
||||
column: 2,
|
||||
functionDisplayName: "x",
|
||||
parent: 0
|
||||
}, {
|
||||
source: "B",
|
||||
line: 3,
|
||||
column: 4,
|
||||
functionDisplayName: "y",
|
||||
parent: 1
|
||||
}, {
|
||||
source: "C",
|
||||
line: 5,
|
||||
column: 6,
|
||||
functionDisplayName: null,
|
||||
parent: 2
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
let EXPECTED_OUTPUT = {
|
||||
name: "allocations",
|
||||
samples: {
|
||||
"schema": {
|
||||
"stack": 0,
|
||||
"time": 1,
|
||||
"size": 2,
|
||||
},
|
||||
data: [
|
||||
[ 1, 150, 100 ],
|
||||
[ 2, 200, 200 ],
|
||||
[ 3, 250, 300 ]
|
||||
]
|
||||
},
|
||||
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"
|
||||
],
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
head = head.js
|
||||
tail =
|
||||
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||
|
||||
[test_perf-utils-allocations-to-samples.js]
|
|
@ -157,7 +157,7 @@ let gProfileThreadFromAllocationCache = new WeakMap();
|
|||
* @see MemoryActor.prototype.getAllocations for more information.
|
||||
*
|
||||
* @param object allocations
|
||||
* A list of { sites, timestamps, frames, counts } arrays.
|
||||
* A list of { sites, timestamps, frames, sizes } arrays.
|
||||
* @return object
|
||||
* The "profile" describing the allocations log.
|
||||
*/
|
||||
|
@ -167,7 +167,7 @@ function getProfileThreadFromAllocations(allocations) {
|
|||
return cached;
|
||||
}
|
||||
|
||||
let { sites, timestamps, frames } = allocations;
|
||||
let { sites, timestamps, frames, sizes } = allocations;
|
||||
let uniqueStrings = new UniqueStrings();
|
||||
|
||||
// Convert allocation frames to the the stack and frame tables expected by
|
||||
|
@ -239,14 +239,14 @@ function getProfileThreadFromAllocations(allocations) {
|
|||
let writePos = 0;
|
||||
for (let i = 0; i < sites.length; i++) {
|
||||
// Schema:
|
||||
// [stack, time]
|
||||
// [stack, time, size]
|
||||
//
|
||||
// Originally, sites[i] indexes into the frames array. Note that in the
|
||||
// loop above, stackTable[sites[i]] and frames[sites[i]] index the same
|
||||
// information.
|
||||
let stackIndex = sites[i];
|
||||
if (frames[stackIndex]) {
|
||||
samples[writePos++] = [stackIndex, timestamps[i]];
|
||||
samples[writePos++] = [stackIndex, timestamps[i], sizes[i]];
|
||||
}
|
||||
}
|
||||
samples.length = writePos;
|
||||
|
@ -269,6 +269,7 @@ function allocationsWithSchema (data) {
|
|||
schema: {
|
||||
stack: slot++,
|
||||
time: slot++,
|
||||
size: slot++,
|
||||
},
|
||||
data: data
|
||||
};
|
||||
|
|
|
@ -163,7 +163,7 @@ let PerformanceRecordingActor = exports.PerformanceRecordingActor = protocol.Act
|
|||
this._frames = [];
|
||||
this._memory = [];
|
||||
this._ticks = [];
|
||||
this._allocations = { sites: [], timestamps: [], frames: [], counts: [] };
|
||||
this._allocations = { sites: [], timestamps: [], frames: [], sizes: [] };
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -252,7 +252,7 @@ let PerformanceRecordingFront = exports.PerformanceRecordingFront = protocol.Fro
|
|||
this._frames = [];
|
||||
this._memory = [];
|
||||
this._ticks = [];
|
||||
this._allocations = { sites: [], timestamps: [], frames: [], counts: [] };
|
||||
this._allocations = { sites: [], timestamps: [], frames: [], sizes: [] };
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
|
@ -344,6 +344,7 @@ let PerformanceRecordingFront = exports.PerformanceRecordingFront = protocol.Fro
|
|||
let {
|
||||
allocations: sites,
|
||||
allocationsTimestamps: timestamps,
|
||||
allocationSizes: sizes,
|
||||
frames,
|
||||
} = data;
|
||||
|
||||
|
@ -351,6 +352,7 @@ let PerformanceRecordingFront = exports.PerformanceRecordingFront = protocol.Fro
|
|||
RecordingUtils.pushAll(this._allocations.sites, sites);
|
||||
RecordingUtils.pushAll(this._allocations.timestamps, timestamps);
|
||||
RecordingUtils.pushAll(this._allocations.frames, frames);
|
||||
RecordingUtils.pushAll(this._allocations.sizes, sizes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ subsuite = devtools
|
|||
support-files =
|
||||
head.js
|
||||
animation.html
|
||||
doc_allocations.html
|
||||
doc_force_cc.html
|
||||
doc_force_gc.html
|
||||
doc_innerHTML.html
|
||||
|
@ -56,6 +57,8 @@ skip-if = e10s # Bug 1183605 - toolkit/devtools/server/tests/browser/ tests are
|
|||
[browser_markers-timestamp.js]
|
||||
[browser_navigateEvents.js]
|
||||
skip-if = e10s # Bug 1183605 - toolkit/devtools/server/tests/browser/ tests are still disabled in E10S
|
||||
[browser_perf-allocation-data.js]
|
||||
skip-if = e10s # Bug 1183605 - toolkit/devtools/server/tests/browser/ tests are still disabled in E10S
|
||||
[browser_perf-legacy-front-01.js]
|
||||
skip-if = e10s # Bug 1183605 - toolkit/devtools/server/tests/browser/ tests are still disabled in E10S
|
||||
[browser_perf-legacy-front-02.js]
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we have allocation data coming from the front.
|
||||
*/
|
||||
|
||||
const { PerformanceFront } = require("devtools/server/actors/performance");
|
||||
|
||||
add_task(function*() {
|
||||
let doc = yield addTab(MAIN_DOMAIN + "doc_allocations.html");
|
||||
|
||||
initDebuggerServer();
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
let form = yield connectDebuggerClient(client);
|
||||
let front = PerformanceFront(client, form);
|
||||
yield front.connect();
|
||||
|
||||
let rec = yield front.startRecording({ withMarkers: true, withAllocations: true, withTicks: true });
|
||||
|
||||
yield waitUntil(() => rec.getAllocations().frames.length);
|
||||
yield waitUntil(() => rec.getAllocations().timestamps.length);
|
||||
yield waitUntil(() => rec.getAllocations().sizes.length);
|
||||
yield waitUntil(() => rec.getAllocations().sites.length);
|
||||
|
||||
yield front.stopRecording(rec);
|
||||
|
||||
let { frames, timestamps, sizes, sites } = rec.getAllocations();
|
||||
|
||||
is(timestamps.length, sizes.length, "we have the same amount of timestamps and sizes");
|
||||
ok(timestamps.every(time => time > 0 && typeof time === "number"), "all timestamps have numeric values");
|
||||
ok(sizes.every(n => n > 0 && typeof n === "number"), "all sizes are positive numbers");
|
||||
|
||||
yield closeDebuggerClient(client);
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
var allocs = [];
|
||||
function allocator() {
|
||||
allocs.push(new Object);
|
||||
}
|
||||
|
||||
window.setInterval(allocator, 1);
|
||||
};
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче