Bug 1811853 - Merge gecko-profiler-interface.js into the perf actor r=jdescottes,canaltinova

Differential Revision: https://phabricator.services.mozilla.com/D167844
This commit is contained in:
Julien Wajsberg 2023-02-13 16:46:38 +00:00
Родитель 6712709070
Коммит 6fa044fc63
3 изменённых файлов: 166 добавлений и 231 удалений

Просмотреть файл

@ -6,50 +6,182 @@
const { Actor } = require("resource://devtools/shared/protocol.js");
const { perfSpec } = require("resource://devtools/shared/specs/perf.js");
const {
actorBridgeWithSpec,
} = require("resource://devtools/server/actors/common.js");
const {
ActorReadyGeckoProfilerInterface,
} = require("resource://devtools/shared/performance-new/gecko-profiler-interface.js");
loader.lazyRequireGetter(
this,
"RecordingUtils",
"resource://devtools/shared/performance-new/recording-utils.js"
);
// Some platforms are built without the Gecko Profiler.
const IS_SUPPORTED_PLATFORM = "nsIProfiler" in Ci;
/**
* Pass on the events from the bridge to the actor.
* @param {Object} actor The perf actor
* @param {Array<string>} names The event names
*/
function _bridgeEvents(actor, names) {
for (const name of names) {
actor.bridge.on(name, (...args) => actor.emit(name, ...args));
}
}
/**
* The PerfActor wraps the Gecko Profiler interface
* The PerfActor wraps the Gecko Profiler interface (aka Services.profiler).
*/
exports.PerfActor = class PerfActor extends Actor {
constructor(conn, targetActor) {
constructor(conn) {
super(conn, perfSpec);
// The "bridge" is the actual implementation of the actor. It is separated
// for historical reasons, and could be merged into this class.
this.bridge = new ActorReadyGeckoProfilerInterface();
_bridgeEvents(this, ["profiler-started", "profiler-stopped"]);
// Only setup the observers on a supported platform.
if (IS_SUPPORTED_PLATFORM) {
this._observer = {
observe: this._observe.bind(this),
};
Services.obs.addObserver(this._observer, "profiler-started");
Services.obs.addObserver(this._observer, "profiler-stopped");
}
}
destroy() {
super.destroy();
this.bridge.destroy();
if (!IS_SUPPORTED_PLATFORM) {
return;
}
Services.obs.removeObserver(this._observer, "profiler-started");
Services.obs.removeObserver(this._observer, "profiler-stopped");
}
// Connect the rest of the ActorReadyGeckoProfilerInterface's methods to the PerfActor.
startProfiler = actorBridgeWithSpec("startProfiler");
stopProfilerAndDiscardProfile = actorBridgeWithSpec(
"stopProfilerAndDiscardProfile"
);
getSymbolTable = actorBridgeWithSpec("getSymbolTable");
getProfileAndStopProfiler = actorBridgeWithSpec("getProfileAndStopProfiler");
isActive = actorBridgeWithSpec("isActive");
isSupportedPlatform = actorBridgeWithSpec("isSupportedPlatform");
getSupportedFeatures = actorBridgeWithSpec("getSupportedFeatures");
startProfiler(options) {
if (!IS_SUPPORTED_PLATFORM) {
return false;
}
// For a quick implementation, decide on some default values. These may need
// to be tweaked or made configurable as needed.
const settings = {
entries: options.entries || 1000000,
duration: options.duration || 0,
interval: options.interval || 1,
features: options.features || [
"js",
"stackwalk",
"cpu",
"responsiveness",
],
threads: options.threads || ["GeckoMain", "Compositor"],
activeTabID: RecordingUtils.getActiveBrowserID(),
};
try {
// This can throw an error if the profiler is in the wrong state.
Services.profiler.StartProfiler(
settings.entries,
settings.interval,
settings.features,
settings.threads,
settings.activeTabID,
settings.duration
);
} catch (e) {
// In case any errors get triggered, bailout with a false.
return false;
}
return true;
}
stopProfilerAndDiscardProfile() {
if (!IS_SUPPORTED_PLATFORM) {
return;
}
Services.profiler.StopProfiler();
}
/**
* @type {string} debugPath
* @type {string} breakpadId
* @returns {Promise<[number[], number[], number[]]>}
*/
async getSymbolTable(debugPath, breakpadId) {
const [addr, index, buffer] = await Services.profiler.getSymbolTable(
debugPath,
breakpadId
);
// The protocol does not support the transfer of typed arrays, so we convert
// these typed arrays to plain JS arrays of numbers now.
// Our return value type is declared as "array:array:number".
return [Array.from(addr), Array.from(index), Array.from(buffer)];
}
async getProfileAndStopProfiler() {
if (!IS_SUPPORTED_PLATFORM) {
return null;
}
// Pause profiler before we collect the profile, so that we don't capture
// more samples while the parent process or android threads wait for subprocess profiles.
Services.profiler.Pause();
let profile;
try {
// Attempt to pull out the data.
profile = await Services.profiler.getProfileDataAsync();
if (Object.keys(profile).length === 0) {
console.error(
"An empty object was received from getProfileDataAsync.getProfileDataAsync(), " +
"meaning that a profile could not successfully be serialized and captured."
);
profile = null;
}
} catch (e) {
// Explicitly set the profile to null if there as an error.
profile = null;
console.error(`There was an error fetching a profile`, e);
}
// Stop and discard the buffers.
Services.profiler.StopProfiler();
// Returns a profile when successful, and null when there is an error.
return profile;
}
isActive() {
if (!IS_SUPPORTED_PLATFORM) {
return false;
}
return Services.profiler.IsActive();
}
isSupportedPlatform() {
return IS_SUPPORTED_PLATFORM;
}
/**
* Watch for events that happen within the browser. These can affect the
* current availability and state of the Gecko Profiler.
*/
_observe(subject, topic, _data) {
// Note! If emitting new events make sure and update the list of bridged
// events in the perf actor.
switch (topic) {
case "profiler-started":
const param = subject.QueryInterface(Ci.nsIProfilerStartParams);
this.emit(
topic,
param.entries,
param.interval,
param.features,
param.duration,
param.activeTabID
);
break;
case "profiler-stopped":
this.emit(topic);
break;
}
}
/**
* Lists the supported features of the profiler for the current browser.
* @returns {string[]}
*/
getSupportedFeatures() {
if (!IS_SUPPORTED_PLATFORM) {
return [];
}
return Services.profiler.GetFeatures();
}
};

Просмотреть файл

@ -1,196 +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";
/**
* This file is for the new performance panel that targets profiler.firefox.com,
* not the default-enabled DevTools performance panel.
*/
loader.lazyRequireGetter(
this,
"EventEmitter",
"resource://devtools/shared/event-emitter.js"
);
loader.lazyRequireGetter(
this,
"RecordingUtils",
"resource://devtools/shared/performance-new/recording-utils.js"
);
// Some platforms are built without the Gecko Profiler.
const IS_SUPPORTED_PLATFORM = "nsIProfiler" in Ci;
/**
* This is an implementation of the perf actor API, using nsIProfiler.
* It is in a separate class from the actual perf actor implementation
* for historical reasons only. It could be moved into perf.js.
*/
class ActorReadyGeckoProfilerInterface {
constructor() {
// Only setup the observers on a supported platform.
if (IS_SUPPORTED_PLATFORM) {
this._observer = {
observe: this._observe.bind(this),
};
Services.obs.addObserver(this._observer, "profiler-started");
Services.obs.addObserver(this._observer, "profiler-stopped");
}
EventEmitter.decorate(this);
}
destroy() {
if (!IS_SUPPORTED_PLATFORM) {
return;
}
Services.obs.removeObserver(this._observer, "profiler-started");
Services.obs.removeObserver(this._observer, "profiler-stopped");
}
startProfiler(options) {
if (!IS_SUPPORTED_PLATFORM) {
return false;
}
// For a quick implementation, decide on some default values. These may need
// to be tweaked or made configurable as needed.
const settings = {
entries: options.entries || 1000000,
duration: options.duration || 0,
interval: options.interval || 1,
features: options.features || [
"js",
"stackwalk",
"cpu",
"responsiveness",
],
threads: options.threads || ["GeckoMain", "Compositor"],
activeTabID: RecordingUtils.getActiveBrowserID(),
};
try {
// This can throw an error if the profiler is in the wrong state.
Services.profiler.StartProfiler(
settings.entries,
settings.interval,
settings.features,
settings.threads,
settings.activeTabID,
settings.duration
);
} catch (e) {
// In case any errors get triggered, bailout with a false.
return false;
}
return true;
}
stopProfilerAndDiscardProfile() {
if (!IS_SUPPORTED_PLATFORM) {
return;
}
Services.profiler.StopProfiler();
}
/**
* @type {string} debugPath
* @type {string} breakpadId
* @returns {Promise<[number[], number[], number[]]>}
*/
async getSymbolTable(debugPath, breakpadId) {
const [addr, index, buffer] = await Services.profiler.getSymbolTable(
debugPath,
breakpadId
);
// The protocol does not support the transfer of typed arrays, so we convert
// these typed arrays to plain JS arrays of numbers now.
// Our return value type is declared as "array:array:number".
return [Array.from(addr), Array.from(index), Array.from(buffer)];
}
async getProfileAndStopProfiler() {
if (!IS_SUPPORTED_PLATFORM) {
return null;
}
// Pause profiler before we collect the profile, so that we don't capture
// more samples while the parent process or android threads wait for subprocess profiles.
Services.profiler.Pause();
let profile;
try {
// Attempt to pull out the data.
profile = await Services.profiler.getProfileDataAsync();
if (Object.keys(profile).length === 0) {
console.error(
"An empty object was received from getProfileDataAsync.getProfileDataAsync(), " +
"meaning that a profile could not successfully be serialized and captured."
);
profile = null;
}
} catch (e) {
// Explicitly set the profile to null if there as an error.
profile = null;
console.error(`There was an error fetching a profile`, e);
}
// Stop and discard the buffers.
Services.profiler.StopProfiler();
// Returns a profile when successful, and null when there is an error.
return profile;
}
isActive() {
if (!IS_SUPPORTED_PLATFORM) {
return false;
}
return Services.profiler.IsActive();
}
isSupportedPlatform() {
return IS_SUPPORTED_PLATFORM;
}
/**
* Watch for events that happen within the browser. These can affect the
* current availability and state of the Gecko Profiler.
*/
_observe(subject, topic, _data) {
// Note! If emitting new events make sure and update the list of bridged
// events in the perf actor.
switch (topic) {
case "profiler-started":
const param = subject.QueryInterface(Ci.nsIProfilerStartParams);
this.emit(
topic,
param.entries,
param.interval,
param.features,
param.duration,
param.activeTabID
);
break;
case "profiler-stopped":
this.emit(topic);
break;
}
}
/**
* Lists the supported features of the profiler for the current browser.
* @returns {string[]}
*/
getSupportedFeatures() {
if (!IS_SUPPORTED_PLATFORM) {
return [];
}
return Services.profiler.GetFeatures();
}
}
exports.ActorReadyGeckoProfilerInterface = ActorReadyGeckoProfilerInterface;

Просмотреть файл

@ -5,7 +5,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
"gecko-profiler-interface.js",
"recording-utils.js",
)