Bug 1425613 - Part 2 - Add a test for repeatedly called pref getters. r=florian
☠☠ backed out by b36868e9d272 ☠ ☠
authorJohann Hofmann <jhofmann@mozilla.com>
Sun, 18 Feb 2018 22:48:06 +0100
changeset 408700 6426e089e5c548dc1c75bc055792d85fe89573ea
parent 408699 4939d44bcd0a4aafde8c480e1ba8a66a1f936c7e
child 408701 9712703bd2428c0127e0425c60da22f0b22fe60f
push id101011
push usernerli@mozilla.com
push dateSat, 17 Mar 2018 22:28:18 +0000
treeherdermozilla-inbound@efce78e62b6d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersflorian
bugs1425613
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1425613 - Part 2 - Add a test for repeatedly called pref getters. r=florian MozReview-Commit-ID: 4doKsld9qGF
browser/base/content/test/performance/browser.ini
browser/base/content/test/performance/browser_preferences_usage.js
browser/components/tests/startupRecorder.js
--- a/browser/base/content/test/performance/browser.ini
+++ b/browser/base/content/test/performance/browser.ini
@@ -9,16 +9,18 @@ prefs =
   # representative of common startup.
   browser.migration.version=9999999
   browser.startup.record=true
   gfx.canvas.willReadFrequently.enable=true
 support-files =
   head.js
 [browser_appmenu_reflows.js]
 skip-if = asan || debug # Bug 1382809, bug 1369959
