From 8f40951e4c0bfc63d23313b32c63887e1b46d291 Mon Sep 17 00:00:00 2001 From: David Rajchenbach-Teller Date: Fri, 10 Apr 2015 12:43:10 -0400 Subject: [PATCH] Bug 1151750 - about:performance now recapitulates alerts. r=mossop --- .../content/aboutPerformance.js | 90 ++++++++++++++++++- .../content/aboutPerformance.xhtml | 10 ++- .../tests/browser/browser_aboutperformance.js | 2 +- toolkit/modules/AddonWatcher.jsm | 55 ++++++++++-- 4 files changed, 148 insertions(+), 9 deletions(-) diff --git a/toolkit/components/aboutperformance/content/aboutPerformance.js b/toolkit/components/aboutperformance/content/aboutPerformance.js index f41de4636e0d..514678cc83a8 100644 --- a/toolkit/components/aboutperformance/content/aboutPerformance.js +++ b/toolkit/components/aboutperformance/content/aboutPerformance.js @@ -9,6 +9,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components; const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {}); +const { AddonWatcher } = Cu.import("resource://gre/modules/AddonWatcher.jsm", {}); const { PerformanceStats } = Cu.import("resource://gre/modules/PerformanceStats.jsm", {}); /** @@ -84,8 +85,93 @@ let State = { function update() { + updateLiveData(); + updateSlowAddons(); +} + +/** + * Update the list of slow addons + */ +function updateSlowAddons() { try { - let dataElt = document.getElementById("data"); + let data = AddonWatcher.alerts; + if (data.size == 0) { + // Nothing to display. + return; + } + let alerts = 0; + for (let [addonId, details] of data) { + for (let k of Object.keys(details.alerts)) { + alerts += details.alerts[k]; + } + } + + if (!alerts) { + // Still nothing to display. + return; + } + + + let elData = document.getElementById("slowAddonsList"); + elData.innerHTML = ""; + let elTable = document.createElement("table"); + elData.appendChild(elTable); + + // Generate header + let elHeader = document.createElement("tr"); + elTable.appendChild(elHeader); + for (let name of [ + "Alerts", + "Jank level alerts", + "(highest jank)", + "Cross-Process alerts", + "(highest CPOW)" + ]) { + let elName = document.createElement("td"); + elName.textContent = name; + elHeader.appendChild(elName); + elName.classList.add("header"); + } + for (let [addonId, details] of data) { + let elAddon = document.createElement("tr"); + + // Display the number of occurrences of each alerts + let elTotal = document.createElement("td"); + let total = 0; + for (let k of Object.keys(details.alerts)) { + total += details.alerts[k]; + } + elTotal.textContent = total; + elAddon.appendChild(elTotal); + + for (let filter of ["longestDuration", "totalCPOWTime"]) { + for (let stat of ["alerts", "peaks"]) { + let el = document.createElement("td"); + el.textContent = details[stat][filter] || 0; + elAddon.appendChild(el); + } + } + + // Display the name of the add-on + let elName = document.createElement("td"); + elAddon.appendChild(elName); + AddonManager.getAddonByID(addonId, a => { + elName.textContent = a ? a.name : addonId + }); + + elTable.appendChild(elAddon); + } + } catch (ex) { + console.error(ex); + } +} + +/** + * Update the table of live data. + */ +function updateLiveData() { + try { + let dataElt = document.getElementById("liveData"); dataElt.innerHTML = ""; // Generate table headers @@ -137,7 +223,7 @@ function update() { let _el = el; let _item = item; AddonManager.getAddonByID(item.addonId, a => { - _el.textContent = a?a.name:_item.name + _el.textContent = a ? a.name : _item.name }); } else { el.textContent = item.name; diff --git a/toolkit/components/aboutperformance/content/aboutPerformance.xhtml b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml index 943a2fcfae42..610a8016bcc9 100644 --- a/toolkit/components/aboutperformance/content/aboutPerformance.xhtml +++ b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml @@ -74,7 +74,15 @@ - + +

Performance monitor

+
+ +

Slow add-ons alerts

+
+ (none) +
+ diff --git a/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js b/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js index c4e57740fd34..79b994d6338b 100644 --- a/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js +++ b/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js @@ -16,7 +16,7 @@ function frameScript() { let hasURL = false; try { - let eltData = content.document.getElementById("data"); + let eltData = content.document.getElementById("liveData"); if (!eltData) { return; } diff --git a/toolkit/modules/AddonWatcher.jsm b/toolkit/modules/AddonWatcher.jsm index d73e2428fe0a..bf6db1b9cb0a 100644 --- a/toolkit/modules/AddonWatcher.jsm +++ b/toolkit/modules/AddonWatcher.jsm @@ -23,8 +23,16 @@ XPCOMUtils.defineLazyServiceGetter(this, "Telemetry", XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); +const FILTERS = ["longestDuration", "totalCPOWTime"]; + let AddonWatcher = { _previousPerformanceIndicators: {}, + + /** + * Stats, designed to be consumed by clients of AddonWatcher. + * + */ + _stats: new Map(), _timer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer), _callback: null, /** @@ -161,13 +169,31 @@ let AddonWatcher = { add(addonId, diff.totalCPOWTime / 1000); } - // Report mibehaviors to the user. + // Store misbehaviors for about:performance and other clients + + let stats = this._stats.get(addonId); + if (!stats) { + stats = { + peaks: {}, + alerts: {}, + }; + this._stats.set(addonId, stats); + } + + // Report misbehaviors to the user. + let reason = null; - for (let k of ["longestDuration", "totalCPOWTime"]) { - if (limits[k] > 0 && diff[k] > limits[k]) { - reason = k; + for (let filter of FILTERS) { + let peak = stats.peaks[filter] || 0; + stats.peaks[filter] = Math.max(diff[filter], peak); + + if (limits[filter] <= 0 || diff[filter] <= limits[filter]) { + continue; } + + reason = filter; + stats.alerts[filter] = (stats.alerts[filter] || 0) + 1; } if (!reason) { @@ -200,5 +226,24 @@ let AddonWatcher = { } catch (ex) { Preferences.set("browser.addon-watch.ignore", JSON.stringify([addonid])); } - } + }, + /** + * The list of alerts for this session. + * + * @type {Map} A map associating addonId to + * objects with fields + * - {Object} peaks The highest values encountered for each filter. + * - {number} longestDuration + * - {number} totalCPOWTime + * - {Object} alerts The number of alerts for each filter. + * - {number} longestDuration + * - {number} totalCPOWTime + */ + get alerts() { + let result = new Map(); + for (let [k, v] of this._stats) { + result.set(k, Cu.cloneInto(v, this)); + } + return result; + }, };