зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1167899 - Profiler data should compute timing information from sample count, not timestamps in the samples. r=shu
This commit is contained in:
Родитель
93bf3a90af
Коммит
db4860b1f2
|
@ -23,21 +23,21 @@ loader.lazyRequireGetter(this, "FrameUtils",
|
|||
* The raw thread object received from the backend. Contains samples,
|
||||
* stackTable, frameTable, and stringTable.
|
||||
* @param object options
|
||||
* Additional supported options, @see ThreadNode.prototype.insert
|
||||
* - number startTime [optional]
|
||||
* - number endTime [optional]
|
||||
* Additional supported options
|
||||
* - number startTime
|
||||
* - number endTime
|
||||
* - boolean contentOnly [optional]
|
||||
* - boolean invertTree [optional]
|
||||
* - boolean flattenRecursion [optional]
|
||||
*/
|
||||
function ThreadNode(thread, options = {}) {
|
||||
if (options.endTime == void 0 || options.startTime == void 0) {
|
||||
throw new Error("ThreadNode requires both `startTime` and `endTime`.");
|
||||
}
|
||||
this.samples = 0;
|
||||
this.duration = 0;
|
||||
this.youngestFrameSamples = 0;
|
||||
this.calls = [];
|
||||
|
||||
// Maps of frame to their self counts and duration.
|
||||
this.selfCount = Object.create(null);
|
||||
this.selfDuration = Object.create(null);
|
||||
this.duration = options.endTime - options.startTime;
|
||||
|
||||
let { samples, stackTable, frameTable, stringTable, allocationsTable } = thread;
|
||||
|
||||
|
@ -74,8 +74,8 @@ ThreadNode.prototype = {
|
|||
* index.
|
||||
* @param object options
|
||||
* Additional supported options
|
||||
* - number startTime [optional]
|
||||
* - number endTime [optional]
|
||||
* - number startTime
|
||||
* - number endTime
|
||||
* - boolean contentOnly [optional]
|
||||
* - boolean invertTree [optional]
|
||||
*/
|
||||
|
@ -120,9 +120,6 @@ ThreadNode.prototype = {
|
|||
const InflatedFrame = FrameUtils.InflatedFrame;
|
||||
const getOrAddInflatedFrame = FrameUtils.getOrAddInflatedFrame;
|
||||
|
||||
let selfCount = this.selfCount;
|
||||
let selfDuration = this.selfDuration;
|
||||
|
||||
let samplesData = samples.data;
|
||||
let stacksData = stackTable.data;
|
||||
|
||||
|
@ -130,8 +127,8 @@ ThreadNode.prototype = {
|
|||
let inflatedFrameCache = FrameUtils.getInflatedFrameCache(frameTable);
|
||||
let leafTable = Object.create(null);
|
||||
|
||||
let startTime = options.startTime || 0
|
||||
let endTime = options.endTime || Infinity;
|
||||
let startTime = options.startTime;
|
||||
let endTime = options.endTime;
|
||||
let flattenRecursion = options.flattenRecursion;
|
||||
|
||||
// Take the timestamp of the first sample as prevSampleTime. 0 is
|
||||
|
@ -163,7 +160,6 @@ ThreadNode.prototype = {
|
|||
continue;
|
||||
}
|
||||
|
||||
let sampleDuration = sampleTime - prevSampleTime;
|
||||
let stackIndex = sample[SAMPLE_STACK_SLOT];
|
||||
let calls = this.calls;
|
||||
let prevCalls = this.calls;
|
||||
|
@ -223,22 +219,9 @@ ThreadNode.prototype = {
|
|||
mutableFrameKeyOptions.isRoot = stackIndex === null;
|
||||
let frameKey = inflatedFrame.getFrameKey(mutableFrameKeyOptions);
|
||||
|
||||
// Leaf frames are never skipped and require self count and duration
|
||||
// bookkeeping.
|
||||
if (isLeaf) {
|
||||
// Tabulate self count and duration for the leaf frame. The frameKey
|
||||
// is never empty for a leaf frame.
|
||||
if (selfCount[frameKey] === undefined) {
|
||||
selfCount[frameKey] = 0;
|
||||
selfDuration[frameKey] = 0;
|
||||
}
|
||||
selfCount[frameKey]++;
|
||||
selfDuration[frameKey] += sampleDuration;
|
||||
} else {
|
||||
// An empty frame key means this frame should be skipped.
|
||||
if (frameKey === "") {
|
||||
continue;
|
||||
}
|
||||
// An empty frame key means this frame should be skipped.
|
||||
if (frameKey === "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we shouldn't flatten the current frame into the previous one, advance a
|
||||
|
@ -250,18 +233,18 @@ ThreadNode.prototype = {
|
|||
let frameNode = getOrAddFrameNode(calls, isLeaf, frameKey, inflatedFrame,
|
||||
mutableFrameKeyOptions.isMetaCategoryOut,
|
||||
leafTable);
|
||||
|
||||
frameNode._countSample(prevSampleTime, sampleTime, inflatedFrame.optimizations,
|
||||
stringTable);
|
||||
if (isLeaf) {
|
||||
frameNode.youngestFrameSamples++;
|
||||
}
|
||||
frameNode.samples++;
|
||||
frameNode._addOptimizations(inflatedFrame.optimizations, stringTable);
|
||||
|
||||
prevFrameKey = frameKey;
|
||||
prevCalls = frameNode.calls;
|
||||
isLeaf = mutableFrameKeyOptions.isLeaf = false;
|
||||
}
|
||||
|
||||
this.duration += sampleDuration;
|
||||
this.samples++;
|
||||
prevSampleTime = sampleTime;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -347,7 +330,19 @@ ThreadNode.prototype = {
|
|||
};
|
||||
|
||||
/**
|
||||
* A function call node in a tree.
|
||||
* A function call node in a tree. Represents a function call with a unique context,
|
||||
* resulting in each FrameNode having its own row in the corresponding tree view.
|
||||
* Take samples:
|
||||
* A()->B()->C()
|
||||
* A()->B()
|
||||
* Q()->B()
|
||||
*
|
||||
* In inverted tree, A()->B()->C() would have one frame node, and A()->B() and
|
||||
* Q()->B() would share a frame node.
|
||||
* In an uninverted tree, A()->B()->C() and A()->B() would share a frame node,
|
||||
* with Q()->B() having its own.
|
||||
*
|
||||
* In all cases, all the frame nodes originated from the same InflatedFrame.
|
||||
*
|
||||
* @param string frameKey
|
||||
* The key associated with this frame. The key determines identity of
|
||||
|
@ -372,8 +367,8 @@ function FrameNode(frameKey, { location, line, category, allocations, isContent
|
|||
this.location = location;
|
||||
this.line = line;
|
||||
this.allocations = allocations;
|
||||
this.youngestFrameSamples = 0;
|
||||
this.samples = 0;
|
||||
this.duration = 0;
|
||||
this.calls = [];
|
||||
this.isContent = !!isContent;
|
||||
this._optimizations = null;
|
||||
|
@ -384,22 +379,15 @@ function FrameNode(frameKey, { location, line, category, allocations, isContent
|
|||
|
||||
FrameNode.prototype = {
|
||||
/**
|
||||
* Count a sample as associated with this node.
|
||||
* Take optimization data observed for this frame.
|
||||
*
|
||||
* @param number prevSampleTime
|
||||
* The time when the immediate previous sample was sampled.
|
||||
* @param number sampleTime
|
||||
* The time when the current sample was sampled.
|
||||
* @param object optimizationSite
|
||||
* Any JIT optimization information attached to the current
|
||||
* sample. Lazily inflated via stringTable.
|
||||
* @param object stringTable
|
||||
* The string table used to inflate the optimizationSite.
|
||||
*/
|
||||
_countSample: function (prevSampleTime, sampleTime, optimizationSite, stringTable) {
|
||||
this.samples++;
|
||||
this.duration += sampleTime - prevSampleTime;
|
||||
|
||||
_addOptimizations: function (optimizationSite, stringTable) {
|
||||
// Simply accumulate optimization sites for now. Processing is done lazily
|
||||
// by JITOptimizations, if optimization information is actually displayed.
|
||||
if (optimizationSite) {
|
||||
|
@ -424,7 +412,7 @@ FrameNode.prototype = {
|
|||
}
|
||||
|
||||
this.samples += otherNode.samples;
|
||||
this.duration += otherNode.duration;
|
||||
this.youngestFrameSamples += otherNode.youngestFrameSamples;
|
||||
|
||||
if (otherNode._optimizations) {
|
||||
let opts = this._optimizations;
|
||||
|
|
|
@ -28,7 +28,7 @@ const DEFAULT_SORTING_PREDICATE = (frameA, frameB) => {
|
|||
}
|
||||
return dataA.selfPercentage < dataB.selfPercentage ? 1 : - 1;
|
||||
}
|
||||
return dataA.samples < dataB.samples ? 1 : -1;
|
||||
return dataA.totalPercentage < dataB.totalPercentage ? 1 : -1;
|
||||
};
|
||||
|
||||
const DEFAULT_AUTO_EXPAND_DEPTH = 3; // depth
|
||||
|
@ -358,54 +358,28 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
|
|||
|
||||
// Leaf nodes in an inverted tree don't have to do anything special.
|
||||
let isLeaf = this._level === 0;
|
||||
let totalSamples = this.root.frame.samples;
|
||||
let totalDuration = this.root.frame.duration;
|
||||
|
||||
if (this.inverted && !isLeaf && this.parent != null) {
|
||||
let calleeData = this.parent.getDisplayedData();
|
||||
// Percentage of time that this frame called the callee
|
||||
// in this branch
|
||||
let callerPercentage = this.frame.samples / calleeData.samples;
|
||||
// Self duration, cost
|
||||
if (this.visibleCells.selfDuration) {
|
||||
data.selfDuration = this.frame.youngestFrameSamples / totalSamples * totalDuration;
|
||||
}
|
||||
if (this.visibleCells.selfPercentage) {
|
||||
data.selfPercentage = this.frame.youngestFrameSamples / totalSamples * 100;
|
||||
}
|
||||
|
||||
// Self/total duration.
|
||||
if (this.visibleCells.duration) {
|
||||
data.totalDuration = calleeData.totalDuration * callerPercentage;
|
||||
}
|
||||
if (this.visibleCells.selfDuration) {
|
||||
data.selfDuration = 0;
|
||||
}
|
||||
// Total duration, cost
|
||||
if (this.visibleCells.duration) {
|
||||
data.totalDuration = this.frame.samples / totalSamples * totalDuration;
|
||||
}
|
||||
if (this.visibleCells.percentage) {
|
||||
data.totalPercentage = this.frame.samples / totalSamples * 100;
|
||||
}
|
||||
|
||||
// Self/total samples percentage.
|
||||
if (this.visibleCells.percentage) {
|
||||
data.totalPercentage = calleeData.totalPercentage * callerPercentage;
|
||||
}
|
||||
if (this.visibleCells.selfPercentage) {
|
||||
data.selfPercentage = 0;
|
||||
}
|
||||
|
||||
// Raw samples.
|
||||
if (this.visibleCells.samples) {
|
||||
data.samples = this.frame.samples;
|
||||
}
|
||||
} else {
|
||||
// Self/total duration.
|
||||
if (this.visibleCells.duration) {
|
||||
data.totalDuration = this.frame.duration;
|
||||
}
|
||||
if (this.visibleCells.selfDuration) {
|
||||
data.selfDuration = this.root.frame.selfDuration[this.frame.key];
|
||||
}
|
||||
|
||||
// Self/total samples percentage.
|
||||
if (this.visibleCells.percentage) {
|
||||
data.totalPercentage = this.frame.samples / this.root.frame.samples * 100;
|
||||
}
|
||||
if (this.visibleCells.selfPercentage) {
|
||||
data.selfPercentage = this.root.frame.selfCount[this.frame.key] / this.root.frame.samples * 100;
|
||||
}
|
||||
|
||||
// Raw samples.
|
||||
if (this.visibleCells.samples) {
|
||||
data.samples = this.frame.samples;
|
||||
}
|
||||
// Raw samples.
|
||||
if (this.visibleCells.samples) {
|
||||
data.samples = this.frame.youngestFrameSamples;
|
||||
}
|
||||
|
||||
// Self/total allocations count.
|
||||
|
|
|
@ -127,8 +127,6 @@ const EVENTS = {
|
|||
|
||||
// Emitted by the OverviewView when a range has been selected in the graphs
|
||||
OVERVIEW_RANGE_SELECTED: "Performance:UI:OverviewRangeSelected",
|
||||
// Emitted by the OverviewView when a selection range has been removed
|
||||
OVERVIEW_RANGE_CLEARED: "Performance:UI:OverviewRangeCleared",
|
||||
|
||||
// Emitted by the DetailsView when a subview is selected
|
||||
DETAILS_VIEW_SELECTED: "Performance:UI:DetailsViewSelected",
|
||||
|
|
|
@ -14,7 +14,7 @@ function* spawnTest() {
|
|||
Services.prefs.setBoolPref(MEMORY_PREF, true);
|
||||
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, $, DetailsView, JsCallTreeView, MemoryCallTreeView } = panel.panelWin;
|
||||
let { EVENTS, $, DetailsView, OverviewView, JsCallTreeView, MemoryCallTreeView } = panel.panelWin;
|
||||
|
||||
yield DetailsView.selectView("js-calltree");
|
||||
ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
|
||||
|
@ -26,7 +26,7 @@ function* spawnTest() {
|
|||
yield rendered;
|
||||
|
||||
// Mock the profile used so we can get a deterministic tree created
|
||||
let threadNode = new ThreadNode(gProfile.threads[0]);
|
||||
let threadNode = new ThreadNode(gProfile.threads[0], OverviewView.getTimeInterval());
|
||||
JsCallTreeView._populateCallTree(threadNode);
|
||||
JsCallTreeView.emit(EVENTS.JS_CALL_TREE_RENDERED);
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ function* spawnTest() {
|
|||
|
||||
// Force a rerender
|
||||
let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
|
||||
JsCallTreeView.render();
|
||||
JsCallTreeView.render(OverviewView.getTimeInterval());
|
||||
yield rendered;
|
||||
|
||||
is($("#jit-optimizations-view").hidden, true, "JIT Optimizations panel still hidden when rerendered");
|
||||
|
|
|
@ -61,7 +61,7 @@ function* spawnTest() {
|
|||
|
||||
// Force a rerender
|
||||
let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
|
||||
JsCallTreeView.render();
|
||||
JsCallTreeView.render(OverviewView.getTimeInterval());
|
||||
yield rendered;
|
||||
|
||||
Services.prefs.setBoolPref(JIT_PREF, true);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
function* spawnTest() {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, PerformanceController, OverviewView } = panel.panelWin;
|
||||
let startTime, endTime, params, _;
|
||||
let params, _;
|
||||
|
||||
yield startRecording(panel);
|
||||
|
||||
|
@ -21,32 +21,49 @@ function* spawnTest() {
|
|||
|
||||
let graph = OverviewView.graphs.get("timeline");
|
||||
let MAX = graph.width;
|
||||
let duration = PerformanceController.getCurrentRecording().getDuration();
|
||||
let selection = null;
|
||||
|
||||
// Throw out events that select everything, as this will occur on the
|
||||
// first click
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, function handler (_, interval) {
|
||||
if (interval.endTime !== duration) {
|
||||
selection = interval;
|
||||
OverviewView.off(handler);
|
||||
}
|
||||
});
|
||||
|
||||
// Select the first half of the graph
|
||||
let results = onceSpread(OverviewView, EVENTS.OVERVIEW_RANGE_SELECTED);
|
||||
// Select the first half of the graph
|
||||
dragStart(graph, 0);
|
||||
dragStop(graph, MAX / 2);
|
||||
[_, { startTime, endTime }] = yield results;
|
||||
yield waitUntil(() => selection);
|
||||
let { startTime, endTime } = selection;
|
||||
|
||||
let mapStart = () => 0;
|
||||
let mapEnd = () => PerformanceController.getCurrentRecording().getDuration();
|
||||
let mapEnd = () => duration;
|
||||
let actual = graph.getMappedSelection({ mapStart, mapEnd });
|
||||
is(actual.min, 0, "graph selection starts at 0");
|
||||
is(actual.max, duration/2, `graph selection ends at ${duration/2}`);
|
||||
|
||||
is(graph.hasSelection(), true,
|
||||
"A selection exists on the graph.");
|
||||
is(startTime, actual.min,
|
||||
is(startTime, 0,
|
||||
"OVERVIEW_RANGE_SELECTED fired with startTime value on click.");
|
||||
is(endTime, actual.max,
|
||||
is(endTime, duration / 2,
|
||||
"OVERVIEW_RANGE_SELECTED fired with endTime value on click.");
|
||||
|
||||
// Listen to deselection
|
||||
results = onceSpread(OverviewView, EVENTS.OVERVIEW_RANGE_CLEARED);
|
||||
results = onceSpread(OverviewView, EVENTS.OVERVIEW_RANGE_SELECTED);
|
||||
dropSelection(graph);
|
||||
[_, params] = yield results;
|
||||
|
||||
is(graph.hasSelection(), false,
|
||||
"A selection no longer on the graph.");
|
||||
is(params, undefined,
|
||||
"OVERVIEW_RANGE_CLEARED fired with no additional arguments.");
|
||||
is(params.startTime, 0,
|
||||
"OVERVIEW_RANGE_SELECTED fired with 0 as a startTime.");
|
||||
is(params.endTime, duration,
|
||||
"OVERVIEW_RANGE_SELECTED fired with max duration as endTime");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
|
|
|
@ -40,7 +40,7 @@ function* spawnTest() {
|
|||
ok(true, "Flamegraph rerenders after its corresponding pane is shown.");
|
||||
|
||||
rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
|
||||
OverviewView.emit(EVENTS.OVERVIEW_RANGE_CLEARED);
|
||||
OverviewView.emit(EVENTS.OVERVIEW_RANGE_SELECTED);
|
||||
yield rendered;
|
||||
ok(true, "Flamegraph rerenders when a range in the overview graph is removed.");
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ function test() {
|
|||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
let { CallView } = devtools.require("devtools/performance/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
@ -31,7 +31,7 @@ function test() {
|
|||
|
||||
is(container.childNodes[0].childNodes[0].getAttribute("type"), "duration",
|
||||
"The root node in the tree has a duration cell.");
|
||||
is(container.childNodes[0].childNodes[0].getAttribute("value"), "15 ms",
|
||||
is(container.childNodes[0].childNodes[0].getAttribute("value"), "20 ms",
|
||||
"The root node in the tree has the correct duration cell value.");
|
||||
|
||||
is(container.childNodes[0].childNodes[1].getAttribute("type"), "percentage",
|
||||
|
@ -51,7 +51,7 @@ function test() {
|
|||
|
||||
is(container.childNodes[0].childNodes[4].getAttribute("type"), "samples",
|
||||
"The root node in the tree has an samples cell.");
|
||||
is(container.childNodes[0].childNodes[4].getAttribute("value"), "4",
|
||||
is(container.childNodes[0].childNodes[4].getAttribute("value"), "0",
|
||||
"The root node in the tree has the correct samples cell value.");
|
||||
|
||||
is(container.childNodes[0].childNodes[5].getAttribute("type"), "function",
|
||||
|
|
|
@ -12,7 +12,7 @@ function test() {
|
|||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
let { CallView } = devtools.require("devtools/performance/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
@ -33,11 +33,11 @@ function test() {
|
|||
is(container.childNodes[0].className, "call-tree-item",
|
||||
"The root node in the tree has the correct class name.");
|
||||
|
||||
is($$dur(0).getAttribute("value"), "15 ms",
|
||||
is($$dur(0).getAttribute("value"), "20 ms",
|
||||
"The root's duration cell displays the correct value.");
|
||||
is($$perc(0).getAttribute("value"), "100%",
|
||||
"The root's percentage cell displays the correct value.");
|
||||
is($$sampl(0).getAttribute("value"), "4",
|
||||
is($$sampl(0).getAttribute("value"), "0",
|
||||
"The root's samples cell displays the correct value.");
|
||||
is($$fun(".call-tree-name")[0].getAttribute("value"), "(root)",
|
||||
"The root's function cell displays the correct name.");
|
||||
|
@ -59,11 +59,11 @@ function test() {
|
|||
is(container.childNodes[1].className, "call-tree-item",
|
||||
"The .A node in the tree has the correct class name.");
|
||||
|
||||
is($$dur(1).getAttribute("value"), "15 ms",
|
||||
is($$dur(1).getAttribute("value"), "20 ms",
|
||||
"The .A node's duration cell displays the correct value.");
|
||||
is($$perc(1).getAttribute("value"), "100%",
|
||||
"The .A node's percentage cell displays the correct value.");
|
||||
is($$sampl(1).getAttribute("value"), "4",
|
||||
is($$sampl(1).getAttribute("value"), "0",
|
||||
"The .A node's samples cell displays the correct value.");
|
||||
is($fun(".call-tree-name", $$(".call-tree-item")[1]).getAttribute("value"), "A",
|
||||
"The .A node's function cell displays the correct name.");
|
||||
|
@ -88,11 +88,11 @@ function test() {
|
|||
is(container.childNodes[3].className, "call-tree-item",
|
||||
"The .E node in the tree has the correct class name.");
|
||||
|
||||
is($$dur(2).getAttribute("value"), "8 ms",
|
||||
is($$dur(2).getAttribute("value"), "15 ms",
|
||||
"The .A.B node's duration cell displays the correct value.");
|
||||
is($$perc(2).getAttribute("value"), "75%",
|
||||
"The .A.B node's percentage cell displays the correct value.");
|
||||
is($$sampl(2).getAttribute("value"), "3",
|
||||
is($$sampl(2).getAttribute("value"), "0",
|
||||
"The .A.B node's samples cell displays the correct value.");
|
||||
is($fun(".call-tree-name", $$(".call-tree-item")[2]).getAttribute("value"), "B",
|
||||
"The .A.B node's function cell displays the correct name.");
|
||||
|
@ -107,11 +107,11 @@ function test() {
|
|||
is($fun(".call-tree-category", $$(".call-tree-item")[2]).getAttribute("value"), "Styles",
|
||||
"The .A.B node's function cell displays the correct category.");
|
||||
|
||||
is($$dur(3).getAttribute("value"), "7 ms",
|
||||
is($$dur(3).getAttribute("value"), "5 ms",
|
||||
"The .A.E node's duration cell displays the correct value.");
|
||||
is($$perc(3).getAttribute("value"), "25%",
|
||||
"The .A.E node's percentage cell displays the correct value.");
|
||||
is($$sampl(3).getAttribute("value"), "1",
|
||||
is($$sampl(3).getAttribute("value"), "0",
|
||||
"The .A.E node's samples cell displays the correct value.");
|
||||
is($fun(".call-tree-name", $$(".call-tree-item")[3]).getAttribute("value"), "E",
|
||||
"The .A.E node's function cell displays the correct name.");
|
||||
|
|
|
@ -10,7 +10,7 @@ function test() {
|
|||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
let { CallView } = devtools.require("devtools/performance/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
@ -57,19 +57,19 @@ function test() {
|
|||
is($$name(6).getAttribute("value"), "F",
|
||||
"The .A.E.F node's function cell displays the correct name.");
|
||||
|
||||
is($$duration(0).getAttribute("value"), "15 ms",
|
||||
is($$duration(0).getAttribute("value"), "20 ms",
|
||||
"The root node's function cell displays the correct duration.");
|
||||
is($$duration(1).getAttribute("value"), "15 ms",
|
||||
is($$duration(1).getAttribute("value"), "20 ms",
|
||||
"The .A node's function cell displays the correct duration.");
|
||||
is($$duration(2).getAttribute("value"), "8 ms",
|
||||
is($$duration(2).getAttribute("value"), "15 ms",
|
||||
"The .A.B node's function cell displays the correct duration.");
|
||||
is($$duration(3).getAttribute("value"), "3 ms",
|
||||
is($$duration(3).getAttribute("value"), "10 ms",
|
||||
"The .A.B.D node's function cell displays the correct duration.");
|
||||
is($$duration(4).getAttribute("value"), "5 ms",
|
||||
"The .A.B.C node's function cell displays the correct duration.");
|
||||
is($$duration(5).getAttribute("value"), "7 ms",
|
||||
is($$duration(5).getAttribute("value"), "5 ms",
|
||||
"The .A.E node's function cell displays the correct duration.");
|
||||
is($$duration(6).getAttribute("value"), "7 ms",
|
||||
is($$duration(6).getAttribute("value"), "5 ms",
|
||||
"The .A.E.F node's function cell displays the correct duration.");
|
||||
|
||||
finish();
|
||||
|
|
|
@ -12,7 +12,7 @@ function test() {
|
|||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
let { CallView } = devtools.require("devtools/performance/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
|
|
@ -10,7 +10,7 @@ function test() {
|
|||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
let { CallView } = devtools.require("devtools/performance/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
|
|
@ -10,7 +10,7 @@ function* spawnTest() {
|
|||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
let { CallView } = devtools.require("devtools/performance/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
|
|
@ -10,7 +10,7 @@ function* spawnTest() {
|
|||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
let { CallView } = devtools.require("devtools/performance/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
|
||||
// Don't display the synthesized (root) and the real (root) node twice.
|
||||
threadNode.calls = threadNode.calls[0].calls;
|
||||
let treeRoot = new CallView({ frame: threadNode });
|
||||
|
|
|
@ -26,7 +26,7 @@ function test() {
|
|||
* - (JS)
|
||||
*/
|
||||
|
||||
let threadNode = new ThreadNode(gThread, { contentOnly: true });
|
||||
let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 30, 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 });
|
||||
|
|
|
@ -12,7 +12,7 @@ let test = Task.async(function*() {
|
|||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
let { CallView } = devtools.require("devtools/performance/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gSamples, { invertTree: true });
|
||||
let threadNode = new ThreadNode(gSamples, { invertTree: true, startTime: 0, endTime: 10 });
|
||||
let treeRoot = new CallView({ frame: threadNode, inverted: true, autoExpandDepth: 1 });
|
||||
|
||||
let container = document.createElement("vbox");
|
||||
|
|
|
@ -10,7 +10,7 @@ function test() {
|
|||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
let { CallView } = devtools.require("devtools/performance/tree-view");
|
||||
|
||||
let threadNode = new ThreadNode(gThread, { invertTree: true });
|
||||
let threadNode = new ThreadNode(gThread, { invertTree: true, startTime: 0, endTime: 50 });
|
||||
let treeRoot = new CallView({ frame: threadNode, inverted: true, hidden: true });
|
||||
|
||||
let container = document.createElement("vbox");
|
||||
|
|
|
@ -14,7 +14,7 @@ add_task(function test() {
|
|||
|
||||
// Create a root node from a given samples array.
|
||||
|
||||
let threadNode = new ThreadNode(gThread);
|
||||
let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
|
||||
let root = getFrameNodePath(threadNode, "(root)");
|
||||
|
||||
// Test the root node.
|
||||
|
@ -22,8 +22,8 @@ add_task(function test() {
|
|||
equal(threadNode.getInfo().nodeType, "Thread",
|
||||
"The correct node type was retrieved for the root node.");
|
||||
|
||||
equal(root.duration, 20,
|
||||
"The correct duration was calculated for the root node.");
|
||||
equal(threadNode.duration, 20,
|
||||
"The correct duration was calculated for the ThreadNode.");
|
||||
equal(root.getInfo().functionName, "(root)",
|
||||
"The correct function name was retrieved for the root node.");
|
||||
equal(root.getInfo().categoryData.abbrev, "other",
|
||||
|
@ -82,32 +82,38 @@ add_task(function test() {
|
|||
equal(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.
|
||||
// Check the location, sample times, and samples of the root.
|
||||
|
||||
equal(getFrameNodePath(root, "A").location, "A",
|
||||
"The 'A' node has the correct location.");
|
||||
equal(getFrameNodePath(root, "A").duration, 20,
|
||||
"The 'A' node has the correct duration in milliseconds.");
|
||||
equal(getFrameNodePath(root, "A").youngestFrameSamples, 0,
|
||||
"The 'A' has correct `youngestFrameSamples`");
|
||||
equal(getFrameNodePath(root, "A").samples, 4,
|
||||
"The 'A' node has the correct number of samples.");
|
||||
"The 'A' has correct `samples`");
|
||||
|
||||
// A frame that is both a leaf and caught in another stack
|
||||
equal(getFrameNodePath(root, "A > B > C").youngestFrameSamples, 1,
|
||||
"The 'A > B > C' has correct `youngestFrameSamples`");
|
||||
equal(getFrameNodePath(root, "A > B > C").samples, 2,
|
||||
"The 'A > B > C' has correct `samples`");
|
||||
|
||||
// ...and the rightmost leaf.
|
||||
|
||||
equal(getFrameNodePath(root, "A > E > F").location, "F",
|
||||
"The 'A > E > F' node has the correct location.");
|
||||
equal(getFrameNodePath(root, "A > E > F").duration, 7,
|
||||
"The 'A > E > F' node has the correct duration in milliseconds.");
|
||||
equal(getFrameNodePath(root, "A > E > F").samples, 1,
|
||||
"The 'A > E > F' node has the correct number of samples.");
|
||||
equal(getFrameNodePath(root, "A > E > F").youngestFrameSamples, 1,
|
||||
"The 'A > E > F' node has the correct number of youngestFrameSamples.");
|
||||
|
||||
// ...and the leftmost leaf.
|
||||
|
||||
equal(getFrameNodePath(root, "A > B > C > D > E > F > G").location, "G",
|
||||
"The 'A > B > C > D > E > F > G' node has the correct location.");
|
||||
equal(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.");
|
||||
equal(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.");
|
||||
equal(getFrameNodePath(root, "A > B > C > D > E > F > G").youngestFrameSamples, 1,
|
||||
"The 'A > B > C > D > E > F > G' node has the correct number of youngestFrameSamples.");
|
||||
});
|
||||
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
|
|
|
@ -14,12 +14,12 @@ add_task(function test() {
|
|||
|
||||
// Create a root node from a given samples array.
|
||||
|
||||
let root = getFrameNodePath(new ThreadNode(gThread), "(root)");
|
||||
let thread = new ThreadNode(gThread, { startTime: 0, endTime: 10 });
|
||||
let root = getFrameNodePath(thread, "(root)");
|
||||
|
||||
// Test the root node.
|
||||
|
||||
equal(root.duration, 5,
|
||||
"The correct duration was calculated for the root node.");
|
||||
// Test the ThreadNode, only node with a duration.
|
||||
equal(thread.duration, 10,
|
||||
"The correct duration was calculated for the ThreadNode.");
|
||||
|
||||
equal(root.calls.length, 1,
|
||||
"The correct number of child calls were calculated for the root node.");
|
||||
|
|
|
@ -20,12 +20,13 @@ add_task(function test() {
|
|||
// exactly at 18.
|
||||
let startTime = 5;
|
||||
let endTime = 18;
|
||||
let root = getFrameNodePath(new ThreadNode(gThread, { startTime, endTime }), "(root)");
|
||||
let thread = new ThreadNode(gThread, { startTime, endTime });
|
||||
let root = getFrameNodePath(thread, "(root)");
|
||||
|
||||
// Test the root node.
|
||||
|
||||
equal(root.duration, endTime - startTime,
|
||||
"The correct duration was calculated for the root node.");
|
||||
equal(thread.duration, endTime - startTime,
|
||||
"The correct duration was calculated for the ThreadNode.");
|
||||
|
||||
equal(root.calls.length, 1,
|
||||
"The correct number of child calls were calculated for the root node.");
|
||||
|
|
|
@ -17,12 +17,12 @@ add_task(function test() {
|
|||
|
||||
let startTime = 5;
|
||||
let endTime = 18;
|
||||
let root = getFrameNodePath(new ThreadNode(gThread, { startTime: startTime, endTime: endTime, contentOnly: true }), "(root)");
|
||||
let thread = new ThreadNode(gThread, { startTime, endTime, contentOnly: true });
|
||||
let root = getFrameNodePath(thread, "(root)");
|
||||
|
||||
// Test the root node.
|
||||
|
||||
equal(root.duration, endTime - startTime,
|
||||
"The correct duration was calculated for the root node.");
|
||||
// Test the ThreadNode, only node which should have duration
|
||||
equal(thread.duration, endTime - startTime,
|
||||
"The correct duration was calculated for the root ThreadNode.");
|
||||
|
||||
equal(root.calls.length, 2,
|
||||
"The correct number of child calls were calculated for the root node.");
|
||||
|
|
|
@ -49,7 +49,7 @@ function run_test() {
|
|||
add_task(function test() {
|
||||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
|
||||
let root = new ThreadNode(gThread, { invertTree: true });
|
||||
let root = new ThreadNode(gThread, { invertTree: true, startTime: 0, endTime: 4 });
|
||||
|
||||
equal(root.calls.length, 2,
|
||||
"Should get the 2 youngest frames, not the 1 oldest frame");
|
||||
|
|
|
@ -159,7 +159,7 @@ function run_test() {
|
|||
add_task(function test() {
|
||||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
|
||||
let root = new ThreadNode(gThread);
|
||||
let root = new ThreadNode(gThread, { startTime: 0, endTime: 4 });
|
||||
|
||||
let A = getFrameNodePath(root, "(root) > A");
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ add_task(function test() {
|
|||
|
||||
// Create a root node from a given samples array.
|
||||
|
||||
let root = getFrameNodePath(new ThreadNode(gThread, { contentOnly: true }), "(root)");
|
||||
let root = getFrameNodePath(new ThreadNode(gThread, { startTime: 5, endTime: 30, contentOnly: true }), "(root)");
|
||||
|
||||
/*
|
||||
* should have a tree like:
|
||||
|
|
|
@ -17,7 +17,7 @@ add_task(function test() {
|
|||
|
||||
// Create a root node from a given samples array.
|
||||
|
||||
let root = getFrameNodePath(new ThreadNode(gThread, { contentOnly: true }), "(root)");
|
||||
let root = getFrameNodePath(new ThreadNode(gThread, { startTime: 5, endTime: 25, contentOnly: true }), "(root)");
|
||||
|
||||
/*
|
||||
* should have a tree like:
|
||||
|
|
|
@ -20,7 +20,6 @@ let DetailsSubview = {
|
|||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onOverviewRangeChange);
|
||||
DetailsView.on(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
},
|
||||
|
||||
|
@ -34,7 +33,6 @@ let DetailsSubview = {
|
|||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onOverviewRangeChange);
|
||||
DetailsView.off(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
},
|
||||
|
||||
|
@ -92,7 +90,7 @@ let DetailsSubview = {
|
|||
return;
|
||||
}
|
||||
if (DetailsView.isViewSelected(this) || this.canUpdateWhileHidden) {
|
||||
this.render();
|
||||
this.render(OverviewView.getTimeInterval());
|
||||
} else {
|
||||
this.shouldUpdateWhenShown = true;
|
||||
}
|
||||
|
|
|
@ -165,8 +165,10 @@ let OverviewView = {
|
|||
let mapEnd = () => recording.getDuration();
|
||||
let selection = this.graphs.getMappedSelection({ mapStart, mapEnd });
|
||||
// If no selection returned, this means the overview graphs have not been rendered
|
||||
// yet, so act as if we have no selection (the full recording).
|
||||
if (!selection) {
|
||||
// yet, so act as if we have no selection (the full recording). Also
|
||||
// if the selection range distance is tiny, assume the range was cleared or just
|
||||
// clicked, and we do not have a range.
|
||||
if (!selection || (selection.max - selection.min) < 1) {
|
||||
return { startTime: 0, endTime: recording.getDuration() };
|
||||
}
|
||||
return { startTime: selection.min, endTime: selection.max };
|
||||
|
@ -300,14 +302,8 @@ let OverviewView = {
|
|||
if (this._stopSelectionChangeEventPropagation) {
|
||||
return;
|
||||
}
|
||||
// If the range is smaller than a pixel (which can happen when performing
|
||||
// a click on the graphs), treat this as a cleared selection.
|
||||
let interval = this.getTimeInterval();
|
||||
if (interval.endTime - interval.startTime < 1) {
|
||||
this.emit(EVENTS.OVERVIEW_RANGE_CLEARED);
|
||||
} else {
|
||||
this.emit(EVENTS.OVERVIEW_RANGE_SELECTED, interval);
|
||||
}
|
||||
|
||||
this.emit(EVENTS.OVERVIEW_RANGE_SELECTED, this.getTimeInterval());
|
||||
},
|
||||
|
||||
_onGraphRendered: function (_, graphName) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче