mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Bug 1265727 - Decouple ProfilerFront from ProfilerActor;r=fitzgen
This commit is contained in:
parent
44e1776918
commit
32f9a9263f
@ -12,7 +12,7 @@ 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/server/actors/profiler");
|
||||
const { ProfilerFront } = require("devtools/shared/fronts/profiler");
|
||||
|
||||
// how often do we check the status of the profiler's circular buffer
|
||||
const PROFILER_CHECK_TIMER = 5000; // ms
|
||||
|
@ -1,33 +1,14 @@
|
||||
/* 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 { Cu } = require("chrome");
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
const { custom, method, RetVal, Arg, Option, types } = protocol;
|
||||
const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
|
||||
const { Profiler } = require("devtools/server/performance/profiler");
|
||||
const { actorBridge } = require("devtools/server/actors/common");
|
||||
const { actorBridgeWithSpec } = require("devtools/server/actors/common");
|
||||
const { profilerSpec } = require("devtools/shared/specs/profiler");
|
||||
|
||||
loader.lazyRequireGetter(this, "events", "sdk/event/core");
|
||||
loader.lazyRequireGetter(this, "extend", "sdk/util/object", true);
|
||||
|
||||
types.addType("profiler-data", {
|
||||
// On Fx42+, the profile is only deserialized on the front; older
|
||||
// servers will get the profiler data as an object from nsIProfiler,
|
||||
// causing one parse/stringify cycle, then again implicitly in a packet.
|
||||
read: (v) => {
|
||||
if (typeof v.profile === "string") {
|
||||
// Create a new response object since `profile` is read only.
|
||||
let newValue = Object.create(null);
|
||||
newValue.profile = JSON.parse(v.profile);
|
||||
newValue.currentTime = v.currentTime;
|
||||
return newValue;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This actor wraps the Profiler module at devtools/server/performance/profiler.js
|
||||
@ -35,38 +16,9 @@ types.addType("profiler-data", {
|
||||
*
|
||||
* @see devtools/server/performance/profiler.js for documentation.
|
||||
*/
|
||||
var ProfilerActor = exports.ProfilerActor = protocol.ActorClass({
|
||||
typeName: "profiler",
|
||||
|
||||
/**
|
||||
* The set of events the ProfilerActor emits over RDP.
|
||||
*/
|
||||
events: {
|
||||
"console-api-profiler": {
|
||||
data: Arg(0, "json"),
|
||||
},
|
||||
"profiler-started": {
|
||||
data: Arg(0, "json"),
|
||||
},
|
||||
"profiler-stopped": {
|
||||
data: Arg(0, "json"),
|
||||
},
|
||||
"profiler-status": {
|
||||
data: Arg(0, "json"),
|
||||
},
|
||||
|
||||
// Only for older geckos, pre-protocol.js ProfilerActor (<Fx42).
|
||||
// Emitted on other events as a transition from older profiler events
|
||||
// to newer ones.
|
||||
"eventNotification": {
|
||||
subject: Option(0, "json"),
|
||||
topic: Option(0, "string"),
|
||||
details: Option(0, "json")
|
||||
}
|
||||
},
|
||||
|
||||
var ProfilerActor = exports.ProfilerActor = ActorClassWithSpec(profilerSpec, {
|
||||
initialize: function (conn) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
Actor.prototype.initialize.call(this, conn);
|
||||
this._onProfilerEvent = this._onProfilerEvent.bind(this);
|
||||
|
||||
this.bridge = new Profiler();
|
||||
@ -84,76 +36,20 @@ var ProfilerActor = exports.ProfilerActor = protocol.ActorClass({
|
||||
destroy: function () {
|
||||
events.off(this.bridge, "*", this._onProfilerEvent);
|
||||
this.bridge.destroy();
|
||||
protocol.Actor.prototype.destroy.call(this);
|
||||
Actor.prototype.destroy.call(this);
|
||||
},
|
||||
|
||||
startProfiler: actorBridge("start", {
|
||||
// Write out every property in the request, since we want all these options to be
|
||||
// on the packet's top-level for backwards compatibility, when the profiler actor
|
||||
// was not using protocol.js (<Fx42)
|
||||
request: {
|
||||
entries: Option(0, "nullable:number"),
|
||||
interval: Option(0, "nullable:number"),
|
||||
features: Option(0, "nullable:array:string"),
|
||||
threadFilters: Option(0, "nullable:array:string"),
|
||||
},
|
||||
response: RetVal("json"),
|
||||
}),
|
||||
|
||||
stopProfiler: actorBridge("stop", {
|
||||
response: RetVal("json"),
|
||||
}),
|
||||
|
||||
getProfile: actorBridge("getProfile", {
|
||||
request: {
|
||||
startTime: Option(0, "nullable:number"),
|
||||
stringify: Option(0, "nullable:boolean")
|
||||
},
|
||||
response: RetVal("profiler-data")
|
||||
}),
|
||||
|
||||
getFeatures: actorBridge("getFeatures", {
|
||||
response: RetVal("json")
|
||||
}),
|
||||
|
||||
getBufferInfo: actorBridge("getBufferInfo", {
|
||||
response: RetVal("json")
|
||||
}),
|
||||
|
||||
getStartOptions: actorBridge("getStartOptions", {
|
||||
response: RetVal("json")
|
||||
}),
|
||||
|
||||
isActive: actorBridge("isActive", {
|
||||
response: RetVal("json")
|
||||
}),
|
||||
|
||||
getSharedLibraryInformation: actorBridge("getSharedLibraryInformation", {
|
||||
response: RetVal("json")
|
||||
}),
|
||||
|
||||
registerEventNotifications: actorBridge("registerEventNotifications", {
|
||||
// Explicitly enumerate the arguments
|
||||
// @see ProfilerActor#startProfiler
|
||||
request: {
|
||||
events: Option(0, "nullable:array:string"),
|
||||
},
|
||||
response: RetVal("json")
|
||||
}),
|
||||
|
||||
unregisterEventNotifications: actorBridge("unregisterEventNotifications", {
|
||||
// Explicitly enumerate the arguments
|
||||
// @see ProfilerActor#startProfiler
|
||||
request: {
|
||||
events: Option(0, "nullable:array:string"),
|
||||
},
|
||||
response: RetVal("json")
|
||||
}),
|
||||
|
||||
setProfilerStatusInterval: actorBridge("setProfilerStatusInterval", {
|
||||
request: { interval: Arg(0, "number") },
|
||||
oneway: true
|
||||
}),
|
||||
startProfiler: actorBridgeWithSpec("start"),
|
||||
stopProfiler: actorBridgeWithSpec("stop"),
|
||||
getProfile: actorBridgeWithSpec("getProfile"),
|
||||
getFeatures: actorBridgeWithSpec("getFeatures"),
|
||||
getBufferInfo: actorBridgeWithSpec("getBufferInfo"),
|
||||
getStartOptions: actorBridgeWithSpec("getStartOptions"),
|
||||
isActive: actorBridgeWithSpec("isActive"),
|
||||
getSharedLibraryInformation: actorBridgeWithSpec("getSharedLibraryInformation"),
|
||||
registerEventNotifications: actorBridgeWithSpec("registerEventNotifications"),
|
||||
unregisterEventNotifications: actorBridgeWithSpec("unregisterEventNotifications"),
|
||||
setProfilerStatusInterval: actorBridgeWithSpec("setProfilerStatusInterval"),
|
||||
|
||||
/**
|
||||
* Pipe events from Profiler module to this actor.
|
||||
@ -162,69 +58,3 @@ var ProfilerActor = exports.ProfilerActor = protocol.ActorClass({
|
||||
events.emit(this, eventName, ...data);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* This can be used on older Profiler implementations, but the methods cannot
|
||||
* be changed -- you must introduce a new method, and detect the server.
|
||||
*/
|
||||
exports.ProfilerFront = protocol.FrontClass(ProfilerActor, {
|
||||
initialize: function (client, form) {
|
||||
protocol.Front.prototype.initialize.call(this, client, form);
|
||||
this.actorID = form.profilerActor;
|
||||
this.manage(this);
|
||||
|
||||
this._onProfilerEvent = this._onProfilerEvent.bind(this);
|
||||
events.on(this, "*", this._onProfilerEvent);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
events.off(this, "*", this._onProfilerEvent);
|
||||
protocol.Front.prototype.destroy.call(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* If using the protocol.js Fronts, then make stringify default,
|
||||
* since the read/write mechanisms will expose it as an object anyway, but
|
||||
* this lets other consumers who connect directly (xpcshell tests, Gecko Profiler) to
|
||||
* have unchanged behaviour.
|
||||
*/
|
||||
getProfile: custom(function (options) {
|
||||
return this._getProfile(extend({ stringify: true }, options));
|
||||
}, {
|
||||
impl: "_getProfile"
|
||||
}),
|
||||
|
||||
/**
|
||||
* Also emit an old `eventNotification` for older consumers of the profiler.
|
||||
*/
|
||||
_onProfilerEvent: function (eventName, data) {
|
||||
// If this event already passed through once, don't repropagate
|
||||
if (data.relayed) {
|
||||
return;
|
||||
}
|
||||
data.relayed = true;
|
||||
|
||||
// If this is `eventNotification`, this is coming from an older Gecko (<Fx42)
|
||||
// that doesn't use protocol.js style events. Massage it to emit a protocol.js
|
||||
// style event as well.
|
||||
if (eventName === "eventNotification") {
|
||||
events.emit(this, data.topic, data);
|
||||
}
|
||||
// Otherwise if a modern protocol.js event, emit it also as `eventNotification`
|
||||
// for compatibility reasons on the client (like for any add-ons/Gecko Profiler using this
|
||||
// event) and log a deprecation message if there is a listener.
|
||||
else {
|
||||
this.conn.emit("eventNotification", {
|
||||
subject: data.subject,
|
||||
topic: data.topic,
|
||||
data: data.data,
|
||||
details: data.details
|
||||
});
|
||||
if (this.conn._getListeners("eventNotification").length) {
|
||||
Cu.reportError(`
|
||||
ProfilerActor's "eventNotification" on the DebuggerClient has been deprecated.
|
||||
Use the ProfilerFront found in "devtools/server/actors/profiler".`);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
const Profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
|
||||
const { ProfilerFront } = require("devtools/server/actors/profiler");
|
||||
const { ProfilerFront } = require("devtools/shared/fronts/profiler");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
const Profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
|
||||
const MAX_PROFILER_ENTRIES = 10000000;
|
||||
const { ProfilerFront } = require("devtools/server/actors/profiler");
|
||||
const { ProfilerFront } = require("devtools/shared/fronts/profiler");
|
||||
const { waitForTime } = DevToolsUtils;
|
||||
|
||||
function run_test() {
|
||||
|
@ -23,6 +23,7 @@ DevToolsModules(
|
||||
'performance-recording.js',
|
||||
'performance.js',
|
||||
'preference.js',
|
||||
'profiler.js',
|
||||
'promises.js',
|
||||
'settings.js',
|
||||
'storage.js',
|
||||
|
80
devtools/shared/fronts/profiler.js
Normal file
80
devtools/shared/fronts/profiler.js
Normal file
@ -0,0 +1,80 @@
|
||||
/* 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 { Cu } = require("chrome");
|
||||
const {
|
||||
Front,
|
||||
FrontClassWithSpec,
|
||||
custom
|
||||
} = require("devtools/shared/protocol");
|
||||
const { profilerSpec } = require("devtools/shared/specs/profiler");
|
||||
|
||||
loader.lazyRequireGetter(this, "events", "sdk/event/core");
|
||||
loader.lazyRequireGetter(this, "extend", "sdk/util/object", true);
|
||||
|
||||
/**
|
||||
* This can be used on older Profiler implementations, but the methods cannot
|
||||
* be changed -- you must introduce a new method, and detect the server.
|
||||
*/
|
||||
exports.ProfilerFront = FrontClassWithSpec(profilerSpec, {
|
||||
initialize: function (client, form) {
|
||||
Front.prototype.initialize.call(this, client, form);
|
||||
this.actorID = form.profilerActor;
|
||||
this.manage(this);
|
||||
|
||||
this._onProfilerEvent = this._onProfilerEvent.bind(this);
|
||||
events.on(this, "*", this._onProfilerEvent);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
events.off(this, "*", this._onProfilerEvent);
|
||||
Front.prototype.destroy.call(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* If using the protocol.js Fronts, then make stringify default,
|
||||
* since the read/write mechanisms will expose it as an object anyway, but
|
||||
* this lets other consumers who connect directly (xpcshell tests, Gecko Profiler) to
|
||||
* have unchanged behaviour.
|
||||
*/
|
||||
getProfile: custom(function (options) {
|
||||
return this._getProfile(extend({ stringify: true }, options));
|
||||
}, {
|
||||
impl: "_getProfile"
|
||||
}),
|
||||
|
||||
/**
|
||||
* Also emit an old `eventNotification` for older consumers of the profiler.
|
||||
*/
|
||||
_onProfilerEvent: function (eventName, data) {
|
||||
// If this event already passed through once, don't repropagate
|
||||
if (data.relayed) {
|
||||
return;
|
||||
}
|
||||
data.relayed = true;
|
||||
|
||||
if (eventName === "eventNotification") {
|
||||
// If this is `eventNotification`, this is coming from an older Gecko (<Fx42)
|
||||
// that doesn't use protocol.js style events. Massage it to emit a protocol.js
|
||||
// style event as well.
|
||||
events.emit(this, data.topic, data);
|
||||
} else {
|
||||
// Otherwise if a modern protocol.js event, emit it also as `eventNotification`
|
||||
// for compatibility reasons on the client (like for any add-ons/Gecko Profiler
|
||||
// using this event) and log a deprecation message if there is a listener.
|
||||
this.conn.emit("eventNotification", {
|
||||
subject: data.subject,
|
||||
topic: data.topic,
|
||||
data: data.data,
|
||||
details: data.details
|
||||
});
|
||||
if (this.conn._getListeners("eventNotification").length) {
|
||||
Cu.reportError(`
|
||||
ProfilerActor's "eventNotification" on the DebuggerClient has been deprecated.
|
||||
Use the ProfilerFront found in "devtools/server/actors/profiler".`);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
@ -28,6 +28,7 @@ DevToolsModules(
|
||||
'performance-recording.js',
|
||||
'performance.js',
|
||||
'preference.js',
|
||||
'profiler.js',
|
||||
'promises.js',
|
||||
'script.js',
|
||||
'settings.js',
|
||||
|
121
devtools/shared/specs/profiler.js
Normal file
121
devtools/shared/specs/profiler.js
Normal file
@ -0,0 +1,121 @@
|
||||
/* 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 {
|
||||
Arg,
|
||||
Option,
|
||||
RetVal,
|
||||
generateActorSpec,
|
||||
types
|
||||
} = require("devtools/shared/protocol");
|
||||
|
||||
types.addType("profiler-data", {
|
||||
// On Fx42+, the profile is only deserialized on the front; older
|
||||
// servers will get the profiler data as an object from nsIProfiler,
|
||||
// causing one parse/stringify cycle, then again implicitly in a packet.
|
||||
read: (v) => {
|
||||
if (typeof v.profile === "string") {
|
||||
// Create a new response object since `profile` is read only.
|
||||
let newValue = Object.create(null);
|
||||
newValue.profile = JSON.parse(v.profile);
|
||||
newValue.currentTime = v.currentTime;
|
||||
return newValue;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
});
|
||||
|
||||
const profilerSpec = generateActorSpec({
|
||||
typeName: "profiler",
|
||||
|
||||
/**
|
||||
* The set of events the ProfilerActor emits over RDP.
|
||||
*/
|
||||
events: {
|
||||
"console-api-profiler": {
|
||||
data: Arg(0, "json"),
|
||||
},
|
||||
"profiler-started": {
|
||||
data: Arg(0, "json"),
|
||||
},
|
||||
"profiler-stopped": {
|
||||
data: Arg(0, "json"),
|
||||
},
|
||||
"profiler-status": {
|
||||
data: Arg(0, "json"),
|
||||
},
|
||||
|
||||
// Only for older geckos, pre-protocol.js ProfilerActor (<Fx42).
|
||||
// Emitted on other events as a transition from older profiler events
|
||||
// to newer ones.
|
||||
"eventNotification": {
|
||||
subject: Option(0, "json"),
|
||||
topic: Option(0, "string"),
|
||||
details: Option(0, "json")
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
startProfiler: {
|
||||
// Write out every property in the request, since we want all these options to be
|
||||
// on the packet's top-level for backwards compatibility, when the profiler actor
|
||||
// was not using protocol.js (<Fx42)
|
||||
request: {
|
||||
entries: Option(0, "nullable:number"),
|
||||
interval: Option(0, "nullable:number"),
|
||||
features: Option(0, "nullable:array:string"),
|
||||
threadFilters: Option(0, "nullable:array:string"),
|
||||
},
|
||||
response: RetVal("json"),
|
||||
},
|
||||
stopProfiler: {
|
||||
response: RetVal("json"),
|
||||
},
|
||||
getProfile: {
|
||||
request: {
|
||||
startTime: Option(0, "nullable:number"),
|
||||
stringify: Option(0, "nullable:boolean")
|
||||
},
|
||||
response: RetVal("profiler-data")
|
||||
},
|
||||
getFeatures: {
|
||||
response: RetVal("json")
|
||||
},
|
||||
getBufferInfo: {
|
||||
response: RetVal("json")
|
||||
},
|
||||
getStartOptions: {
|
||||
response: RetVal("json")
|
||||
},
|
||||
isActive: {
|
||||
response: RetVal("json")
|
||||
},
|
||||
getSharedLibraryInformation: {
|
||||
response: RetVal("json")
|
||||
},
|
||||
registerEventNotifications: {
|
||||
// Explicitly enumerate the arguments
|
||||
// @see ProfilerActor#startProfiler
|
||||
request: {
|
||||
events: Option(0, "nullable:array:string"),
|
||||
},
|
||||
response: RetVal("json")
|
||||
},
|
||||
unregisterEventNotifications: {
|
||||
// Explicitly enumerate the arguments
|
||||
// @see ProfilerActor#startProfiler
|
||||
request: {
|
||||
events: Option(0, "nullable:array:string"),
|
||||
},
|
||||
response: RetVal("json")
|
||||
},
|
||||
setProfilerStatusInterval: {
|
||||
request: { interval: Arg(0, "number") },
|
||||
oneway: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
exports.profilerSpec = profilerSpec;
|
Loading…
Reference in New Issue
Block a user