Bug 1184486 - Let PerformanceStats.jsm play nicer with process-per-tab. r=mconley
authorDavid Rajchenbach-Teller <dteller@mozilla.com>
Thu, 16 Jul 2015 12:19:17 +0200
changeset 286882 14ae070d4a419ea30357a588985694b35f63ae2b
parent 286881 7910e4e1015a8843580a63bfed4097945c2df2d1
child 286883 b2562e1376d232b5ebf5a845b5acb9bc558e0826
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley
bugs1184486
milestone42.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 1184486 - Let PerformanceStats.jsm play nicer with process-per-tab. r=mconley
toolkit/components/perfmonitoring/PerformanceStats.jsm
toolkit/components/perfmonitoring/nsIPerformanceStats.idl
toolkit/components/perfmonitoring/nsPerformanceStats.cpp
toolkit/components/perfmonitoring/tests/browser/browser_compartments.js
--- a/toolkit/components/perfmonitoring/PerformanceStats.jsm
+++ b/toolkit/components/perfmonitoring/PerformanceStats.jsm
@@ -44,17 +44,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
   "@mozilla.org/toolkit/finalizationwitness;1",
   Ci.nsIFinalizationWitnessService
 );
 
 // The topic used to notify that a PerformanceMonitor has been garbage-collected
 // and that we can release/close the probes it holds.
 const FINALIZATION_TOPIC = "performancemonitor-finalize";
 
-const PROPERTIES_META_IMMUTABLE = ["addonId", "isSystem", "isChildProcess", "groupId"];
+const PROPERTIES_META_IMMUTABLE = ["addonId", "isSystem", "isChildProcess", "groupId", "processId"];
 const PROPERTIES_META = [...PROPERTIES_META_IMMUTABLE, "windowId", "title", "name"];
 
 // How long we wait for children processes to respond.
 const MAX_WAIT_FOR_CHILD_PROCESS_MS = 5000;
 
 let isContent = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
 /**
  * Access to a low-level performance probe.
--- a/toolkit/components/perfmonitoring/nsIPerformanceStats.idl
+++ b/toolkit/components/perfmonitoring/nsIPerformanceStats.idl
@@ -17,17 +17,17 @@
 
 /**
  * Snapshot of the performance of a component, e.g. an add-on, a web
  * page, system built-ins, a module or the entire process itself.
  *
  * All values are monotonic and are updated only when
  * `nsIPerformanceStatsService.isStopwatchActive` is `true`.
  */
-[scriptable, uuid(1bc2d016-e9ae-4186-97c6-9478eddda245)]
+[scriptable, uuid(89440555-dd81-4a22-8747-049bcf0ab586)]
 interface nsIPerformanceStats: nsISupports {
   /**
    * An identifier unique to the component.
    *
    * This identifier is somewhat human-readable to aid with debugging,
    * but clients should not rely upon the format.
    */
   readonly attribute AString groupId;
@@ -87,29 +87,34 @@ interface nsIPerformanceStats: nsISuppor
   /**
    * `true` if this component is executed with system privileges
    * (e.g. the platform itself or an add-on), `false` otherwise
    * (e.g. webpages).
    */
   readonly attribute bool isSystem;
 
   /**
+   * The process running this group.
+   */
+  readonly attribute unsigned long long processId;
+
+  /**
    * Jank indicator.
    *
    * durations[i] == number of times execution of this group
    * lasted at lest 2^i ms.
    */
   void getDurations([optional] out unsigned long aCount,
                     [retval, array, size_is(aCount)]out unsigned long long aNumberOfOccurrences);
 };
 
 /**
  * A snapshot of the performance data of the process.
  */
