Bug 674779 - Merging cpow measurements and own/system time measurements draft
authorDavid Rajchenbach-Teller <dteller@mozilla.com>
Tue, 17 Feb 2015 11:32:22 +0100
changeset 243070 7077823cbada87b6022b45fd720c16620acb0ce9
parent 243069 f7b0f8e0b98e2efc0afac41437ca05ccc1c384ab
child 243491 aef01ec09647d1fb319adc64f84d306e4399f0a3
push id701
push userdteller@mozilla.com
push dateTue, 17 Feb 2015 10:33:08 +0000
bugs674779
milestone38.0a1
Bug 674779 - Merging cpow measurements and own/system time measurements
js/ipc/CPOWTimer.cpp
js/ipc/moz.build
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscompartment.h
js/src/jspubtd.h
js/src/jsutil.h
js/src/vm/Interpreter.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
toolkit/components/aboutcompartments/content/aboutCompartments.js
toolkit/components/aboutcompartments/content/aboutCompartments.xhtml
toolkit/components/aboutcompartments/nsCompartmentInfo.cpp
toolkit/components/aboutcompartments/nsICompartmentInfo.idl
--- a/js/ipc/CPOWTimer.cpp
+++ b/js/ipc/CPOWTimer.cpp
@@ -14,14 +14,16 @@ CPOWTimer::~CPOWTimer()
     /* This is a best effort to find the compartment responsible for this CPOW call */
     nsIGlobalObject *global = mozilla::dom::GetIncumbentGlobal();
     if (!global)
         return;
     JSObject *obj = global->GetGlobalJSObject();
     if (!obj)
         return;
     JSCompartment *compartment = js::GetObjectCompartment(obj);
-    xpc::CompartmentPrivate *compartmentPrivate = xpc::CompartmentPrivate::Get(compartment);
-    if (!compartmentPrivate)
+    if (!compartment)
         return;
-    PRIntervalTime time = PR_IntervalNow() - startInterval;
-    compartmentPrivate->CPOWTime += time;
+    js::PerformanceData *performance = js::GetPerformanceData(compartment);
+    if (!performance)
+        return;
+    uint64_t time = PR_IntervalToMilliseconds(PR_IntervalNow() - startInterval);
+    performance->cpowTime += time;
 }
--- a/js/ipc/moz.build
+++ b/js/ipc/moz.build
@@ -30,12 +30,13 @@ EXPORTS.mozilla.jsipc = [
     'CpowHolder.h',
     'CrossProcessObjectWrappers.h',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/base',
     '/js/ipc',
     '/js/public',
+    '/js/src',
     '/js/xpconnect/src',
     '/js/xpconnect/wrappers',
 ]
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -163,23 +163,24 @@ JS_GetEmptyString(JSRuntime *rt)
 {
     MOZ_ASSERT(rt->hasContexts());
     return rt->emptyString;
 }
 
 JS_PUBLIC_API(bool)
 JS_GetCompartmentStats(JSRuntime *rt, CompartmentStatsVector &stats)
 {
-    for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         if (!stats.growBy(1))
             return false;
 
         CompartmentTimeStats *stat = &stats.back();
-        stat->time = c.get()->totalTime;
         stat->compartment = c.get();
+        stat->performance = stat->compartment->performance;
+        stat->isSystem = stat->compartment->isSystem;
         stat->addonId = c.get()->addonId;
         if (rt->compartmentNameCallback) {
             (*rt->compartmentNameCallback)(rt, stat->compartment,
                                            stat->compartmentName,
                                            MOZ_ARRAY_LENGTH(stat->compartmentName));
         } else {
             strcpy(stat->compartmentName, "<unknown>");
         }
@@ -5801,8 +5802,16 @@ JS::CaptureCurrentStack(JSContext *cx, J
     return true;
 }
 
 JS_PUBLIC_API(Zone *)
 JS::GetObjectZone(JSObject *obj)
 {
     return obj->zone();
 }
+
+JS_PUBLIC_API(PerformanceData*)
+js::GetPerformanceData(JSCompartment *cx)
+{
+    if (!cx)
+        return nullptr;
+    return &cx->performance;
+}
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1024,18 +1024,18 @@ JS_GetEmptyStringValue(JSContext *cx);
 
 extern JS_PUBLIC_API(JSString *)
 JS_GetEmptyString(JSRuntime *rt);
 
 struct CompartmentTimeStats {
     char compartmentName[1024];
     JSAddonId *addonId;
     JSCompartment *compartment;
-    uint64_t time;  // microseconds
-    uint64_t cpowTime; // microseconds
+    bool isSystem;
+    js::PerformanceData performance;
 };
 
 typedef js::Vector<CompartmentTimeStats, 0, js::SystemAllocPolicy> CompartmentStatsVector;
 
 extern JS_PUBLIC_API(bool)
 JS_GetCompartmentStats(JSRuntime *rt, CompartmentStatsVector &stats);
 
 extern JS_PUBLIC_API(bool)
@@ -5122,11 +5122,17 @@ ResetStopwatches(JSRuntime*);
 /**
  * Turn on/off stopwatch-based CPU monitoring.
  */
 extern JS_PUBLIC_API(void)
 SetStopwatchActive(JSRuntime*, bool);
 extern JS_PUBLIC_API(bool)
 IsStopwatchActive(JSRuntime*);
 
+/**
+ * Access the performance information stored in a compartment.
+ */
+extern JS_PUBLIC_API(PerformanceData*)
+GetPerformanceData(JSCompartment*);
+
 } /* namespace js */
 
 #endif /* jsapi_h */
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -165,47 +165,17 @@ struct JSCompartment
     friend struct JSContext;
     friend class js::ExclusiveContext;
     js::ReadBarrieredGlobalObject global_;
 
     unsigned                     enterCompartmentDepth;
     int64_t                      startInterval;
 
   public:
