mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 15:25:52 +00:00
Bug 1189799 - Make sure that about:performance displays each add-on only once (back-end);r=felipe
--HG-- extra : transplant_source : %8A%95z%C5D%DEM%94%0Ak%82%CB%F7%187%071%C1%21%9C
This commit is contained in:
parent
131d787115
commit
e28a2a27f5
@ -214,22 +214,15 @@ var AddonWatcher = {
|
||||
// By default, warn only after an add-on has been spotted misbehaving 3 times.
|
||||
let tolerance = Preferences.get("browser.addon-watch.tolerance", 3);
|
||||
|
||||
for (let item of snapshot.componentsData) {
|
||||
let addonId = item.addonId;
|
||||
if (!item.isSystem || !addonId) {
|
||||
// We are only interested in add-ons.
|
||||
continue;
|
||||
}
|
||||
for (let [addonId, item] of snapshot.addons) {
|
||||
if (this._ignoreList.has(addonId)) {
|
||||
// This add-on has been explicitly put in the ignore list
|
||||
// by the user. Don't waste time with it.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Store the activity for the group – not the entire add-on, as we
|
||||
// can have one group per process for each add-on.
|
||||
let previous = this._previousPerformanceIndicators[item.groupId];
|
||||
this._previousPerformanceIndicators[item.groupId] = item;
|
||||
let previous = this._previousPerformanceIndicators[addonId];
|
||||
this._previousPerformanceIndicators[addonId] = item;
|
||||
|
||||
if (!previous) {
|
||||
// This is the first time we see the addon, so we are probably
|
||||
@ -281,7 +274,7 @@ var AddonWatcher = {
|
||||
|
||||
stats.alerts[filter] = (stats.alerts[filter] || 0) + 1;
|
||||
|
||||
if (stats.alerts[filter] % tolerance != 0) {
|
||||
if (stats.alerts[filter] % tolerance != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
|
||||
Cu.import("resource://gre/modules/ObjectUtils.jsm", this);
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
|
||||
"resource://gre/modules/PromiseUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
|
||||
@ -152,7 +152,7 @@ Probe.prototype = {
|
||||
if (!Array.isArray(children)) {
|
||||
throw new TypeError();
|
||||
}
|
||||
if (!parent || !(parent instanceof PerformanceData)) {
|
||||
if (!parent || !(parent instanceof PerformanceDataLeaf)) {
|
||||
throw new TypeError();
|
||||
}
|
||||
return this._impl.importChildCompartments(parent, children);
|
||||
@ -163,6 +163,13 @@ Probe.prototype = {
|
||||
*/
|
||||
get name() {
|
||||
return this._name;
|
||||
},
|
||||
|
||||
compose: function(stats) {
|
||||
if (!Array.isArray(stats)) {
|
||||
throw new TypeError();
|
||||
}
|
||||
return this._impl.compose(stats);
|
||||
}
|
||||
};
|
||||
|
||||
@ -208,6 +215,7 @@ var Probes = {
|
||||
return {
|
||||
totalUserTime: xpcom.totalUserTime,
|
||||
totalSystemTime: xpcom.totalSystemTime,
|
||||
totalCPUTime: xpcom.totalUserTime + xpcom.totalSystemTime,
|
||||
durations: durations,
|
||||
longestDuration: lastNonZero(durations)
|
||||
}
|
||||
@ -232,6 +240,7 @@ var Probes = {
|
||||
let result = {
|
||||
totalUserTime: a.totalUserTime - b.totalUserTime,
|
||||
totalSystemTime: a.totalSystemTime - b.totalSystemTime,
|
||||
totalCPUTime: a.totalCPUTime - b.totalCPUTime,
|
||||
durations: [],
|
||||
longestDuration: -1,
|
||||
};
|
||||
@ -242,6 +251,25 @@ var Probes = {
|
||||
return result;
|
||||
},
|
||||
importChildCompartments: function() { /* nothing to do */ },
|
||||
compose: function(stats) {
|
||||
let result = {
|
||||
totalUserTime: 0,
|
||||
totalSystemTime: 0,
|
||||
totalCPUTime: 0,
|
||||
durations: [],
|
||||
longestDuration: -1
|
||||
};
|
||||
for (let stat of stats) {
|
||||
result.totalUserTime += stat.totalUserTime;
|
||||
result.totalSystemTime += stat.totalSystemTime;
|
||||
result.totalCPUTime += stat.totalCPUTime;
|
||||
for (let i = 0; i < stat.durations.length; ++i) {
|
||||
result.durations[i] += stat.durations[i];
|
||||
}
|
||||
result.longestDuration = Math.max(result.longestDuration, stat.longestDuration);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -273,6 +301,13 @@ var Probes = {
|
||||
};
|
||||
},
|
||||
importChildCompartments: function() { /* nothing to do */ },
|
||||
compose: function(stats) {
|
||||
let totalCPOWTime = 0;
|
||||
for (let stat of stats) {
|
||||
totalCPOWTime += stat.totalCPOWTime;
|
||||
}
|
||||
return { totalCPOWTime };
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -303,6 +338,13 @@ var Probes = {
|
||||
};
|
||||
},
|
||||
importChildCompartments: function() { /* nothing to do */ },
|
||||
compose: function(stats) {
|
||||
let ticks = 0;
|
||||
for (let stat of stats) {
|
||||
ticks += stat.ticks;
|
||||
}
|
||||
return { ticks };
|
||||
},
|
||||
}),
|
||||
|
||||
compartments: new Probe("compartments", {
|
||||
@ -324,6 +366,9 @@ var Probes = {
|
||||
importChildCompartments: function(parent, children) {
|
||||
parent.children = children;
|
||||
},
|
||||
compose: function(stats) {
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
@ -391,7 +436,7 @@ PerformanceMonitor.prototype = {
|
||||
* @return {Promise}
|
||||
* @resolve {Snapshot}
|
||||
*/
|
||||
promiseSnapshot: function(options = null) {
|
||||
_checkBeforeSnapshot: function(options) {
|
||||
if (!this._finalizer) {
|
||||
throw new Error("dispose() has already been called, this PerformanceMonitor is not usable anymore");
|
||||
}
|
||||
@ -414,12 +459,22 @@ PerformanceMonitor.prototype = {
|
||||
} else {
|
||||
probes = this._probes;
|
||||
}
|
||||
return probes;
|
||||
},
|
||||
promiseContentSnapshot: function(options = null) {
|
||||
this._checkBeforeSnapshot(options);
|
||||
return (new ProcessSnapshot(performanceStatsService.getSnapshot()));
|
||||
},
|
||||
promiseSnapshot: function(options = null) {
|
||||
let probes = this._checkBeforeSnapshot(options);
|
||||
return Task.spawn(function*() {
|
||||
let collected = yield Process.broadcastAndCollect("collect", {probeNames: [for (probe of probes) probe.name]});
|
||||
return new Snapshot({
|
||||
xpcom: performanceStatsService.getSnapshot(),
|
||||
childProcesses: collected,
|
||||
probes
|
||||
let childProcesses = yield Process.broadcastAndCollect("collect", {probeNames: [for (probe of probes) probe.name]});
|
||||
let xpcom = performanceStatsService.getSnapshot();
|
||||
return new ApplicationSnapshot({
|
||||
xpcom,
|
||||
childProcesses,
|
||||
probes,
|
||||
date: Cu.now()
|
||||
});
|
||||
});
|
||||
},
|
||||
@ -466,7 +521,7 @@ PerformanceMonitor.make = function(probeNames) {
|
||||
probes.push(Probes[probeName]);
|
||||
}
|
||||
|
||||
return new PerformanceMonitor(probes);
|
||||
return (new PerformanceMonitor(probes));
|
||||
};
|
||||
|
||||
/**
|
||||
@ -547,7 +602,7 @@ this.PerformanceStats = {
|
||||
* @field {object|undefined} cpow See the documentation of probe "cpow".
|
||||
* `undefined` if this probe is not active.
|
||||
*/
|
||||
function PerformanceData({xpcom, json, probes}) {
|
||||
function PerformanceDataLeaf({xpcom, json, probes}) {
|
||||
if (xpcom && json) {
|
||||
throw new TypeError("Cannot import both xpcom and json data");
|
||||
}
|
||||
@ -566,15 +621,16 @@ function PerformanceData({xpcom, json, probes}) {
|
||||
}
|
||||
this.isChildProcess = true;
|
||||
}
|
||||
this.owner = null;
|
||||
}
|
||||
PerformanceData.prototype = {
|
||||
PerformanceDataLeaf.prototype = {
|
||||
/**
|
||||
* Compare two instances of `PerformanceData`
|
||||
*
|
||||
* @return `true` if `this` and `to` have equal values in all fields.
|
||||
*/
|
||||
equals: function(to) {
|
||||
if (!(to instanceof PerformanceData)) {
|
||||
if (!(to instanceof PerformanceDataLeaf)) {
|
||||
throw new TypeError();
|
||||
}
|
||||
for (let probeName of Object.keys(Probes)) {
|
||||
@ -594,28 +650,191 @@ PerformanceData.prototype = {
|
||||
* @return {PerformanceDiff} The performance usage between `to` and `this`.
|
||||
*/
|
||||
subtract: function(to = null) {
|
||||
return new PerformanceDiff(this, to);
|
||||
return (new PerformanceDiffLeaf(this, to));
|
||||
}
|
||||
};
|
||||
|
||||
function PerformanceData(timestamp) {
|
||||
this._parent = null;
|
||||
this._content = new Map();
|
||||
this._all = [];
|
||||
this._timestamp = timestamp;
|
||||
}
|
||||
PerformanceData.prototype = {
|
||||
addChild: function(stat) {
|
||||
if (!(stat instanceof PerformanceDataLeaf)) {
|
||||
throw new TypeError(); // FIXME
|
||||
}
|
||||
if (!stat.isChildProcess) {
|
||||
throw new TypeError(); // FIXME
|
||||
}
|
||||
this._content.set(stat.groupId, stat);
|
||||
this._all.push(stat);
|
||||
stat.owner = this;
|
||||
},
|
||||
setParent: function(stat) {
|
||||
if (!(stat instanceof PerformanceDataLeaf)) {
|
||||
throw new TypeError(); // FIXME
|
||||
}
|
||||
if (stat.isChildProcess) {
|
||||
throw new TypeError(); // FIXME
|
||||
}
|
||||
this._parent = stat;
|
||||
this._all.push(stat);
|
||||
stat.owner = this;
|
||||
},
|
||||
equals: function(to) {
|
||||
if (this._parent && !to._parent) {
|
||||
return false;
|
||||
}
|
||||
if (!this._parent && to._parent) {
|
||||
return false;
|
||||
}
|
||||
if (this._content.size != to._content.size) {
|
||||
return false;
|
||||
}
|
||||
if (this._parent && !this._parent.equals(to._parent)) {
|
||||
return false;
|
||||
}
|
||||
for (let [k, v] of this._content) {
|
||||
let v2 = to._content.get(k);
|
||||
if (!v2) {
|
||||
return false;
|
||||
}
|
||||
if (!v.equals(v2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
subtract: function(to = null) {
|
||||
return (new PerformanceDiff(this, to));
|
||||
},
|
||||
get addonId() {
|
||||
return this._all[0].addonId;
|
||||
},
|
||||
get title() {
|
||||
return this._all[0].title;
|
||||
}
|
||||
};
|
||||
|
||||
function PerformanceDiff(current, old = null) {
|
||||
this.addonId = current.addonId;
|
||||
this.title = current.title;
|
||||
this.windowId = current.windowId;
|
||||
this.deltaT = old ? current._timestamp - old._timestamp : Infinity;
|
||||
this._all = [];
|
||||
|
||||
// Handle the parent, if any.
|
||||
if (current._parent) {
|
||||
this._parent = old?current._parent.subtract(old._parent):current._parent;
|
||||
this._all.push(this._parent);
|
||||
this._parent.owner = this;
|
||||
} else {
|
||||
this._parent = null;
|
||||
}
|
||||
|
||||
// Handle the children, if any.
|
||||
this._content = new Map();
|
||||
for (let [k, stat] of current._content) {
|
||||
let diff = stat.subtract(old ? old._content.get(k) : null);
|
||||
this._content.set(k, diff);
|
||||
this._all.push(diff);
|
||||
diff.owner = this;
|
||||
}
|
||||
|
||||
// Now consolidate data
|
||||
for (let k of Object.keys(Probes)) {
|
||||
if (!(k in this._all[0])) {
|
||||
// The stats don't contain data from this probe.
|
||||
continue;
|
||||
}
|
||||
let data = [for (item of this._all) item[k]];
|
||||
let probe = Probes[k];
|
||||
this[k] = probe.compose(data);
|
||||
}
|
||||
}
|
||||
PerformanceDiff.prototype = {
|
||||
toString: function() {
|
||||
return `[PerformanceDiff] ${this.key}`;
|
||||
},
|
||||
get windowIds() {
|
||||
return [for (item of this._all) item.windowId].filter(x => !!x);
|
||||
},
|
||||
get groupIds() {
|
||||
return [for (item of this._all) item.groupId];
|
||||
},
|
||||
get key() {
|
||||
if (this.addonId) {
|
||||
return this.addonId;
|
||||
}
|
||||
if (this._parent) {
|
||||
return this._parent.windowId;
|
||||
}
|
||||
return this._all[0].groupId;
|
||||
},
|
||||
get names() {
|
||||
return [for (item of this._all) item.name];
|
||||
},
|
||||
get processes() {
|
||||
return [for (item of this._all) { isChildProcess: item.isChildProcess, processId: item.processId}];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The delta between two instances of `PerformanceData`.
|
||||
* The delta between two instances of `PerformanceDataLeaf`.
|
||||
*
|
||||
* Used to monitor resource usage between two timestamps.
|
||||
*/
|
||||
function PerformanceDiff(current, old = null) {
|
||||
function PerformanceDiffLeaf(current, old = null) {
|
||||
for (let k of PROPERTIES_META) {
|
||||
this[k] = current[k];
|
||||
}
|
||||
|
||||
for (let probeName of Object.keys(Probes)) {
|
||||
let other = old ? old[probeName] : null;
|
||||
let other = null;
|
||||
if (old && probeName in old) {
|
||||
other = old[probeName];
|
||||
}
|
||||
|
||||
if (probeName in current) {
|
||||
this[probeName] = Probes[probeName].subtract(current[probeName], other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A snapshot of a single process.
|
||||
*/
|
||||
function ProcessSnapshot({xpcom, probes}) {
|
||||
this.componentsData = [];
|
||||
|
||||
let subgroups = new Map();
|
||||
let enumeration = xpcom.getComponentsData().enumerate();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
let xpcom = enumeration.getNext().QueryInterface(Ci.nsIPerformanceStats);
|
||||
let stat = (new PerformanceDataLeaf({xpcom, probes}));
|
||||
|
||||
if (!xpcom.parentId) {
|
||||
this.componentsData.push(stat);
|
||||
} else {
|
||||
let siblings = subgroups.get(xpcom.parentId);
|
||||
if (!siblings) {
|
||||
subgroups.set(xpcom.parentId, (siblings = []));
|
||||
}
|
||||
siblings.push(stat);
|
||||
}
|
||||
}
|
||||
|
||||
for (let group of this.componentsData) {
|
||||
for (let probe of probes) {
|
||||
probe.importChildCompartments(group, subgroups.get(group.groupId) || []);
|
||||
}
|
||||
}
|
||||
|
||||
this.processData = (new PerformanceDataLeaf({xpcom: xpcom.getProcessData(), probes}));
|
||||
}
|
||||
|
||||
/**
|
||||
* A snapshot of the performance usage of the application.
|
||||
*
|
||||
@ -623,45 +842,45 @@ function PerformanceDiff(current, old = null) {
|
||||
* @param {Array<Object>} childProcesses The data acquired from children processes.
|
||||
* @param {Array<Probe>} probes The active probes.
|
||||
*/
|
||||
function Snapshot({xpcom, childProcesses, probes}) {
|
||||
this.componentsData = [];
|
||||
function ApplicationSnapshot({xpcom, childProcesses, probes, date}) {
|
||||
ProcessSnapshot.call(this, {xpcom, probes});
|
||||
|
||||
// Current process
|
||||
if (xpcom) {
|
||||
let children = new Map();
|
||||
let enumeration = xpcom.getComponentsData().enumerate();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
let xpcom = enumeration.getNext().QueryInterface(Ci.nsIPerformanceStats);
|
||||
let stat = new PerformanceData({xpcom, probes});
|
||||
if (!stat.parentId) {
|
||||
this.componentsData.push(stat);
|
||||
} else {
|
||||
let siblings = children.get(stat.parentId);
|
||||
if (!siblings) {
|
||||
children.set(stat.parentId, (siblings = []));
|
||||
}
|
||||
siblings.push(stat);
|
||||
}
|
||||
}
|
||||
|
||||
for (let parent of this.componentsData) {
|
||||
for (let probe of probes) {
|
||||
probe.importChildCompartments(parent, children.get(parent.groupId) || []);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.addons = new Map();
|
||||
this.webpages = new Map();
|
||||
this.date = date;
|
||||
|
||||
// Child processes
|
||||
if (childProcesses) {
|
||||
for (let {componentsData} of childProcesses) {
|
||||
// We are only interested in `componentsData` for the time being.
|
||||
for (let json of componentsData) {
|
||||
this.componentsData.push(new PerformanceData({json, probes}));
|
||||
}
|
||||
for (let {componentsData} of (childProcesses || [])) {
|
||||
// We are only interested in `componentsData` for the time being.
|
||||
for (let json of componentsData) {
|
||||
let leaf = (new PerformanceDataLeaf({json, probes}));
|
||||
this.componentsData.push(leaf);
|
||||
}
|
||||
}
|
||||
|
||||
this.processData = new PerformanceData({xpcom: xpcom.getProcessData(), probes});
|
||||
for (let leaf of this.componentsData) {
|
||||
let key, map;
|
||||
if (leaf.addonId) {
|
||||
key = leaf.addonId;
|
||||
map = this.addons;
|
||||
} else if (leaf.windowId) {
|
||||
key = leaf.windowId;
|
||||
map = this.webpages;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
let combined = map.get(key);
|
||||
if (!combined) {
|
||||
combined = new PerformanceData(date);
|
||||
map.set(key, combined);
|
||||
}
|
||||
if (leaf.isChildProcess) {
|
||||
combined.addChild(leaf);
|
||||
} else {
|
||||
combined.setParent(leaf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user