Bug 1130200 - Fix the mocked memory and timeline actors for older gecko targets (fx2.0, fx2.1). r=vp

This commit is contained in:
Jordan Santell 2015-02-09 12:49:00 +01:00
parent 2b20074ce1
commit d18630fb7a
7 changed files with 291 additions and 40 deletions

View File

@ -0,0 +1,109 @@
/* 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("resource://gre/modules/Task.jsm");
loader.lazyRequireGetter(this, "EventEmitter",
"devtools/toolkit/event-emitter");
const REQUIRED_MEMORY_ACTOR_METHODS = [
"attach", "detach", "startRecordingAllocations", "stopRecordingAllocations", "getAllocations"
];
/**
* A dummy front decorated with the provided methods.
*
* @param array blueprint
* A list of [funcName, retVal] describing the class.
*/
function MockFront (blueprint) {
EventEmitter.decorate(this);
for (let [funcName, retVal] of blueprint) {
this[funcName] = (x => typeof x === "function" ? x() : x).bind(this, retVal);
}
}
function MockMemoryFront () {
MockFront.call(this, [
["attach"],
["detach"],
["initialize"],
["destroy"],
["startRecordingAllocations", 0],
["stopRecordingAllocations", 0],
["getAllocations", createMockAllocations],
]);
}
exports.MockMemoryFront = MockMemoryFront;
function MockTimelineFront () {
MockFront.call(this, [
["start", 0],
["stop", 0],
["initialize"],
["destroy"],
]);
}
exports.MockTimelineFront = MockTimelineFront;
/**
* Create a fake allocations object, to be used with the MockMemoryFront
* so we create a fresh object each time.
*
* @return {Object}
*/
function createMockAllocations () {
return {
allocations: [],
allocationsTimestamps: [],
frames: [],
counts: []
};
}
/**
* Takes a TabTarget, and checks through all methods that are needed
* on the server's memory actor to determine if a mock or real MemoryActor
* should be used. The last of the methods added to MemoryActor
* landed in Gecko 35, so previous versions will fail this. Setting the `target`'s
* TEST_MOCK_MEMORY_ACTOR property to true will cause this function to indicate that
* the memory actor is not supported.
*
* @param {TabTarget} target
* @return {Boolean}
*/
function* memoryActorSupported (target) {
// This `target` property is used only in tests to test
// instances where the memory actor is not available.
if (target.TEST_MOCK_MEMORY_ACTOR) {
return false;
}
for (let method of REQUIRED_MEMORY_ACTOR_METHODS) {
if (!(yield target.actorHasMethod("memory", method))) {
return false;
}
}
return true;
}
exports.memoryActorSupported = Task.async(memoryActorSupported);
/**
* Takes a TabTarget, and checks existence of a TimelineActor on
* the server, or if TEST_MOCK_TIMELINE_ACTOR is to be used.
*
* @param {TabTarget} target
* @return {Boolean}
*/
function* timelineActorSupported(target) {
// This `target` property is used only in tests to test
// instances where the timeline actor is not available.
if (target.TEST_MOCK_TIMELINE_ACTOR) {
return false;
}
return yield target.hasActor("timeline");
}
exports.timelineActorSupported = Task.async(timelineActorSupported);

View File

