From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- devtools/client/performance/legacy/actors.js | 263 +++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 devtools/client/performance/legacy/actors.js (limited to 'devtools/client/performance/legacy/actors.js') diff --git a/devtools/client/performance/legacy/actors.js b/devtools/client/performance/legacy/actors.js new file mode 100644 index 000000000..22b4f85b1 --- /dev/null +++ b/devtools/client/performance/legacy/actors.js @@ -0,0 +1,263 @@ +/* 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 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 }); + 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; -- cgit v1.2.3