-    struct Performance {
-        // Jank indicator.
-        //
-        // missedFrames[i] == number of times
-        // execution of this compartment caused us to
-        // miss at least 2^i successive frames - we
-        // assume that a frame lasts 16ms.
-        uint64_t missedFrames[8]; // FIXME: Magic number
-
-        // Total amount of time spent executing code in
-        // this compartment, in milliseconds, since process
-        // launch.
-        uint64_t totalUserTime;
-        uint64_t totalSystemTime;
-        uint64_t ownUserTime;
-        uint64_t ownSystemTime;
-
-        // Total number of time code was executed in this
-        // compartment, in milliseconds, since process launch.
-        uint64_t visits;
-
-        Performance()
-            : missedFrames{0, 0, 0, 0, 0, 0, 0, 0}
-            , totalUserTime(0)
-            , totalSystemTime(0)
-            , ownUserTime(0)
-            , ownSystemTime(0)
-            , visits(0)
-        {}
-    };
-    Performance performance;
+    js::PerformanceData performance;
     void enter() {
         enterCompartmentDepth++;
     }
     void leave() {
         enterCompartmentDepth--;
     }
     bool hasBeenEntered() { return !!enterCompartmentDepth; }
 
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -474,12 +474,67 @@ struct PerThreadDataFriendFields
         // Tested by a JS_STATIC_ASSERT in |jsfriendapi.cpp|
         return reinterpret_cast<const PerThreadDataFriendFields *>(
             reinterpret_cast<const char*>(rt) + RuntimeMainThreadOffset);
     }
 
     template <typename T> friend class JS::Rooted;
 };
 
