зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1159480 - Pull out actor-specific logic from Performance Front. r=vp
This commit is contained in:
Родитель
109f1ff513
Коммит
d7d4d70a86
|
@ -0,0 +1,293 @@
|
||||||
|
/* 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("resource://gre/modules/Task.jsm");
|
||||||
|
const { Promise } = require("resource://gre/modules/Promise.jsm");
|
||||||
|
const {
|
||||||
|
actorCompatibilityBridge, getProfiler,
|
||||||
|
MockMemoryFront, MockTimelineFront,
|
||||||
|
memoryActorSupported, timelineActorSupported
|
||||||
|
} = require("devtools/performance/compatibility");
|
||||||
|
|
||||||
|
loader.lazyRequireGetter(this, "EventEmitter",
|
||||||
|
"devtools/toolkit/event-emitter");
|
||||||
|
loader.lazyRequireGetter(this, "RecordingUtils",
|
||||||
|
"devtools/performance/recording-utils", true);
|
||||||
|
loader.lazyRequireGetter(this, "TimelineFront",
|
||||||
|
"devtools/server/actors/timeline", true);
|
||||||
|
loader.lazyRequireGetter(this, "MemoryFront",
|
||||||
|
"devtools/server/actors/memory", true);
|
||||||
|
loader.lazyRequireGetter(this, "timers",
|
||||||
|
"resource://gre/modules/Timer.jsm");
|
||||||
|
|
||||||
|
// how often do we pull allocation sites from the memory actor
|
||||||
|
const ALLOCATION_SITE_POLL_TIMER = 200; // ms
|
||||||
|
|
||||||
|
const MEMORY_ACTOR_METHODS = [
|
||||||
|
"destroy", "attach", "detach", "getState", "getAllocationsSettings",
|
||||||
|
"getAllocations", "startRecordingAllocations", "stopRecordingAllocations"
|
||||||
|
];
|
||||||
|
|
||||||
|
const TIMELINE_ACTOR_METHODS = [
|
||||||
|
"start", "stop",
|
||||||
|
];
|
||||||
|
|
||||||
|
const PROFILER_ACTOR_METHODS = [
|
||||||
|
"isActive", "startProfiler", "getStartOptions", "stopProfiler",
|
||||||
|
"registerEventNotifications", "unregisterEventNotifications"
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a facade around an underlying ProfilerFront.
|
||||||
|
*/
|
||||||
|
function ProfilerFrontFacade (target) {
|
||||||
|
this._target = target;
|
||||||
|
this._onProfilerEvent = this._onProfilerEvent.bind(this);
|
||||||
|
EventEmitter.decorate(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilerFrontFacade.prototype = {
|
||||||
|
EVENTS: ["console-api-profiler", "profiler-stopped"],
|
||||||
|
|
||||||
|
// Connects to the targets underlying real ProfilerFront.
|
||||||
|
connect: Task.async(function*() {
|
||||||
|
let target = this._target;
|
||||||
|
this._actor = yield getProfiler(target);
|
||||||
|
|
||||||
|
// Fetch and store information about the SPS 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 });
|
||||||
|
// TODO bug 1159389, listen directly to actor if supporting new front
|
||||||
|
target.client.addListener("eventNotification", this._onProfilerEvent);
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters events for the underlying profiler actor.
|
||||||
|
*/
|
||||||
|
destroy: Task.async(function *() {
|
||||||
|
yield this.unregisterEventNotifications({ events: this.EVENTS });
|
||||||
|
// TODO bug 1159389, listen directly to actor if supporting new front
|
||||||
|
this._target.client.removeListener("eventNotification", this._onProfilerEvent);
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the profiler actor, if necessary.
|
||||||
|
*/
|
||||||
|
start: Task.async(function *(options={}) {
|
||||||
|
// 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.isActive();
|
||||||
|
if (profilerStatus.isActive) {
|
||||||
|
this.emit("profiler-already-active");
|
||||||
|
return profilerStatus.currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
};
|
||||||
|
|
||||||
|
yield this.startProfiler(profilerOptions);
|
||||||
|
|
||||||
|
this.emit("profiler-activated");
|
||||||
|
return 0;
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns profile data from now since `startTime`.
|
||||||
|
*/
|
||||||
|
getProfile: Task.async(function *(options) {
|
||||||
|
let profilerData = yield (actorCompatibilityBridge("getProfile").call(this, options));
|
||||||
|
// 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-end", details);
|
||||||
|
}
|
||||||
|
} else if (topic === "profiler-stopped") {
|
||||||
|
this.emit("profiler-stopped");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toString: () => "[object ProfilerFrontFacade]"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bind all the methods that directly proxy to the actor
|
||||||
|
PROFILER_ACTOR_METHODS.forEach(method => ProfilerFrontFacade.prototype[method] = actorCompatibilityBridge(method));
|
||||||
|
exports.ProfilerFront = ProfilerFrontFacade;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a facade around an underlying TimelineFront.
|
||||||
|
*/
|
||||||
|
function TimelineFrontFacade (target) {
|
||||||
|
this._target = target;
|
||||||
|
EventEmitter.decorate(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TimelineFrontFacade.prototype = {
|
||||||
|
EVENTS: ["markers", "frames", "memory", "ticks"],
|
||||||
|
|
||||||
|
connect: Task.async(function*() {
|
||||||
|
let supported = yield timelineActorSupported(this._target);
|
||||||
|
this._actor = supported ?
|
||||||
|
new TimelineFront(this._target.client, this._target.form) :
|
||||||
|
new 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._actor.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._actor.off(type, this[`_on${type}`]));
|
||||||
|
yield this._actor.destroy();
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An aggregate of all events (markers, frames, memory, ticks) and exposes
|
||||||
|
* to PerformanceActorsConnection as a single event.
|
||||||
|
*/
|
||||||
|
_onTimelineData: function (type, ...data) {
|
||||||
|
this.emit("timeline-data", type, ...data);
|
||||||
|
},
|
||||||
|
|
||||||
|
toString: () => "[object TimelineFrontFacade]"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bind all the methods that directly proxy to the actor
|
||||||
|
TIMELINE_ACTOR_METHODS.forEach(method => TimelineFrontFacade.prototype[method] = actorCompatibilityBridge(method));
|
||||||
|
exports.TimelineFront = TimelineFrontFacade;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a facade around an underlying ProfilerFront.
|
||||||
|
*/
|
||||||
|
function MemoryFrontFacade (target) {
|
||||||
|
this._target = target;
|
||||||
|
this._pullAllocationSites = this._pullAllocationSites.bind(this);
|
||||||
|
EventEmitter.decorate(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryFrontFacade.prototype = {
|
||||||
|
connect: Task.async(function*() {
|
||||||
|
let supported = yield memoryActorSupported(this._target);
|
||||||
|
this._actor = supported ?
|
||||||
|
new MemoryFront(this._target.client, this._target.form) :
|
||||||
|
new MockMemoryFront();
|
||||||
|
|
||||||
|
this.IS_MOCK = !supported;
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts polling for allocation information.
|
||||||
|
*/
|
||||||
|
start: Task.async(function *(options) {
|
||||||
|
if (!options.withAllocations) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield this.attach();
|
||||||
|
|
||||||
|
let startTime = yield this.startRecordingAllocations({
|
||||||
|
probability: options.allocationsSampleProbability,
|
||||||
|
maxLogLength: options.allocationsMaxLogLength
|
||||||
|
});
|
||||||
|
|
||||||
|
yield this._pullAllocationSites();
|
||||||
|
|
||||||
|
return startTime;
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops polling for allocation information.
|
||||||
|
*/
|
||||||
|
stop: 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;
|
||||||
|
timers.clearTimeout(this._sitesPullTimeout);
|
||||||
|
|
||||||
|
let endTime = yield this.stopRecordingAllocations();
|
||||||
|
yield this.detach();
|
||||||
|
|
||||||
|
return endTime;
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* At regular intervals, pull allocations from the memory actor, and
|
||||||
|
* forward them on this Front facade as "timeline-data" events. This
|
||||||
|
* gives the illusion that the MemoryActor supports an EventEmitter-style
|
||||||
|
* event stream.
|
||||||
|
*/
|
||||||
|
_pullAllocationSites: Task.async(function *() {
|
||||||
|
let { promise, resolve } = Promise.defer();
|
||||||
|
this._lastPullAllocationSitesFinished = promise;
|
||||||
|
|
||||||
|
if ((yield this.getState()) !== "attached") {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let memoryData = yield this.getAllocations();
|
||||||
|
// Match the signature of the TimelineFront events, with "timeline-data"
|
||||||
|
// being the event name, and the second argument describing the type.
|
||||||
|
this.emit("timeline-data", "allocations", {
|
||||||
|
sites: memoryData.allocations,
|
||||||
|
timestamps: memoryData.allocationsTimestamps,
|
||||||
|
frames: memoryData.frames,
|
||||||
|
counts: memoryData.counts
|
||||||
|
});
|
||||||
|
|
||||||
|
this._sitesPullTimeout = timers.setTimeout(this._pullAllocationSites, ALLOCATION_SITE_POLL_TIMER);
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
}),
|
||||||
|
|
||||||
|
toString: () => "[object MemoryFrontFacade]"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bind all the methods that directly proxy to the actor
|
||||||
|
MEMORY_ACTOR_METHODS.forEach(method => MemoryFrontFacade.prototype[method] = actorCompatibilityBridge(method));
|
||||||
|
exports.MemoryFront = MemoryFrontFacade;
|
|
@ -4,72 +4,9 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { Task } = require("resource://gre/modules/Task.jsm");
|
const { Task } = require("resource://gre/modules/Task.jsm");
|
||||||
loader.lazyRequireGetter(this, "promise");
|
const { Promise } = require("resource://gre/modules/Promise.jsm");
|
||||||
loader.lazyRequireGetter(this, "EventEmitter",
|
loader.lazyRequireGetter(this, "EventEmitter",
|
||||||
"devtools/toolkit/event-emitter");
|
"devtools/toolkit/event-emitter");
|
||||||
loader.lazyRequireGetter(this, "RecordingUtils",
|
|
||||||
"devtools/performance/recording-utils", true);
|
|
||||||
|
|
||||||
const REQUIRED_MEMORY_ACTOR_METHODS = [
|
|
||||||
"attach", "detach", "startRecordingAllocations", "stopRecordingAllocations", "getAllocations"
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for a facade around an underlying ProfilerFront.
|
|
||||||
*/
|
|
||||||
function ProfilerFront (target) {
|
|
||||||
this._target = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProfilerFront.prototype = {
|
|
||||||
// Connects to the targets underlying real ProfilerFront.
|
|
||||||
connect: Task.async(function*() {
|
|
||||||
let target = this._target;
|
|
||||||
// Chrome and content process targets already have obtained a reference
|
|
||||||
// to the profiler tab actor. Use it immediately.
|
|
||||||
if (target.form && target.form.profilerActor) {
|
|
||||||
this._profiler = 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 (target.root && target.root.profilerActor) {
|
|
||||||
this._profiler = target.root.profilerActor;
|
|
||||||
}
|
|
||||||
// Otherwise, call `listTabs`.
|
|
||||||
else {
|
|
||||||
this._profiler = (yield listTabs(target.client)).profilerActor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch and store information about the SPS profiler and
|
|
||||||
// server profiler.
|
|
||||||
this.traits = {};
|
|
||||||
this.traits.filterable = target.getTrait("profilerDataFilterable");
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes a request to the underlying real profiler actor. Handles
|
|
||||||
* backwards compatibility differences based off of the features
|
|
||||||
* and traits of the actor.
|
|
||||||
*/
|
|
||||||
_request: function (method, ...args) {
|
|
||||||
let deferred = promise.defer();
|
|
||||||
let data = args[0] || {};
|
|
||||||
data.to = this._profiler;
|
|
||||||
data.type = method;
|
|
||||||
this._target.client.request(data, res => {
|
|
||||||
// If the backend does not support filtering by start and endtime on platform (< Fx40),
|
|
||||||
// do it on the client (much slower).
|
|
||||||
if (method === "getProfile" && !this.traits.filterable) {
|
|
||||||
RecordingUtils.filterSamples(res.profile, data.startTime || 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
deferred.resolve(res);
|
|
||||||
});
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.ProfilerFront = ProfilerFront;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dummy front decorated with the provided methods.
|
* A dummy front decorated with the provided methods.
|
||||||
|
@ -87,7 +24,8 @@ function MockFront (blueprint) {
|
||||||
|
|
||||||
function MockMemoryFront () {
|
function MockMemoryFront () {
|
||||||
MockFront.call(this, [
|
MockFront.call(this, [
|
||||||
["initialize"],
|
["start", 0], // for facade
|
||||||
|
["stop", 0], // for facade
|
||||||
["destroy"],
|
["destroy"],
|
||||||
["attach"],
|
["attach"],
|
||||||
["detach"],
|
["detach"],
|
||||||
|
@ -101,7 +39,6 @@ exports.MockMemoryFront = MockMemoryFront;
|
||||||
|
|
||||||
function MockTimelineFront () {
|
function MockTimelineFront () {
|
||||||
MockFront.call(this, [
|
MockFront.call(this, [
|
||||||
["initialize"],
|
|
||||||
["destroy"],
|
["destroy"],
|
||||||
["start", 0],
|
["start", 0],
|
||||||
["stop", 0],
|
["stop", 0],
|
||||||
|
@ -169,12 +106,66 @@ function timelineActorSupported(target) {
|
||||||
exports.timelineActorSupported = Task.async(timelineActorSupported);
|
exports.timelineActorSupported = Task.async(timelineActorSupported);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a promise resolved with a listing of all the tabs in the
|
* Returns a promise resolving to the location of the profiler actor
|
||||||
* provided thread client.
|
* within this context.
|
||||||
|
*
|
||||||
|
* @param {TabTarget} target
|
||||||
|
* @return {Promise<ProfilerActor>}
|
||||||
*/
|
*/
|
||||||
function listTabs(client) {
|
function getProfiler (target) {
|
||||||
let deferred = promise.defer();
|
let { promise, resolve } = Promise.defer();
|
||||||
client.listTabs(deferred.resolve);
|
// Chrome and content process targets already have obtained a reference
|
||||||
return deferred.promise;
|
// to the profiler tab actor. Use it immediately.
|
||||||
|
if (target.form && target.form.profilerActor) {
|
||||||
|
resolve(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 (target.root && target.root.profilerActor) {
|
||||||
|
resolve(target.root.profilerActor);
|
||||||
|
}
|
||||||
|
// Otherwise, call `listTabs`.
|
||||||
|
else {
|
||||||
|
target.client.listTabs(({ profilerActor }) => resolve(profilerActor));
|
||||||
|
}
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
exports.getProfiler = Task.async(getProfiler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a request to an actor that does not have the modern `Front`
|
||||||
|
* interface.
|
||||||
|
*/
|
||||||
|
function legacyRequest (target, actor, method, args) {
|
||||||
|
let { promise, resolve } = Promise.defer();
|
||||||
|
let data = args[0] || {};
|
||||||
|
data.to = actor;
|
||||||
|
data.type = method;
|
||||||
|
target.client.request(data, resolve);
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a function to be used as a method on an "Actor" in ./actors.
|
||||||
|
* Calls the underlying actor's method, supporting the modern `Front`
|
||||||
|
* interface if possible, otherwise, falling back to using
|
||||||
|
* `legacyRequest`.
|
||||||
|
*/
|
||||||
|
function actorCompatibilityBridge (method) {
|
||||||
|
return function () {
|
||||||
|
// Check to see if this is a modern ActorFront, which has its
|
||||||
|
// own `request` method. Also, check if its a mock actor, as it mimicks
|
||||||
|
// the ActorFront interface.
|
||||||
|
// The profiler actor does not currently support the modern `Front`
|
||||||
|
// interface, so we have to manually push packets to it.
|
||||||
|
// TODO bug 1159389, fix up profiler actor to not need this, however
|
||||||
|
// we will need it for backwards compat
|
||||||
|
if (this.IS_MOCK || this._actor.request) {
|
||||||
|
return this._actor[method].apply(this._actor, arguments);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return legacyRequest(this._target, this._actor, method, arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
exports.actorCompatibilityBridge = actorCompatibilityBridge;
|
||||||
|
|
|
@ -9,24 +9,15 @@ const { extend } = require("sdk/util/object");
|
||||||
const { RecordingModel } = require("devtools/performance/recording-model");
|
const { RecordingModel } = require("devtools/performance/recording-model");
|
||||||
|
|
||||||
loader.lazyRequireGetter(this, "Services");
|
loader.lazyRequireGetter(this, "Services");
|
||||||
loader.lazyRequireGetter(this, "promise");
|
|
||||||
loader.lazyRequireGetter(this, "EventEmitter",
|
loader.lazyRequireGetter(this, "EventEmitter",
|
||||||
"devtools/toolkit/event-emitter");
|
"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",
|
loader.lazyRequireGetter(this, "DevToolsUtils",
|
||||||
"devtools/toolkit/DevToolsUtils");
|
"devtools/toolkit/DevToolsUtils");
|
||||||
loader.lazyRequireGetter(this, "compatibility",
|
loader.lazyRequireGetter(this, "actors",
|
||||||
"devtools/performance/compatibility");
|
"devtools/performance/actors");
|
||||||
|
|
||||||
loader.lazyImporter(this, "gDevTools",
|
loader.lazyImporter(this, "gDevTools",
|
||||||
"resource:///modules/devtools/gDevTools.jsm");
|
"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",
|
loader.lazyImporter(this, "Promise",
|
||||||
"resource://gre/modules/Promise.jsm");
|
"resource://gre/modules/Promise.jsm");
|
||||||
|
|
||||||
|
@ -41,9 +32,6 @@ const CONNECTION_PIPE_EVENTS = [
|
||||||
"recording-started", "recording-stopped"
|
"recording-started", "recording-stopped"
|
||||||
];
|
];
|
||||||
|
|
||||||
// Events to listen to from the profiler actor
|
|
||||||
const PROFILER_EVENTS = ["console-api-profiler", "profiler-stopped"];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cache of all PerformanceActorsConnection instances.
|
* A cache of all PerformanceActorsConnection instances.
|
||||||
* The keys are Target objects.
|
* The keys are Target objects.
|
||||||
|
@ -84,17 +72,15 @@ function PerformanceActorsConnection(target) {
|
||||||
|
|
||||||
this._target = target;
|
this._target = target;
|
||||||
this._client = this._target.client;
|
this._client = this._target.client;
|
||||||
this._request = this._request.bind(this);
|
|
||||||
this._pendingConsoleRecordings = [];
|
this._pendingConsoleRecordings = [];
|
||||||
this._sitesPullTimeout = 0;
|
this._sitesPullTimeout = 0;
|
||||||
this._recordings = [];
|
this._recordings = [];
|
||||||
|
|
||||||
this._onTimelineMarkers = this._onTimelineMarkers.bind(this);
|
this._pipeToConnection = this._pipeToConnection.bind(this);
|
||||||
this._onTimelineFrames = this._onTimelineFrames.bind(this);
|
this._onTimelineData = this._onTimelineData.bind(this);
|
||||||
this._onTimelineMemory = this._onTimelineMemory.bind(this);
|
this._onConsoleProfileStart = this._onConsoleProfileStart.bind(this);
|
||||||
this._onTimelineTicks = this._onTimelineTicks.bind(this);
|
this._onConsoleProfileEnd = this._onConsoleProfileEnd.bind(this);
|
||||||
this._onProfilerEvent = this._onProfilerEvent.bind(this);
|
this._onProfilerUnexpectedlyStopped = this._onProfilerUnexpectedlyStopped.bind(this);
|
||||||
this._pullAllocationSites = this._pullAllocationSites.bind(this);
|
|
||||||
|
|
||||||
Services.obs.notifyObservers(null, "performance-actors-connection-created", null);
|
Services.obs.notifyObservers(null, "performance-actors-connection-created", null);
|
||||||
}
|
}
|
||||||
|
@ -128,10 +114,7 @@ PerformanceActorsConnection.prototype = {
|
||||||
// Only initialize the timeline and memory fronts if the respective actors
|
// Only initialize the timeline and memory fronts if the respective actors
|
||||||
// are available. Older Gecko versions don't have existing implementations,
|
// are available. Older Gecko versions don't have existing implementations,
|
||||||
// in which case all the methods we need can be easily mocked.
|
// in which case all the methods we need can be easily mocked.
|
||||||
yield this._connectProfilerActor();
|
yield this._connectActors();
|
||||||
yield this._connectTimelineActor();
|
|
||||||
yield this._connectMemoryActor();
|
|
||||||
|
|
||||||
yield this._registerListeners();
|
yield this._registerListeners();
|
||||||
|
|
||||||
this._connected = true;
|
this._connected = true;
|
||||||
|
@ -159,132 +142,64 @@ PerformanceActorsConnection.prototype = {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a connection to the profiler actor. Uses a facade around the ProfilerFront
|
* Initializes fronts and connects to the underlying actors using the facades
|
||||||
* for similarity to the other actors in the shared connection.
|
* found in ./actors.js.
|
||||||
*/
|
*/
|
||||||
_connectProfilerActor: Task.async(function*() {
|
_connectActors: Task.async(function*() {
|
||||||
this._profiler = new compatibility.ProfilerFront(this._target);
|
this._profiler = new actors.ProfilerFront(this._target);
|
||||||
yield this._profiler.connect();
|
this._memory = new actors.MemoryFront(this._target);
|
||||||
}),
|
this._timeline = new actors.TimelineFront(this._target);
|
||||||
|
|
||||||
/**
|
yield Promise.all([
|
||||||
* Initializes a connection to a timeline actor.
|
this._profiler.connect(),
|
||||||
*/
|
this._memory.connect(),
|
||||||
_connectTimelineActor: function() {
|
this._timeline.connect()
|
||||||
let supported = yield compatibility.timelineActorSupported(this._target);
|
]);
|
||||||
if (supported) {
|
|
||||||
this._timeline = new TimelineFront(this._target.client, this._target.form);
|
|
||||||
} else {
|
|
||||||
this._timeline = new compatibility.MockTimelineFront();
|
|
||||||
}
|
|
||||||
this._timelineSupported = supported;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
// Expose server support status of underlying actors
|
||||||
* Initializes a connection to a memory actor.
|
// after connecting.
|
||||||
*/
|
this._memorySupported = !this._memory.IS_MOCK;
|
||||||
_connectMemoryActor: Task.async(function* () {
|
this._timelineSupported = !this._timeline.IS_MOCK;
|
||||||
let supported = yield compatibility.memoryActorSupported(this._target);
|
|
||||||
if (supported) {
|
|
||||||
this._memory = new MemoryFront(this._target.client, this._target.form);
|
|
||||||
} else {
|
|
||||||
this._memory = new compatibility.MockMemoryFront();
|
|
||||||
}
|
|
||||||
this._memorySupported = supported;
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers listeners on events from the underlying
|
* Registers listeners on events from the underlying
|
||||||
* actors, so the connection can handle them.
|
* actors, so the connection can handle them.
|
||||||
*/
|
*/
|
||||||
_registerListeners: Task.async(function*() {
|
_registerListeners: function () {
|
||||||
// Pipe events from TimelineActor to the PerformanceFront
|
this._timeline.on("timeline-data", this._onTimelineData);
|
||||||
this._timeline.on("markers", this._onTimelineMarkers);
|
this._memory.on("timeline-data", this._onTimelineData);
|
||||||
this._timeline.on("frames", this._onTimelineFrames);
|
this._profiler.on("console-profile-start", this._onConsoleProfileStart);
|
||||||
this._timeline.on("memory", this._onTimelineMemory);
|
this._profiler.on("console-profile-end", this._onConsoleProfileEnd);
|
||||||
this._timeline.on("ticks", this._onTimelineTicks);
|
this._profiler.on("profiler-stopped", this._onProfilerUnexpectedlyStopped);
|
||||||
|
this._profiler.on("profiler-already-active", this._pipeToConnection);
|
||||||
// Register events on the profiler actor to hook into `console.profile*` calls.
|
this._profiler.on("profiler-activated", this._pipeToConnection);
|
||||||
yield this._request("profiler", "registerEventNotifications", { events: PROFILER_EVENTS });
|
},
|
||||||
this._client.addListener("eventNotification", this._onProfilerEvent);
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters listeners on events on the underlying actors.
|
* Unregisters listeners on events on the underlying actors.
|
||||||
*/
|
*/
|
||||||
_unregisterListeners: Task.async(function*() {
|
_unregisterListeners: function () {
|
||||||
this._timeline.off("markers", this._onTimelineMarkers);
|
this._timeline.off("timeline-data", this._onTimelineData);
|
||||||
this._timeline.off("frames", this._onTimelineFrames);
|
this._memory.off("timeline-data", this._onTimelineData);
|
||||||
this._timeline.off("memory", this._onTimelineMemory);
|
this._profiler.off("console-profile-start", this._onConsoleProfileStart);
|
||||||
this._timeline.off("ticks", this._onTimelineTicks);
|
this._profiler.off("console-profile-end", this._onConsoleProfileEnd);
|
||||||
|
this._profiler.off("profiler-stopped", this._onProfilerUnexpectedlyStopped);
|
||||||
yield this._request("profiler", "unregisterEventNotifications", { events: PROFILER_EVENTS });
|
this._profiler.off("profiler-already-active", this._pipeToConnection);
|
||||||
this._client.removeListener("eventNotification", this._onProfilerEvent);
|
this._profiler.off("profiler-activated", this._pipeToConnection);
|
||||||
}),
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the connections to non-profiler actors.
|
* Closes the connections to non-profiler actors.
|
||||||
*/
|
*/
|
||||||
_disconnectActors: Task.async(function* () {
|
_disconnectActors: Task.async(function* () {
|
||||||
yield this._timeline.destroy();
|
yield Promise.all([
|
||||||
yield this._memory.destroy();
|
this._profiler.destroy(),
|
||||||
|
this._timeline.destroy(),
|
||||||
|
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") {
|
|
||||||
return this._profiler._request(method, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
* Invoked whenever `console.profile` is called.
|
||||||
*
|
*
|
||||||
|
@ -294,7 +209,7 @@ PerformanceActorsConnection.prototype = {
|
||||||
* The time (in milliseconds) when the call was made, relative to when
|
* The time (in milliseconds) when the call was made, relative to when
|
||||||
* the nsIProfiler module was started.
|
* the nsIProfiler module was started.
|
||||||
*/
|
*/
|
||||||
_onConsoleProfileStart: Task.async(function *({ profileLabel, currentTime: startTime }) {
|
_onConsoleProfileStart: Task.async(function *(_, { profileLabel, currentTime: startTime }) {
|
||||||
let recordings = this._recordings;
|
let recordings = this._recordings;
|
||||||
|
|
||||||
// Abort if a profile with this label already exists.
|
// Abort if a profile with this label already exists.
|
||||||
|
@ -325,7 +240,7 @@ PerformanceActorsConnection.prototype = {
|
||||||
* The time (in milliseconds) when the call was made, relative to when
|
* The time (in milliseconds) when the call was made, relative to when
|
||||||
* the nsIProfiler module was started.
|
* the nsIProfiler module was started.
|
||||||
*/
|
*/
|
||||||
_onConsoleProfileEnd: Task.async(function *(data) {
|
_onConsoleProfileEnd: Task.async(function *(_, data) {
|
||||||
// If no data, abort; can occur if profiler isn't running and we get a surprise
|
// If no data, abort; can occur if profiler isn't running and we get a surprise
|
||||||
// call to console.profileEnd()
|
// call to console.profileEnd()
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
@ -361,14 +276,12 @@ PerformanceActorsConnection.prototype = {
|
||||||
this.emit("console-profile-end", model);
|
this.emit("console-profile-end", model);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handlers for TimelineActor events. All pipe to `_onTimelineData`
|
* TODO handle bug 1144438
|
||||||
* with the appropriate event name.
|
*/
|
||||||
*/
|
_onProfilerUnexpectedlyStopped: function () {
|
||||||
_onTimelineMarkers: function (markers) { this._onTimelineData("markers", markers); },
|
Cu.reportError("Profiler unexpectedly stopped.", arguments);
|
||||||
_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:
|
* Called whenever there is timeline data of any of the following types:
|
||||||
|
@ -380,8 +293,7 @@ PerformanceActorsConnection.prototype = {
|
||||||
*
|
*
|
||||||
* Populate our internal store of recordings for all currently recording sessions.
|
* Populate our internal store of recordings for all currently recording sessions.
|
||||||
*/
|
*/
|
||||||
|
_onTimelineData: function (_, ...data) {
|
||||||
_onTimelineData: function (...data) {
|
|
||||||
this._recordings.forEach(e => e.addTimelineData.apply(e, data));
|
this._recordings.forEach(e => e.addTimelineData.apply(e, data));
|
||||||
this.emit("timeline-data", ...data);
|
this.emit("timeline-data", ...data);
|
||||||
},
|
},
|
||||||
|
@ -399,15 +311,13 @@ PerformanceActorsConnection.prototype = {
|
||||||
let model = new RecordingModel(options);
|
let model = new RecordingModel(options);
|
||||||
// All actors are started asynchronously over the remote debugging protocol.
|
// All actors are started asynchronously over the remote debugging protocol.
|
||||||
// Get the corresponding start times from each one of them.
|
// Get the corresponding start times from each one of them.
|
||||||
let profilerStartTime = yield this._startProfiler(options);
|
// The timeline and memory actors are target-dependent, so start those as well,
|
||||||
let timelineStartTime = yield this._startTimeline(options);
|
// even though these are mocked in older Geckos (FF < 35)
|
||||||
let memoryStartTime = yield this._startMemory(options);
|
let profilerStartTime = yield this._profiler.start(options);
|
||||||
|
let timelineStartTime = yield this._timeline.start(options);
|
||||||
|
let memoryStartTime = yield this._memory.start(options);
|
||||||
|
|
||||||
let data = {
|
let data = { profilerStartTime, timelineStartTime, memoryStartTime };
|
||||||
profilerStartTime,
|
|
||||||
timelineStartTime,
|
|
||||||
memoryStartTime
|
|
||||||
};
|
|
||||||
|
|
||||||
// Signify to the model that the recording has started,
|
// Signify to the model that the recording has started,
|
||||||
// populate with data and store the recording model here.
|
// populate with data and store the recording model here.
|
||||||
|
@ -445,7 +355,7 @@ PerformanceActorsConnection.prototype = {
|
||||||
|
|
||||||
let config = model.getConfiguration();
|
let config = model.getConfiguration();
|
||||||
let startTime = model.getProfilerStartTime();
|
let startTime = model.getProfilerStartTime();
|
||||||
let profilerData = yield this._request("profiler", "getProfile", { startTime });
|
let profilerData = yield this._profiler.getProfile({ startTime });
|
||||||
let memoryEndTime = Date.now();
|
let memoryEndTime = Date.now();
|
||||||
let timelineEndTime = Date.now();
|
let timelineEndTime = Date.now();
|
||||||
|
|
||||||
|
@ -454,8 +364,8 @@ PerformanceActorsConnection.prototype = {
|
||||||
// juse use Date.now() for the memory and timeline end times, as those
|
// juse use Date.now() for the memory and timeline end times, as those
|
||||||
// are only used in tests.
|
// are only used in tests.
|
||||||
if (!this.isRecording()) {
|
if (!this.isRecording()) {
|
||||||
memoryEndTime = yield this._stopMemory(config);
|
memoryEndTime = yield this._memory.stop(config);
|
||||||
timelineEndTime = yield this._stopTimeline(config);
|
timelineEndTime = yield this._timeline.stop(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the results on the RecordingModel itself.
|
// Set the results on the RecordingModel itself.
|
||||||
|
@ -484,127 +394,12 @@ PerformanceActorsConnection.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the profiler actor, if necessary.
|
* An event from an underlying actor that we just want
|
||||||
|
* to pipe to the connection itself.
|
||||||
*/
|
*/
|
||||||
_startProfiler: Task.async(function *(options={}) {
|
_pipeToConnection: function (eventName, ...args) {
|
||||||
// Start the profiler only if it wasn't already active. The built-in
|
this.emit(eventName, ...args);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
};
|
|
||||||
|
|
||||||
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]"
|
toString: () => "[object PerformanceActorsConnection]"
|
||||||
};
|
};
|
||||||
|
@ -620,7 +415,6 @@ function PerformanceFront(connection) {
|
||||||
EventEmitter.decorate(this);
|
EventEmitter.decorate(this);
|
||||||
|
|
||||||
this._connection = connection;
|
this._connection = connection;
|
||||||
this._request = connection._request;
|
|
||||||
|
|
||||||
// Set when mocks are being used
|
// Set when mocks are being used
|
||||||
this._memorySupported = connection._memorySupported;
|
this._memorySupported = connection._memorySupported;
|
||||||
|
@ -680,6 +474,17 @@ PerformanceFront.prototype = {
|
||||||
*/
|
*/
|
||||||
isRecording: function () {
|
isRecording: function () {
|
||||||
return this._connection.isRecording();
|
return this._connection.isRecording();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interacts with the connection's actors. Should only be used in tests.
|
||||||
|
*/
|
||||||
|
_request: function (actorName, method, ...args) {
|
||||||
|
if (!gDevTools.testing) {
|
||||||
|
throw new Error("PerformanceFront._request may only be used in tests.");
|
||||||
|
}
|
||||||
|
let actor = this._connection[`_${actorName}`];
|
||||||
|
return actor[method].apply(actor, args);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
EXTRA_JS_MODULES.devtools.performance += [
|
EXTRA_JS_MODULES.devtools.performance += [
|
||||||
|
'modules/actors.js',
|
||||||
'modules/compatibility.js',
|
'modules/compatibility.js',
|
||||||
'modules/front.js',
|
'modules/front.js',
|
||||||
'modules/graphs.js',
|
'modules/graphs.js',
|
||||||
|
|
|
@ -18,7 +18,7 @@ function spawnTest () {
|
||||||
|
|
||||||
ok(sharedConnection,
|
ok(sharedConnection,
|
||||||
"A shared profiler connection for the current toolbox was retrieved.");
|
"A shared profiler connection for the current toolbox was retrieved.");
|
||||||
is(sharedConnection._request, panel.panelWin.gFront._request,
|
is(panel.panelWin.gFront._connection, sharedConnection,
|
||||||
"The same shared profiler connection is used by the panel's front.");
|
"The same shared profiler connection is used by the panel's front.");
|
||||||
|
|
||||||
yield sharedConnection.open();
|
yield sharedConnection.open();
|
||||||
|
|
Загрузка…
Ссылка в новой задаче