Bug 1354614 - Remove legacy performance front. r=gregtatum

The legacy performance path supports servers running Firefox 42 and earlier.
Now that Firefox OS is no longer supported, there's no reason to keep this
around.

MozReview-Commit-ID: K40e93VUjj9

--HG--
extra : rebase_source : ec2fbe68b7fe2f4d8e53b30d194dc026294b5f53
This commit is contained in:
J. Ryan Stinnett 2017-04-07 13:14:26 -05:00
parent 1564d36454
commit fefcbc4f99
8 changed files with 2 additions and 1017 deletions

View File

@ -1,263 +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";
const { Task } = require("devtools/shared/task");
const EventEmitter = require("devtools/shared/event-emitter");
const { Poller } = require("devtools/client/shared/poller");
const CompatUtils = require("devtools/client/performance/legacy/compatibility");
const RecordingUtils = require("devtools/shared/performance/recording-utils");
const { TimelineFront } = require("devtools/shared/fronts/timeline");
const { ProfilerFront } = require("devtools/shared/fronts/profiler");
// How often do we check the status of the profiler's circular buffer in milliseconds.
const PROFILER_CHECK_TIMER = 5000;
const TIMELINE_ACTOR_METHODS = [
"start", "stop",
];
const PROFILER_ACTOR_METHODS = [
"startProfiler", "getStartOptions", "stopProfiler",
"registerEventNotifications", "unregisterEventNotifications"
];
/**
* Constructor for a facade around an underlying ProfilerFront.
*/
function LegacyProfilerFront(target) {
this._target = target;
this._onProfilerEvent = this._onProfilerEvent.bind(this);
this._checkProfilerStatus = this._checkProfilerStatus.bind(this);
this._PROFILER_CHECK_TIMER = this._target.TEST_MOCK_PROFILER_CHECK_TIMER ||
PROFILER_CHECK_TIMER;
EventEmitter.decorate(this);
}
LegacyProfilerFront.prototype = {
EVENTS: ["console-api-profiler", "profiler-stopped"],
// Connects to the targets underlying real ProfilerFront.
connect: Task.async(function* () {
let target = this._target;
this._front = new ProfilerFront(target.client, target.form);
// Fetch and store information about the Gecko Profiler and
// server profiler.
this.traits = {};
this.traits.filterable = target.getTrait("profilerDataFilterable");
// Directly register to event notifications when connected
// to hook into `console.profile|profileEnd` calls.
yield this.registerEventNotifications({ events: this.EVENTS });
target.client.addListener("eventNotification", this._onProfilerEvent);
}),
/**
* Unregisters events for the underlying profiler actor.
*/
destroy: Task.async(function* () {
if (this._poller) {
yield this._poller.destroy();
}
yield this.unregisterEventNotifications({ events: this.EVENTS });
this._target.client.removeListener("eventNotification", this._onProfilerEvent);
yield this._front.destroy();
}),
/**
* Starts the profiler actor, if necessary.
*
* @option {number?} bufferSize
* @option {number?} sampleFrequency
*/
start: Task.async(function* (options = {}) {
// Check for poller status even if the profiler is already active --
// profiler can be activated via `console.profile` or another source, like
// the Gecko Profiler.
if (!this._poller) {
this._poller = new Poller(this._checkProfilerStatus, this._PROFILER_CHECK_TIMER,
false);
}
if (!this._poller.isPolling()) {
this._poller.on();
}
// Start the profiler only if it wasn't already active. The built-in
// nsIPerformance module will be kept recording, because it's the same instance
// for all targets and interacts with the whole platform, so we don't want
// to affect other clients by stopping (or restarting) it.
let {
isActive,
currentTime,
position,
generation,
totalSize
} = yield this.getStatus();
if (isActive) {
return { startTime: currentTime, position, generation, totalSize };
}
// Translate options from the recording model into profiler-specific
// options for the nsIProfiler
let profilerOptions = {
entries: options.bufferSize,
interval: options.sampleFrequency
? (1000 / (options.sampleFrequency * 1000))
: void 0
};
let startInfo = yield this.startProfiler(profilerOptions);
let startTime = 0;
if ("currentTime" in startInfo) {
startTime = startInfo.currentTime;
}
return { startTime, position, generation, totalSize };
}),
/**
* Indicates the end of a recording -- does not actually stop the profiler
* (stopProfiler does that), but notes that we no longer need to poll
* for buffer status.
*/
stop: Task.async(function* () {
yield this._poller.off();
}),
/**
* Wrapper around `profiler.isActive()` to take profiler status data and emit.
*/
getStatus: Task.async(function* () {
let data = yield (CompatUtils.callFrontMethod("isActive").call(this));
// If no data, the last poll for `isActive()` was wrapping up, and the target.client
// is now null, so we no longer have data, so just abort here.
if (!data) {
return undefined;
}
// If TEST_PROFILER_FILTER_STATUS defined (via array of fields), filter
// out any field from isActive, used only in tests. Used to filter out
// buffer status fields to simulate older geckos.
if (this._target.TEST_PROFILER_FILTER_STATUS) {
data = Object.keys(data).reduce((acc, prop) => {
if (this._target.TEST_PROFILER_FILTER_STATUS.indexOf(prop) === -1) {
acc[prop] = data[prop];
}
return acc;
}, {});
}
this.emit("profiler-status", data);
return data;
}),
/**
* Returns profile data from now since `startTime`.
*/
getProfile: Task.async(function* (options) {
let profilerData = yield (CompatUtils.callFrontMethod("getProfile")
.call(this, options));
// If the backend is not deduped, dedupe it ourselves, as rest of the code
// expects a deduped profile.
if (profilerData.profile.meta.version === 2) {
RecordingUtils.deflateProfile(profilerData.profile);
}
// If the backend does not support filtering by start and endtime on
// platform (< Fx40), do it on the client (much slower).
if (!this.traits.filterable) {
RecordingUtils.filterSamples(profilerData.profile, options.startTime || 0);
}
return profilerData;
}),
/**
* Invoked whenever a registered event was emitted by the profiler actor.
*
* @param object response
* The data received from the backend.
*/
_onProfilerEvent: function (_, { topic, subject, details }) {
if (topic === "console-api-profiler") {
if (subject.action === "profile") {
this.emit("console-profile-start", details);
} else if (subject.action === "profileEnd") {
this.emit("console-profile-stop", details);
}
} else if (topic === "profiler-stopped") {
this.emit("profiler-stopped");
}
},
_checkProfilerStatus: Task.async(function* () {
// Calling `getStatus()` will emit the "profiler-status" on its own
yield this.getStatus();
}),
toString: () => "[object LegacyProfilerFront]"
};
/**
* Constructor for a facade around an underlying TimelineFront.
*/
function LegacyTimelineFront(target) {
this._target = target;
EventEmitter.decorate(this);
}
LegacyTimelineFront.prototype = {
EVENTS: ["markers", "frames", "ticks"],
connect: Task.async(function* () {
let supported = yield CompatUtils.timelineActorSupported(this._target);
this._front = supported ?
new TimelineFront(this._target.client, this._target.form) :
new CompatUtils.MockTimelineFront();
this.IS_MOCK = !supported;
// Binds underlying actor events and consolidates them to a `timeline-data`
// exposed event.
this.EVENTS.forEach(type => {
let handler = this[`_on${type}`] = this._onTimelineData.bind(this, type);
this._front.on(type, handler);
});
}),
/**
* Override actor's destroy, so we can unregister listeners before
* destroying the underlying actor.
*/
destroy: Task.async(function* () {
this.EVENTS.forEach(type => this._front.off(type, this[`_on${type}`]));
yield this._front.destroy();
}),
/**
* An aggregate of all events (markers, frames, ticks) and exposes
* to PerformanceActorsConnection as a single event.
*/
_onTimelineData: function (type, ...data) {
this.emit("timeline-data", type, ...data);
},
toString: () => "[object LegacyTimelineFront]"
};
// Bind all the methods that directly proxy to the actor
PROFILER_ACTOR_METHODS.forEach(m => {
LegacyProfilerFront.prototype[m] = CompatUtils.callFrontMethod(m);
});
TIMELINE_ACTOR_METHODS.forEach(m => {
LegacyTimelineFront.prototype[m] = CompatUtils.callFrontMethod(m);
});
exports.LegacyProfilerFront = LegacyProfilerFront;
exports.LegacyTimelineFront = LegacyTimelineFront;