+// Container for performance data
+struct PerformanceData {
+    // Jank indicator.
+    //
+    // missedFrames[i] == number of times
+    // execution of this compartment caused us to
+    // miss at least 2^i successive frames - we
+    // assume that a frame lasts 16ms.
+    uint64_t missedFrames[8];
+
+    // Total amount of time spent executing code in
+    // this compartment, in milliseconds, since process
+    // launch.
+    uint64_t totalUserTime;
+    uint64_t totalSystemTime;
+    uint64_t ownUserTime;
+    uint64_t ownSystemTime;
+    uint64_t cpowTime;
+
+    // Total number of time code was executed in this
+    // compartment, since process launch.
+    uint64_t visits;
+
+    PerformanceData()
+        : missedFrames{0, 0, 0, 0, 0, 0, 0, 0}
+        , totalUserTime(0)
+        , totalSystemTime(0)
+        , ownUserTime(0)
+        , ownSystemTime(0)
+        , cpowTime(0)
+        , visits(0)
+    {}
+    PerformanceData(const PerformanceData& from)
+        : missedFrames{0, 0, 0, 0, 0, 0, 0, 0}
+        , totalUserTime(from.totalUserTime)
+        , totalSystemTime(from.totalSystemTime)
+        , ownUserTime(from.ownUserTime)
+        , ownSystemTime(from.ownSystemTime)
+        , cpowTime(from.cpowTime)
+        , visits(from.visits)
+    {
+        memcpy(missedFrames, from.missedFrames, sizeof(missedFrames));
+    }
+    PerformanceData& operator=(const PerformanceData& from)
+    {
+        memcpy(missedFrames, from.missedFrames, sizeof(missedFrames));
+        totalUserTime = from.totalUserTime;
+        totalSystemTime = from.totalSystemTime;
+        ownUserTime = from.ownUserTime;
+        ownSystemTime = from.ownSystemTime;
+        cpowTime = from.cpowTime;
+        visits = from.visits;
+        return *this;
+    }
+};
 
 } /* namespace js */
 
 #endif /* jspubtd_h */
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -373,9 +373,10 @@ typedef size_t jsbitmap;
 
 #if !defined(JS_SILENCE_UNUSED_VALUE_IN_EXPR)
 # define JS_SILENCE_UNUSED_VALUE_IN_EXPR(expr)                                \
     JS_BEGIN_MACRO                                                            \
         expr;                                                                 \
     JS_END_MACRO
 #endif
 