@ -15,13 +15,13 @@ loader.lazyRequireGetter(this, "TimelineFront",
"devtools/server/actors/timeline", true);
loader.lazyRequireGetter(this, "MemoryFront",
"devtools/server/actors/memory", true);
loader.lazyRequireGetter(this, "DevToolsUtils",
"devtools/toolkit/DevToolsUtils");
loader.lazyRequireGetter(this, "compatibility",
"devtools/performance/compatibility");
loader.lazyImporter(this, "gDevTools",
"resource:///modules/devtools/gDevTools.jsm");
loader.lazyImporter(this, "setTimeout",
"resource://gre/modules/Timer.jsm");
loader.lazyImporter(this, "clearTimeout",
@ -55,25 +55,6 @@ SharedPerformanceActors.forTarget = function(target) {
return instance;
};
/**
* A dummy front decorated with the provided methods.
*
* @param array blueprint
* A list of [funcName, retVal] describing the class.
*/
function MockedFront(blueprint) {
EventEmitter.decorate(this);
for (let [funcName, retVal] of blueprint) {
this[funcName] = (x => x).bind(this, retVal);
}
}
MockedFront.prototype = {
initialize: function() {},
destroy: function() {}
};
/**
* A connection to underlying actors (profiler, memory, framerate, etc.)
* shared by all tools in a target.
@ -95,6 +76,11 @@ function PerformanceActorsConnection(target) {
}
PerformanceActorsConnection.prototype = {
// Properties set when mocks are being used
_usingMockMemory: false,
_usingMockTimeline: false,
/**
* Initializes a connection to the profiler and other miscellaneous actors.
* If in the process of opening, or already open, nothing happens.
@ -158,36 +144,29 @@ PerformanceActorsConnection.prototype = {
/**
* Initializes a connection to a timeline actor.
* TODO: use framework level feature detection from bug 1069673
*/
_connectTimelineActor: function() {
if (this._target.form && this._target.form.timelineActor) {
let supported = yield compatibility.timelineActorSupported(this._target);
if (supported) {
this._timeline = new TimelineFront(this._target.client, this._target.form);
} else {
this._timeline = new MockedFront([
["start", 0],
["stop", 0]
]);
this._usingMockTimeline = true;
this._timeline = new compatibility.MockTimelineFront();
}
},
/**
* Initializes a connection to a memory actor.
* TODO: use framework level feature detection from bug 1069673
*/
_connectMemoryActor: function() {
if (this._target.form && this._target.form.memoryActor) {
_connectMemoryActor: Task.async(function* () {
let supported = yield compatibility.memoryActorSupported(this._target);
if (supported) {
this._memory = new MemoryFront(this._target.client, this._target.form);
} else {
this._memory = new MockedFront([
["attach"],
["detach"],
["startRecordingAllocations", 0],
["stopRecordingAllocations", 0],
["getAllocations"]
]);
this._usingMockMemory = true;
this._memory = new compatibility.MockMemoryFront();
}
},
}),
/**
* Closes the connections to non-profiler actors.
@ -251,11 +230,16 @@ function PerformanceFront(connection) {
connection._timeline.on("memory", (delta, measurement) => this.emit("memory", delta, measurement));
connection._timeline.on("ticks", (delta, timestamps) => this.emit("ticks", delta, timestamps));
// Set when mocks are being used
this._usingMockMemory = connection._usingMockMemory;
this._usingMockTimeline = connection._usingMockTimeline;
this._pullAllocationSites = this._pullAllocationSites.bind(this);
this._sitesPullTimeout = 0;
}
PerformanceFront.prototype = {
/**
* Manually begins a recording session.
*
@ -396,6 +380,16 @@ PerformanceFront.prototype = {
entries: 1000000,
interval: 1,
features: ["js"]
},
/**
* Returns an object indicating if mock actors are being used or not.
*/
getMocksInUse: function () {
return {
memory: this._usingMockMemory,
timeline: this._usingMockTimeline
};
}
};

View File

@ -4,6 +4,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_JS_MODULES.devtools.performance += [
'modules/compatibility.js',
'modules/front.js',
'modules/io.js',
'modules/recording-model.js',

View File

@ -10,6 +10,8 @@ support-files =
[browser_perf-aaa-run-first-leaktest.js]
[browser_perf-allocations-to-samples.js]
[browser_perf-compatibility-01.js]
[browser_perf-compatibility-02.js]
[browser_perf-clear-01.js]
[browser_perf-clear-02.js]
[browser_perf-data-massaging-01.js]

View File

@ -0,0 +1,63 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test basic functionality of PerformanceFront with mock memory and timeline actors.
*/
let WAIT_TIME = 100;
function spawnTest () {
let { target, front } = yield initBackend(SIMPLE_URL, {
TEST_MOCK_MEMORY_ACTOR: true,
TEST_MOCK_TIMELINE_ACTOR: true
});
let { memory, timeline } = front.getMocksInUse();
ok(memory, "memory should be mocked.");
ok(timeline, "timeline should be mocked.");
let {
profilerStartTime,
timelineStartTime,
memoryStartTime
} = yield front.startRecording({
withTicks: true,
withMemory: true,
withAllocations: true
});
ok(typeof profilerStartTime === "number",
"The front.startRecording() emits a profiler start time.");
ok(typeof timelineStartTime === "number",
"The front.startRecording() emits a timeline start time.");
ok(typeof memoryStartTime === "number",
"The front.startRecording() emits a memory start time.");
yield busyWait(WAIT_TIME);
let {
profilerEndTime,
timelineEndTime,
memoryEndTime
} = yield front.stopRecording({
withAllocations: true
});
ok(typeof profilerEndTime === "number",
"The front.stopRecording() emits a profiler end time.");
ok(typeof timelineEndTime === "number",
"The front.stopRecording() emits a timeline end time.");
ok(typeof memoryEndTime === "number",
"The front.stopRecording() emits a memory end time.");
ok(profilerEndTime > profilerStartTime,
"The profilerEndTime is after profilerStartTime.");
is(timelineEndTime, timelineStartTime,
"The timelineEndTime is the same as timelineStartTime.");
is(memoryEndTime, memoryStartTime,
"The memoryEndTime is the same as memoryStartTime.");
yield removeTab(target.tab);
finish();
}

View File

@ -0,0 +1,67 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the recording model is populated correctly when using timeline
* and memory actor mocks.
*/
const WAIT_TIME = 1000;
let test = Task.async(function*() {
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL, "performance", {
TEST_MOCK_MEMORY_ACTOR: true,
TEST_MOCK_TIMELINE_ACTOR: true
});
let { EVENTS, gFront, PerformanceController, PerformanceView } = panel.panelWin;
let { memory: memoryMock, timeline: timelineMock } = gFront.getMocksInUse();
ok(memoryMock, "memory should be mocked.");
ok(timelineMock, "timeline should be mocked.");
yield startRecording(panel);
busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
yield stopRecording(panel);
let {
label, duration, markers, frames, memory, ticks, allocations, profile
} = PerformanceController.getCurrentRecording().getAllData();
is(label, "", "Empty label for mock.");
is(typeof duration, "number", "duration is a number");
ok(duration > 0, "duration is not 0");
isEmptyArray(markers, "markers");
isEmptyArray(frames, "frames");
isEmptyArray(memory, "memory");
isEmptyArray(ticks, "ticks");
isEmptyArray(allocations.sites, "allocations.sites");
isEmptyArray(allocations.timestamps, "allocations.timestamps");
isEmptyArray(allocations.frames, "allocations.frames");
isEmptyArray(allocations.counts, "allocations.counts");
let sampleCount = 0;
for (let thread of profile.threads) {
info("Checking thread: " + thread.name);
for (let sample of thread.samples) {
sampleCount++;
if (sample.frames[0].location != "(root)") {
ok(false, "The sample " + sample.toSource() + " doesn't have a root node.");
}
}
}
ok(sampleCount > 0,
"At least some samples have been iterated over, checking for root nodes.");
yield teardown(panel);
finish();
});
function isEmptyArray (array, name) {
ok(Array.isArray(array), `${name} is an array`);
is(array.length, 0, `${name} is empty`);
}

View File

@ -12,6 +12,7 @@ let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
let { merge } = devtools.require("sdk/util/object");
let { getPerformanceActorsConnection, PerformanceFront } = devtools.require("devtools/performance/front");
let nsIProfilerModule = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
@ -161,7 +162,7 @@ function test () {
Task.spawn(spawnTest).then(finish, handleError);
}
function initBackend(aUrl) {
function initBackend(aUrl, targetOps={}) {
info("Initializing a performance front.");
if (!DebuggerServer.initialized) {
@ -175,6 +176,13 @@ function initBackend(aUrl) {
yield target.makeRemote();
// Attach addition options to `target`. This is used to force mock fronts
// to smokescreen test different servers where memory or timeline actors
// may not exist. Possible options that will actually work:
// TEST_MOCK_MEMORY_ACTOR = true
// TEST_MOCK_TIMELINE_ACTOR = true
merge(target, targetOps);
yield gDevTools.showToolbox(target, "performance");
let connection = getPerformanceActorsConnection(target);
@ -185,7 +193,7 @@ function initBackend(aUrl) {
});
}
function initPerformance(aUrl, selectedTool="performance") {
function initPerformance(aUrl, selectedTool="performance", targetOps={}) {
info("Initializing a performance pane.");
return Task.spawn(function*() {
@ -194,6 +202,13 @@ function initPerformance(aUrl, selectedTool="performance") {
yield target.makeRemote();
// Attach addition options to `target`. This is used to force mock fronts
// to smokescreen test different servers where memory or timeline actors
// may not exist. Possible options that will actually work:
// TEST_MOCK_MEMORY_ACTOR = true
// TEST_MOCK_TIMELINE_ACTOR = true
merge(target, targetOps);
let toolbox = yield gDevTools.showToolbox(target, selectedTool);
let panel = toolbox.getCurrentPanel();
return { target, panel, toolbox };