View File

@ -1,66 +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";
const EventEmitter = require("devtools/shared/event-emitter");
/**
* A dummy front decorated with the provided methods.
*
* @param array blueprint
* A list of [funcName, retVal] describing the class.
*/
function MockFront(blueprint) {
EventEmitter.decorate(this);
for (let [funcName, retVal] of blueprint) {
this[funcName] = (x => typeof x === "function" ? x() : x).bind(this, retVal);
}
}
function MockTimelineFront() {
MockFront.call(this, [
["destroy"],
["start", 0],
["stop", 0],
]);
}
/**
* Takes a TabTarget, and checks existence of a TimelineActor on
* the server, or if TEST_MOCK_TIMELINE_ACTOR is to be used.
*
* @param {TabTarget} target
* @return {Boolean}
*/
function timelineActorSupported(target) {
// This `target` property is used only in tests to test
// instances where the timeline actor is not available.
if (target.TEST_MOCK_TIMELINE_ACTOR) {
return false;
}
return target.hasActor("timeline");
}
/**
* Returns a function to be used as a method on an "Front" in ./actors.
* Calls the underlying actor's method.
*/
function callFrontMethod(method) {
return function () {
// If there's no target or client on this actor facade,
// abort silently -- this occurs in tests when polling occurs
// after the test ends, when tests do not wait for toolbox destruction
// (which will destroy the actor facade, turning off the polling).
if (!this._target || !this._target.client) {
return undefined;
}
return this._front[method].apply(this._front, arguments);
};
}
exports.MockTimelineFront = MockTimelineFront;
exports.timelineActorSupported = timelineActorSupported;
exports.callFrontMethod = callFrontMethod;

