Bug 1231415 - Save to disk the list of slow add-ons;r?felipe,f?glind
--- a/toolkit/components/perfmonitoring/AddonWatcher.jsm
+++ b/toolkit/components/perfmonitoring/AddonWatcher.jsm
@@ -46,16 +46,18 @@ this.AddonWatcher = {
// probably some malformed JSON, ignore and carry on
this._ignoreList = new Set();
}
this._warmupPeriod = Preferences.get("browser.addon-watch.warmup-ms", 60 * 1000 /* 1 minute */);
this._idleThreshold = Preferences.get("browser.addon-watch.deactivate-after-idle-ms", 3000);
this.paused = false;
this.callback = callback;
+
+ OS.File.shutdown.addBlocker("AddonWatcher: Writing latest alerts to disk", () => this.saveAlertsToDisk());
},
uninit: function() {
this.paused = true;
},
_initializedTimeStamp: 0,
set callback(callback) {
this._callback = callback;
@@ -220,9 +222,77 @@ this.AddonWatcher = {
*/
get alerts() {
let result = new Map();
for (let [k, v] of this._alerts) {
result.set(k, Cu.cloneInto(v, this));
}
return result;
},
+
+ /**
+ * Write to disk the list of alerts encountered in the session so far.
+ *
+ * Noop if we consider that the session was too short for any measure to be
+ * meaningful. The threshold is controlled by pref
+ * "browser.addon-watch.minimal-meaningful-session-duration-us".
+ *
+ * @return Promise
+ */
+ saveAlertsToDisk: function() {
+ let minimalMeaningfulDurationUS = Preferences.get("browser.addon-watch.minimal-meaningful-session-duration-us", /* 1 hour */ 3600 * 1000 * 1000);
+ if (Cu.now() - this._initializedTimeStamp <= minimalMeaningfulDurationUS) {
+ // The session was short, so we probably don't have enough data to figure
+ // out whether add-ons actually slowed down Firefox.
+ return;
+ }
+
+ let data = {
+ format: ["addonwatcher", 1],
+ alerts: {},
+ };
+ for (let [k, v] of this._alerts) {
+ data.alerts[k] = v;
+ }
+
+ let path = OS.Path.join(OS.Constants.Path.profileDir, "extensions", "addonwatcher.json");
+ return OS.File.writeAtomic(path, JSON.stringify(data), { encoding: "utf-8" });
+ },
+
+ /**
+ * Read the latest list of alerts written to disk.
+ *
+ * @return Promise
+ * @resolve Map<String, Object> a map with the same format as `get alerts()`.
+ * The map is empty if no list was written to disk.
+ * @reject OS.File.Error in case of I/O error.
+ * @reject TypeError if the format of the file is incorrect.
+ */
+ readAlertsFromDisk: function() {
+ return Task.async(function*() {
+ let path = OS.Path.join(OS.Constants.Path.profileDir, "extensions", "addonwatcher.json");
+ try {
+ let data = JSON.parse((yield OS.File.read(path, { encoding: "utf-8" })));
+ } catch (ex) {
+ if (ex.becauseNoSuchFile) {
+ return new Map();
+ }
+ throw ex;
+ }
+
+ // Sanity check.
+ let [name, version] = data.format;
+ if (name != "addonwatcher") {
+ throw new TypeError("Unexpected format " + name);
+ }
+ if (version != 1) {
+ throw new TypeError("Unexpected format version " + version);
+ }
+
+ let result = new Map();
+
+ for (let k of Object.keys(data.alerts)) {
+ result.set(k, data.alerts[k]);
+ }
+ return result;
+ });
+ }
};