Bug 1132758 - Performance feature visibility now based on a per recording-basis, dependent on features enabled and server support. r=vp

This commit is contained in:
Jordan Santell 2015-04-24 21:04:02 -07:00
parent 10fec54e1c
commit b01ec40721
55 changed files with 920 additions and 574 deletions

View File

@ -100,9 +100,9 @@ function PerformanceActorsConnection(target) {
PerformanceActorsConnection.prototype = {
// Properties set when mocks are being used
_usingMockMemory: false,
_usingMockTimeline: false,
// Properties set based off of server actor support
_memorySupported: true,
_timelineSupported: true,
/**
* Initializes a connection to the profiler and other miscellaneous actors.
@ -174,9 +174,9 @@ PerformanceActorsConnection.prototype = {
if (supported) {
this._timeline = new TimelineFront(this._target.client, this._target.form);
} else {
this._usingMockTimeline = true;
this._timeline = new compatibility.MockTimelineFront();
}
this._timelineSupported = supported;
},
/**
@ -187,9 +187,9 @@ PerformanceActorsConnection.prototype = {
if (supported) {
this._memory = new MemoryFront(this._target.client, this._target.form);
} else {
this._usingMockMemory = true;
this._memory = new compatibility.MockMemoryFront();
}
this._memorySupported = supported;
}),
/**
@ -620,8 +620,8 @@ function PerformanceFront(connection) {
this._request = connection._request;
// Set when mocks are being used
this._usingMockMemory = connection._usingMockMemory;
this._usingMockTimeline = connection._usingMockTimeline;
this._memorySupported = connection._memorySupported;
this._timelineSupported = connection._timelineSupported;
// Pipe the console profile events from the connection
// to the front so that the UI can listen.
@ -636,7 +636,8 @@ PerformanceFront.prototype = {
*
* @param object options
* An options object to pass to the actors. Supported properties are
* `withTicks`, `withMemory` and `withAllocations`, `probability` and `maxLogLength`.
* `withTicks`, `withMemory` and `withAllocations`,
* `probability` and `maxLogLength`.
* @return object
* A promise that is resolved once recording has started.
*/
@ -658,12 +659,14 @@ PerformanceFront.prototype = {
},
/**
* Returns an object indicating if mock actors are being used or not.
* Returns an object indicating what server actors are available and
* initialized. A falsy value indicates that the server does not support
* that feature, or that mock actors were explicitly requested (tests).
*/
getMocksInUse: function () {
getActorSupport: function () {
return {
memory: this._usingMockMemory,
timeline: this._usingMockTimeline
memory: this._memorySupported,
timeline: this._timelineSupported
};
}
};
@ -673,6 +676,7 @@ PerformanceFront.prototype = {
*/
function getRecordingModelPrefs () {
return {
withMarkers: true,
withMemory: Services.prefs.getBoolPref("devtools.performance.ui.enable-memory"),
withTicks: Services.prefs.getBoolPref("devtools.performance.ui.enable-framerate"),
withAllocations: Services.prefs.getBoolPref("devtools.performance.ui.enable-memory"),

View File

@ -0,0 +1,409 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
"use strict";
/**
* This file contains the base line graph that all Performance line graphs use.
*/
const {Cc, Ci, Cu, Cr} = require("chrome");
Cu.import("resource:///modules/devtools/Graphs.jsm");
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
const { colorUtils: { setAlpha }} = require("devtools/css-color");
const { getColor } = require("devtools/shared/theme");
loader.lazyRequireGetter(this, "ProfilerGlobal",
"devtools/shared/profiler/global");
loader.lazyRequireGetter(this, "TimelineGlobal",
"devtools/shared/timeline/global");
loader.lazyRequireGetter(this, "MarkersOverview",
"devtools/shared/timeline/markers-overview", true);
loader.lazyRequireGetter(this, "EventEmitter",
"devtools/toolkit/event-emitter");
/**
* For line graphs
*/
const HEIGHT = 35; // px
const STROKE_WIDTH = 1; // px
const DAMPEN_VALUES = 0.95;
const CLIPHEAD_LINE_COLOR = "#666";
const SELECTION_LINE_COLOR = "#555";
const SELECTION_BACKGROUND_COLOR_NAME = "highlight-blue";
const FRAMERATE_GRAPH_COLOR_NAME = "highlight-green";
const MEMORY_GRAPH_COLOR_NAME = "highlight-blue";
/**
* For timeline overview
*/
const MARKERS_GRAPH_HEADER_HEIGHT = 14; // px
const MARKERS_GRAPH_ROW_HEIGHT = 10; // px
const MARKERS_GROUP_VERTICAL_PADDING = 4; // px
/**
* A base class for performance graphs to inherit from.
*
* @param nsIDOMNode parent
* The parent node holding the overview.
* @param string metric
* The unit of measurement for this graph.
*/
function PerformanceGraph(parent, metric) {
LineGraphWidget.call(this, parent, { metric });
this.setTheme();
}
PerformanceGraph.prototype = Heritage.extend(LineGraphWidget.prototype, {
strokeWidth: STROKE_WIDTH,
dampenValuesFactor: DAMPEN_VALUES,
fixedHeight: HEIGHT,
clipheadLineColor: CLIPHEAD_LINE_COLOR,
selectionLineColor: SELECTION_LINE_COLOR,
withTooltipArrows: false,
withFixedTooltipPositions: true,
/**
* Disables selection and empties this graph.
*/
clearView: function() {
this.selectionEnabled = false;
this.dropSelection();
this.setData([]);
},
/**
* Sets the theme via `theme` to either "light" or "dark",
* and updates the internal styling to match. Requires a redraw
* to see the effects.
*/
setTheme: function (theme) {
theme = theme || "light";
let mainColor = getColor(this.mainColor || "highlight-blue", theme);
this.backgroundColor = getColor("body-background", theme);
this.strokeColor = mainColor;
this.backgroundGradientStart = setAlpha(mainColor, 0.2);
this.backgroundGradientEnd = setAlpha(mainColor, 0.2);
this.selectionBackgroundColor = setAlpha(getColor(SELECTION_BACKGROUND_COLOR_NAME, theme), 0.25);
this.selectionStripesColor = "rgba(255, 255, 255, 0.1)";
this.maximumLineColor = setAlpha(mainColor, 0.4);
this.averageLineColor = setAlpha(mainColor, 0.7);
this.minimumLineColor = setAlpha(mainColor, 0.9);
}
});
/**
* Constructor for the framerate graph. Inherits from PerformanceGraph.
*
* @param nsIDOMNode parent
* The parent node holding the overview.
*/
function FramerateGraph(parent) {
PerformanceGraph.call(this, parent, ProfilerGlobal.L10N.getStr("graphs.fps"));
}
FramerateGraph.prototype = Heritage.extend(PerformanceGraph.prototype, {
mainColor: FRAMERATE_GRAPH_COLOR_NAME,
setPerformanceData: function ({ duration, ticks }, resolution) {
this.dataDuration = duration;
return this.setDataFromTimestamps(ticks, resolution);
}
});
/**
* Constructor for the memory graph. Inherits from PerformanceGraph.
*
* @param nsIDOMNode parent
* The parent node holding the overview.
*/
function MemoryGraph(parent) {
PerformanceGraph.call(this, parent, TimelineGlobal.L10N.getStr("graphs.memory"));
}
MemoryGraph.prototype = Heritage.extend(PerformanceGraph.prototype, {
mainColor: MEMORY_GRAPH_COLOR_NAME,
setPerformanceData: function ({ duration, memory }) {
this.dataDuration = duration;
return this.setData(memory);
}
});
function TimelineGraph(parent, blueprint) {
MarkersOverview.call(this, parent, blueprint);
}
TimelineGraph.prototype = Heritage.extend(MarkersOverview.prototype, {
headerHeight: MARKERS_GRAPH_HEADER_HEIGHT,
rowHeight: MARKERS_GRAPH_ROW_HEIGHT,
groupPadding: MARKERS_GROUP_VERTICAL_PADDING,
setPerformanceData: MarkersOverview.prototype.setData
});
/**
* Definitions file for GraphsController, indicating the constructor,
* selector and other meta for each of the graphs controller by
* GraphsController.
*/
const GRAPH_DEFINITIONS = {
memory: {
constructor: MemoryGraph,
selector: "#memory-overview",
},
framerate: {
constructor: FramerateGraph,
selector: "#time-framerate",
},
timeline: {
constructor: TimelineGraph,
selector: "#markers-overview",
needsBlueprints: true,
primaryLink: true
}
};
/**
* A controller for orchestrating the performance's tool overview graphs. Constructs,
* syncs, toggles displays and defines the memory, framerate and timeline view.
*
* @param {object} definition
* @param {DOMElement} root
* @param {function} getBlueprint
* @param {function} getTheme
*/
function GraphsController ({ definition, root, getBlueprint, getTheme }) {
this._graphs = {};
this._enabled = new Set();
this._definition = definition || GRAPH_DEFINITIONS;
this._root = root;
this._getBlueprint = getBlueprint;
this._getTheme = getTheme;
this._primaryLink = Object.keys(this._definition).filter(name => this._definition[name].primaryLink)[0];
this.$ = root.ownerDocument.querySelector.bind(root.ownerDocument);
EventEmitter.decorate(this);
this._onSelecting = this._onSelecting.bind(this);
}
GraphsController.prototype = {
/**
* Returns the corresponding graph by `graphName`.
*/
get: function (graphName) {
return this._graphs[graphName];
},
/**
* Iterates through all graphs and renders the data
* from a RecordingModel. Takes a resolution value used in
* some graphs.
* Saves rendering progress as a promise to be consumed by `destroy`,
* to wait for cleaning up rendering during destruction.
*/
render: Task.async(function *(recordingData, resolution) {
// Get the previous render promise so we don't start rendering
// until the previous render cycle completes, which can occur
// especially when a recording is finished, and triggers a
// fresh rendering at a higher rate
yield (this._rendering && this._rendering.promise);
// Check after yielding to ensure we're not tearing down,
// as this can create a race condition in tests
if (this._destroyed) {
return;
}
this._rendering = Promise.defer();
for (let graph of (yield this._getEnabled())) {
yield graph.setPerformanceData(recordingData, resolution);
this.emit("rendered", graph.graphName);
}
this._rendering.resolve();
}),
/**
* Destroys the underlying graphs.
*/
destroy: Task.async(function *() {
let primary = this._getPrimaryLink();
this._destroyed = true;
if (primary) {
primary.off("selecting", this._onSelecting);
}
// If there was rendering, wait until the most recent render cycle
// has finished
if (this._rendering) {
yield this._rendering.promise;
}
for (let graphName in this._graphs) {
yield this._graphs[graphName].destroy();
}
}),
/**
* Applies the theme to the underlying graphs. Optionally takes
* a `redraw` boolean in the options to force redraw.
*/
setTheme: function (options={}) {
let theme = options.theme || this._getTheme();
for (let graph in this._graphs) {
this._graphs[graph].setTheme(theme);
this._graphs[graph].refresh({ force: options.redraw });
}
},
/**
* Sets up the graph, if needed. Returns a promise resolving
* to the graph if it is enabled once it's ready, or otherwise returns
* null if disabled.
*/
isAvailable: Task.async(function *(graphName) {
if (!this._enabled.has(graphName)) {
return null;
}
let graph = this.get(graphName);
if (!graph) {
graph = yield this._construct(graphName);
}
yield graph.ready();
return graph;
}),
/**
* Enable or disable a subgraph controlled by GraphsController.
* This determines what graphs are visible and get rendered.
*/
enable: function (graphName, isEnabled) {
let el = this.$(this._definition[graphName].selector);
el.hidden = !isEnabled;
// If no status change, just return
if (this._enabled.has(graphName) === isEnabled) {
return;
}
if (isEnabled) {
this._enabled.add(graphName);
} else {
this._enabled.delete(graphName);
}
// Invalidate our cache of ready-to-go graphs
this._enabledGraphs = null;
},
/**
* Disables all graphs controller by the GraphsController, and
* also hides the root element. This is a one way switch, and used
* when older platforms do not have any timeline data.
*/
disableAll: function () {
this._root.hidden = true;
// Hide all the subelements
Object.keys(this._definition).forEach(graphName => this.enable(graphName, false));
},
/**
* Sets a mapped selection on the graph that is the main controller
* for keeping the graphs' selections in sync.
*/
setMappedSelection: function (selection, { mapStart, mapEnd }) {
return this._getPrimaryLink().setMappedSelection(selection, { mapStart, mapEnd });
},
getMappedSelection: function ({ mapStart, mapEnd }) {
return this._getPrimaryLink().getMappedSelection({ mapStart, mapEnd });
},
/**
* Drops the selection.
*/
dropSelection: function () {
if (this._getPrimaryLink()) {
return this._getPrimaryLink().dropSelection();
}
},
/**
* Makes sure the selection is enabled or disabled in all the graphs.
*/
selectionEnabled: Task.async(function *(enabled) {
for (let graph of (yield this._getEnabled())) {
graph.selectionEnabled = enabled;
}
}),
/**
* Creates the graph `graphName` and initializes it.
*/
_construct: Task.async(function *(graphName) {
let def = this._definition[graphName];
let el = this.$(def.selector);
let blueprint = def.needsBlueprints ? this._getBlueprint() : void 0;
let graph = this._graphs[graphName] = new def.constructor(el, blueprint);
graph.graphName = graphName;
yield graph.ready();
// Sync the graphs' animations and selections together
if (def.primaryLink) {
graph.on("selecting", this._onSelecting);
} else {
CanvasGraphUtils.linkAnimation(this._getPrimaryLink(), graph);
CanvasGraphUtils.linkSelection(this._getPrimaryLink(), graph);
}
this.setTheme();
return graph;
}),
/**
* Returns the main graph for this collection, that all graphs
* are bound to for syncing and selection.
*/
_getPrimaryLink: function () {
return this.get(this._primaryLink);
},
/**
* Emitted when a selection occurs.
*/
_onSelecting: function () {
this.emit("selecting");
},
/**
* Resolves to an array with all graphs that are enabled, and
* creates them if needed. Different than just iterating over `this._graphs`,
* as those could be enabled. Uses caching, as rendering happens many times per second,
* compared to how often which graphs/features are changed (rarely).
*/
_getEnabled: Task.async(function *() {
if (this._enabledGraphs) {
return this._enabledGraphs;
}
let enabled = [];
for (let graphName of this._enabled) {
let graph;
if (graph = yield this.isAvailable(graphName)) {
enabled.push(graph);
}
}
return this._enabledGraphs = enabled;
}),
};
exports.FramerateGraph = FramerateGraph;
exports.MemoryGraph = MemoryGraph;
exports.TimelineGraph = TimelineGraph;
exports.GraphsController = GraphsController;

View File

@ -148,7 +148,15 @@ function convertLegacyData (legacyData) {
memory: [],
ticks: ticksData,
allocations: { sites: [], timestamps: [], frames: [], counts: [] },
profile: profilerData.profile
profile: profilerData.profile,
// Fake a configuration object here if there's tick data,
// so that it can be rendered
configuration: {
withTicks: !!ticksData.length,
withMarkers: false,
withMemory: false,
withAllocations: false
}
};
return data;

View File

@ -1,113 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
"use strict";
/**
* This file contains the base line graph that all Performance line graphs use.
*/
const {Cc, Ci, Cu, Cr} = require("chrome");
Cu.import("resource:///modules/devtools/Graphs.jsm");
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
const { colorUtils: { setAlpha }} = require("devtools/css-color");
const { getColor } = require("devtools/shared/theme");
loader.lazyRequireGetter(this, "ProfilerGlobal",
"devtools/shared/profiler/global");
loader.lazyRequireGetter(this, "TimelineGlobal",
"devtools/shared/timeline/global");
const HEIGHT = 35; // px
const STROKE_WIDTH = 1; // px
const DAMPEN_VALUES = 0.95;
const CLIPHEAD_LINE_COLOR = "#666";
const SELECTION_LINE_COLOR = "#555";
const SELECTION_BACKGROUND_COLOR_NAME = "highlight-blue";
const FRAMERATE_GRAPH_COLOR_NAME = "highlight-green";
const MEMORY_GRAPH_COLOR_NAME = "highlight-blue";
/**
* A base class for performance graphs to inherit from.
*
* @param nsIDOMNode parent
* The parent node holding the overview.
* @param string metric
* The unit of measurement for this graph.
*/
function PerformanceGraph(parent, metric) {
LineGraphWidget.call(this, parent, { metric });
this.setTheme();
}
PerformanceGraph.prototype = Heritage.extend(LineGraphWidget.prototype, {
strokeWidth: STROKE_WIDTH,
dampenValuesFactor: DAMPEN_VALUES,
fixedHeight: HEIGHT,
clipheadLineColor: CLIPHEAD_LINE_COLOR,
selectionLineColor: SELECTION_LINE_COLOR,
withTooltipArrows: false,
withFixedTooltipPositions: true,
/**
* Disables selection and empties this graph.
*/
clearView: function() {
this.selectionEnabled = false;
this.dropSelection();
this.setData([]);
},
/**
* Sets the theme via `theme` to either "light" or "dark",
* and updates the internal styling to match. Requires a redraw
* to see the effects.
*/
setTheme: function (theme) {
theme = theme || "light";
let mainColor = getColor(this.mainColor || "highlight-blue", theme);
this.backgroundColor = getColor("body-background", theme);
this.strokeColor = mainColor;
this.backgroundGradientStart = setAlpha(mainColor, 0.2);
this.backgroundGradientEnd = setAlpha(mainColor, 0.2);
this.selectionBackgroundColor = setAlpha(getColor(SELECTION_BACKGROUND_COLOR_NAME, theme), 0.25);
this.selectionStripesColor = "rgba(255, 255, 255, 0.1)";
this.maximumLineColor = setAlpha(mainColor, 0.4);
this.averageLineColor = setAlpha(mainColor, 0.7);
this.minimumLineColor = setAlpha(mainColor, 0.9);
}
});
/**
* Constructor for the framerate graph. Inherits from PerformanceGraph.
*
* @param nsIDOMNode parent
* The parent node holding the overview.
*/
function FramerateGraph(parent) {
PerformanceGraph.call(this, parent, ProfilerGlobal.L10N.getStr("graphs.fps"));
}
FramerateGraph.prototype = Heritage.extend(PerformanceGraph.prototype, {
mainColor: FRAMERATE_GRAPH_COLOR_NAME
});
exports.FramerateGraph = FramerateGraph;
/**
* Constructor for the memory graph. Inherits from PerformanceGraph.
*
* @param nsIDOMNode parent
* The parent node holding the overview.
*/
function MemoryGraph(parent) {
PerformanceGraph.call(this, parent, TimelineGlobal.L10N.getStr("graphs.memory"));
}
MemoryGraph.prototype = Heritage.extend(PerformanceGraph.prototype, {
mainColor: MEMORY_GRAPH_COLOR_NAME
});
exports.MemoryGraph = MemoryGraph;

View File

@ -22,6 +22,7 @@ const RecordingModel = function (options={}) {
this._console = options.console || false;
this._configuration = {
withMarkers: options.withMarkers || false,
withTicks: options.withTicks || false,
withMemory: options.withMemory || false,
withAllocations: options.withAllocations || false,
@ -70,6 +71,7 @@ RecordingModel.prototype = {
this._ticks = recordingData.ticks;
this._allocations = recordingData.allocations;
this._profile = recordingData.profile;
this._configuration = recordingData.configuration || {};
}),
/**
@ -226,7 +228,8 @@ RecordingModel.prototype = {
let ticks = this.getTicks();
let allocations = this.getAllocations();
let profile = this.getProfile();
return { label, duration, markers, frames, memory, ticks, allocations, profile };
let configuration = this.getConfiguration();
return { label, duration, markers, frames, memory, ticks, allocations, profile, configuration };
},
/**
@ -263,10 +266,13 @@ RecordingModel.prototype = {
return;
}
let config = this.getConfiguration();
switch (eventName) {
// Accumulate timeline markers into an array. Furthermore, the timestamps
// do not have a zero epoch, so offset all of them by the start time.
case "markers": {
if (!config.withMarkers) { break; }
let [markers] = data;
RecordingUtils.offsetMarkerTimes(markers, this._timelineStartTime);
Array.prototype.push.apply(this._markers, markers);
@ -274,6 +280,7 @@ RecordingModel.prototype = {
}
// Accumulate stack frames into an array.
case "frames": {
if (!config.withMarkers) { break; }
let [, frames] = data;
Array.prototype.push.apply(this._frames, frames);
break;
@ -281,6 +288,7 @@ RecordingModel.prototype = {
// Accumulate memory measurements into an array. Furthermore, the timestamp
// does not have a zero epoch, so offset it by the actor's start time.
case "memory": {
if (!config.withMemory) { break; }
let [currentTime, measurement] = data;
this._memory.push({
delta: currentTime - this._timelineStartTime,
@ -290,6 +298,7 @@ RecordingModel.prototype = {
}
// Save the accumulated refresh driver ticks.
case "ticks": {
if (!config.withTicks) { break; }
let [, timestamps] = data;
this._ticks = timestamps;
break;
@ -298,6 +307,7 @@ RecordingModel.prototype = {
// do not have a zero epoch, and are microseconds instead of milliseconds,
// so offset all of them by the start time, also converting from µs to ms.
case "allocations": {
if (!config.withAllocations) { break; }
let [{ sites, timestamps, frames, counts }] = data;
let timeOffset = this._memoryStartTime * 1000;
let timeScale = 1000;

View File

@ -6,8 +6,8 @@
EXTRA_JS_MODULES.devtools.performance += [
'modules/compatibility.js',
'modules/front.js',
'modules/graphs.js',
'modules/io.js',
'modules/performance-graphs.js',
'modules/recording-model.js',
'modules/recording-utils.js',
'panel.js'

View File

@ -27,12 +27,8 @@ devtools.lazyRequireGetter(this, "RecordingUtils",
"devtools/performance/recording-utils", true);
devtools.lazyRequireGetter(this, "RecordingModel",
"devtools/performance/recording-model", true);
devtools.lazyRequireGetter(this, "FramerateGraph",
"devtools/performance/performance-graphs", true);
devtools.lazyRequireGetter(this, "MemoryGraph",
"devtools/performance/performance-graphs", true);
devtools.lazyRequireGetter(this, "MarkersOverview",
"devtools/shared/timeline/markers-overview", true);
devtools.lazyRequireGetter(this, "GraphsController",
"devtools/performance/graphs", true);
devtools.lazyRequireGetter(this, "Waterfall",
"devtools/shared/timeline/waterfall", true);
devtools.lazyRequireGetter(this, "MarkerDetails",
@ -52,8 +48,6 @@ devtools.lazyRequireGetter(this, "FlameGraphUtils",
devtools.lazyRequireGetter(this, "FlameGraph",
"devtools/shared/widgets/FlameGraph", true);
devtools.lazyImporter(this, "CanvasGraphUtils",
"resource:///modules/devtools/Graphs.jsm");
devtools.lazyImporter(this, "SideMenuWidget",
"resource:///modules/devtools/SideMenuWidget.jsm");
devtools.lazyImporter(this, "PluralForm",
@ -291,6 +285,7 @@ let PerformanceController = {
*/
startRecording: Task.async(function *() {
let options = {
withMarkers: true,
withMemory: this.getOption("enable-memory"),
withTicks: this.getOption("enable-framerate"),
withAllocations: this.getOption("enable-memory"),
@ -470,6 +465,42 @@ let PerformanceController = {
return this._recordings;
},
/**
* Utility method taking the currently selected recording item's features, or optionally passed
* in recording item, as well as the actor support on the server, returning a boolean
* indicating if the requirements pass or not. Used to toggle features' visibility mostly.
*
* @option {Array<string>} features
* An array of strings indicating what configuration is needed on the recording
* model, like `withTicks`, or `withMemory`.
* @option {Array<string>} actors
* An array of strings indicating what actors must exist.
* @option {boolean} isRecording
* A boolean indicating whether the recording must be either recording or not
* recording. Setting to undefined will allow either state.
* @param {RecordingModel} recording
* An optional recording model to use instead of the currently selected.
*
* @return boolean
*/
isFeatureSupported: function ({ features, actors, isRecording: shouldBeRecording }, recording) {
recording = recording || this.getCurrentRecording();
let recordingConfig = recording ? recording.getConfiguration() : {};
let currentRecordingState = recording ? recording.isRecording() : void 0;
let actorsSupported = gFront.getActorSupport();
if (shouldBeRecording != null && shouldBeRecording !== currentRecordingState) {
return false;
}
if (actors && !actors.every(a => actorsSupported[a])) {
return false;
}
if (features && !features.every(f => recordingConfig[f])) {
return false;
}
return true;
},
toString: () => "[object PerformanceController]"
};

View File

@ -102,22 +102,27 @@
<toolbarbutton id="select-waterfall-view"
class="devtools-toolbarbutton devtools-button"
label="&profilerUI.toolbar.waterfall;"
hidden="true"
data-view="waterfall" />
<toolbarbutton id="select-js-calltree-view"
class="devtools-toolbarbutton devtools-button"
label="&profilerUI.toolbar.js-calltree;"
hidden="true"
data-view="js-calltree" />
<toolbarbutton id="select-js-flamegraph-view"
class="devtools-toolbarbutton devtools-button"
label="&profilerUI.toolbar.js-flamegraph;"
hidden="true"
data-view="js-flamegraph" />
<toolbarbutton id="select-memory-calltree-view"
class="devtools-toolbarbutton devtools-button"
label="&profilerUI.toolbar.memory-calltree1;"
hidden="true"
data-view="memory-calltree" />
<toolbarbutton id="select-memory-flamegraph-view"
class="devtools-toolbarbutton devtools-button"
label="&profilerUI.toolbar.memory-flamegraph1;"
hidden="true"
data-view="memory-flamegraph" />
</hbox>
<spacer flex="1"></spacer>

View File

@ -13,7 +13,6 @@ support-files =
[browser_perf-aaa-run-first-leaktest.js]
[browser_markers-parse-html.js]
[browser_perf-allocations-to-samples.js]
[browser_perf-compatibility-01.js]
[browser_perf-compatibility-02.js]
@ -43,6 +42,7 @@ support-files =
[browser_perf-details-02.js]
[browser_perf-details-03.js]
[browser_perf-details-04.js]
[browser_perf-details-05.js]
[browser_perf-events-calltree.js]
[browser_perf-front-basic-profiler-01.js]
[browser_perf-front-basic-timeline-01.js]

View File

@ -16,7 +16,7 @@ function* getMarkers(front) {
};
front.on("timeline-data", handler);
yield front.startRecording({ withTicks: true });
yield front.startRecording({ withMarkers: true, withTicks: true });
const markers = yield promise;
front.off("timeline-data", handler);

View File

@ -11,14 +11,13 @@ function spawnTest () {
// Enable platform data to show the `busyWait` function in the tree.
Services.prefs.setBoolPref(PLATFORM_DATA_PREF, true);
yield DetailsView.selectView("js-calltree");
ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
yield startRecording(panel);
yield busyWait(1000);
let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("js-calltree");
ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
yield rendered;
testCells($, $$, {

View File

@ -11,14 +11,13 @@ function spawnTest () {
// Enable memory to test.
Services.prefs.setBoolPref(MEMORY_PREF, true);
yield DetailsView.selectView("memory-calltree");
ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");
yield startRecording(panel);
yield busyWait(1000);
let rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("memory-calltree");
ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");
yield rendered;
testCells($, $$, {

View File

@ -14,12 +14,13 @@ function spawnTest () {
});
Services.prefs.setBoolPref(MEMORY_PREF, true);
let { memory, timeline } = front.getMocksInUse();
ok(memory, "memory should be mocked.");
ok(timeline, "timeline should be mocked.");
let { memory, timeline } = front.getActorSupport();
ok(!memory, "memory should be mocked.");
ok(!timeline, "timeline should be mocked.");
let recording = yield front.startRecording({
withTicks: true,
withMarkers: true,
withMemory: true,
withAllocations: true,
allocationsSampleProbability: +Services.prefs.getCharPref(MEMORY_SAMPLE_PROB_PREF),

View File

@ -16,9 +16,9 @@ let test = Task.async(function*() {
Services.prefs.setBoolPref(MEMORY_PREF, true);
let { EVENTS, $, gFront, PerformanceController, PerformanceView, DetailsView, JsCallTreeView } = panel.panelWin;
let { memory: memoryMock, timeline: timelineMock } = gFront.getMocksInUse();
ok(memoryMock, "memory should be mocked.");
ok(timelineMock, "timeline should be mocked.");
let { memory: memorySupport, timeline: timelineSupport } = gFront.getActorSupport();
ok(!memorySupport, "memory should be mocked.");
ok(!timelineSupport, "timeline should be mocked.");
yield startRecording(panel, { waitForOverview: false });
busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity

View File

@ -13,12 +13,13 @@ function spawnTest () {
});
Services.prefs.setBoolPref(MEMORY_PREF, true);
let { memory, timeline } = front.getMocksInUse();
ok(memory, "memory should be mocked.");
ok(!timeline, "timeline should not be mocked.");
let { memory, timeline } = front.getActorSupport();
ok(!memory, "memory should be mocked.");
ok(timeline, "timeline should not be mocked.");
let recording = yield front.startRecording({
withTicks: true,
withMarkers: true,
withMemory: true,
withAllocations: true,
allocationsSampleProbability: +Services.prefs.getCharPref(MEMORY_SAMPLE_PROB_PREF),

View File

@ -16,9 +16,9 @@ let test = Task.async(function*() {
let { EVENTS, $, gFront, PerformanceController, PerformanceView, DetailsView, WaterfallView } = panel.panelWin;
let { memory: memoryMock, timeline: timelineMock } = gFront.getMocksInUse();
ok(memoryMock, "memory should be mocked.");
ok(!timelineMock, "timeline should not be mocked.");
let { memory: memorySupport, timeline: timelineSupport } = gFront.getActorSupport();
ok(!memorySupport, "memory should be mocked.");
ok(timelineSupport, "timeline should not be mocked.");
yield startRecording(panel);
yield busyWait(100);

View File

@ -29,9 +29,7 @@ function spawnTest () {
yield once(OverviewView, EVENTS.OVERVIEW_RENDERED);
yield once(OverviewView, EVENTS.OVERVIEW_RENDERED);
let detailsRendered = once(WaterfallView, EVENTS.WATERFALL_RENDERED);
yield consoleProfileEnd(panel.panelWin, "rust");
yield detailsRendered;
recordings = PerformanceController.getRecordings();
is(recordings.length, 2, "two recordings found in the performance panel.");
@ -40,7 +38,7 @@ function spawnTest () {
is(RecordingsView.selectedItem.attachment.isRecording(), false,
"The first console recording should no longer be recording.");
detailsRendered = once(WaterfallView, EVENTS.WATERFALL_RENDERED);
let detailsRendered = once(WaterfallView, EVENTS.WATERFALL_RENDERED);
yield consoleProfileEnd(panel.panelWin, "golang");
yield detailsRendered;

View File

@ -8,6 +8,9 @@ function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, $, DetailsView, document: doc } = panel.panelWin;
yield startRecording(panel);
yield stopRecording(panel);
info("views on startup");
checkViews(DetailsView, doc, "waterfall");

View File

@ -9,6 +9,9 @@ function spawnTest () {
let { EVENTS, DetailsView } = panel.panelWin;
let { WaterfallView, JsCallTreeView, JsFlameGraphView } = panel.panelWin;
yield startRecording(panel);
yield stopRecording(panel);
ok(DetailsView.isViewSelected(WaterfallView),
"The waterfall view is selected by default in the details view.");
@ -18,7 +21,7 @@ function spawnTest () {
yield Promise.all([selected, notified]);
ok(DetailsView.isViewSelected(JsCallTreeView),
"The waterfall view is now selected in the details view.");
"The jscalltree view is now selected in the details view.");
selected = DetailsView.whenViewSelected(JsFlameGraphView);
notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);

View File

@ -2,17 +2,23 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the details view hides the memory buttons when `enable-memory` is toggled,
* and that it switches to default panel if toggling while a memory panel is selected.
* Tests that the details view hides the memory buttons when a recording does not
* have memory data (withMemory: false), and that when a memory panel is selected,
* switching to a panel that does not have memory goes to a default panel instead.
*/
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, PerformanceController, OverviewView, DetailsView } = panel.panelWin;
let { $, WaterfallView, MemoryCallTreeView, MemoryFlameGraphView } = panel.panelWin;
let { $, RecordingsView, WaterfallView, MemoryCallTreeView, MemoryFlameGraphView } = panel.panelWin;
Services.prefs.setBoolPref(MEMORY_PREF, false);
yield startRecording(panel);
yield stopRecording(panel);
ok(DetailsView.isViewSelected(WaterfallView),
"The waterfall view is selected by default in the details view.");
Services.prefs.setBoolPref(MEMORY_PREF, true);
// The toolbar buttons will always be hidden when a recording isn't available,
// so make sure we have one that's finished.
yield startRecording(panel);
@ -21,13 +27,8 @@ function spawnTest () {
let flameBtn = $("toolbarbutton[data-view='memory-flamegraph']");
let callBtn = $("toolbarbutton[data-view='memory-calltree']");
Services.prefs.setBoolPref(MEMORY_PREF, false);
is(flameBtn.hidden, true, "memory-flamegraph button hidden when enable-memory=false");
is(callBtn.hidden, true, "memory-calltree button hidden when enable-memory=false");
Services.prefs.setBoolPref(MEMORY_PREF, true);
is(flameBtn.hidden, false, "memory-flamegraph button shown when enable-memory=true");
is(callBtn.hidden, false, "memory-calltree button shown when enable-memory=true");
is(flameBtn.hidden, false, "memory-flamegraph button shown when recording has memory data");
is(callBtn.hidden, false, "memory-calltree button shown when recording has memory data");
let selected = DetailsView.whenViewSelected(MemoryCallTreeView);
let notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
@ -45,15 +46,21 @@ function spawnTest () {
ok(DetailsView.isViewSelected(MemoryFlameGraphView),
"The memory flamegraph view can now be selected.");
// Select the first recording with no memory data
selected = DetailsView.whenViewSelected(WaterfallView);
notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
Services.prefs.setBoolPref(MEMORY_PREF, false);
RecordingsView.selectedIndex = 0;
yield Promise.all([selected, notified]);
ok(DetailsView.isViewSelected(WaterfallView),
"The waterfall view is now selected when toggling off enable-memory when a memory panel is selected.");
"The waterfall view is now selected when switching back to a recording that does not have memory data");
is(flameBtn.hidden, true, "memory-flamegraph button hidden when recording does not have memory data");
is(callBtn.hidden, true, "memory-calltree button hidden when recording does not have memory data");
Services.prefs.setBoolPref(MEMORY_PREF, true);
// Go back to the recording with memory data
let render = WaterfallView.once(EVENTS.WATERFALL_RENDERED);
RecordingsView.selectedIndex = 1;
yield render;
selected = DetailsView.whenViewSelected(MemoryCallTreeView);
notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
@ -61,23 +68,9 @@ function spawnTest () {
yield Promise.all([selected, notified]);
ok(DetailsView.isViewSelected(MemoryCallTreeView),
"The memory call tree view can be selected again after re-enabling memory.");
selected = DetailsView.whenViewSelected(MemoryFlameGraphView);
notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
DetailsView.selectView("memory-flamegraph");
yield Promise.all([selected, notified]);
ok(DetailsView.isViewSelected(MemoryFlameGraphView),
"The memory flamegraph view can be selected again after re-enabling memory.");
selected = DetailsView.whenViewSelected(WaterfallView);
notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
Services.prefs.setBoolPref(MEMORY_PREF, false);
yield Promise.all([selected, notified]);
ok(DetailsView.isViewSelected(WaterfallView),
"The waterfall view is now selected when toggling off enable-memory when a memory panel is selected.");
"The memory call tree view can be selected again after going back to the view with memory data");
is(flameBtn.hidden, false, "memory-flamegraph button shown when recording has memory data");
is(callBtn.hidden, false, "memory-calltree button shown when recording has memory data");
yield teardown(panel);
finish();

View File

@ -7,7 +7,7 @@
*/
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, $, $$, PerformanceController, RecordingsView, DetailsView } = panel.panelWin;
let { EVENTS, $, $$, PerformanceController, RecordingsView, WaterfallView } = panel.panelWin;
let waterfallBtn = $("toolbarbutton[data-view='waterfall']");
let jsFlameBtn = $("toolbarbutton[data-view='js-flamegraph']");
@ -46,8 +46,9 @@ function spawnTest () {
is(memCallBtn.hidden, true, "memory-calltree button hidden when another recording starts.");
let select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
let render = once(WaterfallView, EVENTS.WATERFALL_RENDERED);
mousedown(panel.panelWin, $$(".recording-item")[0]);
yield select;
yield Promise.all([select, render]);
is(RecordingsView.selectedIndex, 0,
"The first recording was selected again.");
@ -71,7 +72,9 @@ function spawnTest () {
is(memFlameBtn.hidden, true, "memory-flamegraph button still hidden when second recording selected.");
is(memCallBtn.hidden, true, "memory-calltree button still hidden when second recording selected.");
render = once(WaterfallView, EVENTS.WATERFALL_RENDERED);
yield stopRecording(panel);
yield render;
is(RecordingsView.selectedIndex, 1,
"The second recording is still selected.");

View File

@ -0,0 +1,38 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the details view utility functions work as advertised.
*/
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, DetailsView } = panel.panelWin;
let { PerformanceController, WaterfallView, JsCallTreeView } = panel.panelWin;
yield startRecording(panel);
yield stopRecording(panel);
ok(DetailsView.isViewSelected(WaterfallView),
"The waterfall view is selected by default in the details view.");
let selected = DetailsView.whenViewSelected(JsCallTreeView);
let notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
yield DetailsView.selectView("js-calltree");
yield Promise.all([selected, notified]);
ok(DetailsView.isViewSelected(JsCallTreeView),
"The jscalltree view is now selected in the details view.");
yield PerformanceController.clearRecordings();
yield startRecording(panel);
let render = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
yield stopRecording(panel);
yield render;
ok(DetailsView.isViewSelected(JsCallTreeView),
"The jscalltree view is still selected in the details view");
yield teardown(panel);
finish();
}

View File

@ -8,14 +8,13 @@ function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, DetailsView, JsCallTreeView } = panel.panelWin;
yield DetailsView.selectView("js-calltree");
ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
yield startRecording(panel);
yield busyWait(100);
yield stopRecording(panel);
let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("js-calltree");
ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
yield rendered;
ok(true, "JsCallTreeView rendered after recording is stopped.");

View File

@ -8,14 +8,13 @@ function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, DetailsView, JsFlameGraphView } = panel.panelWin;
yield DetailsView.selectView("js-flamegraph");
ok(DetailsView.isViewSelected(JsFlameGraphView), "The flamegraph is now selected.");
yield startRecording(panel);
yield busyWait(100);
yield stopRecording(panel);
let rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("js-flamegraph");
ok(DetailsView.isViewSelected(JsFlameGraphView), "The flamegraph is now selected.");
yield rendered;
ok(true, "JsFlameGraphView rendered after recording is stopped.");

View File

@ -11,14 +11,13 @@ function spawnTest () {
// Enable memory to test.
Services.prefs.setBoolPref(MEMORY_PREF, true);
yield DetailsView.selectView("memory-calltree");
ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");
yield startRecording(panel);
yield busyWait(100);
yield stopRecording(panel);
let rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("memory-calltree");
ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");
yield rendered;
ok(true, "MemoryCallTreeView rendered after recording is stopped.");

View File

@ -11,14 +11,13 @@ function spawnTest () {
// Enable memory to test.
Services.prefs.setBoolPref(MEMORY_PREF, true);
yield DetailsView.selectView("memory-flamegraph");
ok(DetailsView.isViewSelected(MemoryFlameGraphView), "The flamegraph is now selected.");
yield startRecording(panel);
yield busyWait(100);
yield stopRecording(panel);
let rendered = once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("memory-flamegraph");
ok(DetailsView.isViewSelected(MemoryFlameGraphView), "The flamegraph is now selected.");
yield rendered;
ok(true, "MemoryFlameGraphView rendered after recording is stopped.");

View File

@ -8,14 +8,13 @@ function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, PerformanceController, DetailsView, WaterfallView } = panel.panelWin;
ok(DetailsView.isViewSelected(WaterfallView),
"The waterfall view is selected by default in the details view.");
yield startRecording(panel);
yield waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length);
let rendered = once(WaterfallView, EVENTS.WATERFALL_RENDERED);
yield stopRecording(panel);
ok(DetailsView.isViewSelected(WaterfallView),
"The waterfall view is selected by default in the details view.");
yield rendered;
ok(true, "WaterfallView rendered after recording is stopped.");

View File

@ -10,7 +10,7 @@ let WAIT_TIME = 1000;
function spawnTest () {
let { target, front } = yield initBackend(SIMPLE_URL);
let config = { withMemory: true, withTicks: true };
let config = { withMarkers: true, withMemory: true, withTicks: true };
yield front._request("memory", "attach");

View File

@ -25,7 +25,7 @@ function spawnTest () {
front.on("timeline-data", handler);
yield front.startRecording({ withMemory: true, withTicks: true });
yield front.startRecording({ withMarkers: true, withMemory: true, withTicks: true });
yield Promise.all(Object.keys(deferreds).map(type => deferreds[type].promise));
yield front.stopRecording();
front.off("timeline-data", handler);

View File

@ -8,22 +8,22 @@
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, PerformanceController, $ } = panel.panelWin;
Services.prefs.setBoolPref(FRAMERATE_PREF, false);
ok($("#time-framerate").hidden, "fps graph is hidden when ticks disabled");
yield startRecording(panel);
yield stopRecording(panel);
is(PerformanceController.getCurrentRecording().getConfiguration().withTicks, false,
"PerformanceFront started without ticks recording.");
ok($("#time-framerate").hidden, "fps graph is hidden when ticks disabled");
Services.prefs.setBoolPref(FRAMERATE_PREF, true);
ok(!$("#time-framerate").hidden, "fps graph is not hidden when ticks enabled");
ok($("#time-framerate").hidden, "fps graph is still hidden if recording does not contain ticks.");
yield startRecording(panel);
yield stopRecording(panel);
ok(!$("#time-framerate").hidden, "fps graph is not hidden when ticks enabled before recording");
is(PerformanceController.getCurrentRecording().getConfiguration().withTicks, true,
"PerformanceFront started with ticks recording.");

View File

@ -10,7 +10,6 @@ function spawnTest () {
let { EVENTS, PerformanceController, $ } = panel.panelWin;
Services.prefs.setBoolPref(MEMORY_PREF, false);
ok($("#memory-overview").hidden, "memory graph is hidden when memory disabled");
yield startRecording(panel);
yield stopRecording(panel);
@ -19,13 +18,16 @@ function spawnTest () {
"PerformanceFront started without memory recording.");
is(PerformanceController.getCurrentRecording().getConfiguration().withAllocations, false,
"PerformanceFront started without allocations recording.");
ok($("#memory-overview").hidden, "memory graph is hidden when memory disabled");
Services.prefs.setBoolPref(MEMORY_PREF, true);
ok(!$("#memory-overview").hidden, "memory graph is not hidden when memory enabled");
ok($("#memory-overview").hidden,
"memory graph is still hidden after enabling if recording did not start recording memory");
yield startRecording(panel);
yield stopRecording(panel);
ok(!$("#memory-overview").hidden, "memory graph is not hidden when memory enabled before recording");
is(PerformanceController.getCurrentRecording().getConfiguration().withMemory, true,
"PerformanceFront started with memory recording.");
is(PerformanceController.getCurrentRecording().getConfiguration().withAllocations, true,

View File

@ -10,13 +10,12 @@ function spawnTest () {
Services.prefs.setBoolPref(FLATTEN_PREF, true);
yield DetailsView.selectView("js-flamegraph");
yield startRecording(panel);
yield busyWait(100);
let rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
yield stopRecording(panel);
let rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
yield DetailsView.selectView("js-flamegraph");
yield rendered;
let samples1 = PerformanceController.getCurrentRecording().getProfile().threads[0].samples;

View File

@ -12,13 +12,12 @@ function spawnTest () {
Services.prefs.setBoolPref(MEMORY_PREF, true);
Services.prefs.setBoolPref(FLATTEN_PREF, true);
yield DetailsView.selectView("memory-flamegraph");
yield startRecording(panel);
yield busyWait(100);
let rendered = once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("memory-flamegraph");
yield rendered;
let allocations1 = PerformanceController.getCurrentRecording().getAllocations();

View File

@ -10,14 +10,13 @@ function spawnTest () {
Services.prefs.setBoolPref(INVERT_PREF, true);
yield DetailsView.selectView("js-calltree");
ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
yield startRecording(panel);
yield busyWait(100);
yield stopRecording(panel);
let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("js-calltree");
ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
yield rendered;
rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);

View File

@ -12,14 +12,13 @@ function spawnTest () {
Services.prefs.setBoolPref(MEMORY_PREF, true);
Services.prefs.setBoolPref(INVERT_PREF, true);
yield DetailsView.selectView("memory-calltree");
ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");
yield startRecording(panel);
yield busyWait(100);
yield stopRecording(panel);
let rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("memory-calltree");
ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");
yield rendered;
rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);

View File

@ -8,15 +8,14 @@ function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, DetailsView, JsFlameGraphView } = panel.panelWin;
yield DetailsView.selectView("js-flamegraph");
Services.prefs.setBoolPref(INVERT_FLAME_PREF, true);
yield startRecording(panel);
yield busyWait(100);
yield stopRecording(panel);
let rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("js-flamegraph");
yield rendered;
rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);

View File

@ -11,13 +11,12 @@ function spawnTest () {
Services.prefs.setBoolPref(MEMORY_PREF, true);
Services.prefs.setBoolPref(INVERT_FLAME_PREF, true);
yield DetailsView.selectView("memory-flamegraph");
yield startRecording(panel);
yield busyWait(100);
yield stopRecording(panel);
let rendered = once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("memory-flamegraph");
yield rendered;
rendered = once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED);

View File

@ -10,13 +10,12 @@ function spawnTest () {
Services.prefs.setBoolPref(IDLE_PREF, true);
yield DetailsView.selectView("js-flamegraph");
yield startRecording(panel);
yield busyWait(100);
yield stopRecording(panel);
let rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("js-flamegraph");
yield rendered;
rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);

View File

@ -12,13 +12,12 @@ function spawnTest () {
Services.prefs.setBoolPref(MEMORY_PREF, true);
Services.prefs.setBoolPref(IDLE_PREF, true);
yield DetailsView.selectView("memory-flamegraph");
yield startRecording(panel);
yield busyWait(100);
yield stopRecording(panel);
let rendered = once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("memory-flamegraph");
yield rendered;
rendered = once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED);

View File

@ -10,13 +10,12 @@ function spawnTest () {
Services.prefs.setBoolPref(PLATFORM_DATA_PREF, true);
yield DetailsView.selectView("js-calltree");
yield startRecording(panel);
yield busyWait(100);
yield stopRecording(panel);
let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("js-calltree");
yield rendered;
rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);

View File

@ -10,13 +10,12 @@ function spawnTest () {
Services.prefs.setBoolPref(PLATFORM_DATA_PREF, false);
yield DetailsView.selectView("js-flamegraph");
yield startRecording(panel);
yield busyWait(100);
yield stopRecording(panel);
let rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
yield stopRecording(panel);
yield DetailsView.selectView("js-flamegraph");
yield rendered;
rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);

View File

@ -21,25 +21,28 @@ function spawnTest () {
once(OverviewView, EVENTS.OVERVIEW_RENDERED),
]);
ok("selectionEnabled" in OverviewView.framerateGraph,
let framerate = OverviewView.graphs.get("framerate");
ok("selectionEnabled" in framerate,
"The selection should not be enabled for the framerate overview (1).");
is(OverviewView.framerateGraph.selectionEnabled, false,
is(framerate.selectionEnabled, false,
"The selection should not be enabled for the framerate overview (2).");
is(OverviewView.framerateGraph.hasSelection(), false,
is(framerate.hasSelection(), false,
"The framerate overview shouldn't have a selection before recording.");
ok("selectionEnabled" in OverviewView.markersOverview,
let markers = OverviewView.graphs.get("timeline");
ok("selectionEnabled" in markers,
"The selection should not be enabled for the markers overview (1).");
is(OverviewView.markersOverview.selectionEnabled, false,
is(markers.selectionEnabled, false,
"The selection should not be enabled for the markers overview (2).");
is(OverviewView.markersOverview.hasSelection(), false,
is(markers.hasSelection(), false,
"The markers overview shouldn't have a selection before recording.");
ok("selectionEnabled" in OverviewView.memoryOverview,
let memory = OverviewView.graphs.get("memory");
ok("selectionEnabled" in memory,
"The selection should not be enabled for the memory overview (1).");
is(OverviewView.memoryOverview.selectionEnabled, false,
is(memory.selectionEnabled, false,
"The selection should not be enabled for the memory overview (2).");
is(OverviewView.memoryOverview.hasSelection(), false,
is(memory.hasSelection(), false,
"The memory overview shouldn't have a selection before recording.");
let updated = 0;
@ -48,36 +51,36 @@ function spawnTest () {
ok((yield waitUntil(() => updated > 10)),
"The overviews were updated several times.");
ok("selectionEnabled" in OverviewView.framerateGraph,
ok("selectionEnabled" in framerate,
"The selection should still not be enabled for the framerate overview (1).");
is(OverviewView.framerateGraph.selectionEnabled, false,
is(framerate.selectionEnabled, false,
"The selection should still not be enabled for the framerate overview (2).");
is(OverviewView.framerateGraph.hasSelection(), false,
is(framerate.hasSelection(), false,
"The framerate overview still shouldn't have a selection before recording.");
ok("selectionEnabled" in OverviewView.markersOverview,
ok("selectionEnabled" in markers,
"The selection should still not be enabled for the markers overview (1).");
is(OverviewView.markersOverview.selectionEnabled, false,
is(markers.selectionEnabled, false,
"The selection should still not be enabled for the markers overview (2).");
is(OverviewView.markersOverview.hasSelection(), false,
is(markers.hasSelection(), false,
"The markers overview still shouldn't have a selection before recording.");
ok("selectionEnabled" in OverviewView.memoryOverview,
ok("selectionEnabled" in memory,
"The selection should still not be enabled for the memory overview (1).");
is(OverviewView.memoryOverview.selectionEnabled, false,
is(memory.selectionEnabled, false,
"The selection should still not be enabled for the memory overview (2).");
is(OverviewView.memoryOverview.hasSelection(), false,
is(memory.hasSelection(), false,
"The memory overview still shouldn't have a selection before recording.");
yield stopRecording(panel);
is(OverviewView.framerateGraph.selectionEnabled, true,
is(framerate.selectionEnabled, true,
"The selection should now be enabled for the framerate overview.");
is(OverviewView.markersOverview.selectionEnabled, true,
is(markers.selectionEnabled, true,
"The selection should now be enabled for the markers overview.");
is(OverviewView.memoryOverview.selectionEnabled, true,
is(memory.selectionEnabled, true,
"The selection should now be enabled for the memory overview.");
yield teardown(panel);

View File

@ -24,41 +24,40 @@ function spawnTest () {
yield stopRecording(panel);
ok(OverviewView.markersOverview.width > 0,
"The overview's framerate graph has a width.");
ok(OverviewView.markersOverview.dataScaleX > 0,
"The overview's framerate graph has a data scale factor.");
let markers = OverviewView.graphs.get("timeline");
let framerate = OverviewView.graphs.get("framerate");
let memory = OverviewView.graphs.get("memory");
ok(OverviewView.memoryOverview.width > 0,
ok(markers.width > 0,
"The overview's markers graph has a width.");
ok(markers.dataScaleX > 0,
"The overview's markers graph has a data scale factor.");
ok(memory.width > 0,
"The overview's memory graph has a width.");
ok(memory.dataDuration > 0,
"The overview's memory graph has a data duration.");
ok(memory.dataScaleX > 0,
"The overview's memory graph has a data scale factor.");
ok(framerate.width > 0,
"The overview's framerate graph has a width.");
ok(OverviewView.memoryOverview.dataDuration > 0,
ok(framerate.dataDuration > 0,
"The overview's framerate graph has a data duration.");
ok(OverviewView.memoryOverview.dataScaleX > 0,
ok(framerate.dataScaleX > 0,
"The overview's framerate graph has a data scale factor.");
ok(OverviewView.framerateGraph.width > 0,
"The overview's framerate graph has a width.");
ok(OverviewView.framerateGraph.dataDuration > 0,
"The overview's framerate graph has a data duration.");
ok(OverviewView.framerateGraph.dataScaleX > 0,
"The overview's framerate graph has a data scale factor.");
is(OverviewView.markersOverview.width,
OverviewView.memoryOverview.width,
"The markers and memory graphs widths are the same.")
is(OverviewView.markersOverview.width,
OverviewView.framerateGraph.width,
is(markers.width, memory.width,
"The markers and memory graphs widths are the same.");
is(markers.width, framerate.width,
"The markers and framerate graphs widths are the same.");
is(OverviewView.memoryOverview.dataDuration,
OverviewView.framerateGraph.dataDuration,
is(memory.dataDuration, framerate.dataDuration,
"The memory and framerate graphs data duration are the same.");
is(OverviewView.markersOverview.dataScaleX,
OverviewView.memoryOverview.dataScaleX,
"The markers and memory graphs data scale are the same.")
is(OverviewView.markersOverview.dataScaleX,
OverviewView.framerateGraph.dataScaleX,
is(markers.dataScaleX, memory.dataScaleX,
"The markers and memory graphs data scale are the same.");
is(markers.dataScaleX, framerate.dataScaleX,
"The markers and framerate graphs data scale are the same.");
yield teardown(panel);

View File

@ -19,7 +19,7 @@ function spawnTest () {
yield stopRecording(panel);
let graph = OverviewView.markersOverview;
let graph = OverviewView.graphs.get("timeline");
let MAX = graph.width;
// Select the first half of the graph

View File

@ -20,9 +20,9 @@ function spawnTest () {
once(OverviewView, EVENTS.OVERVIEW_RENDERED),
]);
let markersOverview = OverviewView.markersOverview;
let memoryOverview = OverviewView.memoryOverview;
let framerateGraph = OverviewView.framerateGraph;
let markersOverview = OverviewView.graphs.get("timeline");
let memoryOverview = OverviewView.graphs.get("memory");
let framerateGraph = OverviewView.graphs.get("framerate");
ok(markersOverview,
"The markers graph should have been created now.");

View File

@ -22,9 +22,9 @@ function spawnTest () {
yield stopRecording(panel);
let framerateGraph = OverviewView.framerateGraph;
let markersOverview = OverviewView.markersOverview;
let memoryOverview = OverviewView.memoryOverview;
let framerateGraph = OverviewView.graphs.get("framerate");
let markersOverview = OverviewView.graphs.get("timeline");
let memoryOverview = OverviewView.graphs.get("memory");
let MAX = framerateGraph.width;
// Perform a selection inside the framerate graph.

View File

@ -17,6 +17,9 @@ let test = Task.async(function*() {
// `waitForWidgetsRendered`.
DetailsSubview.canUpdateWhileHidden = true;
yield startRecording(panel);
yield stopRecording(panel);
// Cycle through all the views to initialize them, otherwise we can't use
// `waitForWidgetsRendered`. The waterfall is shown by default, but all the
// other views are created lazily, so won't emit any events.
@ -28,9 +31,6 @@ let test = Task.async(function*() {
yield startRecording(panel);
yield stopRecording(panel);
yield startRecording(panel);
yield stopRecording(panel);
let rerender = waitForWidgetsRendered(panel);
RecordingsView.selectedIndex = 0;
yield rerender;

View File

@ -11,15 +11,15 @@ function spawnTest () {
is(PerformanceView.getState(), "empty",
"The intial state of the performance panel view is correct.");
ok(!("markersOverview" in OverviewView),
ok(!(OverviewView.graphs.get("timeline")),
"The markers graph should not have been created yet.");
ok(!("memoryOverview" in OverviewView),
ok(!(OverviewView.graphs.get("memory")),
"The memory graph should not have been created yet.");
ok(!("framerateGraph" in OverviewView),
ok(!(OverviewView.graphs.get("framerate")),
"The framerate graph should not have been created yet.");
ok(DetailsView.components["waterfall"].initialized,
"The waterfall detail view should have been created by default.");
ok(!DetailsView.components["waterfall"].initialized,
"The waterfall detail view should not have been created yet.");
ok(!DetailsView.components["js-calltree"].initialized,
"The js-calltree detail view should not have been created yet.");
ok(!DetailsView.components["js-flamegraph"].initialized,
@ -31,11 +31,11 @@ function spawnTest () {
Services.prefs.setBoolPref(MEMORY_PREF, true);
ok(!("markersOverview" in OverviewView),
ok(!(OverviewView.graphs.get("timeline")),
"The markers graph should still not have been created yet.");
ok(!("memoryOverview" in OverviewView),
ok(!(OverviewView.graphs.get("memory")),
"The memory graph should still not have been created yet.");
ok(!("framerateGraph" in OverviewView),
ok(!(OverviewView.graphs.get("framerate")),
"The framerate graph should still not have been created yet.");
let stateChanged = once(PerformanceView, EVENTS.UI_STATE_CHANGED);
@ -44,9 +44,9 @@ function spawnTest () {
is(PerformanceView.getState(), "recording",
"The current state of the performance panel view is 'recording'.");
ok(OverviewView.memoryOverview,
ok(OverviewView.graphs.get("memory"),
"The memory graph should have been created now.");
ok(OverviewView.framerateGraph,
ok(OverviewView.graphs.get("framerate"),
"The framerate graph should have been created now.");
stateChanged = once(PerformanceView, EVENTS.UI_STATE_CHANGED);

View File

@ -17,20 +17,21 @@ function spawnTest () {
let { EVENTS, $, OverviewView, document: doc } = panel.panelWin;
yield startRecording(panel);
is(OverviewView.markersOverview.backgroundColor, DARK_BG,
let markers = OverviewView.graphs.get("timeline");
is(markers.backgroundColor, DARK_BG,
"correct theme on load for markers.");
yield stopRecording(panel);
let refreshed = once(OverviewView.markersOverview, "refresh");
let refreshed = once(markers, "refresh");
setTheme("light");
yield refreshed;
ok(true, "markers were rerendered after theme change.");
is(OverviewView.markersOverview.backgroundColor, LIGHT_BG,
is(markers.backgroundColor, LIGHT_BG,
"correct theme on after toggle for markers.");
// reset back to dark
refreshed = once(OverviewView.markersOverview, "refresh");
refreshed = once(markers, "refresh");
setTheme("dark");
yield refreshed;
@ -39,26 +40,27 @@ function spawnTest () {
Services.prefs.setBoolPref(MEMORY_PREF, true);
yield startRecording(panel);
is(OverviewView.memoryOverview.backgroundColor, DARK_BG,
let memory = OverviewView.graphs.get("memory");
is(memory.backgroundColor, DARK_BG,
"correct theme on load for memory.");
yield stopRecording(panel);
refreshed = Promise.all([
once(OverviewView.markersOverview, "refresh"),
once(OverviewView.memoryOverview, "refresh"),
once(markers, "refresh"),
once(memory, "refresh"),
]);
setTheme("light");
yield refreshed;
ok(true, "Both memory and markers were rerendered after theme change.");
is(OverviewView.markersOverview.backgroundColor, LIGHT_BG,
is(markers.backgroundColor, LIGHT_BG,
"correct theme on after toggle for markers.");
is(OverviewView.memoryOverview.backgroundColor, LIGHT_BG,
is(memory.backgroundColor, LIGHT_BG,
"correct theme on after toggle for memory.");
refreshed = Promise.all([
once(OverviewView.markersOverview, "refresh"),
once(OverviewView.memoryOverview, "refresh"),
once(markers, "refresh"),
once(memory, "refresh"),
]);
// Set theme back to light

View File

@ -11,6 +11,14 @@ let test = Task.async(function*() {
// Enable memory to test the memory-calltree and memory-flamegraph.
Services.prefs.setBoolPref(MEMORY_PREF, true);
Services.prefs.setBoolPref(FRAMERATE_PREF, true);
// Need to allow widgets to be updated while hidden, otherwise we can't use
// `waitForWidgetsRendered`.
DetailsSubview.canUpdateWhileHidden = true;
yield startRecording(panel);
yield stopRecording(panel);
// Cycle through all the views to initialize them, otherwise we can't use
// `waitForWidgetsRendered`. The waterfall is shown by default, but all the
@ -20,12 +28,6 @@ let test = Task.async(function*() {
yield DetailsView.selectView("memory-calltree");
yield DetailsView.selectView("memory-flamegraph");
// Need to allow widgets to be updated while hidden, otherwise we can't use
// `waitForWidgetsRendered`.
DetailsSubview.canUpdateWhileHidden = true;
yield startRecording(panel);
yield stopRecording(panel);
// Verify original recording.
@ -60,19 +62,23 @@ let test = Task.async(function*() {
let importedData = PerformanceController.getCurrentRecording().getAllData();
is(importedData.label, originalData.label,
"The impored data is identical to the original data (1).");
"The imported data is identical to the original data (1).");
is(importedData.duration, originalData.duration,
"The impored data is identical to the original data (2).");
"The imported data is identical to the original data (2).");
is(importedData.markers.toSource(), originalData.markers.toSource(),
"The impored data is identical to the original data (3).");
"The imported data is identical to the original data (3).");
is(importedData.memory.toSource(), originalData.memory.toSource(),
"The impored data is identical to the original data (4).");
"The imported data is identical to the original data (4).");
is(importedData.ticks.toSource(), originalData.ticks.toSource(),
"The impored data is identical to the original data (5).");
"The imported data is identical to the original data (5).");
is(importedData.allocations.toSource(), originalData.allocations.toSource(),
"The impored data is identical to the original data (6).");
"The imported data is identical to the original data (6).");
is(importedData.profile.toSource(), originalData.profile.toSource(),
"The impored data is identical to the original data (7).");
"The imported data is identical to the original data (7).");
is(importedData.configuration.withTicks, originalData.configuration.withTicks,
"The imported data is identical to the original data (8).");
is(importedData.configuration.withMemory, originalData.configuration.withMemory,
"The imported data is identical to the original data (9).");
yield teardown(panel);
finish();

View File

@ -3,28 +3,16 @@
/**
* Tests if the performance tool can import profiler data from the
* original profiler tool.
* original profiler tool and the correct views and graphs are loaded.
*/
let test = Task.async(function*() {
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
let { EVENTS, PerformanceController, DetailsView, DetailsSubview } = panel.panelWin;
let { $, EVENTS, PerformanceController, DetailsView, OverviewView, JsCallTreeView } = panel.panelWin;
// Enable memory to test the memory-calltree and memory-flamegraph.
Services.prefs.setBoolPref(MEMORY_PREF, true);
// Cycle through all the views to initialize them, otherwise we can't use
// `waitForWidgetsRendered`. The waterfall is shown by default, but all the
// other views are created lazily, so won't emit any events.
yield DetailsView.selectView("js-calltree");
yield DetailsView.selectView("js-flamegraph");
yield DetailsView.selectView("memory-calltree");
yield DetailsView.selectView("memory-flamegraph");
// Need to allow widgets to be updated while hidden, otherwise we can't use
// `waitForWidgetsRendered`.
DetailsSubview.canUpdateWhileHidden = true;
yield startRecording(panel);
yield stopRecording(panel);
@ -49,16 +37,30 @@ let test = Task.async(function*() {
// Import recording.
let rerendered = waitForWidgetsRendered(panel);
let calltreeRendered = once(OverviewView, EVENTS.FRAMERATE_GRAPH_RENDERED);
let fpsRendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
yield PerformanceController.importRecording("", file);
yield imported;
ok(true, "The original profiler data appears to have been successfully imported.");
yield rerendered;
yield calltreeRendered;
yield fpsRendered;
ok(true, "The imported data was re-rendered.");
// Ensure that only framerate and js calltree/flamegraph view are available
is($("#overview-pane").hidden, false, "overview graph container still shown");
is($("#memory-overview").hidden, true, "memory graph hidden");
is($("#markers-overview").hidden, true, "markers overview graph hidden");
is($("#time-framerate").hidden, false, "fps graph shown");
is($("#select-waterfall-view").hidden, true, "waterfall button hidden");
is($("#select-js-calltree-view").hidden, false, "jscalltree button shown");
is($("#select-js-flamegraph-view").hidden, false, "jsflamegraph button shown");
is($("#select-memory-calltree-view").hidden, true, "memorycalltree button hidden");
is($("#select-memory-flamegraph-view").hidden, true, "memoryflamegraph button hidden");
ok(DetailsView.isViewSelected(JsCallTreeView), "jscalltree view selected as its the only option");
// Verify imported recording.
let importedData = PerformanceController.getCurrentRecording().getAllData();
@ -79,6 +81,12 @@ let test = Task.async(function*() {
"The imported legacy data was successfully converted for the current tool (7).");
is(importedData.profile.toSource(), data.profile.toSource(),
"The imported legacy data was successfully converted for the current tool (8).");
is(importedData.configuration.withTicks, true,
"The imported legacy data was successfully converted for the current tool (9).");
is(importedData.configuration.withMemory, false,
"The imported legacy data was successfully converted for the current tool (10).");
is(importedData.configuration.sampleFrequency, void 0,
"The imported legacy data was successfully converted for the current tool (11).");
yield teardown(panel);
finish();

View File

@ -8,7 +8,8 @@
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { $, $$, PerformanceController, OverviewView, WaterfallView } = panel.panelWin;
let { MARKERS_GRAPH_ROW_HEIGHT } = panel.panelWin;
let { TimelineGraph } = devtools.require("devtools/performance/graphs");
let { rowHeight: MARKERS_GRAPH_ROW_HEIGHT } = TimelineGraph.prototype;
yield startRecording(panel);
ok(true, "Recording has started.");
@ -23,7 +24,7 @@ function spawnTest () {
yield stopRecording(panel);
let overview = OverviewView.markersOverview;
let overview = OverviewView.graphs.get("timeline");
let waterfall = WaterfallView.waterfall;
// Select everything

View File

@ -354,6 +354,7 @@ function* startRecording(panel, options = {
: Promise.resolve();
yield hasStarted;
let overviewRendered = options.waitForOverview
? once(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED)
: Promise.resolve();
@ -379,6 +380,7 @@ function* stopRecording(panel, options = {
let willStop = panel.panelWin.PerformanceController.once(win.EVENTS.RECORDING_WILL_STOP);
let hasStopped = panel.panelWin.PerformanceController.once(win.EVENTS.RECORDING_STOPPED);
let button = win.$("#main-record-button");
let overviewRendered = null;
ok(button.hasAttribute("checked"),
"The record button should already be checked.");
@ -399,12 +401,17 @@ function* stopRecording(panel, options = {
: Promise.resolve();
yield hasStopped;
let overviewRendered = options.waitForOverview
? once(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED)
: Promise.resolve();
// Wait for the final rendering of the overview, not a low res
// incremental rendering and less likely to be from another rendering that was selected
while (!overviewRendered && options.waitForOverview) {
let [_, res] = yield onceSpread(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED);
if (res === win.FRAMERATE_GRAPH_HIGH_RES_INTERVAL) {
overviewRendered = true;
}
}
yield stateChanged;
yield overviewRendered;
is(win.PerformanceView.getState(), "recorded",
"The current state is 'recorded'.");

View File

@ -16,7 +16,8 @@ let DetailsView = {
"waterfall": {
id: "waterfall-view",
view: WaterfallView,
requires: ["timeline"]
actors: ["timeline"],
features: ["withMarkers"]
},
"js-calltree": {
id: "js-profile-view",
@ -25,19 +26,19 @@ let DetailsView = {
"js-flamegraph": {
id: "js-flamegraph-view",
view: JsFlameGraphView,
requires: ["timeline"]
actors: ["timeline"]
},
"memory-calltree": {
id: "memory-calltree-view",
view: MemoryCallTreeView,
requires: ["memory"],
pref: "enable-memory"
actors: ["memory"],
features: ["withAllocations"]
},
"memory-flamegraph": {
id: "memory-flamegraph-view",
view: MemoryFlameGraphView,
requires: ["memory", "timeline"],
pref: "enable-memory"
actors: ["memory", "timeline"],
features: ["withAllocations"]
}
},
@ -56,7 +57,6 @@ let DetailsView = {
button.addEventListener("command", this._onViewToggle);
}
yield this.selectDefaultView();
yield this.setAvailableViews();
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
@ -84,32 +84,53 @@ let DetailsView = {
}),
/**
* Sets the possible views based off of prefs and server actor support by hiding/showing the
* buttons that select them and going to default view if currently selected.
* Called when a preference changes in `devtools.performance.ui.`.
* Sets the possible views based off of recording features and server actor support
* by hiding/showing the buttons that select them and going to default view
* if currently selected. Called when a preference changes in `devtools.performance.ui.`.
*/
setAvailableViews: Task.async(function* () {
let mocks = gFront.getMocksInUse();
let recording = PerformanceController.getCurrentRecording();
let isRecording = recording && recording.isRecording();
let invalidCurrentView = false;
for (let [name, { view, pref, requires }] of Iterator(this.components)) {
let recording = PerformanceController.getCurrentRecording();
for (let [name, { view }] of Iterator(this.components)) {
let isSupported = this._isViewSupported(name, false);
let isRecorded = recording && !recording.isRecording();
// View is enabled by its corresponding pref
let isEnabled = !pref || PerformanceController.getOption(pref);
// View is supported by the server actor, and the requried actor is not being mocked
let isSupported = !requires || requires.every(r => !mocks[r]);
$(`toolbarbutton[data-view=${name}]`).hidden = !isSupported;
$(`toolbarbutton[data-view=${name}]`).hidden = !isRecorded || !(isEnabled && isSupported);
// If the view is currently selected and not enabled, go back to the
// If the view is currently selected and not supported, go back to the
// default view.
if (!isEnabled && this.isViewSelected(view)) {
yield this.selectDefaultView();
if (!isSupported && this.isViewSelected(view)) {
invalidCurrentView = true;
}
}
// Two scenarios in which we select the default view.
//
// 1: If we currently have selected a view that is no longer valid due
// to feature support, and this isn't the first view, and the current recording
// is not recording.
//
// 2. If we have a finished recording and no panel was selected yet,
// use a default now that we have the recording configurations
if ((this._initialized && !isRecording && invalidCurrentView) ||
(!this._initialized && !isRecording && recording)) {
yield this.selectDefaultView();
}
}),
/**
* Takes a view name and optionally if there must be a currently recording in progress.
*
* @param {string} viewName
* @param {boolean?} isRecording
* @return {boolean}
*/
_isViewSupported: function (viewName, isRecording) {
let { features, actors } = this.components[viewName];
return PerformanceController.isFeatureSupported({ features, actors, isRecording });
},
/**
* Select one of the DetailView's subviews to be rendered,
* hiding the others.
@ -131,6 +152,10 @@ let DetailsView = {
}
}
// Set a flag indicating that a view was explicitly set based on a
// recording's features.
this._initialized = true;
this.emit(EVENTS.DETAILS_VIEW_SELECTED, viewName);
}),
@ -139,14 +164,15 @@ let DetailsView = {
* and preferences enabled.
*/
selectDefaultView: function () {
let { timeline: mockTimeline } = gFront.getMocksInUse();
// If timelines are mocked, the first view available is the js-calltree.
if (mockTimeline) {
return this.selectView("js-calltree");
} else {
// In every other scenario with preferences and mocks, waterfall will
// be the default view.
// We want the waterfall to be default view in almost all cases, except when
// timeline actor isn't supported, or we have markers disabled (which should only
// occur temporarily via bug 1156499
if (this._isViewSupported("waterfall")) {
return this.selectView("waterfall");
} else {
// The JS CallTree should always be supported since the profiler
// actor is as old as the world.
return this.selectView("js-calltree");
}
},
@ -157,6 +183,12 @@ let DetailsView = {
* @return boolean
*/
isViewSelected: function(viewObject) {
// If not initialized, and we have no recordings,
// no views are selected (even though there's a selected panel)
if (!this._initialized) {
return false;
}
let selectedPanel = this.el.selectedPanel;
let selectedId = selectedPanel.id;

View File

@ -7,26 +7,45 @@
// backend. Make sure this isn't lower than DEFAULT_TIMELINE_DATA_PULL_TIMEOUT
// in toolkit/devtools/server/actors/timeline.js
const OVERVIEW_UPDATE_INTERVAL = 200; // ms
const FRAMERATE_GRAPH_LOW_RES_INTERVAL = 100; // ms
const FRAMERATE_GRAPH_HIGH_RES_INTERVAL = 16; // ms
const MARKERS_GRAPH_HEADER_HEIGHT = 14; // px
const MARKERS_GRAPH_ROW_HEIGHT = 10; // px
const MARKERS_GROUP_VERTICAL_PADDING = 4; // px
const GRAPH_REQUIREMENTS = {
timeline: {
actors: ["timeline"],
features: ["withMarkers"]
},
framerate: {
actors: ["timeline"],
features: ["withTicks"]
},
memory: {
actors: ["memory"],
features: ["withMemory"]
},
}
/**
* View handler for the overview panel's time view, displaying
* framerate, markers and memory over time.
* framerate, timeline and memory over time.
*/
let OverviewView = {
/**
* Sets up the view with event binding.
*/
initialize: function () {
if (gFront.getMocksInUse().timeline) {
this.graphs = new GraphsController({
root: $("#overview-pane"),
getBlueprint: () => PerformanceController.getTimelineBlueprint(),
getTheme: () => PerformanceController.getTheme(),
});
// If no timeline support, shut it all down.
if (!gFront.getActorSupport().timeline) {
this.disable();
return;
}
this._onRecordingWillStart = this._onRecordingWillStart.bind(this);
this._onRecordingStarted = this._onRecordingStarted.bind(this);
this._onRecordingWillStop = this._onRecordingWillStop.bind(this);
@ -34,14 +53,12 @@ let OverviewView = {
this._onRecordingSelected = this._onRecordingSelected.bind(this);
this._onRecordingTick = this._onRecordingTick.bind(this);
this._onGraphSelecting = this._onGraphSelecting.bind(this);
this._onGraphRendered = this._onGraphRendered.bind(this);
this._onPrefChanged = this._onPrefChanged.bind(this);
this._onThemeChanged = this._onThemeChanged.bind(this);
// Toggle the initial visibility of memory and framerate graph containers
// based off of prefs.
$("#memory-overview").hidden = !PerformanceController.getOption("enable-memory");
$("#time-framerate").hidden = !PerformanceController.getOption("enable-framerate");
PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
PerformanceController.on(EVENTS.THEME_CHANGED, this._onThemeChanged);
PerformanceController.on(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
@ -52,22 +69,14 @@ let OverviewView = {
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STARTED, this._onRecordingStarted);
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.on(EVENTS.CONSOLE_RECORDING_WILL_STOP, this._onRecordingWillStop);
this.graphs.on("selecting", this._onGraphSelecting);
this.graphs.on("rendered", this._onGraphRendered);
},
/**
* Unbinds events.
*/
destroy: Task.async(function*() {
if (this.markersOverview) {
yield this.markersOverview.destroy();
}
if (this.memoryOverview) {
yield this.memoryOverview.destroy();
}
if (this.framerateGraph) {
yield this.framerateGraph.destroy();
}
PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged);
PerformanceController.off(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
@ -78,16 +87,19 @@ let OverviewView = {
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STARTED, this._onRecordingStarted);
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.off(EVENTS.CONSOLE_RECORDING_WILL_STOP, this._onRecordingWillStop);
this.graphs.off("selecting", this._onGraphSelecting);
this.graphs.off("rendered", this._onGraphRendered);
yield this.graphs.destroy();
}),
/**
* Disabled in the event we're using a Timeline mock, so we'll have no
* markers, ticks or memory data to show, so just block rendering and hide
* timeline, ticks or memory data to show, so just block rendering and hide
* the panel.
*/
disable: function () {
this._disabled = true;
$("#overview-pane").hidden = true;
this.graphs.disableAll();
},
/**
@ -99,28 +111,6 @@ let OverviewView = {
return this._disabled;
},
/**
* Sets the theme for the markers overview and memory overview.
*/
setTheme: function (options={}) {
let theme = options.theme || PerformanceController.getTheme();
if (this.framerateGraph) {
this.framerateGraph.setTheme(theme);
this.framerateGraph.refresh({ force: options.redraw });
}
if (this.markersOverview) {
this.markersOverview.setTheme(theme);
this.markersOverview.refresh({ force: options.redraw });
}
if (this.memoryOverview) {
this.memoryOverview.setTheme(theme);
this.memoryOverview.refresh({ force: options.redraw });
}
},
/**
* Sets the time interval selection for all graphs in this overview.
*
@ -139,7 +129,7 @@ let OverviewView = {
let mapEnd = () => recording.getDuration();
let selection = { start: interval.startTime, end: interval.endTime };
this._stopSelectionChangeEventPropagation = options.stopPropagation;
this.markersOverview.setMappedSelection(selection, { mapStart, mapEnd });
this.graphs.setMappedSelection(selection, { mapStart, mapEnd });
this._stopSelectionChangeEventPropagation = false;
},
@ -159,80 +149,10 @@ let OverviewView = {
}
let mapStart = () => 0;
let mapEnd = () => recording.getDuration();
let selection = this.markersOverview.getMappedSelection({ mapStart, mapEnd });
let selection = this.graphs.getMappedSelection({ mapStart, mapEnd });
return { startTime: selection.min, endTime: selection.max };
},
/**
* Sets up the markers overivew graph, if needed.
*
* @return object
* A promise resolved to `true` when the graph was initialized.
*/
_markersGraphAvailable: Task.async(function *() {
if (this.markersOverview) {
yield this.markersOverview.ready();
return true;
}
let blueprint = PerformanceController.getTimelineBlueprint();
this.markersOverview = new MarkersOverview($("#markers-overview"), blueprint);
this.markersOverview.headerHeight = MARKERS_GRAPH_HEADER_HEIGHT;
this.markersOverview.rowHeight = MARKERS_GRAPH_ROW_HEIGHT;
this.markersOverview.groupPadding = MARKERS_GROUP_VERTICAL_PADDING;
this.markersOverview.on("selecting", this._onGraphSelecting);
yield this.markersOverview.ready();
this.setTheme();
return true;
}),
/**
* Sets up the memory overview graph, if allowed and needed.
*
* @return object
* A promise resolved to `true` if the graph was initialized and is
* ready to use, `false` if the graph is disabled.
*/
_memoryGraphAvailable: Task.async(function *() {
if (!PerformanceController.getOption("enable-memory")) {
return false;
}
if (this.memoryOverview) {
yield this.memoryOverview.ready();
return true;
}
this.memoryOverview = new MemoryGraph($("#memory-overview"));
yield this.memoryOverview.ready();
this.setTheme();
CanvasGraphUtils.linkAnimation(this.markersOverview, this.memoryOverview);
CanvasGraphUtils.linkSelection(this.markersOverview, this.memoryOverview);
return true;
}),
/**
* Sets up the framerate graph, if allowed and needed.
*
* @return object
* A promise resolved to `true` if the graph was initialized and is
* ready to use, `false` if the graph is disabled.
*/
_framerateGraphAvailable: Task.async(function *() {
if (!PerformanceController.getOption("enable-framerate")) {
return false;
}
if (this.framerateGraph) {
yield this.framerateGraph.ready();
return true;
}
this.framerateGraph = new FramerateGraph($("#time-framerate"));
yield this.framerateGraph.ready();
this.setTheme();
CanvasGraphUtils.linkAnimation(this.markersOverview, this.framerateGraph);
CanvasGraphUtils.linkSelection(this.markersOverview, this.framerateGraph);
return true;
}),
/**
* Method for handling all the set up for rendering the overview graphs.
*
@ -244,29 +164,10 @@ let OverviewView = {
return;
}
let recording = PerformanceController.getCurrentRecording();
let duration = recording.getDuration();
let markers = recording.getMarkers();
let memory = recording.getMemory();
let timestamps = recording.getTicks();
// Empty or older recordings might yield no markers, memory or timestamps.
if (markers && (yield this._markersGraphAvailable())) {
this.markersOverview.setData({ markers, duration });
this.emit(EVENTS.MARKERS_GRAPH_RENDERED);
}
if (memory && (yield this._memoryGraphAvailable())) {
this.memoryOverview.dataDuration = duration;
this.memoryOverview.setData(memory);
this.emit(EVENTS.MEMORY_GRAPH_RENDERED);
}
if (timestamps && (yield this._framerateGraphAvailable())) {
this.framerateGraph.dataDuration = duration;
yield this.framerateGraph.setDataFromTimestamps(timestamps, resolution);
this.emit(EVENTS.FRAMERATE_GRAPH_RENDERED);
}
yield this.graphs.render(recording.getAllData(), resolution);
// Finished rendering all graphs in this overview.
this.emit(EVENTS.OVERVIEW_RENDERED);
this.emit(EVENTS.OVERVIEW_RENDERED, resolution);
}),
/**
@ -290,24 +191,6 @@ let OverviewView = {
}
},
/**
* Fired when the graph selection has changed. Called by
* mouseup and scroll events.
*/
_onGraphSelecting: function () {
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);
}
},
/**
* Called when recording will start. No recording because it does not
* exist yet, but can just disable from here. This will only trigger for
@ -316,7 +199,7 @@ let OverviewView = {
_onRecordingWillStart: Task.async(function* () {
this._onRecordingStateChange();
yield this._checkSelection();
this.markersOverview.dropSelection();
this.graphs.dropSelection();
}),
/**
@ -358,12 +241,14 @@ let OverviewView = {
return;
}
this._onRecordingStateChange();
this._setGraphVisibilityFromRecordingFeatures(recording);
// If this recording is complete, render the high res graph
if (!recording.isRecording()) {
yield this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
}
yield this._checkSelection(recording);
this.markersOverview.dropSelection();
this.graphs.dropSelection();
}),
/**
@ -407,49 +292,73 @@ let OverviewView = {
* based on whether a recording currently exists and is not in progress.
*/
_checkSelection: Task.async(function* (recording) {
let selectionEnabled = recording ? !recording.isRecording() : false;
if (yield this._markersGraphAvailable()) {
this.markersOverview.selectionEnabled = selectionEnabled;
}
if (yield this._memoryGraphAvailable()) {
this.memoryOverview.selectionEnabled = selectionEnabled;
}
if (yield this._framerateGraphAvailable()) {
this.framerateGraph.selectionEnabled = selectionEnabled;
}
let isEnabled = recording ? !recording.isRecording() : false;
yield this.graphs.selectionEnabled(isEnabled);
}),
/**
* Called whenever a preference in `devtools.performance.ui.` changes. Used
* to toggle the visibility of memory and framerate graphs.
* Fired when the graph selection has changed. Called by
* mouseup and scroll events.
*/
_onGraphSelecting: function () {
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);
}
},
_onGraphRendered: function (_, graphName) {
switch (graphName) {
case "timeline":
this.emit(EVENTS.MARKERS_GRAPH_RENDERED);
break;
case "memory":
this.emit(EVENTS.MEMORY_GRAPH_RENDERED);
break;
case "framerate":
this.emit(EVENTS.FRAMERATE_GRAPH_RENDERED);
break;
}
},
/**
* Called whenever a preference in `devtools.performance.ui.` changes.
* Does not care about the enabling of memory/framerate graphs,
* because those will set values on a recording model, and
* the graphs will render based on the existence.
*/
_onPrefChanged: Task.async(function* (_, prefName, prefValue) {
switch (prefName) {
case "enable-memory": {
$("#memory-overview").hidden = !prefValue;
break;
}
case "enable-framerate": {
$("#time-framerate").hidden = !prefValue;
break;
}
case "hidden-markers": {
if (yield this._markersGraphAvailable()) {
let graph;
if (graph = yield this.graphs.isAvailable("timeline")) {
let blueprint = PerformanceController.getTimelineBlueprint();
this.markersOverview.setBlueprint(blueprint);
this.markersOverview.refresh({ force: true });
graph.setBlueprint(blueprint);
graph.refresh({ force: true });
}
break;
}
}
}),
_setGraphVisibilityFromRecordingFeatures: function (recording) {
for (let [graphName, requirements] of Iterator(GRAPH_REQUIREMENTS)) {
this.graphs.enable(graphName, PerformanceController.isFeatureSupported(requirements));
}
},
/**
* Called when `devtools.theme` changes.
*/
_onThemeChanged: function (_, theme) {
this.setTheme({ theme, redraw: true });
this.graphs.setTheme({ theme, redraw: true });
},
toString: () => "[object OverviewView]"