View File

@ -1,484 +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";
const { Task } = require("devtools/shared/task");
const Services = require("Services");
const promise = require("promise");
const { extend } = require("sdk/util/object");
const Actors = require("devtools/client/performance/legacy/actors");
const { LegacyPerformanceRecording } = require("devtools/client/performance/legacy/recording");
const { importRecording } = require("devtools/client/performance/legacy/recording");
const { normalizePerformanceFeatures } = require("devtools/shared/performance/recording-utils");
const flags = require("devtools/shared/flags");
const { getDeviceFront } = require("devtools/shared/device/device");
const { getSystemInfo } = require("devtools/shared/system");
const events = require("sdk/event/core");
const { EventTarget } = require("sdk/event/target");
const { Class } = require("sdk/core/heritage");
/**
* A connection to underlying actors (profiler, framerate, etc.)
* shared by all tools in a target.
*/
const LegacyPerformanceFront = Class({
extends: EventTarget,
LEGACY_FRONT: true,
traits: {
features: {
withMarkers: true,
withTicks: true,
withMemory: false,
withFrames: false,
withGCEvents: false,
withDocLoadingEvents: false,
withAllocations: false,
},
},
initialize: function (target) {
let { form, client } = target;
this._target = target;
this._form = form;
this._client = client;
this._pendingConsoleRecordings = [];
this._sitesPullTimeout = 0;
this._recordings = [];
this._pipeToFront = this._pipeToFront.bind(this);
this._onTimelineData = this._onTimelineData.bind(this);
this._onConsoleProfileStart = this._onConsoleProfileStart.bind(this);
this._onConsoleProfileStop = this._onConsoleProfileStop.bind(this);
this._onProfilerStatus = this._onProfilerStatus.bind(this);
this._onProfilerUnexpectedlyStopped = this._onProfilerUnexpectedlyStopped.bind(this);
},
/**
* Initializes a connection to the profiler and other miscellaneous actors.
* If in the process of opening, or already open, nothing happens.
*
* @return object
* A promise that is resolved once the connection is established.
*/
connect: Task.async(function* () {
if (this._connecting) {
return this._connecting.promise;
}
// Create a promise that gets resolved upon connecting, so that
// other attempts to open the connection use the same resolution promise
this._connecting = promise.defer();
// Sets `this._profiler`, `this._timeline`.
// Only initialize the timeline fronts if the respective actors
// are available. Older Gecko versions don't have existing implementations,
// in which case all the methods we need can be easily mocked.
yield this._connectActors();
yield this._registerListeners();
this._connecting.resolve();
return this._connecting.promise;
}),
/**
* Destroys this connection.
*/
destroy: Task.async(function* () {
if (this._connecting) {
yield this._connecting.promise;
} else {
return;
}
yield this._unregisterListeners();
yield this._disconnectActors();
this._connecting = null;
this._profiler = null;
this._timeline = null;
this._client = null;
this._form = null;
this._target = this._target;
}),
/**
* Initializes fronts and connects to the underlying actors using the facades
* found in ./actors.js.
*/
_connectActors: Task.async(function* () {
this._profiler = new Actors.LegacyProfilerFront(this._target);
this._timeline = new Actors.LegacyTimelineFront(this._target);
yield promise.all([
this._profiler.connect(),
this._timeline.connect()
]);
// If mocked timeline, update the traits
this.traits.features.withMarkers = !this._timeline.IS_MOCK;
this.traits.features.withTicks = !this._timeline.IS_MOCK;
}),
/**
* Registers listeners on events from the underlying
* actors, so the connection can handle them.
*/
_registerListeners: function () {
this._timeline.on("timeline-data", this._onTimelineData);
this._profiler.on("console-profile-start", this._onConsoleProfileStart);
this._profiler.on("console-profile-stop", this._onConsoleProfileStop);
this._profiler.on("profiler-stopped", this._onProfilerUnexpectedlyStopped);
this._profiler.on("profiler-status", this._onProfilerStatus);
},
/**
* Unregisters listeners on events on the underlying actors.
*/
_unregisterListeners: function () {
this._timeline.off("timeline-data", this._onTimelineData);
this._profiler.off("console-profile-start", this._onConsoleProfileStart);
this._profiler.off("console-profile-stop", this._onConsoleProfileStop);
this._profiler.off("profiler-stopped", this._onProfilerUnexpectedlyStopped);
this._profiler.off("profiler-status", this._onProfilerStatus);
},
/**
* Closes the connections to non-profiler actors.
*/
_disconnectActors: Task.async(function* () {
yield promise.all([
this._profiler.destroy(),
this._timeline.destroy(),
]);
}),
/**
* Invoked whenever `console.profile` is called.
*
* @param string profileLabel
* The provided string argument if available; undefined otherwise.
* @param number currentTime
* The time (in milliseconds) when the call was made, relative to when
* the nsIProfiler module was started.
*/
_onConsoleProfileStart: Task.async(function* (_, { profileLabel,
currentTime: startTime }) {
let recordings = this._recordings;
// Abort if a profile with this label already exists.
if (recordings.find(e => e.getLabel() === profileLabel)) {
return;
}
events.emit(this, "console-profile-start");
yield this.startRecording(extend({}, getLegacyPerformanceRecordingPrefs(), {
console: true,
label: profileLabel
}));
}),
/**
* Invoked whenever `console.profileEnd` is called.
*
* @param string profileLabel
* The provided string argument if available; undefined otherwise.
* @param number currentTime
* The time (in milliseconds) when the call was made, relative to when
* the nsIProfiler module was started.
*/
_onConsoleProfileStop: Task.async(function* (_, data) {
// If no data, abort; can occur if profiler isn't running and we get a surprise
// call to console.profileEnd()
if (!data) {
return;
}
let { profileLabel } = data;
let pending = this._recordings.filter(r => r.isConsole() && r.isRecording());
if (pending.length === 0) {
return;
}
let model;
// Try to find the corresponding `console.profile` call if
// a label was used in profileEnd(). If no matches, abort.
if (profileLabel) {
model = pending.find(e => e.getLabel() === profileLabel);
} else {
// If no label supplied, pop off the most recent pending console recording
model = pending[pending.length - 1];
}
// If `profileEnd()` was called with a label, and there are no matching
// sessions, abort.
if (!model) {
console.error(
"console.profileEnd() called with label that does not match a recording.");
return;
}
yield this.stopRecording(model);
}),
/**
* TODO handle bug 1144438
*/
_onProfilerUnexpectedlyStopped: function () {
console.error("Profiler unexpectedly stopped.", arguments);
},
/**
* Called whenever there is timeline data of any of the following types:
* - markers
* - frames
* - ticks
*
* Populate our internal store of recordings for all currently recording sessions.
*/
_onTimelineData: function (_, ...data) {
this._recordings.forEach(e => e._addTimelineData.apply(e, data));
events.emit(this, "timeline-data", ...data);
},
/**
* Called whenever the underlying profiler polls its current status.
*/
_onProfilerStatus: function (_, data) {
// If no data emitted (whether from an older actor being destroyed
// from a previous test, or the server does not support it), just ignore.
if (!data || data.position === void 0) {
return;
}
this._currentBufferStatus = data;
events.emit(this, "profiler-status", data);
},
/**
* Begins a recording session
*
* @param object options
* An options object to pass to the actors. Supported properties are
* `withTicks`, `withMemory` and `withAllocations`, `probability`, and
* `maxLogLength`.
* @return object
* A promise that is resolved once recording has started.
*/
startRecording: Task.async(function* (options = {}) {
let model = new LegacyPerformanceRecording(
normalizePerformanceFeatures(options, this.traits.features));
// All actors are started asynchronously over the remote debugging protocol.
// Get the corresponding start times from each one of them.
// The timeline actors are target-dependent, so start those as well,
// even though these are mocked in older Geckos (FF < 35)
let profilerStart = this._profiler.start(options);
let timelineStart = this._timeline.start(options);
let { startTime, position, generation, totalSize } = yield profilerStart;
let timelineStartTime = yield timelineStart;
let data = {
profilerStartTime: startTime, timelineStartTime,
generation, position, totalSize
};
// Signify to the model that the recording has started,
// populate with data and store the recording model here.
model._populate(data);
this._recordings.push(model);
events.emit(this, "recording-started", model);
return model;
}),
/**
* Manually ends the recording session for the corresponding LegacyPerformanceRecording.
*
* @param LegacyPerformanceRecording model
* The corresponding LegacyPerformanceRecording that belongs to the recording
* session wished to stop.
* @return LegacyPerformanceRecording
* Returns the same model, populated with the profiling data.
*/
stopRecording: Task.async(function* (model) {
// If model isn't in the LegacyPerformanceFront internal store,
// then do nothing.
if (this._recordings.indexOf(model) === -1) {
return undefined;
}
// 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);
events.emit(this, "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
// from the LegacyPerformanceFront or via `console-profile-stop` event) and then
// remove it from the internal store.
//
// In the case where a console.profile is generated via the console (so the tools are
// open), we initialize the Performance tool so it can listen to those events.
this._recordings.splice(this._recordings.indexOf(model), 1);
let config = model.getConfiguration();
let startTime = model._getProfilerStartTime();
let profilerData = yield this._profiler.getProfile({ startTime });
let timelineEndTime = Date.now();
// Only if there are no more sessions recording do we stop
// the underlying timeline actors. If we're still recording,
// juse use Date.now() for the timeline end times, as those
// are only used in tests.
if (!this.isRecording()) {
// This doesn't stop the profiler, just turns off polling for
// events, and also turns off events on timeline actors.
yield this._profiler.stop();
timelineEndTime = yield this._timeline.stop(config);
}
let form = yield this._client.listTabs();
let systemHost = yield getDeviceFront(this._client, form).getDescription();
let systemClient = yield getSystemInfo();
// Set the results on the LegacyPerformanceRecording itself.
model._onStopRecording({
// Data available only at the end of a recording.
profile: profilerData.profile,
// End times for all the actors.
profilerEndTime: profilerData.currentTime,
timelineEndTime: timelineEndTime,
systemHost,
systemClient,
});
events.emit(this, "recording-stopped", model);
return model;
}),
/**
* Creates a recording object when given a nsILocalFile.
*
* @param {nsILocalFile} file
* The file to import the data from.
* @return {Promise<LegacyPerformanceRecording>}
*/
importRecording: function (file) {
return importRecording(file);
},
/**
* Checks all currently stored recording models and returns a boolean
* if there is a session currently being recorded.
*
* @return Boolean
*/
isRecording: function () {
return this._recordings.some(recording => recording.isRecording());
},
/**
* Pass in a PerformanceRecording and get a normalized value from 0 to 1 of how much
* of this recording's lifetime remains without being overwritten.
*
* @param {PerformanceRecording} recording
* @return {number?}
*/
getBufferUsageForRecording: function (recording) {
if (!recording.isRecording() || !this._currentBufferStatus) {
return null;
}
let {
position: currentPosition,
totalSize,
generation: currentGeneration
} = this._currentBufferStatus;
let {
position: origPosition,
generation: origGeneration
} = recording.getStartingBufferStatus();
let normalizedCurrent = (totalSize * (currentGeneration - origGeneration))
+ currentPosition;
let percent = (normalizedCurrent - origPosition) / totalSize;
return percent > 1 ? 1 : percent;
},
/**
* Returns the configurations set on underlying components, used in tests.
* Returns an object with `probability`, `maxLogLength` for allocations, and
* `entries` and `interval` for profiler.
*
* @return {object}
*/
getConfiguration: Task.async(function* () {
let profilerConfig = yield this._request("profiler", "getStartOptions");
return profilerConfig;
}),
/**
* An event from an underlying actor that we just want
* to pipe to the front itself.
*/
_pipeToFront: function (eventName, ...args) {
events.emit(this, eventName, ...args);
},
/**
* Helper method to interface with the underlying actors directly.
* Used only in tests.
*/
_request: function (actorName, method, ...args) {
if (!flags.testing) {
throw new Error("LegacyPerformanceFront._request may only be used in tests.");
}
let actor = this[`_${actorName}`];
return actor[method].apply(actor, args);
},
/**
* Sets how often the "profiler-status" event should be emitted.
* Used in tests.
*/
setProfilerStatusInterval: function (n) {
if (this._profiler._poller) {
this._profiler._poller._wait = n;
}
this._profiler._PROFILER_CHECK_TIMER = n;
},
toString: () => "[object LegacyPerformanceFront]"
});
/**
* Creates an object of configurations based off of preferences for a
* LegacyPerformanceRecording.
*/
function getLegacyPerformanceRecordingPrefs() {
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-allocations"),
allocationsSampleProbability: +Services.prefs.getCharPref(
"devtools.performance.memory.sample-probability"),
allocationsMaxLogLength: Services.prefs.getIntPref(
"devtools.performance.memory.max-log-length")
};
}
exports.LegacyPerformanceFront = LegacyPerformanceFront;