-[scriptable, uuid(29ecebd0-908a-4b34-8f62-a6015dea1141)]
+[scriptable, uuid(2e0c50e2-3aff-4cc8-88a6-c0dc200da8fc)]
 interface nsIPerformanceSnapshot: nsISupports {
   /**
    * Data on all individual components.
    */
   nsIArray getComponentsData();
 
   /**
    * Information on the process itself.
--- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
+++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
@@ -30,23 +30,25 @@
 class nsPerformanceStats: public nsIPerformanceStats {
 public:
   nsPerformanceStats(const nsAString& aName,
                      nsIPerformanceStats* aParent,
                      const nsAString& aGroupId,
                      const nsAString& aAddonId,
                      const nsAString& aTitle,
                      const uint64_t aWindowId,
+                     const uint64_t aProcessId,
                      const bool aIsSystem,
                      const js::PerformanceData& aPerformanceData)
     : mName(aName)
     , mGroupId(aGroupId)
     , mAddonId(aAddonId)
     , mTitle(aTitle)
     , mWindowId(aWindowId)
+    , mProcessId(aProcessId)
     , mIsSystem(aIsSystem)
     , mPerformanceData(aPerformanceData)
   {
     if (aParent) {
       mozilla::DebugOnly<nsresult> rv = aParent->GetGroupId(mParentId);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   }
@@ -116,35 +118,44 @@ public:
 
   /* readonly attribute unsigned long long ticks; */
   NS_IMETHOD GetTicks(uint64_t *aTicks) override {
     *aTicks = mPerformanceData.ticks;
     return NS_OK;
   };
 
   /* void getDurations (out unsigned long aCount, [array, size_is (aCount), retval] out unsigned long long aNumberOfOccurrences); */
-  NS_IMETHODIMP GetDurations(uint32_t *aCount, uint64_t **aNumberOfOccurrences) override {
+  NS_IMETHOD GetDurations(uint32_t *aCount, uint64_t **aNumberOfOccurrences) override {
     const size_t length = mozilla::ArrayLength(mPerformanceData.durations);
     if (aCount) {
       *aCount = length;
     }
     *aNumberOfOccurrences = new uint64_t[length];
     for (size_t i = 0; i < length; ++i) {
       (*aNumberOfOccurrences)[i] = mPerformanceData.durations[i];
     }
     return NS_OK;
   };
 
+  /*
+    readonly attribute unsigned long long processId;
+  */
+  NS_IMETHODIMP GetProcessId(uint64_t* processId) override {
+    *processId = mProcessId;
+    return NS_OK;
+  }
+
 private:
   nsString mName;
   nsString mParentId;
   nsString mGroupId;
   nsString mAddonId;
   nsString mTitle;
   uint64_t mWindowId;
+  uint64_t mProcessId;
   bool mIsSystem;
 
   js::PerformanceData mPerformanceData;
 
   virtual ~nsPerformanceStats() {}
 };
 
 NS_IMPL_ISUPPORTS(nsPerformanceStats, nsIPerformanceStats)
@@ -353,17 +364,17 @@ nsPerformanceSnapshot::ImportStats(JSCon
   GetWindowData(cx, title, &windowId);
 
   nsString name;
   GetName(cx, global, name);
 
   bool isSystem = GetIsSystem(cx, global);
 
   nsCOMPtr<nsIPerformanceStats> result =
-    new nsPerformanceStats(name, parent, groupId, addonId, title, windowId, isSystem, performance);
+    new nsPerformanceStats(name, parent, groupId, addonId, title, windowId, mProcessId, isSystem, performance);
   return result.forget();
 }
 
 /*static*/ bool
 nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx,
                                                     const js::PerformanceData& stats, const uint64_t id,
                                                     const uint64_t* parentId,
                                                     void* self) {
@@ -397,16 +408,17 @@ nsPerformanceSnapshot::Init(JSContext* c
   processGroupId.AssignLiteral("process: ");
   processGroupId.AppendInt(processId);
   mProcessData = new nsPerformanceStats(NS_LITERAL_STRING("<process>"), // name
                                         nullptr,                        // parent
                                         processGroupId,                 // group id
                                         NS_LITERAL_STRING(""),          // add-on id
                                         NS_LITERAL_STRING(""),          // title
                                         0,                              // window id
+                                        mProcessId,                     // process id
                                         true,                           // isSystem
                                         processStats);
   return NS_OK;
 }
 
 
 /* void getComponentsData (out nsIArray aComponents); */
 NS_IMETHODIMP nsPerformanceSnapshot::GetComponentsData(nsIArray * *aComponents)
--- a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js
+++ b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js
@@ -4,41 +4,45 @@
 "use strict";
 
 /**
  * Test that we see jank that takes place in a webpage,
  * and that jank from several iframes are actually charged
  * to the top window.
  */
 Cu.import("resource://gre/modules/PerformanceStats.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://testing-common/ContentTask.jsm", this);
 
+
 const URL = "http://example.com/browser/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html?test=" + Math.random();
 const PARENT_TITLE = `Main frame for test browser_compartments.js ${Math.random()}`;
 const FRAME_TITLE = `Subframe for test browser_compartments.js ${Math.random()}`;
 
+const PARENT_PID = Services.appinfo.processID;
+
 // This function is injected as source as a frameScript
 function frameScript() {
   try {
     "use strict";
 
     const { utils: Cu, classes: Cc, interfaces: Ci } = Components;
     Cu.import("resource://gre/modules/PerformanceStats.jsm");
-
+    Cu.import("resource://gre/modules/Services.jsm");
     let performanceStatsService =
       Cc["@mozilla.org/toolkit/performance-stats-service;1"].
       getService(Ci.nsIPerformanceStatsService);
 
     // Make sure that the stopwatch is now active.
     let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks", "compartments"]);
 
     addMessageListener("compartments-test:getStatistics", () => {
       try {
         monitor.promiseSnapshot().then(snapshot => {
-          sendAsyncMessage("compartments-test:getStatistics", snapshot);
+          sendAsyncMessage("compartments-test:getStatistics", {snapshot, pid: Services.appinfo.processID});
         });
       } catch (ex) {
         Cu.reportError("Error in content (getStatistics): " + ex);
         Cu.reportError(ex.stack);
       }
     });
 
     addMessageListener("compartments-test:setTitles", titles => {
@@ -130,45 +134,51 @@ function monotinicity_tester(source, tes
   };
   let iteration = 0;
   let frameCheck = Task.async(function*() {
     if (isShuttingDown) {
       window.clearInterval(interval);
       return;
     }
     let name = `${testName}: ${iteration++}`;
-    let snapshot = yield source();
-    if (!snapshot) {
+    let result = yield source();
+    if (!result) {
       // This can happen at the end of the test when we attempt
       // to communicate too late with the content process.
       window.clearInterval(interval);
       return;
     }
+    let {pid, snapshot} = result;
 
     // Sanity check on the process data.
     sanityCheck(previous.processData, snapshot.processData);
     SilentAssert.equal(snapshot.processData.isSystem, true);
     SilentAssert.equal(snapshot.processData.name, "<process>");
     SilentAssert.equal(snapshot.processData.addonId, "");
+    SilentAssert.equal(snapshot.processData.processId, pid);
     previous.procesData = snapshot.processData;
 
     // Sanity check on components data.
     let map = new Map();
     for (let item of snapshot.componentsData) {
       for (let [probe, k] of [
         ["jank", "totalUserTime"],
         ["jank", "totalSystemTime"],
         ["cpow", "totalCPOWTime"]
       ]) {
         // Note that we cannot expect components data to be always smaller
         // than process data, as `getrusage` & co are not monotonic.
-        SilentAssert.leq(item[probe][k], 2 * snapshot.processData[probe][k],
-          `Sanity check (${testName}): ${k} of component is not impossibly larger than that of process`);
+        SilentAssert.leq(item[probe][k], 3 * snapshot.processData[probe][k],
+          `Sanity check (${name}): ${k} of component is not impossibly larger than that of process`);
       }
 
+      let isCorrectPid = (item.processId == pid && !item.isChildProcess)
+        || (item.processId != pid && item.isChildProcess);
+      SilentAssert.ok(isCorrectPid, `Pid check (${name}): the item comes from the right process`);
+
       let key = item.groupId;
       if (map.has(key)) {
         let old = map.get(key);
         Assert.ok(false, `Component ${key} has already been seen. Latest: ${item.title||item.addonId||item.name}, previous: ${old.title||old.addonId||old.name}`);
       }
       map.set(key, item);
     }
     for (let item of snapshot.componentsData) {
@@ -217,17 +227,17 @@ add_task(function* test() {
 
   info("Opening URL");
   newTab.linkedBrowser.loadURI(URL);
 
   if (Services.sysinfo.getPropertyAsAString("name") == "Windows_NT") {
     info("Deactivating sanity checks under Windows (bug 1151240)");
   } else {
     info("Setting up sanity checks");
-    monotinicity_tester(() => monitor.promiseSnapshot(), "parent process");
+    monotinicity_tester(() => monitor.promiseSnapshot().then(snapshot => ({snapshot, pid: PARENT_PID})), "parent process");
     monotinicity_tester(() => promiseContentResponseOrNull(browser, "compartments-test:getStatistics", null), "content process" );
   }
 
   let skipTotalUserTime = hasLowPrecision();
 
 
   while (true) {
     yield new Promise(resolve => setTimeout(resolve, 100));
@@ -237,17 +247,17 @@ add_task(function* test() {
     // repeatedly for the title to be changed, until this works.
     info("Setting titles");
     yield promiseContentResponse(browser, "compartments-test:setTitles", {
       parent: PARENT_TITLE,
       frames: FRAME_TITLE
     });
     info("Titles set");
 
-    let stats = (yield promiseContentResponse(browser, "compartments-test:getStatistics", null));
+    let {snapshot: stats} = (yield promiseContentResponse(browser, "compartments-test:getStatistics", null));
 
     let titles = [for(stat of stats.componentsData) stat.title];
 
     for (let stat of stats.componentsData) {
       info(`Compartment: ${stat.name} => ${stat.title} (${stat.isSystem?"system":"web"})`);
     }
 
     // While the webpage consists in three compartments, we should see only