Bug 1265727 - Decouple ProfilerFront from ProfilerActor;r=fitzgen

This commit is contained in:
Eddy Bruel 2016-07-01 15:27:41 +02:00
parent 44e1776918
commit 32f9a9263f
8 changed files with 223 additions and 190 deletions

View File

@ -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

View File

@ -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".`);
}
}
},
});

View File

@ -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();

View File

@ -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() {

View File

@ -23,6 +23,7 @@ DevToolsModules(
'performance-recording.js',
'performance.js',
'preference.js',
'profiler.js',
'promises.js',
'settings.js',
'storage.js',

View 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".`);
}
}
},
});

View File

@ -28,6 +28,7 @@ DevToolsModules(
'performance-recording.js',
'performance.js',
'preference.js',
'profiler.js',
'promises.js',
'script.js',
'settings.js',

View 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;