View File

@ -1,12 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DevToolsModules(
'actors.js',
'compatibility.js',
'front.js',
'recording.js',
)

View File

@ -1,174 +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";
const { Task } = require("devtools/shared/task");
const PerformanceIO = require("devtools/client/performance/modules/io");
const RecordingUtils = require("devtools/shared/performance/recording-utils");
const { PerformanceRecordingCommon } = require("devtools/shared/performance/recording-common");
const { merge } = require("sdk/util/object");
/**
* Model for a wholistic profile, containing the duration, profiling data,
* frames data, timeline (marker, tick, memory) data, and methods to mark
* a recording as 'in progress' or 'finished'.
*/
const LegacyPerformanceRecording = function (options = {}) {
this._label = options.label || "";
this._console = options.console || false;
this._configuration = {
withMarkers: options.withMarkers || false,
withTicks: options.withTicks || false,
withMemory: options.withMemory || false,
withAllocations: options.withAllocations || false,
allocationsSampleProbability: options.allocationsSampleProbability || 0,
allocationsMaxLogLength: options.allocationsMaxLogLength || 0,
bufferSize: options.bufferSize || 0,
sampleFrequency: options.sampleFrequency || 1
};
};
LegacyPerformanceRecording.prototype = merge({
_profilerStartTime: 0,
_timelineStartTime: 0,
_memoryStartTime: 0,
/**
* Saves the current recording to a file.
*
* @param nsILocalFile file
* The file to stream the data into.
*/
exportRecording: Task.async(function* (file) {
let recordingData = this.getAllData();
yield PerformanceIO.saveRecordingToFile(recordingData, file);
}),
/**
* Sets up the instance with data from the PerformanceFront when
* starting a recording. Should only be called by PerformanceFront.
*/
_populate: function (info) {
// Times must come from the actor in order to be self-consistent.
// 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._profilerStartTime = info.profilerStartTime;
this._timelineStartTime = info.timelineStartTime;
this._memoryStartTime = info.memoryStartTime;
this._startingBufferStatus = {
position: info.position,
totalSize: info.totalSize,
generation: info.generation
};
this._recording = true;
this._systemHost = {};
this._systemClient = {};
this._markers = [];
this._frames = [];
this._memory = [];
this._ticks = [];
this._allocations = { sites: [], timestamps: [], frames: [], sizes: [] };
},
/**
* 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 PerformanceFront.
* Should only be called by PerformanceFront.
*/
_onStopRecording: Task.async(function* ({ profilerEndTime, profile, systemClient,
systemHost }) {
// Update the duration with the accurate profilerEndTime, so we don't have
// samples outside of the approximate duration set in `_onStoppingRecording`.
this._duration = profilerEndTime - this._profilerStartTime;
this._profile = profile;
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.
RecordingUtils.offsetSampleTimes(this._profile, this._profilerStartTime);
// Markers need to be sorted ascending by time, to be properly displayed
// in a waterfall view.
this._markers = this._markers.sort((a, b) => (a.start > b.start));
this._systemHost = systemHost;
this._systemClient = systemClient;
}),
/**
* Gets the profile's start time.
* @return number
*/
_getProfilerStartTime: function () {
return this._profilerStartTime;
},
/**
* Fired whenever the PerformanceFront emits markers, memory or ticks.
*/
_addTimelineData: function (eventName, ...data) {
// If this model isn't currently recording,
// ignore the timeline data.
if (!this.isRecording()) {
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);
RecordingUtils.pushAll(this._markers, markers);
break;
}
// Accumulate stack frames into an array.
case "frames": {
if (!config.withMarkers) {
break;
}
let [, frames] = data;
RecordingUtils.pushAll(this._frames, frames);
break;
}
// Save the accumulated refresh driver ticks.
case "ticks": {
if (!config.withTicks) {
break;
}
let [, timestamps] = data;
this._ticks = timestamps;
break;
}
}
},
toString: () => "[object LegacyPerformanceRecording]"
}, PerformanceRecordingCommon);
exports.LegacyPerformanceRecording = LegacyPerformanceRecording;

