mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-06-06 08:29:08 +00:00
695 lines
24 KiB
JavaScript
695 lines
24 KiB
JavaScript
/* 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 { Cc, Ci, Cu, Cr } = require("chrome");
|
|
const { Task } = require("resource://gre/modules/Task.jsm");
|
|
const { extend } = require("sdk/util/object");
|
|
const { RecordingModel } = require("devtools/performance/recording-model");
|
|
|
|
loader.lazyRequireGetter(this, "Services");
|
|
loader.lazyRequireGetter(this, "promise");
|
|
loader.lazyRequireGetter(this, "EventEmitter",
|
|
"devtools/toolkit/event-emitter");
|
|
loader.lazyRequireGetter(this, "TimelineFront",
|
|
"devtools/server/actors/timeline", true);
|
|
loader.lazyRequireGetter(this, "MemoryFront",
|
|
"devtools/server/actors/memory", true);
|
|
loader.lazyRequireGetter(this, "DevToolsUtils",
|
|
"devtools/toolkit/DevToolsUtils");
|
|
loader.lazyRequireGetter(this, "compatibility",
|
|
"devtools/performance/compatibility");
|
|
|
|
loader.lazyImporter(this, "gDevTools",
|
|
"resource:///modules/devtools/gDevTools.jsm");
|
|
loader.lazyImporter(this, "setTimeout",
|
|
"resource://gre/modules/Timer.jsm");
|
|
loader.lazyImporter(this, "clearTimeout",
|
|
"resource://gre/modules/Timer.jsm");
|
|
loader.lazyImporter(this, "Promise",
|
|
"resource://gre/modules/Promise.jsm");
|
|
|
|
|
|
// How often do we pull allocation sites from the memory actor.
|
|
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"
|
|
];
|
|
|
|
// Events to listen to from the profiler actor
|
|
const PROFILER_EVENTS = ["console-api-profiler", "profiler-stopped"];
|
|
|
|
/**
|
|
* A cache of all PerformanceActorsConnection instances.
|
|
* The keys are Target objects.
|
|
*/
|
|
let SharedPerformanceActors = new WeakMap();
|
|
|
|
/**
|
|
* Instantiates a shared PerformanceActorsConnection for the specified target.
|
|
* Consumers must yield on `open` to make sure the connection is established.
|
|
*
|
|
* @param Target target
|
|
* The target owning this connection.
|
|
* @return PerformanceActorsConnection
|
|
* The shared connection for the specified target.
|
|
*/
|
|
SharedPerformanceActors.forTarget = function(target) {
|
|
if (this.has(target)) {
|
|
return this.get(target);
|
|
}
|
|
|
|
let instance = new PerformanceActorsConnection(target);
|
|
this.set(target, instance);
|
|
return instance;
|
|
};
|
|
|
|
/**
|
|
* A connection to underlying actors (profiler, memory, framerate, etc.)
|
|
* shared by all tools in a target.
|
|
*
|
|
* Use `SharedPerformanceActors.forTarget` to make sure you get the same
|
|
* instance every time, and the `PerformanceFront` to start/stop recordings.
|
|
*
|
|
* @param Target target
|
|
* The target owning this connection.
|
|
*/
|
|
function PerformanceActorsConnection(target) {
|
|
EventEmitter.decorate(this);
|
|
|
|
this._target = target;
|
|
this._client = this._target.client;
|
|
this._request = this._request.bind(this);
|
|
this._pendingConsoleRecordings = [];
|
|
this._sitesPullTimeout = 0;
|
|
this._recordings = [];
|
|
|
|
this._onTimelineMarkers = this._onTimelineMarkers.bind(this);
|
|
this._onTimelineFrames = this._onTimelineFrames.bind(this);
|
|
this._onTimelineMemory = this._onTimelineMemory.bind(this);
|
|
this._onTimelineTicks = this._onTimelineTicks.bind(this);
|
|
this._onProfilerEvent = this._onProfilerEvent.bind(this);
|
|
this._pullAllocationSites = this._pullAllocationSites.bind(this);
|
|
|
|
Services.obs.notifyObservers(null, "performance-actors-connection-created", null);
|
|
}
|
|
|
|
PerformanceActorsConnection.prototype = {
|
|
|
|
// Properties set when mocks are being used
|
|
_usingMockMemory: false,
|
|
_usingMockTimeline: false,
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
open: 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();
|
|
|
|
// Local debugging needs to make the target remote.
|
|
yield this._target.makeRemote();
|
|
|
|
// Sets `this._profiler`, `this._timeline` and `this._memory`.
|
|
// Only initialize the timeline and memory 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._connectProfilerActor();
|
|
yield this._connectTimelineActor();
|
|
yield this._connectMemoryActor();
|
|
|
|
yield this._registerListeners();
|
|
|
|
this._connected = true;
|
|
|
|
this._connecting.resolve();
|
|
Services.obs.notifyObservers(null, "performance-actors-connection-opened", null);
|
|
}),
|
|
|
|
/**
|
|
* Destroys this connection.
|
|
*/
|
|
destroy: Task.async(function*() {
|
|
if (this._connecting && !this._connected) {
|
|
console.warn("Attempting to destroy SharedPerformanceActorsConnection before initialization completion. If testing, ensure `gDevTools.testing` is set.");
|
|
}
|
|
|
|
yield this._unregisterListeners();
|
|
yield this._disconnectActors();
|
|
|
|
this._memory = this._timeline = this._profiler = this._target = this._client = null;
|
|
this._connected = false;
|
|
}),
|
|
|
|
/**
|
|
* Initializes a connection to the profiler actor.
|
|
*/
|
|
_connectProfilerActor: Task.async(function*() {
|
|
// Chrome and content process targets already have obtained a reference
|
|
// to the profiler tab actor. Use it immediately.
|
|
if (this._target.form && this._target.form.profilerActor) {
|
|
this._profiler = this._target.form.profilerActor;
|
|
}
|
|
// Check if we already have a grip to the `listTabs` response object
|
|
// and, if we do, use it to get to the profiler actor.
|
|
else if (this._target.root && this._target.root.profilerActor) {
|
|
this._profiler = this._target.root.profilerActor;
|
|
}
|
|
// Otherwise, call `listTabs`.
|
|
else {
|
|
this._profiler = (yield listTabs(this._client)).profilerActor;
|
|
}
|
|
}),
|
|
|
|
/**
|
|
* Initializes a connection to a timeline actor.
|
|
*/
|
|
_connectTimelineActor: function() {
|
|
let supported = yield compatibility.timelineActorSupported(this._target);
|
|
if (supported) {
|
|
this._timeline = new TimelineFront(this._target.client, this._target.form);
|
|
} else {
|
|
this._usingMockTimeline = true;
|
|
this._timeline = new compatibility.MockTimelineFront();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Initializes a connection to a memory actor.
|
|
*/
|
|
_connectMemoryActor: Task.async(function* () {
|
|
let supported = yield compatibility.memoryActorSupported(this._target);
|
|
if (supported) {
|
|
this._memory = new MemoryFront(this._target.client, this._target.form);
|
|
} else {
|
|
this._usingMockMemory = true;
|
|
this._memory = new compatibility.MockMemoryFront();
|
|
}
|
|
}),
|
|
|
|
/**
|
|
* Registers listeners on events from the underlying
|
|
* actors, so the connection can handle them.
|
|
*/
|
|
_registerListeners: Task.async(function*() {
|
|
// Pipe events from TimelineActor to the PerformanceFront
|
|
this._timeline.on("markers", this._onTimelineMarkers);
|
|
this._timeline.on("frames", this._onTimelineFrames);
|
|
this._timeline.on("memory", this._onTimelineMemory);
|
|
this._timeline.on("ticks", this._onTimelineTicks);
|
|
|
|
// Register events on the profiler actor to hook into `console.profile*` calls.
|
|
yield this._request("profiler", "registerEventNotifications", { events: PROFILER_EVENTS });
|
|
this._client.addListener("eventNotification", this._onProfilerEvent);
|
|
}),
|
|
|
|
/**
|
|
* Unregisters listeners on events on the underlying actors.
|
|
*/
|
|
_unregisterListeners: Task.async(function*() {
|
|
this._timeline.off("markers", this._onTimelineMarkers);
|
|
this._timeline.off("frames", this._onTimelineFrames);
|
|
this._timeline.off("memory", this._onTimelineMemory);
|
|
this._timeline.off("ticks", this._onTimelineTicks);
|
|
|
|
yield this._request("profiler", "unregisterEventNotifications", { events: PROFILER_EVENTS });
|
|
this._client.removeListener("eventNotification", this._onProfilerEvent);
|
|
}),
|
|
|
|
/**
|
|
* Closes the connections to non-profiler actors.
|
|
*/
|
|
_disconnectActors: Task.async(function* () {
|
|
yield this._timeline.destroy();
|
|
yield this._memory.destroy();
|
|
}),
|
|
|
|
/**
|
|
* Sends the request over the remote debugging protocol to the
|
|
* specified actor.
|
|
*
|
|
* @param string actor
|
|
* Currently supported: "profiler", "timeline", "memory".
|
|
* @param string method
|
|
* Method to call on the backend.
|
|
* @param any args [optional]
|
|
* Additional data or arguments to send with the request.
|
|
* @return object
|
|
* A promise resolved with the response once the request finishes.
|
|
*/
|
|
_request: function(actor, method, ...args) {
|
|
// Handle requests to the profiler actor.
|
|
if (actor == "profiler") {
|
|
let deferred = promise.defer();
|
|
let data = args[0] || {};
|
|
data.to = this._profiler;
|
|
data.type = method;
|
|
this._client.request(data, deferred.resolve);
|
|
return deferred.promise;
|
|
}
|
|
|
|
// Handle requests to the timeline actor.
|
|
if (actor == "timeline") {
|
|
return this._timeline[method].apply(this._timeline, args);
|
|
}
|
|
|
|
// Handle requests to the memory actor.
|
|
if (actor == "memory") {
|
|
return this._memory[method].apply(this._memory, args);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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._onConsoleProfileStart(details);
|
|
} else if (subject.action === "profileEnd") {
|
|
this._onConsoleProfileEnd(details);
|
|
}
|
|
} else if (topic === "profiler-stopped") {
|
|
this._onProfilerUnexpectedlyStopped();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* TODO handle bug 1144438
|
|
*/
|
|
_onProfilerUnexpectedlyStopped: function () {
|
|
|
|
},
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
// Ensure the performance front is set up and ready.
|
|
// Slight performance overhead for this, should research some more.
|
|
// This is to ensure that there is a front to receive the events for
|
|
// the console profiles.
|
|
yield gDevTools.getToolbox(this._target).loadTool("performance");
|
|
|
|
let model = yield this.startRecording(extend(getRecordingModelPrefs(), {
|
|
console: true,
|
|
label: profileLabel
|
|
}));
|
|
|
|
this.emit("console-profile-start", model);
|
|
}),
|
|
|
|
/**
|
|
* Invoked whenever `console.profileEnd` is called.
|
|
*
|
|
* @param object profilerData
|
|
* The dump of data from the profiler triggered by this console.profileEnd call.
|
|
*/
|
|
_onConsoleProfileEnd: Task.async(function *(profilerData) {
|
|
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 (profilerData.profileLabel) {
|
|
model = pending.find(e => e.getLabel() === profilerData.profileLabel);
|
|
}
|
|
// If no label supplied, pop off the most recent pending console recording
|
|
else {
|
|
model = pending[pending.length - 1];
|
|
}
|
|
|
|
// If `profileEnd()` was called with a label, and there are no matching
|
|
// sessions, abort.
|
|
if (!model) {
|
|
Cu.reportError("console.profileEnd() called with label that does not match a recording.");
|
|
return;
|
|
}
|
|
|
|
this.emit("console-profile-ending", model);
|
|
yield this.stopRecording(model);
|
|
this.emit("console-profile-end", model);
|
|
}),
|
|
|
|
/**
|
|
* Handlers for TimelineActor events. All pipe to `_onTimelineData`
|
|
* with the appropriate event name.
|
|
*/
|
|
_onTimelineMarkers: function (markers) { this._onTimelineData("markers", markers); },
|
|
_onTimelineFrames: function (delta, frames) { this._onTimelineData("frames", delta, frames); },
|
|
_onTimelineMemory: function (delta, measurement) { this._onTimelineData("memory", delta, measurement); },
|
|
_onTimelineTicks: function (delta, timestamps) { this._onTimelineData("ticks", delta, timestamps); },
|
|
|
|
/**
|
|
* Called whenever there is timeline data of any of the following types:
|
|
* - markers
|
|
* - frames
|
|
* - memory
|
|
* - ticks
|
|
* - allocations
|
|
*
|
|
* Populate our internal store of recordings for all currently recording sessions.
|
|
*/
|
|
|
|
_onTimelineData: function (...data) {
|
|
this._recordings.forEach(e => e.addTimelineData.apply(e, data));
|
|
this.emit("timeline-data", ...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 RecordingModel(options);
|
|
// All actors are started asynchronously over the remote debugging protocol.
|
|
// Get the corresponding start times from each one of them.
|
|
let profilerStartTime = yield this._startProfiler();
|
|
let timelineStartTime = yield this._startTimeline(options);
|
|
let memoryStartTime = yield this._startMemory(options);
|
|
|
|
let data = {
|
|
profilerStartTime,
|
|
timelineStartTime,
|
|
memoryStartTime
|
|
};
|
|
|
|
// 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);
|
|
|
|
return model;
|
|
}),
|
|
|
|
/**
|
|
* Manually ends the recording session for the corresponding RecordingModel.
|
|
*
|
|
* @param RecordingModel model
|
|
* The corresponding RecordingModel that belongs to the recording session wished to stop.
|
|
* @return RecordingModel
|
|
* Returns the same model, populated with the profiling data.
|
|
*/
|
|
stopRecording: Task.async(function*(model) {
|
|
// If model isn't in the PerformanceActorsConnections internal store,
|
|
// then do nothing.
|
|
if (!this._recordings.includes(model)) {
|
|
return;
|
|
}
|
|
|
|
// 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 PerformanceFront or via `console-profile-end` 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 profilerData = yield this._request("profiler", "getProfile");
|
|
let memoryEndTime = Date.now();
|
|
let timelineEndTime = Date.now();
|
|
|
|
// Only if there are no more sessions recording do we stop
|
|
// the underlying memory and timeline actors. If we're still recording,
|
|
// juse use Date.now() for the memory and timeline end times, as those
|
|
// are only used in tests.
|
|
if (!this.isRecording()) {
|
|
memoryEndTime = yield this._stopMemory(config);
|
|
timelineEndTime = yield this._stopTimeline(config);
|
|
}
|
|
|
|
// Set the results on the RecordingModel 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,
|
|
memoryEndTime: memoryEndTime
|
|
});
|
|
|
|
return model;
|
|
}),
|
|
|
|
/**
|
|
* 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());
|
|
},
|
|
|
|
/**
|
|
* Starts the profiler actor, if necessary.
|
|
*/
|
|
_startProfiler: Task.async(function *() {
|
|
// 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 profilerStatus = yield this._request("profiler", "isActive");
|
|
if (profilerStatus.isActive) {
|
|
this.emit("profiler-already-active");
|
|
return profilerStatus.currentTime;
|
|
}
|
|
|
|
// If this._customProfilerOptions is defined, use those to pass in
|
|
// to the profiler actor. The profiler actor handles all the defaults
|
|
// now, so this should only be used for tests.
|
|
let profilerOptions = this._customProfilerOptions || {};
|
|
yield this._request("profiler", "startProfiler", profilerOptions);
|
|
|
|
this.emit("profiler-activated");
|
|
return 0;
|
|
}),
|
|
|
|
/**
|
|
* Starts the timeline actor.
|
|
*/
|
|
_startTimeline: Task.async(function *(options) {
|
|
// The timeline actor is target-dependent, so just make sure it's recording.
|
|
// It won't, however, be available in older Geckos (FF < 35).
|
|
return (yield this._request("timeline", "start", options));
|
|
}),
|
|
|
|
/**
|
|
* Stops the timeline actor.
|
|
*/
|
|
_stopTimeline: Task.async(function *(options) {
|
|
return (yield this._request("timeline", "stop"));
|
|
}),
|
|
|
|
/**
|
|
* Starts polling for allocations from the memory actor, if necessary.
|
|
*/
|
|
_startMemory: Task.async(function *(options) {
|
|
if (!options.withAllocations) {
|
|
return 0;
|
|
}
|
|
let memoryStartTime = yield this._startRecordingAllocations(options);
|
|
yield this._pullAllocationSites();
|
|
return memoryStartTime;
|
|
}),
|
|
|
|
/**
|
|
* Stops polling for allocations from the memory actor, if necessary.
|
|
*/
|
|
_stopMemory: Task.async(function *(options) {
|
|
if (!options.withAllocations) {
|
|
return 0;
|
|
}
|
|
// Since `_pullAllocationSites` is usually running inside a timeout, and
|
|
// it's performing asynchronous requests to the server, a recording may
|
|
// be stopped before that method finishes executing. Therefore, we need to
|
|
// wait for the last request to `getAllocations` to finish before actually
|
|
// stopping recording allocations.
|
|
yield this._lastPullAllocationSitesFinished;
|
|
clearTimeout(this._sitesPullTimeout);
|
|
|
|
return yield this._stopRecordingAllocations();
|
|
}),
|
|
|
|
/**
|
|
* Starts recording allocations in the memory actor.
|
|
*/
|
|
_startRecordingAllocations: Task.async(function*(options) {
|
|
yield this._request("memory", "attach");
|
|
let memoryStartTime = yield this._request("memory", "startRecordingAllocations", {
|
|
probability: options.allocationsSampleProbability,
|
|
maxLogLength: options.allocationsMaxLogLength
|
|
});
|
|
return memoryStartTime;
|
|
}),
|
|
|
|
/**
|
|
* Stops recording allocations in the memory actor.
|
|
*/
|
|
_stopRecordingAllocations: Task.async(function*() {
|
|
let memoryEndTime = yield this._request("memory", "stopRecordingAllocations");
|
|
yield this._request("memory", "detach");
|
|
return memoryEndTime;
|
|
}),
|
|
|
|
/**
|
|
* At regular intervals, pull allocations from the memory actor, and forward
|
|
* them to consumers.
|
|
*/
|
|
_pullAllocationSites: Task.async(function *() {
|
|
let deferred = promise.defer();
|
|
this._lastPullAllocationSitesFinished = deferred.promise;
|
|
|
|
let isDetached = (yield this._request("memory", "getState")) !== "attached";
|
|
if (isDetached) {
|
|
deferred.resolve();
|
|
return;
|
|
}
|
|
|
|
let memoryData = yield this._request("memory", "getAllocations");
|
|
|
|
this._onTimelineData("allocations", {
|
|
sites: memoryData.allocations,
|
|
timestamps: memoryData.allocationsTimestamps,
|
|
frames: memoryData.frames,
|
|
counts: memoryData.counts
|
|
});
|
|
|
|
let delay = DEFAULT_ALLOCATION_SITES_PULL_TIMEOUT;
|
|
this._sitesPullTimeout = setTimeout(this._pullAllocationSites, delay);
|
|
|
|
deferred.resolve();
|
|
}),
|
|
|
|
toString: () => "[object PerformanceActorsConnection]"
|
|
};
|
|
|
|
/**
|
|
* A thin wrapper around a shared PerformanceActorsConnection for the parent target.
|
|
* Handles manually starting and stopping a recording.
|
|
*
|
|
* @param PerformanceActorsConnection connection
|
|
* The shared instance for the parent target.
|
|
*/
|
|
function PerformanceFront(connection) {
|
|
EventEmitter.decorate(this);
|
|
|
|
this._connection = connection;
|
|
this._request = connection._request;
|
|
|
|
// Set when mocks are being used
|
|
this._usingMockMemory = connection._usingMockMemory;
|
|
this._usingMockTimeline = connection._usingMockTimeline;
|
|
|
|
// Pipe the console profile events from the connection
|
|
// to the front so that the UI can listen.
|
|
CONNECTION_PIPE_EVENTS.forEach(eventName => this._connection.on(eventName, () => this.emit.apply(this, arguments)));
|
|
}
|
|
|
|
PerformanceFront.prototype = {
|
|
|
|
/**
|
|
* Manually begins a recording session and creates a RecordingModel.
|
|
* Calls the underlying PerformanceActorsConnection's startRecording method.
|
|
*
|
|
* @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: function (options) {
|
|
return this._connection.startRecording(options);
|
|
},
|
|
|
|
/**
|
|
* Manually ends the recording session for the corresponding RecordingModel.
|
|
* Calls the underlying PerformanceActorsConnection's
|
|
*
|
|
* @param RecordingModel model
|
|
* The corresponding RecordingModel that belongs to the recording session wished to stop.
|
|
* @return RecordingModel
|
|
* Returns the same model, populated with the profiling data.
|
|
*/
|
|
stopRecording: function (model) {
|
|
return this._connection.stopRecording(model);
|
|
},
|
|
|
|
/**
|
|
* Returns an object indicating if mock actors are being used or not.
|
|
*/
|
|
getMocksInUse: function () {
|
|
return {
|
|
memory: this._usingMockMemory,
|
|
timeline: this._usingMockTimeline
|
|
};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns a promise resolved with a listing of all the tabs in the
|
|
* provided thread client.
|
|
*/
|
|
function listTabs(client) {
|
|
let deferred = promise.defer();
|
|
client.listTabs(deferred.resolve);
|
|
return deferred.promise;
|
|
}
|
|
|
|
/**
|
|
* Creates an object of configurations based off of preferences for a RecordingModel.
|
|
*/
|
|
function getRecordingModelPrefs () {
|
|
return {
|
|
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"),
|
|
allocationsSampleProbability: +Services.prefs.getCharPref("devtools.performance.memory.sample-probability"),
|
|
allocationsMaxLogLength: Services.prefs.getIntPref("devtools.performance.memory.max-log-length")
|
|
};
|
|
}
|
|
|
|
exports.getPerformanceActorsConnection = target => SharedPerformanceActors.forTarget(target);
|
|
exports.PerformanceFront = PerformanceFront;
|