Bug 1061653 - Remote profiler doesn't work with old geckos, r=pbrosset

This commit is contained in:
Victor Porof 2014-09-22 16:34:06 -04:00
parent c8680336b0
commit f363f784c9
4 changed files with 62 additions and 11 deletions

View File

@ -24,6 +24,8 @@ devtools.lazyRequireGetter(this, "CATEGORIES",
"devtools/profiler/global", true);
devtools.lazyRequireGetter(this, "CATEGORY_MAPPINGS",
"devtools/profiler/global", true);
devtools.lazyRequireGetter(this, "CATEGORY_OTHER",
"devtools/profiler/global", true);
devtools.lazyRequireGetter(this, "ThreadNode",
"devtools/profiler/tree-model", true);
devtools.lazyRequireGetter(this, "CallView",

View File

@ -382,6 +382,7 @@ let ProfileView = {
if (oldGraph) {
oldGraph.destroy();
}
// Don't create a graph if there's not enough data to show.
if (!framerateData || framerateData.length < 2) {
return null;
@ -620,15 +621,28 @@ let RecordingUtils = {
if (!time || time < beginAt || time > endAt) continue;
let blocks = [];
for (let { category } of frames) {
if (!category) continue;
let ordinal = CATEGORY_MAPPINGS[category].ordinal;
for (let { category: bitmask } of frames) {
if (!bitmask) continue;
let category = CATEGORY_MAPPINGS[bitmask];
if (!blocks[ordinal]) {
blocks[ordinal] = 1;
} else {
blocks[ordinal]++;
// Guard against categories that aren't found in the frontend mappings.
// This usually means that a new category was added in the platform,
// but browser/devtools/profiler/utils/global.js wasn't updated yet.
if (!category) {
category = CATEGORY_MAPPINGS[CATEGORY_OTHER];
}
if (!blocks[category.ordinal]) {
blocks[category.ordinal] = 1;
} else {
blocks[category.ordinal]++;
}
}
// If no categories were found in the frames, default to using a
// single block using the stack depth as height.
if (blocks.length == 0) {
blocks[CATEGORY_MAPPINGS[CATEGORY_OTHER].ordinal] = frames.length;
}
categoriesData.push({
@ -654,6 +668,12 @@ let RecordingUtils = {
* A data source useful for a LineGraphWidget.
*/
plotFramerateFor: function(ticksData, beginAt, endAt) {
// Older Gecko versions don't have a framerate actor implementation,
// in which case the returned ticks data is null.
if (ticksData == null) {
return [];
}
let framerateData = this._frameratePlotsCache.get(ticksData);
if (framerateData == null) {
framerateData = FramerateFront.plotFPS(ticksData, FRAMERATE_CALC_INTERVAL);

View File

@ -44,6 +44,11 @@ const CATEGORY_MAPPINGS = {
"2048": CATEGORIES[7], // js::ProfileEntry::Category::EVENTS
};
// Human-readable "other" category bitmask. Older Geckos don't have all the
// necessary instrumentation in the sampling profiler backend for creating
// a categories graph, in which case we default to the "other" category.
const CATEGORY_OTHER = 8;
// Human-readable JIT category bitmask. Certain pseudo-frames in a sample,
// like "EnterJIT", don't have any associated `cateogry` information.
const CATEGORY_JIT = 32;
@ -52,4 +57,5 @@ const CATEGORY_JIT = 32;
exports.L10N = L10N;
exports.CATEGORIES = CATEGORIES;
exports.CATEGORY_MAPPINGS = CATEGORY_MAPPINGS;
exports.CATEGORY_OTHER = CATEGORY_OTHER;
exports.CATEGORY_JIT = CATEGORY_JIT;

View File

@ -96,7 +96,7 @@ ProfilerConnection.prototype = {
}
// Check if we already have a grip to the `listTabs` response object
// and, if we do, use it to get to the profiler actor.
else if (this._target.root) {
else if (this._target.root && this._target.root.profilerActor) {
this._profiler = this._target.root.profilerActor;
yield this._registerEventNotifications();
}
@ -117,7 +117,20 @@ ProfilerConnection.prototype = {
* used in tandem with the profiler actor.
*/
_connectMiscActors: function() {
// Only initialize the framerate front if the respective actor is available.
// Older Gecko versions don't have an existing implementation, in which case
// all the methods we need can be easily mocked.
if (this._target.form && this._target.form.framerateActor) {
this._framerate = new FramerateFront(this._target.client, this._target.form);
} else {
this._framerate = {
startRecording: () => {},
stopRecording: () => {},
cancelRecording: () => {},
isRecording: () => false,
getPendingTicks: () => null
};
}
},
/**
@ -146,11 +159,11 @@ ProfilerConnection.prototype = {
// Handle requests to the framerate actor.
if (actor == "framerate") {
switch (method) {
// Only stop recording framerate if there are no other pending consumers.
// Otherwise, for example, the next time `console.profileEnd` is called
// there won't be any framerate data available, since we're reusing the
// same actor for multiple overlapping recordings.
switch (method) {
case "startRecording":
this._pendingFramerateConsumers++;
break;
@ -158,6 +171,12 @@ ProfilerConnection.prototype = {
case "cancelRecording":
if (--this._pendingFramerateConsumers > 0) return;
break;
// Some versions of the framerate actor don't have a 'getPendingTicks'
// method available, in which case it's impossible to get all the
// accumulated framerate data without stopping the recording. Bail out.
case "getPendingTicks":
if (method in this._framerate) break;
return null;
}
checkPendingFramerateConsumers(this);
return this._framerate[method].apply(this._framerate, args);
@ -387,9 +406,13 @@ ProfilerFront.prototype = {
* Overrides the options sent to the built-in profiler module when activating,
* such as the maximum entries count, the sampling interval etc.
*
* Used in tests.
* Used in tests and for older backend implementations.
*/
_options: undefined
_customProfilerOptions: {
entries: 1000000,
interval: 1,
features: ["js"]
}
};
/**