mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 00:05:36 +00:00
Bug 1159052 - Performance recording should stop rendering and recording as soon as the recording stops. r=vp
This commit is contained in:
parent
726123824a
commit
8593e43ebd
@ -223,10 +223,17 @@ MemoryFrontFacade.prototype = {
|
||||
|
||||
yield this.attach();
|
||||
|
||||
let startTime = yield this.startRecordingAllocations({
|
||||
probability: options.allocationsSampleProbability,
|
||||
maxLogLength: options.allocationsMaxLogLength
|
||||
});
|
||||
// Reconstruct our options because the server actor fails when being passed
|
||||
// undefined values in objects.
|
||||
let allocationOptions = {};
|
||||
if (options.allocationsSampleProbability !== void 0) {
|
||||
allocationOptions.probability = options.allocationsSampleProbability;
|
||||
}
|
||||
if (options.allocationsMaxLogLength !== void 0) {
|
||||
allocationOptions.maxLogLength = options.allocationsMaxLogLength;
|
||||
}
|
||||
|
||||
let startTime = yield this.startRecordingAllocations(allocationOptions);
|
||||
|
||||
yield this._pullAllocationSites();
|
||||
|
||||
|
@ -27,9 +27,8 @@ const DEFAULT_ALLOCATION_SITES_PULL_TIMEOUT = 200; // ms
|
||||
|
||||
// Events to pipe from PerformanceActorsConnection to the PerformanceFront
|
||||
const CONNECTION_PIPE_EVENTS = [
|
||||
"console-profile-start", "console-profile-ending", "console-profile-end",
|
||||
"timeline-data", "profiler-already-active", "profiler-activated",
|
||||
"recording-started", "recording-stopped"
|
||||
"recording-starting", "recording-started", "recording-stopping", "recording-stopped"
|
||||
];
|
||||
|
||||
/**
|
||||
@ -227,8 +226,6 @@ PerformanceActorsConnection.prototype = {
|
||||
console: true,
|
||||
label: profileLabel
|
||||
}));
|
||||
|
||||
this.emit("console-profile-start", model);
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -271,9 +268,7 @@ PerformanceActorsConnection.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit("console-profile-ending", model);
|
||||
yield this.stopRecording(model);
|
||||
this.emit("console-profile-end", model);
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -309,6 +304,8 @@ PerformanceActorsConnection.prototype = {
|
||||
*/
|
||||
startRecording: Task.async(function*(options = {}) {
|
||||
let model = new RecordingModel(options);
|
||||
this.emit("recording-starting", model);
|
||||
|
||||
// All actors are started asynchronously over the remote debugging protocol.
|
||||
// Get the corresponding start times from each one of them.
|
||||
// The timeline and memory actors are target-dependent, so start those as well,
|
||||
@ -343,6 +340,13 @@ PerformanceActorsConnection.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Flag the recording as no longer recording, so that `model.isRecording()`
|
||||
// is false. Do this before we fetch all the data, and then subsequently
|
||||
// the recording can be considered "completed".
|
||||
let endTime = Date.now();
|
||||
model._onStoppingRecording(endTime);
|
||||
this.emit("recording-stopping", model);
|
||||
|
||||
// Currently there are two ways profiles stop recording. Either manually in the
|
||||
// performance tool, or via console.profileEnd. Once a recording is done,
|
||||
// we want to deliver the model to the performance tool (either as a return
|
||||
@ -485,7 +489,9 @@ PerformanceFront.prototype = {
|
||||
}
|
||||
let actor = this._connection[`_${actorName}`];
|
||||
return actor[method].apply(actor, args);
|
||||
}
|
||||
},
|
||||
|
||||
toString: () => "[object PerformanceFront]"
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -38,6 +38,7 @@ RecordingModel.prototype = {
|
||||
_console: false,
|
||||
_imported: false,
|
||||
_recording: false,
|
||||
_completed: false,
|
||||
_profilerStartTime: 0,
|
||||
_timelineStartTime: 0,
|
||||
_memoryStartTime: 0,
|
||||
@ -94,7 +95,7 @@ RecordingModel.prototype = {
|
||||
// However, we also want to update the view with the elapsed time
|
||||
// even when the actor is not generating data. To do this we get
|
||||
// the local time and use it to compute a reasonable elapsed time.
|
||||
this._localStartTime = Date.now()
|
||||
this._localStartTime = Date.now();
|
||||
|
||||
this._profilerStartTime = info.profilerStartTime;
|
||||
this._timelineStartTime = info.timelineStartTime;
|
||||
@ -108,19 +109,30 @@ RecordingModel.prototype = {
|
||||
this._allocations = { sites: [], timestamps: [], frames: [], counts: [] };
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the signal was sent to the front to no longer record more
|
||||
* data, and begin fetching the data. There's some delay during fetching,
|
||||
* even though the recording is stopped, the model is not yet completed until
|
||||
* all the data is fetched.
|
||||
*/
|
||||
_onStoppingRecording: function (endTime) {
|
||||
this._duration = endTime - this._localStartTime;
|
||||
this._recording = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets results available from stopping a recording from SharedPerformanceConnection.
|
||||
* Should only be called by SharedPerformanceConnection.
|
||||
*/
|
||||
_onStopRecording: Task.async(function *(info) {
|
||||
this._profile = info.profile;
|
||||
this._duration = info.profilerEndTime - this._profilerStartTime;
|
||||
this._recording = false;
|
||||
this._completed = true;
|
||||
|
||||
// We filter out all samples that fall out of current profile's range
|
||||
// since the profiler is continuously running. Because of this, sample
|
||||
// times are not guaranteed to have a zero epoch, so offset the
|
||||
// timestamps.
|
||||
// TODO move this into FakeProfilerFront in ./actors.js after bug 1154115
|
||||
RecordingUtils.offsetSampleTimes(this._profile, this._profilerStartTime);
|
||||
|
||||
// Markers need to be sorted ascending by time, to be properly displayed
|
||||
@ -248,9 +260,21 @@ RecordingModel.prototype = {
|
||||
return this._console;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating whether or not this recording model
|
||||
* has finished recording.
|
||||
* There is some delay in fetching data between when the recording stops, and
|
||||
* when the recording is considered completed once it has all the profiler and timeline data.
|
||||
*/
|
||||
isCompleted: function () {
|
||||
return this._completed || this.isImported();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating whether or not this recording model
|
||||
* is recording.
|
||||
* A model may no longer be recording, yet still not have the profiler data. In that
|
||||
* case, use `isCompleted()`.
|
||||
*/
|
||||
isRecording: function () {
|
||||
return this._recording;
|
||||
@ -262,7 +286,7 @@ RecordingModel.prototype = {
|
||||
addTimelineData: function (eventName, ...data) {
|
||||
// If this model isn't currently recording,
|
||||
// ignore the timeline data.
|
||||
if (!this._recording) {
|
||||
if (!this.isRecording()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -63,13 +63,6 @@ const EVENTS = {
|
||||
// Fired by the PerformanceController when the devtools theme changes.
|
||||
THEME_CHANGED: "Performance:ThemeChanged",
|
||||
|
||||
// When the SharedPerformanceConnection handles profiles created via `console.profile()`,
|
||||
// the controller handles those events and emits the below events for consumption
|
||||
// by other views.
|
||||
CONSOLE_RECORDING_STARTED: "Performance:ConsoleRecordingStarted",
|
||||
CONSOLE_RECORDING_WILL_STOP: "Performance:ConsoleRecordingWillStop",
|
||||
CONSOLE_RECORDING_STOPPED: "Performance:ConsoleRecordingStopped",
|
||||
|
||||
// Emitted by the PerformanceView when the state (display mode) changes,
|
||||
// for example when switching between "empty", "recording" or "recorded".
|
||||
// This causes certain panels to be hidden or visible.
|
||||
@ -190,9 +183,7 @@ let PerformanceController = {
|
||||
this._onRecordingSelectFromView = this._onRecordingSelectFromView.bind(this);
|
||||
this._onPrefChanged = this._onPrefChanged.bind(this);
|
||||
this._onThemeChanged = this._onThemeChanged.bind(this);
|
||||
this._onConsoleProfileStart = this._onConsoleProfileStart.bind(this);
|
||||
this._onConsoleProfileEnd = this._onConsoleProfileEnd.bind(this);
|
||||
this._onConsoleProfileEnding = this._onConsoleProfileEnding.bind(this);
|
||||
this._onRecordingStateChange = this._onRecordingStateChange.bind(this);
|
||||
|
||||
// All boolean prefs should be handled via the OptionsView in the
|
||||
// ToolbarView, so that they may be accessible via the "gear" menu.
|
||||
@ -208,9 +199,10 @@ let PerformanceController = {
|
||||
this._nonBooleanPrefs.registerObserver();
|
||||
this._nonBooleanPrefs.on("pref-changed", this._onPrefChanged);
|
||||
|
||||
gFront.on("console-profile-start", this._onConsoleProfileStart);
|
||||
gFront.on("console-profile-ending", this._onConsoleProfileEnding);
|
||||
gFront.on("console-profile-end", this._onConsoleProfileEnd);
|
||||
gFront.on("recording-starting", this._onRecordingStateChange);
|
||||
gFront.on("recording-started", this._onRecordingStateChange);
|
||||
gFront.on("recording-stopping", this._onRecordingStateChange);
|
||||
gFront.on("recording-stopped", this._onRecordingStateChange);
|
||||
ToolbarView.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
PerformanceView.on(EVENTS.UI_START_RECORDING, this.startRecording);
|
||||
PerformanceView.on(EVENTS.UI_STOP_RECORDING, this.stopRecording);
|
||||
@ -229,9 +221,10 @@ let PerformanceController = {
|
||||
this._nonBooleanPrefs.unregisterObserver();
|
||||
this._nonBooleanPrefs.off("pref-changed", this._onPrefChanged);
|
||||
|
||||
gFront.off("console-profile-start", this._onConsoleProfileStart);
|
||||
gFront.off("console-profile-ending", this._onConsoleProfileEnding);
|
||||
gFront.off("console-profile-end", this._onConsoleProfileEnd);
|
||||
gFront.off("recording-starting", this._onRecordingStateChange);
|
||||
gFront.off("recording-started", this._onRecordingStateChange);
|
||||
gFront.off("recording-stopping", this._onRecordingStateChange);
|
||||
gFront.off("recording-stopped", this._onRecordingStateChange);
|
||||
ToolbarView.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
PerformanceView.off(EVENTS.UI_START_RECORDING, this.startRecording);
|
||||
PerformanceView.off(EVENTS.UI_STOP_RECORDING, this.stopRecording);
|
||||
@ -300,12 +293,7 @@ let PerformanceController = {
|
||||
sampleFrequency: this.getPref("profiler-sample-frequency")
|
||||
};
|
||||
|
||||
this.emit(EVENTS.RECORDING_WILL_START);
|
||||
|
||||
let recording = yield gFront.startRecording(options);
|
||||
this._recordings.push(recording);
|
||||
|
||||
this.emit(EVENTS.RECORDING_STARTED, recording);
|
||||
yield gFront.startRecording(options);
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -315,9 +303,7 @@ let PerformanceController = {
|
||||
stopRecording: Task.async(function *() {
|
||||
let recording = this.getLatestManualRecording();
|
||||
|
||||
this.emit(EVENTS.RECORDING_WILL_STOP, recording);
|
||||
yield gFront.stopRecording(recording);
|
||||
this.emit(EVENTS.RECORDING_STOPPED, recording);
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -344,6 +330,11 @@ let PerformanceController = {
|
||||
if (latest && latest.isRecording()) {
|
||||
yield this.stopRecording();
|
||||
}
|
||||
// If last recording is not recording, but finalizing itself,
|
||||
// wait for that to finish
|
||||
if (latest && !latest.isCompleted()) {
|
||||
yield this.once(EVENTS.RECORDING_STOPPED);
|
||||
}
|
||||
|
||||
this._recordings.length = 0;
|
||||
this.setCurrentRecording(null);
|
||||
@ -440,27 +431,33 @@ let PerformanceController = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when `console.profile()` is executed.
|
||||
* Fired when a recording model changes state.
|
||||
*
|
||||
* @param {string} state
|
||||
* @param {RecordingModel} model
|
||||
*/
|
||||
_onConsoleProfileStart: function (_, recording) {
|
||||
this._recordings.push(recording);
|
||||
this.emit(EVENTS.CONSOLE_RECORDING_STARTED, recording);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when `console.profileEnd()` is executed, and the profile
|
||||
* is stopping soon, as it fetches profiler data.
|
||||
*/
|
||||
_onConsoleProfileEnding: function (_, recording) {
|
||||
this.emit(EVENTS.CONSOLE_RECORDING_WILL_STOP, recording);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when `console.profileEnd()` is executed, and
|
||||
* has a corresponding `console.profile()` session.
|
||||
*/
|
||||
_onConsoleProfileEnd: function (_, recording) {
|
||||
this.emit(EVENTS.CONSOLE_RECORDING_STOPPED, recording);
|
||||
_onRecordingStateChange: function (state, model) {
|
||||
switch (state) {
|
||||
// Fired when a RecordingModel was just created from the front
|
||||
case "recording-starting":
|
||||
// When a recording is just starting, store it internally
|
||||
this._recordings.push(model);
|
||||
this.emit(EVENTS.RECORDING_WILL_START, model);
|
||||
break;
|
||||
// Fired when a RecordingModel has started recording
|
||||
case "recording-started":
|
||||
this.emit(EVENTS.RECORDING_STARTED, model);
|
||||
break;
|
||||
// Fired when a RecordingModel is no longer recording, and
|
||||
// starting to fetch all the profiler data
|
||||
case "recording-stopping":
|
||||
this.emit(EVENTS.RECORDING_WILL_STOP, model);
|
||||
break;
|
||||
// Fired when a RecordingModel is finished fetching all of its data
|
||||
case "recording-stopped":
|
||||
this.emit(EVENTS.RECORDING_STOPPED, model);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -480,21 +477,21 @@ let PerformanceController = {
|
||||
* 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.
|
||||
* @option {boolean} mustBeCompleted
|
||||
* A boolean indicating whether the recording must be either completed or not.
|
||||
* 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) {
|
||||
isFeatureSupported: function ({ features, actors, mustBeCompleted }, recording) {
|
||||
recording = recording || this.getCurrentRecording();
|
||||
let recordingConfig = recording ? recording.getConfiguration() : {};
|
||||
let currentRecordingState = recording ? recording.isRecording() : void 0;
|
||||
let currentCompletedState = recording ? recording.isCompleted() : void 0;
|
||||
let actorsSupported = gFront.getActorSupport();
|
||||
|
||||
if (shouldBeRecording != null && shouldBeRecording !== currentRecordingState) {
|
||||
if (mustBeCompleted != null && mustBeCompleted !== currentCompletedState) {
|
||||
return false;
|
||||
}
|
||||
if (actors && !actors.every(a => actorsSupported[a])) {
|
||||
|
@ -41,12 +41,11 @@ let PerformanceView = {
|
||||
this._onRecordButtonClick = this._onRecordButtonClick.bind(this);
|
||||
this._onImportButtonClick = this._onImportButtonClick.bind(this);
|
||||
this._onClearButtonClick = this._onClearButtonClick.bind(this);
|
||||
this._lockRecordButton = this._lockRecordButton.bind(this);
|
||||
this._unlockRecordButton = this._unlockRecordButton.bind(this);
|
||||
this._lockRecordButtons = this._lockRecordButtons.bind(this);
|
||||
this._unlockRecordButtons = this._unlockRecordButtons.bind(this);
|
||||
this._onRecordingSelected = this._onRecordingSelected.bind(this);
|
||||
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
||||
this._onRecordingWillStop = this._onRecordingWillStop.bind(this);
|
||||
this._onRecordingWillStart = this._onRecordingWillStart.bind(this);
|
||||
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
||||
|
||||
for (let button of $$(".record-button")) {
|
||||
button.addEventListener("click", this._onRecordButtonClick);
|
||||
@ -55,11 +54,8 @@ let PerformanceView = {
|
||||
this._clearButton.addEventListener("click", this._onClearButtonClick);
|
||||
|
||||
// Bind to controller events to unlock the record button
|
||||
PerformanceController.on(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
|
||||
PerformanceController.on(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
|
||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
|
||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
|
||||
this.setState("empty");
|
||||
@ -82,11 +78,8 @@ let PerformanceView = {
|
||||
this._importButton.removeEventListener("click", this._onImportButtonClick);
|
||||
this._clearButton.removeEventListener("click", this._onClearButtonClick);
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
|
||||
PerformanceController.off(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
|
||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
|
||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
|
||||
yield ToolbarView.destroy();
|
||||
@ -130,31 +123,30 @@ let PerformanceView = {
|
||||
* Adds the `locked` attribute on the record button. This prevents it
|
||||
* from being clicked while recording is started or stopped.
|
||||
*/
|
||||
_lockRecordButton: function () {
|
||||
this._recordButton.setAttribute("locked", "true");
|
||||
_lockRecordButtons: function () {
|
||||
for (let button of $$(".record-button")) {
|
||||
button.setAttribute("locked", "true");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the `locked` attribute on the record button.
|
||||
*/
|
||||
_unlockRecordButton: function () {
|
||||
this._recordButton.removeAttribute("locked");
|
||||
_unlockRecordButtons: function () {
|
||||
for (let button of $$(".record-button")) {
|
||||
button.removeAttribute("locked");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a recording is starting, but not yet completed.
|
||||
* When a recording has started.
|
||||
*/
|
||||
_onRecordingWillStart: function () {
|
||||
this._lockRecordButton();
|
||||
this._recordButton.setAttribute("checked", "true");
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a recording is stopping, but not yet completed.
|
||||
*/
|
||||
_onRecordingWillStop: function () {
|
||||
this._lockRecordButton();
|
||||
this._recordButton.removeAttribute("checked");
|
||||
_onRecordingStarted: function (_, recording) {
|
||||
// A stopped recording can be from `console.profileEnd` -- only unlock
|
||||
// the button if it's the main recording that was started via UI.
|
||||
if (!recording.isConsole()) {
|
||||
this._unlockRecordButtons();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -164,7 +156,7 @@ let PerformanceView = {
|
||||
// A stopped recording can be from `console.profileEnd` -- only unlock
|
||||
// the button if it's the main recording that was started via UI.
|
||||
if (!recording.isConsole()) {
|
||||
this._unlockRecordButton();
|
||||
this._unlockRecordButtons();
|
||||
}
|
||||
|
||||
// If the currently selected recording is the one that just stopped,
|
||||
@ -187,7 +179,15 @@ let PerformanceView = {
|
||||
_onRecordButtonClick: function (e) {
|
||||
if (this._recordButton.hasAttribute("checked")) {
|
||||
this.emit(EVENTS.UI_STOP_RECORDING);
|
||||
this._lockRecordButtons();
|
||||
for (let button of $$(".record-button")) {
|
||||
button.removeAttribute("checked");
|
||||
}
|
||||
} else {
|
||||
this._lockRecordButtons();
|
||||
for (let button of $$(".record-button")) {
|
||||
button.setAttribute("checked", "true");
|
||||
}
|
||||
this.emit(EVENTS.UI_START_RECORDING);
|
||||
}
|
||||
},
|
||||
|
@ -77,7 +77,7 @@
|
||||
<hbox id="recordings-controls"
|
||||
class="devtools-toolbarbutton-group">
|
||||
<toolbarbutton id="main-record-button"
|
||||
class="devtools-toolbarbutton record-button"
|
||||
class="devtools-toolbarbutton record-button devtools-thobber"
|
||||
tooltiptext="&profilerUI.recordButton.tooltip;"/>
|
||||
<toolbarbutton id="import-button"
|
||||
class="devtools-toolbarbutton"
|
||||
@ -159,8 +159,7 @@
|
||||
flex="1">
|
||||
<label value="&profilerUI.stopNotice1;"/>
|
||||
<button class="devtools-toolbarbutton record-button"
|
||||
standalone="true"
|
||||
checked="true" />
|
||||
standalone="true" />
|
||||
<label value="&profilerUI.stopNotice2;"/>
|
||||
</hbox>
|
||||
<hbox id="console-recording-notice"
|
||||
|
@ -89,6 +89,7 @@ support-files =
|
||||
[browser_perf-states.js]
|
||||
[browser_perf-refresh.js]
|
||||
[browser_perf-ui-recording.js]
|
||||
[browser_perf-recording-model-01.js]
|
||||
[browser_perf-recording-notices-01.js]
|
||||
[browser_perf-recording-notices-02.js]
|
||||
[browser_perf_recordings-io-01.js]
|
||||
|
@ -15,12 +15,12 @@ function spawnTest () {
|
||||
yield profilerConnected;
|
||||
let connection = getPerformanceActorsConnection(target);
|
||||
|
||||
let profileStart = once(connection, "console-profile-start");
|
||||
let profileStart = once(connection, "recording-started");
|
||||
console.profile("rust");
|
||||
yield profileStart;
|
||||
|
||||
busyWait(WAIT_TIME);
|
||||
let profileEnd = once(connection, "console-profile-end");
|
||||
let profileEnd = once(connection, "recording-stopped");
|
||||
console.profileEnd("rust");
|
||||
yield profileEnd;
|
||||
|
||||
|
@ -15,10 +15,10 @@ function spawnTest () {
|
||||
yield profilerConnected;
|
||||
let connection = getPerformanceActorsConnection(target);
|
||||
|
||||
let profileStart = once(connection, "console-profile-start");
|
||||
let profileStart = once(connection, "recording-started");
|
||||
console.profile("rust");
|
||||
yield profileStart;
|
||||
profileStart = once(connection, "console-profile-start");
|
||||
profileStart = once(connection, "recording-started");
|
||||
console.profile("rust2");
|
||||
yield profileStart;
|
||||
|
||||
@ -38,10 +38,10 @@ function spawnTest () {
|
||||
is(RecordingsView.selectedItem.attachment, recordings[0],
|
||||
"The first console recording should be selected.");
|
||||
|
||||
let profileEnd = once(connection, "console-profile-end");
|
||||
let profileEnd = once(connection, "recording-stopped");
|
||||
console.profileEnd("rust");
|
||||
yield profileEnd;
|
||||
profileEnd = once(connection, "console-profile-end");
|
||||
profileEnd = once(connection, "recording-stopped");
|
||||
console.profileEnd("rust2");
|
||||
yield profileEnd;
|
||||
|
||||
|
@ -15,15 +15,15 @@ function spawnTest () {
|
||||
yield profilerConnected;
|
||||
let connection = getPerformanceActorsConnection(target);
|
||||
|
||||
let profileStart = once(connection, "console-profile-start");
|
||||
let profileStart = once(connection, "recording-started");
|
||||
console.profile("rust");
|
||||
yield profileStart;
|
||||
|
||||
let profileEnd = once(connection, "console-profile-end");
|
||||
let profileEnd = once(connection, "recording-stopped");
|
||||
console.profileEnd("rust");
|
||||
yield profileEnd;
|
||||
|
||||
profileStart = once(connection, "console-profile-start");
|
||||
profileStart = once(connection, "recording-started");
|
||||
console.profile("rust2");
|
||||
yield profileStart;
|
||||
|
||||
@ -43,7 +43,7 @@ function spawnTest () {
|
||||
is(RecordingsView.selectedItem.attachment, recordings[0],
|
||||
"The first console recording should be selected.");
|
||||
|
||||
profileEnd = once(connection, "console-profile-end");
|
||||
profileEnd = once(connection, "recording-stopped");
|
||||
console.profileEnd("rust2");
|
||||
yield profileEnd;
|
||||
|
||||
|
@ -15,14 +15,14 @@ function spawnTest () {
|
||||
let connection = getPerformanceActorsConnection(target);
|
||||
let tab = toolbox.doc.getElementById("toolbox-tab-performance");
|
||||
|
||||
let profileStart = once(connection, "console-profile-start");
|
||||
let profileStart = once(connection, "recording-started");
|
||||
console.profile("rust");
|
||||
yield profileStart;
|
||||
|
||||
ok(tab.hasAttribute("highlighted"),
|
||||
"performance tab is highlighted during recording from console.profile when unloaded");
|
||||
|
||||
let profileEnd = once(connection, "console-profile-end");
|
||||
let profileEnd = once(connection, "recording-stopped");
|
||||
console.profileEnd("rust");
|
||||
yield profileEnd;
|
||||
|
||||
|
@ -0,0 +1,62 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests the state of a recording rec from start to finish for recording,
|
||||
* completed, and rec data.
|
||||
*/
|
||||
|
||||
function spawnTest () {
|
||||
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, gFront: front, PerformanceController } = panel.panelWin;
|
||||
|
||||
let rec = yield front.startRecording({ withMarkers: true, withTicks: true, withMemory: true });
|
||||
ok(rec.isRecording(), "RecordingModel is recording when created");
|
||||
yield busyWait(100);
|
||||
yield waitUntil(() => rec.getMemory().length);
|
||||
ok(true, "RecordingModel populates memory while recording");
|
||||
yield waitUntil(() => rec.getTicks().length);
|
||||
ok(true, "RecordingModel populates ticks while recording");
|
||||
yield waitUntil(() => rec.getMarkers().length);
|
||||
ok(true, "RecordingModel populates markers while recording");
|
||||
|
||||
ok(!rec.isCompleted(), "RecordingModel is not completed when still recording");
|
||||
|
||||
let stopping = once(front, "recording-stopping");
|
||||
let stopped = once(front, "recording-stopped");
|
||||
front.stopRecording(rec);
|
||||
|
||||
yield stopping;
|
||||
ok(!rec.isRecording(), "on 'recording-stopping', model is no longer recording");
|
||||
// This handler should be called BEFORE "recording-stopped" is called, as
|
||||
// there is some delay, but in the event where "recording-stopped" finishes
|
||||
// before we check here, ensure that we're atleast in the right state
|
||||
if (rec.getProfile()) {
|
||||
ok(rec.isCompleted(), "recording is completed once it has profile data");
|
||||
} else {
|
||||
ok(!rec.isCompleted(), "recording is not yet completed on 'recording-stopping'");
|
||||
}
|
||||
|
||||
yield stopped;
|
||||
ok(!rec.isRecording(), "on 'recording-stopped', model is still no longer recording");
|
||||
ok(rec.isCompleted(), "on 'recording-stopped', model is considered 'complete'");
|
||||
|
||||
// Export and import a rec, and ensure it has the correct state.
|
||||
let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
|
||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
|
||||
let exported = once(PerformanceController, EVENTS.RECORDING_EXPORTED);
|
||||
yield PerformanceController.exportRecording("", rec, file);
|
||||
yield exported;
|
||||
|
||||
let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
|
||||
yield PerformanceController.importRecording("", file);
|
||||
|
||||
yield imported;
|
||||
let importedModel = PerformanceController.getCurrentRecording();
|
||||
|
||||
ok(importedModel.isCompleted(), "All imported recordings should be completed");
|
||||
ok(!importedModel.isRecording(), "All imported recordings should not be recording");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
@ -142,7 +142,7 @@ function handleError(aError) {
|
||||
}
|
||||
|
||||
function once(aTarget, aEventName, aUseCapture = false, spread = false) {
|
||||
info("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
|
||||
info(`Waiting for event: '${aEventName}' on ${aTarget}`);
|
||||
|
||||
let deferred = Promise.defer();
|
||||
|
||||
@ -153,6 +153,7 @@ function once(aTarget, aEventName, aUseCapture = false, spread = false) {
|
||||
]) {
|
||||
if ((add in aTarget) && (remove in aTarget)) {
|
||||
aTarget[add](aEventName, function onEvent(...aArgs) {
|
||||
info(`Received event: '${aEventName}' on ${aTarget}`);
|
||||
aTarget[remove](aEventName, onEvent, aUseCapture);
|
||||
deferred.resolve(spread ? aArgs : aArgs[0]);
|
||||
}, aUseCapture);
|
||||
@ -305,13 +306,13 @@ function consoleMethod (...args) {
|
||||
}
|
||||
|
||||
function* consoleProfile(win, label) {
|
||||
let profileStart = once(win.PerformanceController, win.EVENTS.CONSOLE_RECORDING_STARTED);
|
||||
let profileStart = once(win.PerformanceController, win.EVENTS.RECORDING_STARTED);
|
||||
consoleMethod("profile", label);
|
||||
yield profileStart;
|
||||
}
|
||||
|
||||
function* consoleProfileEnd(win, label) {
|
||||
let ended = once(win.PerformanceController, win.EVENTS.CONSOLE_RECORDING_STOPPED);
|
||||
let ended = once(win.PerformanceController, win.EVENTS.RECORDING_STOPPED);
|
||||
consoleMethod("profileEnd", label);
|
||||
yield ended;
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ let DetailsSubview = {
|
||||
this._onPrefChanged = this._onPrefChanged.bind(this);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
|
||||
@ -32,7 +31,6 @@ let DetailsSubview = {
|
||||
clearNamedTimeout("range-change-debounce");
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
|
||||
@ -81,7 +79,7 @@ let DetailsSubview = {
|
||||
* Called when recording stops or is selected.
|
||||
*/
|
||||
_onRecordingStoppedOrSelected: function(_, recording) {
|
||||
if (!recording || recording.isRecording()) {
|
||||
if (!recording || !recording.isCompleted()) {
|
||||
return;
|
||||
}
|
||||
if (DetailsView.isViewSelected(this) || this.canUpdateWhileHidden) {
|
||||
@ -131,7 +129,7 @@ let DetailsSubview = {
|
||||
// All detail views require a recording to be complete, so do not
|
||||
// attempt to render if recording is in progress or does not exist.
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
if (!recording || recording.isRecording()) {
|
||||
if (!recording || !recording.isCompleted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,6 @@ let DetailsView = {
|
||||
|
||||
yield this.setAvailableViews();
|
||||
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.PREF_CHANGED, this.setAvailableViews);
|
||||
@ -77,7 +76,6 @@ let DetailsView = {
|
||||
component.initialized && (yield component.view.destroy());
|
||||
}
|
||||
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.PREF_CHANGED, this.setAvailableViews);
|
||||
@ -90,13 +88,13 @@ let DetailsView = {
|
||||
*/
|
||||
setAvailableViews: Task.async(function* () {
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
let isRecording = recording && recording.isRecording();
|
||||
let isCompleted = recording && recording.isCompleted();
|
||||
let invalidCurrentView = false;
|
||||
|
||||
for (let [name, { view }] of Iterator(this.components)) {
|
||||
// TODO bug 1160313 get rid of retro mode checks.
|
||||
let isRetro = PerformanceController.getOption("retro-mode");
|
||||
let isSupported = isRetro ? name === "js-calltree" : this._isViewSupported(name, false);
|
||||
let isSupported = isRetro ? name === "js-calltree" : this._isViewSupported(name, true);
|
||||
|
||||
// TODO bug 1160313 hide all view buttons, but let js-calltree still be "supported"
|
||||
$(`toolbarbutton[data-view=${name}]`).hidden = isRetro ? true : !isSupported;
|
||||
@ -112,12 +110,12 @@ let DetailsView = {
|
||||
//
|
||||
// 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.
|
||||
// is completed.
|
||||
//
|
||||
// 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)) {
|
||||
if ((this._initialized && isCompleted && invalidCurrentView) ||
|
||||
(!this._initialized && isCompleted && recording)) {
|
||||
yield this.selectDefaultView();
|
||||
}
|
||||
}),
|
||||
@ -126,12 +124,12 @@ let DetailsView = {
|
||||
* Takes a view name and optionally if there must be a currently recording in progress.
|
||||
*
|
||||
* @param {string} viewName
|
||||
* @param {boolean?} isRecording
|
||||
* @param {boolean?} mustBeCompleted
|
||||
* @return {boolean}
|
||||
*/
|
||||
_isViewSupported: function (viewName, isRecording) {
|
||||
_isViewSupported: function (viewName, mustBeCompleted) {
|
||||
let { features, actors } = this.components[viewName];
|
||||
return PerformanceController.isFeatureSupported({ features, actors, isRecording });
|
||||
return PerformanceController.isFeatureSupported({ features, actors, mustBeCompleted });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -238,7 +236,7 @@ let DetailsView = {
|
||||
// All detail views require a recording to be complete, so do not
|
||||
// attempt to render if recording is in progress or does not exist.
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
if (recording && !recording.isRecording()) {
|
||||
if (recording && recording.isCompleted()) {
|
||||
component.view.shouldUpdateWhenShown = true;
|
||||
}
|
||||
}),
|
||||
|
@ -66,9 +66,6 @@ let OverviewView = {
|
||||
PerformanceController.on(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
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);
|
||||
},
|
||||
@ -84,9 +81,6 @@ let OverviewView = {
|
||||
PerformanceController.off(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
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();
|
||||
@ -207,31 +201,25 @@ let OverviewView = {
|
||||
* exist yet, but can just disable from here. This will only trigger for
|
||||
* manual recordings.
|
||||
*/
|
||||
_onRecordingWillStart: Task.async(function* () {
|
||||
this._onRecordingStateChange();
|
||||
_onRecordingWillStart: OverviewViewOnStateChange(Task.async(function* () {
|
||||
yield this._checkSelection();
|
||||
this.graphs.dropSelection();
|
||||
}),
|
||||
})),
|
||||
|
||||
/**
|
||||
* Called when recording actually starts.
|
||||
*/
|
||||
_onRecordingStarted: function (_, recording) {
|
||||
this._onRecordingStateChange();
|
||||
},
|
||||
_onRecordingStarted: OverviewViewOnStateChange(),
|
||||
|
||||
/**
|
||||
* Called when recording will stop.
|
||||
*/
|
||||
_onRecordingWillStop: function(_, recording) {
|
||||
this._onRecordingStateChange();
|
||||
},
|
||||
_onRecordingWillStop: OverviewViewOnStateChange(),
|
||||
|
||||
/**
|
||||
* Called when recording actually stops.
|
||||
*/
|
||||
_onRecordingStopped: Task.async(function* (_, recording) {
|
||||
this._onRecordingStateChange();
|
||||
_onRecordingStopped: OverviewViewOnStateChange(Task.async(function* (_, recording) {
|
||||
// Check to see if the recording that just stopped is the current recording.
|
||||
// If it is, render the high-res graphs. For manual recordings, it will also
|
||||
// be the current recording, but profiles generated by `console.profile` can stop
|
||||
@ -242,39 +230,21 @@ let OverviewView = {
|
||||
}
|
||||
this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
|
||||
yield this._checkSelection(recording);
|
||||
}),
|
||||
})),
|
||||
|
||||
/**
|
||||
* Called when a new recording is selected.
|
||||
*/
|
||||
_onRecordingSelected: Task.async(function* (_, recording) {
|
||||
if (!recording) {
|
||||
return;
|
||||
}
|
||||
this._onRecordingStateChange();
|
||||
_onRecordingSelected: OverviewViewOnStateChange(Task.async(function* (_, recording) {
|
||||
this._setGraphVisibilityFromRecordingFeatures(recording);
|
||||
|
||||
// If this recording is complete, render the high res graph
|
||||
if (!recording.isRecording()) {
|
||||
if (recording.isCompleted()) {
|
||||
yield this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
|
||||
}
|
||||
yield this._checkSelection(recording);
|
||||
this.graphs.dropSelection();
|
||||
}),
|
||||
|
||||
/**
|
||||
* Called when a recording is starting, stopping, or about to start/stop.
|
||||
* Checks the current recording displayed to determine whether or not
|
||||
* the polling for rendering the overview graph needs to start or stop.
|
||||
*/
|
||||
_onRecordingStateChange: function () {
|
||||
let currentRecording = PerformanceController.getCurrentRecording();
|
||||
if (!currentRecording || (this.isRendering() && !currentRecording.isRecording())) {
|
||||
this._stopPolling();
|
||||
} else if (currentRecording.isRecording() && !this.isRendering()) {
|
||||
this._startPolling();
|
||||
}
|
||||
},
|
||||
})),
|
||||
|
||||
/**
|
||||
* Start the polling for rendering the overview graph.
|
||||
@ -303,7 +273,7 @@ let OverviewView = {
|
||||
* based on whether a recording currently exists and is not in progress.
|
||||
*/
|
||||
_checkSelection: Task.async(function* (recording) {
|
||||
let isEnabled = recording ? !recording.isRecording() : false;
|
||||
let isEnabled = recording ? recording.isCompleted() : false;
|
||||
yield this.graphs.selectionEnabled(isEnabled);
|
||||
}),
|
||||
|
||||
@ -375,5 +345,35 @@ let OverviewView = {
|
||||
toString: () => "[object OverviewView]"
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility that can wrap a method of OverviewView that
|
||||
* handles a recording state change like when a recording is starting,
|
||||
* stopping, or about to start/stop, and determines whether or not
|
||||
* the polling for rendering the overview graphs needs to start or stop.
|
||||
* Must be called with the OverviewView context.
|
||||
*
|
||||
* @param {function?} fn
|
||||
* @return {function}
|
||||
*/
|
||||
function OverviewViewOnStateChange (fn) {
|
||||
return function _onRecordingStateChange () {
|
||||
let currentRecording = PerformanceController.getCurrentRecording();
|
||||
|
||||
// All these methods require a recording to exist.
|
||||
if (!currentRecording) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isRendering() && !currentRecording.isRecording()) {
|
||||
this._stopPolling();
|
||||
} else if (currentRecording.isRecording() && !this.isRendering()) {
|
||||
this._startPolling();
|
||||
}
|
||||
if (fn) {
|
||||
fn.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decorates the OverviewView as an EventEmitter
|
||||
EventEmitter.decorate(OverviewView);
|
||||
|
@ -24,8 +24,6 @@ let RecordingsView = Heritage.extend(WidgetMethods, {
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.RECORDING_IMPORTED, this._onRecordingImported);
|
||||
PerformanceController.on(EVENTS.RECORDINGS_CLEARED, this._onRecordingsCleared);
|
||||
this.widget.addEventListener("select", this._onSelect, false);
|
||||
@ -37,8 +35,6 @@ let RecordingsView = Heritage.extend(WidgetMethods, {
|
||||
destroy: function() {
|
||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_IMPORTED, this._onRecordingImported);
|
||||
PerformanceController.off(EVENTS.RECORDINGS_CLEARED, this._onRecordingsCleared);
|
||||
this.widget.removeEventListener("select", this._onSelect, false);
|
||||
|
Loading…
Reference in New Issue
Block a user