зеркало из https://github.com/mozilla/gecko-dev.git
Bug 855244 - Add support for the Profiler running in multiple tabs. r=past, r=robcee
This commit is contained in:
Родитель
cbc8941952
Коммит
5ea69ec3a9
|
@ -18,6 +18,16 @@ XPCOMUtils.defineLazyGetter(this, "DebuggerServer", function () {
|
||||||
return DebuggerServer;
|
return DebuggerServer;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data structure that contains information that has
|
||||||
|
* to be shared between separate ProfilerController
|
||||||
|
* instances.
|
||||||
|
*/
|
||||||
|
const sharedData = {
|
||||||
|
startTime: 0,
|
||||||
|
data: new WeakMap(),
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a structure representing an individual profile.
|
* Makes a structure representing an individual profile.
|
||||||
*/
|
*/
|
||||||
|
@ -29,163 +39,51 @@ function makeProfile(name) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Three functions below all operate with sharedData
|
||||||
* Object acting as a mediator between the ProfilerController and
|
// structure defined above. They should be self-explanatory.
|
||||||
* DebuggerServer.
|
|
||||||
*/
|
function addTarget(target) {
|
||||||
function ProfilerConnection(client) {
|
sharedData.data.set(target, new Map());
|
||||||
this.client = client;
|
|
||||||
this.startTime = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfilerConnection.prototype = {
|
function getProfiles(target) {
|
||||||
actor: null,
|
return sharedData.data.get(target);
|
||||||
startTime: null,
|
}
|
||||||
|
|
||||||
/**
|
function getCurrentTime() {
|
||||||
* Returns how many milliseconds have passed since the connection
|
return (new Date()).getTime() - sharedData.startTime;
|
||||||
* was started (start time is specificed by the startTime property).
|
}
|
||||||
*
|
|
||||||
* @return number
|
|
||||||
*/
|
|
||||||
get currentTime() {
|
|
||||||
return (new Date()).getTime() - this.startTime;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects to a debugee and executes a callback when ready.
|
* Object to control the JavaScript Profiler over the remote
|
||||||
*
|
* debugging protocol.
|
||||||
* @param function aCallback
|
*
|
||||||
* Function to be called once we're connected to the client.
|
* @param Target target
|
||||||
*/
|
* A target object as defined in Target.jsm
|
||||||
connect: function PCn_connect(aCallback) {
|
*/
|
||||||
this.client.listTabs(function (aResponse) {
|
function ProfilerController(target) {
|
||||||
this.actor = aResponse.profilerActor;
|
this.target = target;
|
||||||
aCallback();
|
this.client = target.client;
|
||||||
}.bind(this));
|
this.isConnected = false;
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
addTarget(target);
|
||||||
* Sends a message to check if the profiler is currently active.
|
|
||||||
*
|
|
||||||
* @param function aCallback
|
|
||||||
* Function to be called once we have a response from
|
|
||||||
* the client. It will be called with a single argument
|
|
||||||
* containing a response object.
|
|
||||||
*/
|
|
||||||
isActive: function PCn_isActive(aCallback) {
|
|
||||||
var message = { to: this.actor, type: "isActive" };
|
|
||||||
this.client.request(message, aCallback);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
// Chrome debugging targets have already obtained a reference
|
||||||
* Sends a message to start a profiler.
|
// to the profiler actor.
|
||||||
*
|
if (target.chrome) {
|
||||||
* @param function aCallback
|
this.isConnected = true;
|
||||||
* Function to be called once the profiler is running.
|
this.actor = target.form.profilerActor;
|
||||||
* It will be called with a single argument containing
|
|
||||||
* a response object.
|
|
||||||
*/
|
|
||||||
startProfiler: function PCn_startProfiler(aCallback) {
|
|
||||||
var message = {
|
|
||||||
to: this.actor,
|
|
||||||
type: "startProfiler",
|
|
||||||
entries: 1000000,
|
|
||||||
interval: 1,
|
|
||||||
features: ["js"],
|
|
||||||
};
|
|
||||||
|
|
||||||
this.client.request(message, function () {
|
|
||||||
// Record the current time so we could split profiler data
|
|
||||||
// in chunks later.
|
|
||||||
this.startTime = (new Date()).getTime();
|
|
||||||
aCallback.apply(null, Array.slice(arguments));
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a message to stop a profiler.
|
|
||||||
*
|
|
||||||
* @param function aCallback
|
|
||||||
* Function to be called once the profiler is idle.
|
|
||||||
* It will be called with a single argument containing
|
|
||||||
* a response object.
|
|
||||||
*/
|
|
||||||
stopProfiler: function PCn_stopProfiler(aCallback) {
|
|
||||||
var message = { to: this.actor, type: "stopProfiler" };
|
|
||||||
this.client.request(message, aCallback);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a message to get the generated profile data.
|
|
||||||
*
|
|
||||||
* @param function aCallback
|
|
||||||
* Function to be called once we have the data.
|
|
||||||
* It will be called with a single argument containing
|
|
||||||
* a response object.
|
|
||||||
*/
|
|
||||||
getProfileData: function PCn_getProfileData(aCallback) {
|
|
||||||
var message = { to: this.actor, type: "getProfile" };
|
|
||||||
this.client.request(message, aCallback);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleanup.
|
|
||||||
*/
|
|
||||||
destroy: function PCn_destroy() {
|
|
||||||
this.client = null;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Object defining the profiler controller components.
|
|
||||||
*/
|
|
||||||
function ProfilerController(target) {
|
|
||||||
this.profiler = new ProfilerConnection(target.client);
|
|
||||||
this.profiles = new Map();
|
|
||||||
|
|
||||||
// Chrome debugging targets have already obtained a reference to the
|
|
||||||
// profiler actor.
|
|
||||||
this._connected = !!target.chrome;
|
|
||||||
|
|
||||||
if (target.chrome) {
|
|
||||||
this.profiler.actor = target.form.profilerActor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProfilerController.prototype = {
|
ProfilerController.prototype = {
|
||||||
/**
|
/**
|
||||||
* Connects to the client unless we're already connected.
|
* Return a map of profile results for the current target.
|
||||||
*
|
*
|
||||||
* @param function aCallback
|
* @return Map
|
||||||
* Function to be called once we're connected. If
|
|
||||||
* the controller is already connected, this function
|
|
||||||
* will be called immediately (synchronously).
|
|
||||||
*/
|
*/
|
||||||
connect: function (aCallback) {
|
get profiles() {
|
||||||
if (this._connected) {
|
return getProfiles(this.target);
|
||||||
return void aCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.profiler.connect(function onConnect() {
|
|
||||||
this._connected = true;
|
|
||||||
aCallback();
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the profiler is active.
|
|
||||||
*
|
|
||||||
* @param function aCallback
|
|
||||||
* Function to be called with a response from the
|
|
||||||
* client. It will be called with two arguments:
|
|
||||||
* an error object (may be null) and a boolean
|
|
||||||
* value indicating if the profiler is active or not.
|
|
||||||
*/
|
|
||||||
isActive: function PC_isActive(aCallback) {
|
|
||||||
this.profiler.isActive(function onActive(aResponse) {
|
|
||||||
aCallback(aResponse.error, aResponse.isActive);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -199,6 +97,56 @@ ProfilerController.prototype = {
|
||||||
return profile.timeStarted !== null && profile.timeEnded === null;
|
return profile.timeStarted !== null && profile.timeEnded === null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to the client unless we're already connected.
|
||||||
|
*
|
||||||
|
* @param function cb
|
||||||
|
* Function to be called once we're connected. If
|
||||||
|
* the controller is already connected, this function
|
||||||
|
* will be called immediately (synchronously).
|
||||||
|
*/
|
||||||
|
connect: function (cb) {
|
||||||
|
if (this.isConnected) {
|
||||||
|
return void cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client.listTabs((resp) => {
|
||||||
|
this.actor = resp.profilerActor;
|
||||||
|
this.isConnected = true;
|
||||||
|
cb();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds actor and type information to data and sends the request over
|
||||||
|
* the remote debugging protocol.
|
||||||
|
*
|
||||||
|
* @param string type
|
||||||
|
* Method to call on the other side
|
||||||
|
* @param object data
|
||||||
|
* Data to send with the request
|
||||||
|
* @param function cb
|
||||||
|
* A callback function
|
||||||
|
*/
|
||||||
|
request: function (type, data, cb) {
|
||||||
|
data.to = this.actor;
|
||||||
|
data.type = type;
|
||||||
|
this.client.request(data, cb);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the profiler is active.
|
||||||
|
*
|
||||||
|
* @param function cb
|
||||||
|
* Function to be called with a response from the
|
||||||
|
* client. It will be called with two arguments:
|
||||||
|
* an error object (may be null) and a boolean
|
||||||
|
* value indicating if the profiler is active or not.
|
||||||
|
*/
|
||||||
|
isActive: function (cb) {
|
||||||
|
this.request("isActive", {}, (resp) => cb(resp.error, resp.isActive));
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new profile and starts the profiler, if needed.
|
* Creates a new profile and starts the profiler, if needed.
|
||||||
*
|
*
|
||||||
|
@ -214,35 +162,42 @@ ProfilerController.prototype = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let profiler = this.profiler;
|
|
||||||
let profile = makeProfile(name);
|
let profile = makeProfile(name);
|
||||||
this.profiles.set(name, profile);
|
this.profiles.set(name, profile);
|
||||||
|
|
||||||
|
|
||||||
// If profile is already running, no need to do anything.
|
// If profile is already running, no need to do anything.
|
||||||
if (this.isProfileRecording(profile)) {
|
if (this.isProfileRecording(profile)) {
|
||||||
return void cb();
|
return void cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isActive(function (err, isActive) {
|
this.isActive((err, isActive) => {
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
profile.timeStarted = profiler.currentTime;
|
profile.timeStarted = getCurrentTime();
|
||||||
return void cb();
|
return void cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
profiler.startProfiler(function onStart(aResponse) {
|
let params = {
|
||||||
if (aResponse.error) {
|
entries: 1000000,
|
||||||
return void cb(aResponse.error);
|
interval: 1,
|
||||||
|
features: ["js"],
|
||||||
|
};
|
||||||
|
|
||||||
|
this.request("startProfiler", params, (resp) => {
|
||||||
|
if (resp.error) {
|
||||||
|
return void cb(resp.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
profile.timeStarted = profiler.currentTime;
|
sharedData.startTime = (new Date()).getTime();
|
||||||
|
profile.timeStarted = getCurrentTime();
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops the profiler.
|
* Stops the profiler. NOTE, that we don't stop the actual
|
||||||
|
* SPS Profiler here. It will be stopped as soon as all
|
||||||
|
* clients disconnect from the profiler actor.
|
||||||
*
|
*
|
||||||
* @param string name
|
* @param string name
|
||||||
* Name of the profile that needs to be stopped.
|
* Name of the profile that needs to be stopped.
|
||||||
|
@ -252,50 +207,36 @@ ProfilerController.prototype = {
|
||||||
* argument: an error object (may be null).
|
* argument: an error object (may be null).
|
||||||
*/
|
*/
|
||||||
stop: function PC_stop(name, cb) {
|
stop: function PC_stop(name, cb) {
|
||||||
let profiler = this.profiler;
|
if (!this.profiles.has(name)) {
|
||||||
let profile = this.profiles.get(name);
|
|
||||||
|
|
||||||
if (!profile || !this.isProfileRecording(profile)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let isRecording = function () {
|
let profile = this.profiles.get(name);
|
||||||
for (let [ name, profile ] of this.profiles) {
|
if (!this.isProfileRecording(profile)) {
|
||||||
if (this.isProfileRecording(profile)) {
|
return;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
this.request("getProfile", {}, (resp) => {
|
||||||
|
if (resp.error) {
|
||||||
|
Cu.reportError("Failed to fetch profile data.");
|
||||||
|
return void cb(resp.error, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
let data = resp.profile;
|
||||||
}.bind(this);
|
profile.timeEnded = getCurrentTime();
|
||||||
|
|
||||||
let onStop = function (data) {
|
// Filter out all samples that fall out of current
|
||||||
if (isRecording()) {
|
// profile's range.
|
||||||
return void cb(null, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
profiler.stopProfiler(function onStopProfiler(response) {
|
data.threads = data.threads.map((thread) => {
|
||||||
cb(response.error, data);
|
let samples = thread.samples.filter((sample) => {
|
||||||
});
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
profiler.getProfileData(function onData(aResponse) {
|
|
||||||
if (aResponse.error) {
|
|
||||||
Cu.reportError("Failed to fetch profile data before stopping the profiler.");
|
|
||||||
return void cb(aResponse.error, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = aResponse.profile;
|
|
||||||
profile.timeEnded = profiler.currentTime;
|
|
||||||
|
|
||||||
data.threads = data.threads.map(function (thread) {
|
|
||||||
let samples = thread.samples.filter(function (sample) {
|
|
||||||
return sample.time >= profile.timeStarted;
|
return sample.time >= profile.timeStarted;
|
||||||
});
|
});
|
||||||
|
|
||||||
return { samples: samples };
|
return { samples: samples };
|
||||||
});
|
});
|
||||||
|
|
||||||
onStop(data);
|
cb(null, data);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -303,7 +244,8 @@ ProfilerController.prototype = {
|
||||||
* Cleanup.
|
* Cleanup.
|
||||||
*/
|
*/
|
||||||
destroy: function PC_destroy() {
|
destroy: function PC_destroy() {
|
||||||
this.profiler.destroy();
|
this.client = null;
|
||||||
this.profiler = null;
|
this.target = null;
|
||||||
|
this.actor = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@ MOCHITEST_BROWSER_TESTS = \
|
||||||
browser_profiler_run.js \
|
browser_profiler_run.js \
|
||||||
browser_profiler_controller.js \
|
browser_profiler_controller.js \
|
||||||
browser_profiler_bug_830664_multiple_profiles.js \
|
browser_profiler_bug_830664_multiple_profiles.js \
|
||||||
|
browser_profiler_bug_855244_multiple_tabs.js \
|
||||||
head.js \
|
head.js \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
const URL = "data:text/html;charset=utf8,<p>JavaScript Profiler test</p>";
|
||||||
|
|
||||||
|
let gTab1, gPanel1;
|
||||||
|
let gTab2, gPanel2;
|
||||||
|
|
||||||
|
// Tests that you can run the profiler in multiple tabs at the same
|
||||||
|
// time and that closing the debugger panel in one tab doesn't lock
|
||||||
|
// profilers in other tabs.
|
||||||
|
|
||||||
|
registerCleanupFunction(function () {
|
||||||
|
gTab1 = gTab2 = gPanel1 = gPanel2 = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
|
||||||
|
openTwoTabs()
|
||||||
|
.then(startTwoProfiles)
|
||||||
|
.then(stopFirstProfile)
|
||||||
|
.then(stopSecondProfile)
|
||||||
|
.then(closeTabs)
|
||||||
|
.then(openTwoTabs)
|
||||||
|
.then(startTwoProfiles)
|
||||||
|
.then(closeFirstPanel)
|
||||||
|
.then(stopSecondProfile)
|
||||||
|
.then(closeTabs)
|
||||||
|
.then(finish);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openTwoTabs() {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
|
||||||
|
setUp(URL, (tab, browser, panel) => {
|
||||||
|
gTab1 = tab;
|
||||||
|
gPanel1 = panel;
|
||||||
|
|
||||||
|
loadTab(URL, (tab, browser) => {
|
||||||
|
gTab2 = tab;
|
||||||
|
openProfiler(tab, () => {
|
||||||
|
let target = TargetFactory.forTab(tab);
|
||||||
|
gPanel2 = gDevTools.getToolbox(target).getPanel("jsprofiler");
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTwoProfiles() {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
gPanel1.controller.start("Profile 1", (err) => {
|
||||||
|
ok(!err, "Profile in tab 1 started without errors");
|
||||||
|
gPanel2.controller.start("Profile 1", (err) => {
|
||||||
|
ok(!err, "Profile in tab 2 started without errors");
|
||||||
|
gPanel1.controller.isActive((err, isActive) => {
|
||||||
|
ok(isActive, "Profiler is active");
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopFirstProfile() {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
|
||||||
|
gPanel1.controller.stop("Profile 1", (err, data) => {
|
||||||
|
ok(!err, "Profile in tab 1 stopped without errors");
|
||||||
|
ok(data, "Profile in tab 1 returned some data");
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopSecondProfile() {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
|
||||||
|
gPanel2.controller.stop("Profile 1", (err, data) => {
|
||||||
|
ok(!err, "Profile in tab 2 stopped without errors");
|
||||||
|
ok(data, "Profile in tab 2 returned some data");
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeTabs() {
|
||||||
|
while (gBrowser.tabs.length > 1) {
|
||||||
|
gBrowser.removeCurrentTab();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeFirstPanel() {
|
||||||
|
let target = TargetFactory.forTab(gTab1);
|
||||||
|
let toolbox = gDevTools.getToolbox(target);
|
||||||
|
return toolbox.destroy;
|
||||||
|
}
|
|
@ -13,26 +13,12 @@ function test() {
|
||||||
gPanel = panel;
|
gPanel = panel;
|
||||||
|
|
||||||
panel.once("started", onStart);
|
panel.once("started", onStart);
|
||||||
panel.once("stopped", onStop);
|
|
||||||
panel.once("parsed", onParsed);
|
panel.once("parsed", onParsed);
|
||||||
|
|
||||||
testUI();
|
testUI();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function attemptTearDown() {
|
|
||||||
gAttempts += 1;
|
|
||||||
|
|
||||||
if (gAttempts < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tearDown(gTab, function onTearDown() {
|
|
||||||
gPanel = null;
|
|
||||||
gTab = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testUI() {
|
function testUI() {
|
||||||
ok(gPanel, "Profiler panel exists");
|
ok(gPanel, "Profiler panel exists");
|
||||||
ok(gPanel.activeProfile, "Active profile exists");
|
ok(gPanel.activeProfile, "Active profile exists");
|
||||||
|
@ -58,13 +44,6 @@ function onStart() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onStop() {
|
|
||||||
gPanel.controller.isActive(function (err, isActive) {
|
|
||||||
ok(!isActive, "Profiler is idle");
|
|
||||||
attemptTearDown();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onParsed() {
|
function onParsed() {
|
||||||
function assertSample() {
|
function assertSample() {
|
||||||
let [win,doc] = getProfileInternals();
|
let [win,doc] = getProfileInternals();
|
||||||
|
@ -76,7 +55,11 @@ function onParsed() {
|
||||||
|
|
||||||
ok(sample.length > 0, "We have some items displayed");
|
ok(sample.length > 0, "We have some items displayed");
|
||||||
is(sample[0].innerHTML, "100.0%", "First percentage is 100%");
|
is(sample[0].innerHTML, "100.0%", "First percentage is 100%");
|
||||||
attemptTearDown();
|
|
||||||
|
tearDown(gTab, function onTearDown() {
|
||||||
|
gPanel = null;
|
||||||
|
gTab = null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
assertSample();
|
assertSample();
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var connCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a ProfilerActor. ProfilerActor provides remote access to the
|
* Creates a ProfilerActor. ProfilerActor provides remote access to the
|
||||||
* built-in profiler module.
|
* built-in profiler module.
|
||||||
|
@ -13,6 +15,7 @@ function ProfilerActor(aConnection)
|
||||||
this._profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
|
this._profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
|
||||||
this._started = false;
|
this._started = false;
|
||||||
this._observedEvents = [];
|
this._observedEvents = [];
|
||||||
|
connCount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfilerActor.prototype = {
|
ProfilerActor.prototype = {
|
||||||
|
@ -22,9 +25,18 @@ ProfilerActor.prototype = {
|
||||||
for (var event of this._observedEvents) {
|
for (var event of this._observedEvents) {
|
||||||
Services.obs.removeObserver(this, event);
|
Services.obs.removeObserver(this, event);
|
||||||
}
|
}
|
||||||
if (this._profiler && this._started) {
|
|
||||||
|
// We stop the profiler only after the last client is
|
||||||
|
// disconnected. Otherwise there's a problem where
|
||||||
|
// we stop the profiler as soon as you close the devtools
|
||||||
|
// panel in one tab even though there might be other
|
||||||
|
// profiler instances running in other tabs.
|
||||||
|
|
||||||
|
connCount -= 1;
|
||||||
|
if (connCount <= 0 && this._profiler && this._started) {
|
||||||
this._profiler.StopProfiler();
|
this._profiler.StopProfiler();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._profiler = null;
|
this._profiler = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -147,21 +147,23 @@ function test_profile(aClient, aProfiler)
|
||||||
function test_profiler_status()
|
function test_profiler_status()
|
||||||
{
|
{
|
||||||
var connectionClosed = DebuggerServer._connectionClosed;
|
var connectionClosed = DebuggerServer._connectionClosed;
|
||||||
DebuggerServer._connectionClosed = function (conn) {
|
|
||||||
connectionClosed.call(this, conn);
|
|
||||||
// Check that closing the connection stops the profiler
|
|
||||||
do_check_false(Profiler.IsActive());
|
|
||||||
do_test_finished();
|
|
||||||
};
|
|
||||||
|
|
||||||
var client = new DebuggerClient(DebuggerServer.connectPipe());
|
var client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||||
client.connect(function () {
|
|
||||||
client.listTabs(function(aResponse) {
|
client.connect(() => {
|
||||||
|
client.listTabs((aResponse) => {
|
||||||
|
DebuggerServer._connectionClosed = function (conn) {
|
||||||
|
connectionClosed.call(this, conn);
|
||||||
|
|
||||||
|
// Check that closing the last (only?) connection stops the profiler.
|
||||||
|
do_check_false(Profiler.IsActive());
|
||||||
|
do_test_finished();
|
||||||
|
}
|
||||||
|
|
||||||
var profiler = aResponse.profilerActor;
|
var profiler = aResponse.profilerActor;
|
||||||
do_check_false(Profiler.IsActive());
|
do_check_false(Profiler.IsActive());
|
||||||
client.request({ to: profiler, type: "startProfiler", features: [] }, function (aResponse) {
|
client.request({ to: profiler, type: "startProfiler", features: [] }, (aResponse) => {
|
||||||
do_check_true(Profiler.IsActive());
|
do_check_true(Profiler.IsActive());
|
||||||
client.close(function() { });
|
client.close(function () {});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Загрузка…
Ссылка в новой задаче