+
 #endif /* jsutil_h */
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -430,24 +430,25 @@ struct AutoStopwatch MOZ_FINAL
      * Previous owner is restored upon destruction.
      */
     explicit inline AutoStopwatch(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : parent_(nullptr)
         , context_(cx)
         , descendentsUserTime(0)
         , descendentsSystemTime(0)
     {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         JSRuntime *runtime = context_->runtime();
         if (!runtime->stopWatch.isActive) {
             return;
         }
 
 #if defined(XP_UNIX)
         int err = getrusage(RUSAGE_SELF, &timeStamp);
-        if (!err)
+        if (err)
             return;
 #elif defined(XP_WIN)
         FILETIME creationTime; // Ignored
         FILETIME exitTime; // Ignored
 
         BOOL success = GetProcessTimes(GetCurrentProcessID(),
                                        &creationTime, &exitTime,
                                        &kernelTimeStamp, &userTimeStamp);
@@ -483,25 +484,25 @@ struct AutoStopwatch MOZ_FINAL
         // Durations in museconds of the total user/system time spent
         // by the entire process between construction and destruction
         // of this object.
         uint64_t totalUserTime, totalSystemTime;
 
 #if defined(XP_UNIX)
         struct rusage stop;
         int err = getrusage(RUSAGE_SELF, &stop);
-        if (!err)
+        if (err)
             return;
 
         totalUserTime =
-            (stop.ru_utime.tv_sec - timeStamp.ru_utime.tv_sec) * 1000000
-            + (stop.ru_utime.tv_sec - timeStamp.ru_utime.tv_sec);
+            (stop.ru_utime.tv_usec - timeStamp.ru_utime.tv_usec) / 1000
+            + (stop.ru_utime.tv_sec - timeStamp.ru_utime.tv_sec) * 1000;
         totalSystemTime =
-            (stop.ru_stime.tv_sec - timeStamp.ru_stime.tv_sec) * 1000000
-            + (stop.ru_stime.tv_sec - timeStamp.ru_stime.tv_sec);
+            (stop.ru_stime.tv_usec - timeStamp.ru_stime.tv_usec) / 1000
+            + (stop.ru_stime.tv_sec - timeStamp.ru_stime.tv_sec) * 1000;
 #elif defined(XP_WIN)
         FILETIME creationTime;
         FILETIME exitTime;
         FILETIME kernelTime;
         FILETIME userTime;
         BOOL success = GetProcessTimes(GetCurrentProcessID(),
                                        &creationTime, &exitTime,
                                        &kernelTime, &userTime);
@@ -589,21 +590,16 @@ struct AutoStopwatch MOZ_FINAL
 
 bool
 js::RunScript(JSContext *cx, RunState &state)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     js::AutoStopwatch stopWatch(cx);
 
-#ifdef NIGHTLY_BUILD
-    if (AssertOnScriptEntryHook hook = cx->runtime()->assertOnScriptEntryHook_)
-        (*hook)(cx, state.script());
-#endif
-
     SPSEntryMarker marker(cx->runtime(), state.script());
 
     state.script()->ensureNonLazyCanonicalFunction(cx);
 
     if (jit::IsIonEnabled(cx)) {
         jit::MethodStatus status = jit::CanEnter(cx, state);
         if (status == jit::Method_Error)
             return false;
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -343,23 +343,16 @@ xpc::TraceXPCGlobal(JSTracer *trc, JSObj
     xpc::CompartmentPrivate* compartmentPrivate = xpc::CompartmentPrivate::Get(obj);
     if (compartmentPrivate && compartmentPrivate->scope)
         compartmentPrivate->scope->TraceInside(trc);
 }
 
 
 namespace xpc {
 
-uint64_t
-GetCompartmentCPOWMicroseconds(JSCompartment *compartment)
-{
-    xpc::CompartmentPrivate *compartmentPrivate = xpc::CompartmentPrivate::Get(compartment);
-    return compartmentPrivate ? PR_IntervalToMicroseconds(compartmentPrivate->CPOWTime) : 0;
-}
-
 JSObject*
 CreateGlobalObject(JSContext *cx, const JSClass *clasp, nsIPrincipal *principal,
                    JS::CompartmentOptions& aOptions)
 {
     MOZ_ASSERT(NS_IsMainThread(), "using a principal off the main thread?");
     MOZ_ASSERT(principal);
 
     MOZ_RELEASE_ASSERT(principal != nsContentUtils::GetNullSubjectPrincipal(),
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -621,17 +621,17 @@ public:
     void InitSingletonScopes();
     void DeleteSingletonScopes();
 
     PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
 
     void OnProcessNextEvent() {
         mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
         mSlowScriptSecondHalf = false;
-        js::ResetStopWatches(Get()->Runtime());
+        js::ResetStopwatches(Get()->Runtime());
     }
     void OnAfterProcessNextEvent() {
         mSlowScriptCheckpoint = mozilla::TimeStamp();
         mSlowScriptSecondHalf = false;
     }
 
     nsTArray<nsXPCWrappedJS*>& WrappedJSToReleaseArray() { return mWrappedJSToReleaseArray; }
 
@@ -3627,17 +3627,16 @@ public:
     };
 
     explicit CompartmentPrivate(JSCompartment *c)
         : wantXrays(false)
         , writeToGlobalPrototype(false)
         , skipWriteToGlobalPrototype(false)
         , universalXPConnectEnabled(false)
         , forcePermissiveCOWs(false)
-        , CPOWTime(0)
         , skipCOWCallableChecks(false)
         , scriptability(c)
         , scope(nullptr)
     {
         MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
         mozilla::PodArrayZero(wrapperDenialWarnings);
     }
 
@@ -3681,19 +3680,16 @@ public:
     // This is only ever set during mochitest runs when enablePrivilege is called.
     // It allows the SpecialPowers scope to waive the normal chrome security
     // wrappers and expose properties directly to content. This lets us avoid a
     // bunch of overhead and complexity in our SpecialPowers automation glue.
     //
     // Using it in production is inherently unsafe.
     bool forcePermissiveCOWs;
 
-    // A running count of how much time we've spent processing CPOWs.
-    PRIntervalTime               CPOWTime;
-
     // Disables the XPConnect security checks that deny access to callables and
     // accessor descriptors on COWs. Do not use this unless you are bholley.
     bool skipCOWCallableChecks;
 
     // Whether we've emitted a warning about a property that was filtered out
     // by a security wrapper. See XrayWrapper.cpp.
     bool wrapperDenialWarnings[WrapperDenialTypeCount];
 
--- a/toolkit/components/aboutcompartments/content/aboutCompartments.js
+++ b/toolkit/components/aboutcompartments/content/aboutCompartments.js
@@ -5,71 +5,163 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 
-function go() {
-    let compartmentInfo = Cc["@mozilla.org/compartment-info;1"]
-            .getService(Ci.nsICompartmentInfo);
-    let compartments = compartmentInfo.getCompartments();
-    let count = compartments.length;
-    let addons = {};
-    for (let i = 0; i < count; i++) {
-        let compartment = compartments.queryElementAt(i, Ci.nsICompartment);
-        if (addons[compartment.addonId]) {
-            addons[compartment.addonId].time += compartment.time;
-            addons[compartment.addonId].CPOWTime += compartment.CPOWTime;
-            addons[compartment.addonId].compartments.push(compartment);
-        } else {
-            addons[compartment.addonId] = {
-                time: compartment.time,
-                CPOWTime: compartment.CPOWTime,
-                compartments: [compartment]
-            };
-        }
+/**
+ * The various measures we extract from a nsICompartment.
+ */
+const MEASURES = [
+  {key: "totalUserTime", label: "Total user (ms)"},
+  {key: "totalSystemTime", label: "Total system (ms)"},
+  {key: "ownUserTime", label: "Own user (ms)"},
+  {key: "ownSystemTime", label: "Own system (ms)"},
+  {key: "cpowTime", label: "CPOW (ms)"},
+  {key: "visits", label: "Activations"},
+];
+
+/**
+ * All information on a single owner (add-on, page, built-ins).
+ *
+ * @param {string} id A unique identifier.
+ * @param {string} kind One of "<addon>", "<built-in>", "<page>".
+ * @param {string|null} A name for this owner. Not expected for add-ons.
+ */
+function Owner(id, kind, name) {
+  for (let {key} of MEASURES) {
+    this[key] = 0;
+  }
+  this.compartments = [];
+  this.id = id;
+  this.kind = kind;
+  this.name = name;
+}
+Owner.prototype = {
+  add: function(compartment) {
+    this.compartments.push(compartment);
+    for (let {key} of MEASURES) {
+      this[key] += compartment[key];
+    }
+  },
+  promiseName: function() {
+    if (this.kind != "<addon>") {
+      return Promise.resolve(this.name);
+    }
+    return new Promise(resolve =>
+      AddonManager.getAddonByID(this.id, a =>
+        resolve(a?a.name:null)
+      )
+    );
+  }
+};
+
+function getStatistics() {
+  let compartmentInfo = Cc["@mozilla.org/compartment-info;1"]
+          .getService(Ci.nsICompartmentInfo);
+  let compartments = compartmentInfo.getCompartments();
+  let count = compartments.length;
+  let data = {};
+  for (let i = 0; i < count; i++) {
+    let compartment = compartments.queryElementAt(i, Ci.nsICompartment);
+    let kind, id, name;
+    if (!compartment.isSystem) {
+      name = id = compartment.compartmentName;
+      kind = "<page>";
+    } else if (compartment.addonId == "<non-addon>") {
+      id = kind = name = "<built-in>";
+    } else {
+      name = id = compartment.addonId;
+      kind = "<addon>";
+    }
+    let key = kind + ":" + id;
+    let owner = data[key];
+    if (!owner) {
+      owner = data[key] = new Owner(id, kind, name);
     }
-    let dataDiv = document.getElementById("data");
-    for (let addon in addons) {
-        let el = document.createElement("tr");
-        let name = document.createElement("td");
-        let time = document.createElement("td");
-        let cpow = document.createElement("td");
-        name.className = "addon";
-        time.className = "time";
-        cpow.className = "cpow";
-        name.textContent = addon;
-        AddonManager.getAddonByID(addon, function(a) {
-            if (a) {
-                name.textContent = a.name;
-            }
-        });
-        time.textContent = addons[addon].time +"μs";
-        cpow.textContent = addons[addon].CPOWTime +"μs";
-        el.appendChild(time);
-        el.appendChild(cpow);
-        el.appendChild(name);
-        let div = document.createElement("tr");
-        for (let comp of addons[addon].compartments) {
-            let c = document.createElement("tr");
-            let name = document.createElement("td");
-            let time = document.createElement("td");
-            let cpow = document.createElement("td");
-            name.className = "addon";
-            time.className = "time";
-            cpow.className = "cpow";
-            name.textContent = comp.label;
-            time.textContent = comp.time +"μs";
-            cpow.textContent = comp.CPOWTime +"μs";
-            c.appendChild(time);
-            c.appendChild(cpow);
-            c.appendChild(name);
-            div.appendChild(c);
-            div.className = "details";
+    owner.add(compartment);
+  }
+  return [data[k] for (k of Object.keys(data))].sort((a, b) => a.totalUserTime <= b.totalUserTime);
+}
+
+function update() {
+  try {
+    console.log("Updating");
+
+    // Activate (or reactivate) monitoring
+    Cu.stopwatchMonitoring = true;
+
+    let dataElt = document.getElementById("data");
+    dataElt.innerHTML = "";
+
+    // Generate table headers
+    let headerElt = document.createElement("tr");
+    dataElt.appendChild(headerElt);
+    for (let column of [...MEASURES, {key:"compartments", name: "Compartments"}, {key: "name", name: ""}]) {
+      let el = document.createElement("td");
+      el.classList.add(column.key);
+      el.classList.add("header");
+      el.textContent = column.label;
+      headerElt.appendChild(el);
+    }
+
+    // Generate table contents
+    let data = getStatistics();
+    console.log("Data", data);
+    for (let item of data) {
+      // Make sure that we don't show compartments with
+      // no time spent.
+      let show = false;
+      for (let column of MEASURES) {
+        if (item[column.key]) {
+          show = true;
         }
-        el.addEventListener("click", function() { div.style.display = (div.style.display != "block" ? "block" : "none"); });
-        el.appendChild(div);
-        dataDiv.appendChild(el);
+      }
+      if (!show) {
+        continue;
+      }
+
+      let row = document.createElement("tr");
+      row.classList.add(item.kind);
+      dataElt.appendChild(row);
+
+      // Measures
+      for (let column of MEASURES) {
+        let el = document.createElement("td");
+        el.classList.add(column.key);
+        el.classList.add("contents");
+        el.textContent = item[column.key];
+        row.appendChild(el);
+      }
+
+      // Number of compartments
+      let el = document.createElement("td");
+      el.classList.add("contents");
+      el.classList.add("compartments");
+      el.textContent = item.compartments.length;
+      row.appendChild(el);
+
+      // Name
+      el = document.createElement("td");
+      el.classList.add("contents");
+      el.classList.add("name");
+      row.appendChild(el);
+      item.promiseName().then(name => {
+        name ? el.textContent = name : item.id;
+      });
     }
+  } catch (ex) {
+    console.error(ex);
+  }
 }
+
+function stop() {
+  Cu.stopwatchMonitoring = false;
+}
+
+function go() {
+  update();
+  window.setInterval(update, 5000);
+  window.addEventListener("beforeunload", stop);
+}
--- a/toolkit/components/aboutcompartments/content/aboutCompartments.xhtml
+++ b/toolkit/components/aboutcompartments/content/aboutCompartments.xhtml
@@ -24,20 +24,24 @@
       .header {
         font-weight: bold;
       }
       tr.details {
         font-weight: lighter;
         color: gray;
         display: none;
       }
+      tr.addons {
+        background-color: white;
+      }
+      tr.builtins {
+        background-color: rgb(1, 1, .5);
+      }
+      tr.pages {
+        background-color: rgb(.5, 1, 1);
+      }
     </style>
   </head>
   <body onload="go()">
     <table id="data">
-      <tr class="header">
-        <td class="time">time</td>
-        <td class="cpow">time in CPOWs</td>
-        <td class="addon">name</td>
-      </tr>
     </table>
   </body>
 </html>
--- a/toolkit/components/aboutcompartments/nsCompartmentInfo.cpp
+++ b/toolkit/components/aboutcompartments/nsCompartmentInfo.cpp
@@ -7,53 +7,97 @@
 #include "nsMemory.h"
 #include "nsLiteralString.h"
 #include "nsCRTGlue.h"
 #include "nsIJSRuntimeService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIMutableArray.h"
 #include "nsJSUtils.h"
 #include "xpcpublic.h"
+#include "jspubtd.h"
 
 class nsCompartment : public nsICompartment {
 public:
-  nsCompartment(nsAString& aCompartmentName, nsAString& aAddonId,
-                uint64_t aTime, uint64_t aCPOWTime)
-    : mCompartmentName(aCompartmentName), mAddonId(aAddonId), mTime(aTime), mCPOWTime(aCPOWTime) {}
+  nsCompartment(nsAString& aCompartmentName, nsAString& aAddonId, bool aIsSystem, js::PerformanceData aPerformanceData)
+    : mCompartmentName(aCompartmentName)
+    , mAddonId(aAddonId)
+    , mIsSystem(aIsSystem)
+    , mPerformanceData(aPerformanceData)
+  {}
 
   NS_DECL_ISUPPORTS
 
   /* readonly attribute wstring compartmentName; */
   NS_IMETHOD GetCompartmentName(nsAString& aCompartmentName) MOZ_OVERRIDE {
     aCompartmentName.Assign(mCompartmentName);
     return NS_OK;
   };
 
-  /* readonly attribute unsigned long time; */
-  NS_IMETHOD GetTime(uint64_t* aTime) MOZ_OVERRIDE {
-    *aTime = mTime;
-    return NS_OK;
-  }
   /* readonly attribute wstring addon id; */
   NS_IMETHOD GetAddonId(nsAString& aAddonId) MOZ_OVERRIDE {
     aAddonId.Assign(mAddonId);
     return NS_OK;
   };
 
-  /* readonly attribute unsigned long CPOW time; */
-  NS_IMETHOD GetCPOWTime(uint64_t* aCPOWTime) MOZ_OVERRIDE {
-    *aCPOWTime = mCPOWTime;
+  /* readonly attribute unsigned long long totalUserTime; */
+  NS_IMETHOD GetTotalUserTime(uint64_t *aTotalUserTime) {
+    *aTotalUserTime = mPerformanceData.totalUserTime;
+    return NS_OK;
+  };
+
+  /* readonly attribute unsigned long long totalSystemTime; */
+  NS_IMETHOD GetTotalSystemTime(uint64_t *aTotalSystemTime) {
+    *aTotalSystemTime = mPerformanceData.totalSystemTime;
+    return NS_OK;
+  };
+
+  /* readonly attribute unsigned long long ownUserTime; */
+  NS_IMETHOD GetOwnUserTime(uint64_t *aOwnUserTime) {
+    *aOwnUserTime = mPerformanceData.ownUserTime;
+    return NS_OK;
+  };
+
+  /* readonly attribute unsigned long long ownSystemTime; */
+  NS_IMETHOD GetOwnSystemTime(uint64_t *aOwnSystemTime) {
+    *aOwnSystemTime = mPerformanceData.ownSystemTime;
+    return NS_OK;
+  };
+
+  /* readonly attribute unsigned long long cpowTime; */
+  NS_IMETHOD GetCpowTime(uint64_t *aCpowTime) {
+    *aCpowTime = mPerformanceData.cpowTime;
+    return NS_OK;
+  };
+
+  /* readonly attribute unsigned long long visits; */
+  NS_IMETHOD GetVisits(uint64_t *aVisits) {
+    *aVisits = mPerformanceData.visits;
+    return NS_OK;
+  };
+
+  /* unsigned long long getMissedFrames (in unsigned long i); */
+  NS_IMETHOD GetMissedFrames(uint32_t i, uint64_t *_retval) {
+    if (i >= mozilla::ArrayLength(mPerformanceData.missedFrames)) {
+      return NS_ERROR_INVALID_ARG;
+    }
+    *_retval = mPerformanceData.missedFrames[i];
+    return NS_OK;
+  };
+
+  NS_IMETHOD GetIsSystem(bool *_retval) {
+    *_retval = mIsSystem;
     return NS_OK;
   }
 
 private:
   nsString mCompartmentName;
   nsString mAddonId;
-  uint64_t mTime;
-  uint64_t mCPOWTime;
+  bool mIsSystem;
+  js::PerformanceData mPerformanceData;
+
   virtual ~nsCompartment() {}
 };
 
 NS_IMPL_ISUPPORTS(nsCompartment, nsICompartment)
 NS_IMPL_ISUPPORTS(nsCompartmentInfo, nsICompartmentInfo)
 
 nsCompartmentInfo::nsCompartmentInfo()
 {
@@ -72,23 +116,24 @@ nsCompartmentInfo::GetCompartments(nsIAr
   svc->GetRuntime(&rt);
   nsCOMPtr<nsIMutableArray> compartments = do_CreateInstance(NS_ARRAY_CONTRACTID);
   CompartmentStatsVector stats;
   if (!JS_GetCompartmentStats(rt, stats))
     return NS_ERROR_OUT_OF_MEMORY;
 
   size_t num = stats.length();
   for (size_t pos = 0; pos < num; pos++) {
+    CompartmentTimeStats *c = &stats[pos];
     nsString addonId;
-    if (stats[pos].addonId) {
-      AssignJSFlatString(addonId, (JSFlatString*)stats[pos].addonId);
+    if (c->addonId) {
+      AssignJSFlatString(addonId, (JSFlatString*)c->addonId);
     } else {
       addonId.AssignLiteral("<non-addon>");
     }
 
-    uint32_t cpowTime = xpc::GetCompartmentCPOWMicroseconds(stats[pos].compartment);
-    nsCString compartmentName(stats[pos].compartmentName);
+    nsCString compartmentName(c->compartmentName);
     NS_ConvertUTF8toUTF16 name(compartmentName);
-    compartments->AppendElement(new nsCompartment(name, addonId, stats[pos].time, cpowTime), false);
+
+    compartments->AppendElement(new nsCompartment(name, addonId, c->isSystem, c->performance), false);
   }
   compartments.forget(aCompartments);
   return NS_OK;
 }
--- a/toolkit/components/aboutcompartments/nsICompartmentInfo.idl
+++ b/toolkit/components/aboutcompartments/nsICompartmentInfo.idl
@@ -2,26 +2,62 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIArray.idl"
 
-[scriptable, uuid(13dd4c09-ff11-4943-8dc2-d96eb69c963b)]
+[scriptable, uuid(3a23d383-052e-4199-8914-74c037fe359e)]
 interface nsICompartment : nsISupports {
   /* name of compartment */
   readonly attribute AString compartmentName;
-  /* time spent executing code in this compartment in microseconds */
-  readonly attribute unsigned long long time;
+
   /* the id of the addon associated with this compartment, or null */
   readonly attribute AString addonId;
-  /* time spent processing CPOWs in microseconds */
-  readonly attribute unsigned long long CPOWTime;
+
+  /*
+    Total amount of time spent executing code in this compartment, in
+    milliseconds.
+
+    Note that these durations are only computed while
+    Components.utils.stopwatchMonitoring == true and that they are never
+    reset to 0.
+  */
+  readonly attribute unsigned long long totalUserTime;
+  readonly attribute unsigned long long totalSystemTime;
+  readonly attribute unsigned long long ownUserTime;
+  readonly attribute unsigned long long ownSystemTime;
+  readonly attribute unsigned long long cpowTime;
+
+  /* Number of times we have executed code in this compartment.
+     Updated only while Components.utils.stopwatchMonitoring == true,
+     never reset to 0.
+  */
+  readonly attribute unsigned long long visits;
+
+  /* `true` if this is a system compartment (either an add-on or a built-in).*/
+  readonly attribute bool isSystem;
+
+  /**
+   * The number of times execution of code in this compartment has apparently
+   * caused frame drops.
+   *
+   * getMissedFrames(0): number of times we have dropped at least 1 frame
+   * getMissedFrames(1): number of times we have dropped at least 2 successive frames
+   * ...
+   * getMissedFrames(i): number of times we have dropped at least 2^i successive frames
+   *
+   * Updated only while Components.utils.stopwatchMonitoring == true,
+   * never reset to 0.
+   */
+  unsigned long long getMissedFrames(in unsigned long i);
+  /* Maximal value of the argument to pass to `getMissedFrames`.*/
+  const unsigned long MISSED_FRAMES_RANGE = 8;
 };
 
 [scriptable, builtinclass, uuid(5795113a-39a1-4087-ba09-98b7d07d025a)]
 interface nsICompartmentInfo : nsISupports {
   nsIArray getCompartments();
 };
 
 %{C++