+[browser_preferences_usage.js]
+skip-if = !debug
 [browser_startup.js]
 [browser_startup_content.js]
 skip-if = !e10s
 [browser_startup_flicker.js]
 run-if = debug || devedition || nightly_build # Requires startupRecorder.js, which isn't shipped everywhere by default
 [browser_tabclose_grow_reflows.js]
 [browser_tabclose_reflows.js]
 [browser_tabopen_reflows.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/performance/browser_preferences_usage.js
@@ -0,0 +1,238 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * A test that checks whether any preference getter from the given list
+ * of stats was called more often than the max parameter.
+ *
+ * @param {Array}  stats - an array of [prefName, accessCount] tuples
+ * @param {Number} max - the maximum number of times any of the prefs should
+ *                 have been called.
+ * @param {Object} whitelist (optional) - an object that defines
+ *                 prefs that should be exempt from checking the
+ *                 maximum access. It looks like the following:
+ *
+ *                 pref_name: {
+ *                   min: [Number] the minimum amount of times this should have
+ *                                 been called (to avoid keeping around dead items)
+ *                   max: [Number] the maximum amount of times this should have
+ *                                 been called (to avoid this creeping up further)
+ *                 }
+ */
+function checkPrefGetters(stats, max, whitelist = {}) {
+  let getterStats = Object
+    .entries(stats)
+    .sort(([, val1], [, val2]) => val2 - val1);
+
+  // Clone the whitelist to be able to delete entries to check if we
+  // forgot any later on.
+  whitelist = Object.assign({}, whitelist);
+
+  for (let [pref, count] of getterStats) {
+    let whitelistItem = whitelist[pref];
+    if (!whitelistItem) {
+      Assert.lessOrEqual(count, max, `${pref} should not be accessed more than ${max} times.`);
+    } else {
+      // Still record how much this pref was accessed even if we don't do any real assertions.
+      if (!whitelistItem.min && !whitelistItem.max) {
+        info(`${pref} should not be accessed more than ${max} times and was accessed ${count} times.`);
+      }
+
+      if (whitelistItem.min) {
+        Assert.lessOrEqual(whitelistItem.min, count,
+          `Whitelist item ${pref} should be accessed at least ${whitelistItem.min} times.`);
+      }
+      if (whitelistItem.max) {
+        Assert.lessOrEqual(count, whitelistItem.max,
+          `Whitelist item ${pref} should be accessed at most ${whitelistItem.max} times.`);
+      }
+      delete whitelist[pref];
+    }
+  }
+
+  let remainingWhitelist = Object.keys(whitelist);
+  is(remainingWhitelist.length, 0, `Should have checked all whitelist items. Remaining: ${remainingWhitelist}`);
+}
+
+/**
+ * A helper function to read preference access data
+ * using the Services.prefs.readStats() function.
+ */
+function getPreferenceStats() {
+  let stats = {};
+  Services.prefs.readStats((key, value) => stats[key] = value);
+  return stats;
+}
+
+add_task(async function debug_only() {
+  ok(AppConstants.DEBUG, "You need to run this test on a debug build.");
+});
+
+// Just checks how many prefs were accessed during startup.
+add_task(async function startup() {
+  let max = 40;
+
+  let whitelist = {
+    "browser.startup.record": {
+      min: 200,
+      max: 350,
+    },
+    "layout.css.prefixes.webkit": {
+      min: 140,
+      max: 150,
+    },
+    "browser.search.log": {
+      min: 100,
+      max: 150,
+    },
+    "layout.css.dpi": {
+      min: 45,
+      max: 75,
+    },
+    "extensions.getAddons.cache.enabled": {
+      min: 10,
+      max: 55,
+    },
+    "dom.max_chrome_script_run_time": {
+      min: 30,
+      max: 55,
+    },
+    // This seems to get called frequently only on infra.
+    "network.jar.block-remote-files": {
+      max: 500,
+    },
+  };
+
+  let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
+  await startupRecorder.done;
+
+  ok(startupRecorder.data.prefStats, "startupRecorder has prefStats");
+
+  checkPrefGetters(startupRecorder.data.prefStats, max, whitelist);
+});
+
+// This opens 10 tabs and checks pref getters.
+add_task(async function open_10_tabs() {
+  let max = 15;
+
+  let whitelist = {
+    "layout.css.dpi": {
+      max: 35,
+    },
+    "browser.zoom.full": {
+      min: 10,
+      max: 25,
+    },
+    "security.insecure_connection_icon.pbmode.enabled": {
+      min: 17,
+      max: 25,
+    },
+    "security.insecure_connection_icon.enabled": {
+      min: 17,
+      max: 25,
+    },
+    "security.insecure_connection_text.enabled": {
+      min: 17,
+      max: 25,
+    },
+    "security.insecure_connection_text.pbmode.enabled": {
+      min: 17,
+      max: 25,
+    },
+    "dom.ipc.processCount": {
+      min: 10,
+      max: 15,
+    },
+    "media.autoplay.enabled": {
+      min: 10,
+      max: 30,
+    },
+    "browser.startup.record": {
+      max: 20,
+    },
+    "media.block-autoplay-until-in-foreground": {
+      min: 10,
+      max: 30,
+    },
+    "dom.max_chrome_script_run_time": {
+      max: 20,
+    },
+    "toolkit.cosmeticAnimations.enabled": {
+      min: 5,
+      max: 20,
+    },
+    // This seems to get called frequently only on infra.
+    "network.jar.block-remote-files": { },
+  };
+
+  Services.prefs.resetStats();
+
+  let tabs = [];
+  while (tabs.length < 10) {
+    tabs.push(await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com", true, true));
+  }
+
+  for (let tab of tabs) {
+    await BrowserTestUtils.removeTab(tab);
+  }
+
+  checkPrefGetters(getPreferenceStats(), max, whitelist);
+});
+
+// This navigates to 50 sites and checks pref getters.
+add_task(async function navigate_around() {
+  let max = 40;
+
+  let whitelist = {
+    "browser.zoom.full": {
+      min: 100,
+      max: 110,
+    },
+    "security.insecure_connection_icon.pbmode.enabled": {
+      min: 50,
+      max: 55,
+    },
+    "security.insecure_connection_icon.enabled": {
+      min: 50,
+      max: 55,
+    },
+    "security.insecure_connection_text.enabled": {
+      min: 50,
+      max: 55,
+    },
+    "security.insecure_connection_text.pbmode.enabled": {
+      min: 50,
+      max: 55,
+    },
+    "browser.chrome.favicons": {
+      min: 50,
+      max: 55,
+    },
+    "browser.chrome.site_icons": {
+      min: 50,
+      max: 55,
+    },
+    "toolkit.cosmeticAnimations.enabled": {
+      min: 45,
+      max: 55,
+    },
+  };
+
+  Services.prefs.resetStats();
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com", true, true);
+
+  let urls = ["http://example.com", "https://example.com", "http://example.org", "https://example.org"];
+
+  for (let i = 0; i < 50; i++) {
+    let url = urls[i % urls.length];
+    info(`Navigating to ${url}...`);
+    await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
+    await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+    info(`Loaded ${url}.`);
+  }
+
+  await BrowserTestUtils.removeTab(tab);
+
+  checkPrefGetters(getPreferenceStats(), max, whitelist);
+});
--- a/browser/components/tests/startupRecorder.js
+++ b/browser/components/tests/startupRecorder.js
@@ -44,17 +44,18 @@ let afterPaintListener = () => {
 function startupRecorder() {
   this.wrappedJSObject = this;
   this.loader = Cc["@mozilla.org/moz/jsloader;1"].getService(Ci.xpcIJSModuleLoader);
   this.data = {
     images: {
       "image-drawing": new Set(),
       "image-loading": new Set(),
     },
-    code: {}
+    code: {},
+    prefStats: {},
   };
   this.done = new Promise(resolve => { this._resolve = resolve; });
 }
 startupRecorder.prototype = {
   classID: Components.ID("{11c095b2-e42e-4bdf-9dd0-aed87595f6a4}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
@@ -133,16 +134,18 @@ startupRecorder.prototype = {
           callback();
       })(() => {
         this.record("before becoming idle");
         Services.obs.removeObserver(this, "image-drawing");
         Services.obs.removeObserver(this, "image-loading");
         win.removeEventListener("MozAfterPaint", afterPaintListener);
         win = null;
         this.data.frames = paints;
+        this.data.prefStats = {};
+        Services.prefs.readStats((key, value) => this.data.prefStats[key] = value);
         paints = null;
         this._resolve();
         this._resolve = null;
       });
     } else {
       const topicsToNames = {
         "profile-do-change": "before profile selection",
         "toplevel-window-ready": "before opening first browser window",