View File

@ -5,7 +5,6 @@
DIRS += [
'components',
'legacy',
'modules',
'test',
]
@ -18,5 +17,5 @@ DevToolsModules(
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
with Files('**'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Performance Tools (Profiler/Timeline)')
with Files('**'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Performance Tools (Profiler/Timeline)')

View File

@ -235,11 +235,6 @@ var PerformanceController = {
* @return Promise:boolean
*/
canCurrentlyRecord: Task.async(function* () {
// If we're testing the legacy front, the performance actor will exist,
// with `canCurrentlyRecord` method; this ensures we test the legacy path.
if (gFront.LEGACY_FRONT) {
return true;
}
let hasActor = yield gTarget.hasActor("performance");
if (!hasActor) {
return true;

View File

@ -11,8 +11,6 @@ const { Task } = require("devtools/shared/task");
loader.lazyRequireGetter(this, "PerformanceIO",
"devtools/client/performance/modules/io");
loader.lazyRequireGetter(this, "LegacyPerformanceFront",
"devtools/client/performance/legacy/front", true);
loader.lazyRequireGetter(this, "getSystemInfo",
"devtools/shared/system", true);
@ -136,13 +134,5 @@ const PerformanceFront = FrontClassWithSpec(performanceSpec, {
exports.PerformanceFront = PerformanceFront;
exports.createPerformanceFront = function createPerformanceFront(target) {
// If we force legacy mode, or the server does not have a performance actor (< Fx42),
// use our LegacyPerformanceFront which will handle
// the communication over RDP to other underlying actors.
if (target.TEST_PERFORMANCE_LEGACY_FRONT || !target.form.performanceActor) {
return new LegacyPerformanceFront(target);
}
// If our server has a PerformanceActor implementation, set this
// up like a normal front.
return new PerformanceFront(target.client, target.form);
};