Backed out changesets da71c4aefb3a and 98603c32c9a3 (bug 1149486) for browser_compartments.js permafail.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 29 May 2015 12:59:23 -0400
changeset 278653 8b2ad894ed27de7704666ea6ba18a27e8e7e5e74
parent 278652 ca5480ad6ffbea1a7882a7b01ca2a36ece45c379
child 278654 f5dfe5774099507d635a4fa619ac1a7bbe025501
push id897
push userjlund@mozilla.com
push dateMon, 14 Sep 2015 18:56:12 +0000
treeherdermozilla-release@9411e2d2b214 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1149486
milestone41.0a1
backs outda71c4aefb3a163d38e1851940c612ccf88d3d2a
98603c32c9a30ff89ff88f825821fcd85059dfe7
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
Backed out changesets da71c4aefb3a and 98603c32c9a3 (bug 1149486) for browser_compartments.js permafail. CLOSED TREE
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscompartment.cpp
js/src/vm/Interpreter.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcpublic.h
toolkit/components/aboutperformance/content/aboutPerformance.js
toolkit/components/aboutperformance/tests/browser/browser.ini
toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js
toolkit/components/aboutperformance/tests/browser/browser_compartments.html
toolkit/components/aboutperformance/tests/browser/browser_compartments_frame.html
toolkit/components/aboutperformance/tests/browser/browser_compartments_script.js
toolkit/components/perfmonitoring/PerformanceStats.jsm
toolkit/components/perfmonitoring/moz.build
toolkit/components/perfmonitoring/nsIPerformanceStats.idl
toolkit/components/perfmonitoring/nsPerformanceStats.cpp
toolkit/components/perfmonitoring/tests/browser/browser.ini
toolkit/components/perfmonitoring/tests/browser/browser_compartments.html
toolkit/components/perfmonitoring/tests/browser/browser_compartments.js
toolkit/components/perfmonitoring/tests/browser/browser_compartments_frame.html
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -269,64 +269,82 @@ JS_GetEmptyString(JSRuntime* rt)
 {
     MOZ_ASSERT(rt->hasContexts());
     return rt->emptyString;
 }
 
 namespace js {
 
 JS_PUBLIC_API(bool)
-IterPerformanceStats(JSContext* cx,
-                     PerformanceStatsWalker walker,
-                     PerformanceData* processStats,
-                     void* closure)
+GetPerformanceStats(JSRuntime* rt,
+                    PerformanceStatsVector& stats,
+                    PerformanceStats& processStats)
 {
     // As a PerformanceGroup is typically associated to several
     // compartments, use a HashSet to make sure that we only report
     // each PerformanceGroup once.
     typedef HashSet<js::PerformanceGroup*,
                     js::DefaultHasher<js::PerformanceGroup*>,
                     js::SystemAllocPolicy> Set;
     Set set;
     if (!set.init(100)) {
         return false;
     }
 
-    JSRuntime* rt = JS_GetRuntime(cx);
     for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
         JSCompartment* compartment = c.get();
         if (!compartment->performanceMonitoring.isLinked()) {
             // Don't report compartments that do not even have a PerformanceGroup.
             continue;
         }
-
-        js::AutoCompartment autoCompartment(cx, compartment);
-        PerformanceGroup* group = compartment->performanceMonitoring.getGroup(cx);
+        PerformanceGroup* group = compartment->performanceMonitoring.getGroup();
 
         if (group->data.ticks == 0) {
             // Don't report compartments that have never been used.
             continue;
         }
 
         Set::AddPtr ptr = set.lookupForAdd(group);
         if (ptr) {
             // Don't report the same group twice.
             continue;
         }
 
-        if (!(*walker)(cx, group->data, closure)) {
-            // Issue in callback
+        if (!stats.growBy(1)) {
+            // Memory issue
             return false;
         }
+        PerformanceStats* stat = &stats.back();
+        stat->isSystem = compartment->isSystem();
+        if (compartment->addonId)
+            stat->addonId = compartment->addonId;
+
+        if (compartment->addonId || !compartment->isSystem()) {
+            if (rt->compartmentNameCallback) {
+                (*rt->compartmentNameCallback)(rt, compartment,
+                                               stat->name,
+                                               mozilla::ArrayLength(stat->name));
+            } else {
+                strcpy(stat->name, "<unknown>");
+            }
+        } else {
+            strcpy(stat->name, "<platform>");
+        }
+        stat->performance = group->data;
         if (!set.add(ptr, group)) {
             // Memory issue
             return false;
         }
     }
-    *processStats = rt->stopwatch.performance;
+
+    strcpy(processStats.name, "<process>");
+    processStats.addonId = nullptr;
+    processStats.isSystem = true;
+    processStats.performance = rt->stopwatch.performance;
+
     return true;
 }
 
 void
 AssertHeapIsIdle(JSRuntime* rt)
 {
     MOZ_ASSERT(!rt->isHeapBusy());
 }
@@ -906,17 +924,16 @@ JSAutoCompartment::JSAutoCompartment(JSC
   : cx_(cx),
     oldCompartment_(cx->compartment())
 {
     AssertHeapIsIdleOrIterating(cx_);
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     cx_->enterCompartment(target->compartment());
 }
 
-
 JSAutoCompartment::~JSAutoCompartment()
 {
     cx_->leaveCompartment(oldCompartment_);
 }
 
 JSAutoNullableCompartment::JSAutoNullableCompartment(JSContext* cx,
                                                      JSObject* targetOrNull
                                                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -715,28 +715,16 @@ typedef void
 
 typedef void
 (* JSZoneCallback)(JS::Zone* zone);
 
 typedef void
 (* JSCompartmentNameCallback)(JSRuntime* rt, JSCompartment* compartment,
                               char* buf, size_t bufsize);
 
-/**
- * Callback used to ask the embedding to determine in which
- * Performance Group the current execution belongs. Typically, this is
- * used to regroup JSCompartments from several iframes from the same
- * page or from several compartments of the same addon into a single
- * Performance Group.
- *
- * Returns an opaque key.
- */
-typedef void*
-(* JSCurrentPerfGroupCallback)(JSContext*);
-
 /************************************************************************/
 
 static MOZ_ALWAYS_INLINE jsval
 JS_NumberValue(double d)
 {
     int32_t i;
     d = JS::CanonicalizeNaN(d);
     if (mozilla::NumberIsInt32(d, &i))
@@ -5453,20 +5441,19 @@ struct PerformanceGroup {
     void releaseStopwatch(uint64_t iteration, const AutoStopwatch* stopwatch) {
         if (iteration_ != iteration)
             return;
 
         MOZ_ASSERT(stopwatch == stopwatch_ || stopwatch_ == nullptr);
         stopwatch_ = nullptr;
     }
 
-    explicit PerformanceGroup(void* key)
+    PerformanceGroup()
       : stopwatch_(nullptr)
       , iteration_(0)
-      , key_(key)
       , refCount_(0)
     { }
     ~PerformanceGroup()
     {
         MOZ_ASSERT(refCount_ == 0);
     }
   private:
     PerformanceGroup& operator=(const PerformanceGroup&) = delete;
@@ -5475,70 +5462,69 @@ struct PerformanceGroup {
     // The stopwatch currently monitoring the group,
     // or `nullptr` if none. Used ony for comparison.
     const AutoStopwatch* stopwatch_;
 
     // The current iteration of the event loop. If necessary,
     // may safely overflow.
     uint64_t iteration_;
 
-    // The hash key for this PerformanceGroup.
-    void* const key_;
-
     // Increment/decrement the refcounter, return the updated value.
     uint64_t incRefCount() {
         MOZ_ASSERT(refCount_ + 1 > 0);
         return ++refCount_;
     }
     uint64_t decRefCount() {
         MOZ_ASSERT(refCount_ > 0);
         return --refCount_;
     }
     friend struct PerformanceGroupHolder;
 
-private:
+  private:
     // A reference counter. Maintained by PerformanceGroupHolder.
     uint64_t refCount_;
 };
 
 //
 // Indirection towards a PerformanceGroup.
 // This structure handles reference counting for instances of PerformanceGroup.
 //
 struct PerformanceGroupHolder {
     // Get the group.
     // On first call, this causes a single Hashtable lookup.
     // Successive calls do not require further lookups.
-    js::PerformanceGroup* getGroup(JSContext*);
+    js::PerformanceGroup* getGroup();
 
     // `true` if the this holder is currently associated to a
     // PerformanceGroup, `false` otherwise. Use this method to avoid
     // instantiating a PerformanceGroup if you only need to get
     // available performance data.
     inline bool isLinked() const {
         return group_ != nullptr;
     }
 
     // Remove the link to the PerformanceGroup. This method is designed
     // as an invalidation mechanism if the JSCompartment changes nature
     // (new values of `isSystem()`, `principals()` or `addonId`).
     void unlink();
 
-    explicit PerformanceGroupHolder(JSRuntime* runtime)
+    PerformanceGroupHolder(JSRuntime* runtime, JSCompartment* compartment)
       : runtime_(runtime)
+      , compartment_(compartment)
       , group_(nullptr)
     {   }
     ~PerformanceGroupHolder();
 private:
     // Return the key representing this PerformanceGroup in
     // Runtime::Stopwatch.
     // Do not deallocate the key.
-    void* getHashKey(JSContext* cx);
-
-    JSRuntime *runtime_;
+    void* getHashKey();
+
+    JSRuntime* runtime_;
+    JSCompartment* compartment_;
 
     // The PerformanceGroup held by this object.
     // Initially set to `nullptr` until the first cal to `getGroup`.
     // May be reset to `nullptr` by a call to `unlink`.
     js::PerformanceGroup* group_;
 };
 
 /**
@@ -5561,35 +5547,61 @@ extern JS_PUBLIC_API(bool)
 IsStopwatchActive(JSRuntime*);
 
 /**
  * Access the performance information stored in a compartment.
  */
 extern JS_PUBLIC_API(PerformanceData*)
 GetPerformanceData(JSRuntime*);
 
-typedef bool
-(PerformanceStatsWalker)(JSContext* cx, const PerformanceData& stats, void* closure);
-
 /**
+ * Performance statistics for a performance group (a process, an
+ * add-on, a webpage, the built-ins or a special compartment).
+ */
+struct PerformanceStats {
+    /**
+     * If this group represents an add-on, the ID of the addon,
+     * otherwise `nullptr`.
+     */
+    JSAddonId* addonId;
+
+    /**
+     * If this group represents a webpage, the process itself or a special
+     * compartment, a human-readable name. Unspecified for add-ons.
+     */
+    char name[1024];
+
+    /**
+     * `true` if the group represents in system compartments, `false`
+     * otherwise. A group may never contain both system and non-system
+     * compartments.
+     */
+    bool isSystem;
+
+    /**
+     * Performance information.
+     */
+    js::PerformanceData performance;
+
+    PerformanceStats()
+      : addonId(nullptr)
+      , isSystem(false)
+    {
+        name[0] = '\0';
+    }
+};
+
+typedef js::Vector<PerformanceStats, 0, js::SystemAllocPolicy> PerformanceStatsVector;
+
+    /**
  * Extract the performance statistics.
  *
- * Note that before calling `walker`, we enter the corresponding context.
+ * After a successful call, `stats` holds the `PerformanceStats` for
+ * all performance groups, and `global` holds a `PerformanceStats`
+ * representing the entire process.
  */
 extern JS_PUBLIC_API(bool)
-IterPerformanceStats(JSContext* cx, PerformanceStatsWalker* walker, js::PerformanceData* process, void* closure);
+GetPerformanceStats(JSRuntime* rt, js::PerformanceStatsVector& stats, js::PerformanceStats& global);
 
 } /* namespace js */
 
-/**
- * Callback used to ask the embedding to determine in which
- * Performance Group a compartment belongs. Typically, this is used to
- * regroup JSCompartments from several iframes from the same page or
- * from several compartments of the same addon into a single
- * Performance Group.
- *
- * Returns an opaque key.
- */
-extern JS_PUBLIC_API(void)
-JS_SetCurrentPerfGroupCallback(JSRuntime *rt, JSCurrentPerfGroupCallback cb);
-
 
 #endif /* jsapi_h */
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -48,17 +48,17 @@ JSCompartment::JSCompartment(Zone* zone,
     warnedAboutNoSuchMethod(false),
     warnedAboutFlagsArgument(false),
     addonId(options.addonIdOrNull()),
 #ifdef DEBUG
     firedOnNewGlobalObject(false),
 #endif
     global_(nullptr),
     enterCompartmentDepth(0),
-    performanceMonitoring(runtime_),
+    performanceMonitoring(runtime_, this),
     data(nullptr),
     objectMetadataCallback(nullptr),
     lastAnimationTime(0),
     regExps(runtime_),
     globalWriteBarriered(false),
     neuteredTypedObjects(0),
     propertyTree(thisForCtor()),
     selfHostingScriptSource(nullptr),
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -404,114 +404,111 @@ namespace js {
 struct AutoStopwatch final
 {
     // If the stopwatch is active, constructing an instance of
     // AutoStopwatch causes it to become the current owner of the
     // stopwatch.
     //
     // Previous owner is restored upon destruction.
     explicit inline AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : cx_(cx)
+      : compartment_(nullptr)
+      , runtime_(nullptr)
       , iteration_(0)
       , isActive_(false)
       , isTop_(false)
       , userTimeStart_(0)
       , systemTimeStart_(0)
       , CPOWTimeStart_(0)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-
-        JSRuntime* runtime = JS_GetRuntime(cx_);
-        if (!runtime->stopwatch.isActive())
+        runtime_ = cx->runtime();
+        if (!runtime_->stopwatch.isActive())
             return;
-
-        JSCompartment* compartment = cx_->compartment();
-        if (compartment->scheduledForDestruction)
+        compartment_ = cx->compartment();
+        MOZ_ASSERT(compartment_);
+        if (compartment_->scheduledForDestruction)
             return;
-        iteration_ = runtime->stopwatch.iteration;
-
-        PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx);
+        iteration_ = runtime_->stopwatch.iteration;
+
+        PerformanceGroup* group = compartment_->performanceMonitoring.getGroup();
         MOZ_ASSERT(group);
 
         if (group->hasStopwatch(iteration_)) {
             // Someone is already monitoring this group during this
             // tick, no need for further monitoring.
             return;
         }
 
         // Start the stopwatch.
-        if (!this->getTimes(runtime, &userTimeStart_, &systemTimeStart_))
+        if (!this->getTimes(&userTimeStart_, &systemTimeStart_))
             return;
         isActive_ = true;
-        CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime;
+        CPOWTimeStart_ = runtime_->stopwatch.performance.totalCPOWTime;
 
         // We are now in charge of monitoring this group for the tick,
         // until destruction of `this` or until we enter a nested event
         // loop and `iteration_` is incremented.
         group->acquireStopwatch(iteration_, this);
 
-        if (runtime->stopwatch.isEmpty) {
+        if (runtime_->stopwatch.isEmpty) {
             // This is the topmost stopwatch on the stack.
             // It will be in charge of updating the per-process
             // performance data.
-            runtime->stopwatch.isEmpty = false;
-            runtime->stopwatch.performance.ticks++;
+            runtime_->stopwatch.isEmpty = false;
+            runtime_->stopwatch.performance.ticks++;
             isTop_ = true;
         }
     }
     inline ~AutoStopwatch() {
         if (!isActive_) {
             // We are not in charge of monitoring anything.
             return;
         }
 
-        JSRuntime* runtime = JS_GetRuntime(cx_);
-        JSCompartment* compartment = cx_->compartment();
-
-        MOZ_ASSERT(!compartment->scheduledForDestruction);
-
-        if (!runtime->stopwatch.isActive()) {
+        MOZ_ASSERT(!compartment_->scheduledForDestruction);
+
+        if (!runtime_->stopwatch.isActive()) {
             // Monitoring has been stopped while we were
             // executing the code. Drop everything.
             return;
         }
 
-        if (iteration_ != runtime->stopwatch.iteration) {
+        if (iteration_ != runtime_->stopwatch.iteration) {
             // We have entered a nested event loop at some point.
             // Any information we may have is obsolete.
             return;
         }
 
-        PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx_);
+        PerformanceGroup* group = compartment_->performanceMonitoring.getGroup();
         MOZ_ASSERT(group);
 
         // Compute time spent.
         group->releaseStopwatch(iteration_, this);
         uint64_t userTimeEnd, systemTimeEnd;
-        if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd))
+        if (!this->getTimes(&userTimeEnd, &systemTimeEnd))
             return;
 
         uint64_t userTimeDelta = userTimeEnd - userTimeStart_;
         uint64_t systemTimeDelta = systemTimeEnd - systemTimeStart_;
-        uint64_t CPOWTimeDelta = runtime->stopwatch.performance.totalCPOWTime - CPOWTimeStart_;
+        uint64_t CPOWTimeDelta = runtime_->stopwatch.performance.totalCPOWTime - CPOWTimeStart_;
         group->data.totalUserTime += userTimeDelta;
         group->data.totalSystemTime += systemTimeDelta;
         group->data.totalCPOWTime += CPOWTimeDelta;
 
         uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta;
         updateDurations(totalTimeDelta, group->data.durations);
         group->data.ticks++;
 
         if (isTop_) {
             // This is the topmost stopwatch on the stack.
             // Record the timing information.
-            runtime->stopwatch.performance.totalUserTime = userTimeEnd;
-            runtime->stopwatch.performance.totalSystemTime = systemTimeEnd;
-            updateDurations(totalTimeDelta, runtime->stopwatch.performance.durations);
-            runtime->stopwatch.isEmpty = true;
+            runtime_->stopwatch.performance.totalUserTime = userTimeEnd;
+            runtime_->stopwatch.performance.totalSystemTime = systemTimeEnd;
+            updateDurations(totalTimeDelta, runtime_->stopwatch.performance.durations);
+            runtime_->stopwatch.isEmpty = true;
         }
     }
 
  private:
 
     // Update an array containing the number of times we have missed
     // at least 2^0 successive ms, 2^1 successive ms, ...
     // 2^i successive ms.
@@ -525,17 +522,17 @@ struct AutoStopwatch final
              ++i, duration *= 2) {
             array[i]++;
         }
     }
 
     // Get the OS-reported time spent in userland/systemland, in
     // microseconds. On most platforms, this data is per-thread,
     // but on some platforms we need to fall back to per-process.
-    bool getTimes(JSRuntime* runtime, uint64_t* userTime, uint64_t* systemTime) const {
+    bool getTimes(uint64_t* userTime, uint64_t* systemTime) const {
         MOZ_ASSERT(userTime);
         MOZ_ASSERT(systemTime);
 
 #if defined(XP_MACOSX)
         // On MacOS X, to get we per-thread data, we need to
         // reach into the kernel.
 
         mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
@@ -589,32 +586,36 @@ struct AutoStopwatch final
         if (!success)
             return false;
 
         ULARGE_INTEGER kernelTimeInt;
         ULARGE_INTEGER userTimeInt;
         kernelTimeInt.LowPart = kernelFileTime.dwLowDateTime;
         kernelTimeInt.HighPart = kernelFileTime.dwHighDateTime;
         // Convert 100 ns to 1 us, make sure that the result is monotonic
-        *systemTime = runtime->stopwatch.systemTimeFix.monotonize(kernelTimeInt.QuadPart / 10);
+        *systemTime = runtime_-> stopwatch.systemTimeFix.monotonize(kernelTimeInt.QuadPart / 10);
 
         userTimeInt.LowPart = userFileTime.dwLowDateTime;
         userTimeInt.HighPart = userFileTime.dwHighDateTime;
         // Convert 100 ns to 1 us, make sure that the result is monotonic
-        *userTime = runtime->stopwatch.userTimeFix.monotonize(userTimeInt.QuadPart / 10);
+        *userTime = runtime_-> stopwatch.userTimeFix.monotonize(userTimeInt.QuadPart / 10);
 
 #endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN)
 
         return true;
     }
 
   private:
-    // The context with which this object was initialized.
+    // The compartment with which this object was initialized.
     // Non-null.
-    JSContext* const cx_;
+    JSCompartment* compartment_;
+
+    // The runtime with which this object was initialized.
+    // Non-null.
+    JSRuntime* runtime_;
 
     // An indication of the number of times we have entered the event
     // loop.  Used only for comparison.
     uint64_t iteration_;
 
     // `true` if this object is currently used to monitor performance,
     // `false` otherwise, i.e. if the stopwatch mechanism is off or if
     // another stopwatch is already in charge of monitoring for the
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -893,24 +893,25 @@ js::IsStopwatchActive(JSRuntime* rt)
 }
 
 js::PerformanceGroupHolder::~PerformanceGroupHolder()
 {
     unlink();
 }
 
 void*
-js::PerformanceGroupHolder::getHashKey(JSContext* cx)
+js::PerformanceGroupHolder::getHashKey()
 {
-    if (runtime_->stopwatch.currentPerfGroupCallback) {
-        return (*runtime_->stopwatch.currentPerfGroupCallback)(cx);
-    }
-
-    // As a fallback, put everything in the same PerformanceGroup.
-    return nullptr;
+    return compartment_->isSystem() ?
+        (void*)compartment_->addonId :
+        (void*)JS_GetCompartmentPrincipals(compartment_);
+    // This key may be `nullptr` if we have `isSystem() == true`
+    // and `compartment_->addonId`. This is absolutely correct,
+    // and this represents the `PerformanceGroup` used to track
+    // the performance of the the platform compartments.
 }
 
 void
 js::PerformanceGroupHolder::unlink()
 {
     if (!group_) {
         // The group has never been instantiated.
         return;
@@ -921,47 +922,41 @@ js::PerformanceGroupHolder::unlink()
 
     if (group->decRefCount() > 0) {
         // The group has at least another owner.
         return;
     }
 
 
     JSRuntime::Stopwatch::Groups::Ptr ptr =
-        runtime_->stopwatch.groups_.lookup(group->key_);
+        runtime_->stopwatch.groups_.lookup(getHashKey());
     MOZ_ASSERT(ptr);
     runtime_->stopwatch.groups_.remove(ptr);
     js_delete(group);
 }
 
 PerformanceGroup*
-js::PerformanceGroupHolder::getGroup(JSContext* cx)
+js::PerformanceGroupHolder::getGroup()
 {
     if (group_)
         return group_;
 
-    void* key = getHashKey(cx);
+    void* key = getHashKey();
     JSRuntime::Stopwatch::Groups::AddPtr ptr =
         runtime_->stopwatch.groups_.lookupForAdd(key);
     if (ptr) {
         group_ = ptr->value();
         MOZ_ASSERT(group_);
     } else {
-        group_ = runtime_->new_<PerformanceGroup>(key);
+        group_ = runtime_->new_<PerformanceGroup>();
         runtime_->stopwatch.groups_.add(ptr, key, group_);
     }
 
     group_->incRefCount();
 
     return group_;
 }
 
 PerformanceData*
 js::GetPerformanceData(JSRuntime* rt)
 {
     return &rt->stopwatch.performance;
 }
-
-void
-JS_SetCurrentPerfGroupCallback(JSRuntime *rt, JSCurrentPerfGroupCallback cb)
-{
-    rt->stopwatch.currentPerfGroupCallback = cb;
-}
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1487,32 +1487,19 @@ struct JSRuntime : public JS::shadow::Ru
          */
         bool isEmpty;
 
         /**
          * Performance data on the entire runtime.
          */
         js::PerformanceData performance;
 
-        /**
-         * Callback used to ask the embedding to determine in which
-         * Performance Group the current execution belongs. Typically, this is
-         * used to regroup JSCompartments from several iframes from the same
-         * page or from several compartments of the same addon into a single
-         * Performance Group.
-         *
-         * May be `nullptr`, in which case we put all the JSCompartments
-         * in the same PerformanceGroup.
-         */
-        JSCurrentPerfGroupCallback currentPerfGroupCallback;
-
         Stopwatch()
           : iteration(0)
           , isEmpty(true)
-          , currentPerfGroupCallback(nullptr)
           , isActive_(false)
         { }
 
         /**
          * Reset the stopwatch.
          *
          * This method is meant to be called whenever we start processing
          * an event, to ensure that stop any ongoing measurement that would
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1774,30 +1774,16 @@ GetCompartmentName(JSCompartment* c, nsC
         // (such as about:memory) have to undo this change.
         if (replaceSlashes)
             name.ReplaceChar('/', '\\');
     } else {
         name.AssignLiteral("null-principal");
     }
 }
 
-extern void
-xpc::GetCurrentCompartmentName(JSContext* cx, nsCString& name)
-{
-    RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
-    if (!global) {
-        name.AssignLiteral("no global");
-        return;
-    }
-
-    JSCompartment* compartment = GetObjectCompartment(global);
-    int anonymizeID = 0;
-    GetCompartmentName(compartment, name, &anonymizeID, false);
-}
-
 static int64_t
 JSMainRuntimeGCHeapDistinguishedAmount()
 {
     JSRuntime* rt = nsXPConnect::GetRuntimeInstance()->Runtime();
     return int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
            js::gc::ChunkSize;
 }
 
@@ -3311,57 +3297,16 @@ class XPCJSSourceHook: public js::Source
     }
 };
 
 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
     xpc::WrapperFactory::Rewrap,
     xpc::WrapperFactory::PrepareForWrapping
 };
 
-/**
- * Group JSCompartments into PerformanceGroups.
- *
- * - All JSCompartments from the same add-on belong to the same
- *   PerformanceGroup.
- * - All JSCompartments from the same same webpage (including
- *   frames) belong to the same PerformanceGroup.
- * - All other JSCompartments (normally, system add-ons)
- *   belong to to a big uncategorized PerformanceGroup.
- */
-static void*
-GetCurrentPerfGroupCallback(JSContext* cx) {
-    RootedObject global(cx, CurrentGlobalOrNull(cx));
-    if (!global) {
-        // This can happen for the atom compartments, which is system
-        // code.
-        return nullptr;
-    }
-
-    JSAddonId* addonId = AddonIdOfObject(global);
-    if (addonId) {
-        // If this is an add-on, use the id as key.
-        return addonId;
-    }
-
-    // If the compartment belongs to a webpage, use the address of the
-    // topmost scriptable window, hence regrouping all frames of a
-    // window.
-    nsRefPtr<nsGlobalWindow> win = WindowOrNull(global);
-    if (win) {
-        nsCOMPtr<nsIDOMWindow> top;
-        nsresult rv = win->GetScriptableTop(getter_AddRefs(top));
-        NS_ENSURE_SUCCESS(rv, nullptr);
-
-        return top.get();
-    }
-
-    // Otherwise, this is platform code, use `nullptr` as key.
-    return nullptr;
-}
-
 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
    : CycleCollectedJSRuntime(nullptr, JS::DefaultHeapMaxBytes, JS::DefaultNurseryBytes),
    mJSContextStack(new XPCJSContextStack(this)),
    mCallContext(nullptr),
    mAutoRoots(nullptr),
    mResolveName(JSID_VOID),
    mResolvingWrapper(nullptr),
    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_LENGTH)),
@@ -3531,18 +3476,16 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
     RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
     RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount);
     RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount);
     mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
 
     // Watch for the JS boolean options.
     ReloadPrefsCallback(nullptr, this);
     Preferences::RegisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
-
-    JS_SetCurrentPerfGroupCallback(runtime, ::GetCurrentPerfGroupCallback);
 }
 
 // static
 XPCJSRuntime*
 XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect)
 {
     NS_PRECONDITION(aXPConnect,"bad param");
 
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -134,16 +134,19 @@ IsXrayWrapper(JSObject* obj);
 // To emphasize the obvious: the return value here is not necessarily same-
 // compartment with the argument.
 JSObject*
 XrayAwareCalleeGlobal(JSObject* fun);
 
 void
 TraceXPCGlobal(JSTracer* trc, JSObject* obj);
 
+uint64_t
+GetCompartmentCPOWMicroseconds(JSCompartment* compartment);
+
 } /* namespace xpc */
 
 namespace JS {
 
 struct RuntimeStats;
 
 }
 
@@ -524,22 +527,16 @@ class ErrorReport {
   private:
     ~ErrorReport() {}
 };
 
 void
 DispatchScriptErrorEvent(nsPIDOMWindow* win, JSRuntime* rt, xpc::ErrorReport* xpcReport,
                          JS::Handle<JS::Value> exception);
 
-// Return a name for the compartment.
-// This function makes reasonable efforts to make this name both mostly human-readable
-// and unique. However, there are no guarantees of either property.
-extern void
-GetCurrentCompartmentName(JSContext*, nsCString& name);
-
 } // namespace xpc
 
 namespace mozilla {
 namespace dom {
 
 typedef JSObject*
 (*DefineInterface)(JSContext* cx, JS::Handle<JSObject*> global,
                    JS::Handle<jsid> id, bool defineOnGlobal);
--- a/toolkit/components/aboutperformance/content/aboutPerformance.js
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.js
@@ -6,19 +6,16 @@
 
 "use strict";
 
 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", {});
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-
-const UPDATE_TOPIC = "about:performance-update-immediately";
 
 /**
  * The various measures we display.
  */
 const MEASURES = [
   {key: "longestDuration", percentOfDeltaT: false, label: "Jank level"},
   {key: "totalUserTime", percentOfDeltaT: true, label: "User (%)"},
   {key: "totalSystemTime", percentOfDeltaT: true, label: "System (%)"},
@@ -275,30 +272,27 @@ function updateLiveData() {
       row.appendChild(el);
       if (item.addonId) {
         let _el = el;
         let _item = item;
         AddonManager.getAddonByID(item.addonId, a => {
           _el.textContent = a ? a.name : _item.name
         });
       } else {
-        el.textContent = item.title || item.name;
+        el.textContent = item.name;
       }
     }
   } catch (ex) {
     console.error(ex);
   }
 }
 
 function go() {
   // Compute initial state immediately, then wait a little
   // before we start computing diffs and refreshing.
   document.getElementById("playButton").addEventListener("click", () => AutoUpdate.start());
   document.getElementById("pauseButton").addEventListener("click", () => AutoUpdate.stop());
 
   document.getElementById("intervalDropdown").addEventListener("change", () => AutoUpdate.updateRefreshRate());
 
   State.update();
-  let observer = update;
-  
-  Services.obs.addObserver(update, UPDATE_TOPIC, false);
-  window.addEventListener("unload", () => Services.obs.removeObserver(update, UPDATE_TOPIC));
-}
+  setTimeout(update, 1000);
+}
\ No newline at end of file
--- a/toolkit/components/aboutperformance/tests/browser/browser.ini
+++ b/toolkit/components/aboutperformance/tests/browser/browser.ini
@@ -1,9 +1,11 @@
+# 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/.
+
 [DEFAULT]
 head = head.js
 support-files = 
   browser_compartments.html
-  browser_compartments_frame.html
-  browser_compartments_script.js
 
 [browser_aboutperformance.js]
 skip-if = e10s # Feature not implemented yet – bug 1140310
--- a/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js
+++ b/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js
@@ -6,79 +6,60 @@
 Cu.import("resource://testing-common/ContentTask.jsm", this);
 
 const URL = "http://example.com/browser/toolkit/components/aboutperformance/tests/browser/browser_compartments.html?test=" + Math.random();
 
 // This function is injected as source as a frameScript
 function frameScript() {
   "use strict";
 
-  addMessageListener("aboutperformance-test:done", () => {
-    content.postMessage("stop", "*");
-    sendAsyncMessage("aboutperformance-test:done", null);
-  });
-  addMessageListener("aboutperformance-test:setTitle", ({data: title}) => {
-    content.document.title = title;
-    sendAsyncMessage("aboutperformance-test:setTitle", null);
-  });
-  
-  addMessageListener("aboutperformance-test:hasItems", ({data: title}) => {
-    Services.obs.notifyObservers(null, "about:performance-update-immediately", "");
+  addMessageListener("aboutperformance-test:hasItems", ({data: url}) => {
     let hasPlatform = false;
-    let hasTitle = false;
+    let hasURL = false;
 
     try {
       let eltData = content.document.getElementById("liveData");
       if (!eltData) {
         return;
       }
 
       // Find if we have a row for "platform"
       hasPlatform = eltData.querySelector("tr.platform") != null;
 
-      // Find if we have a row for our content page
-      let titles = [for (eltContent of eltData.querySelectorAll("td.contents.name")) eltContent.textContent];
+      // Find if we have a row for our URL
+      hasURL = false;
+      for (let eltContent of eltData.querySelectorAll("tr.content td.name")) {
+        if (eltContent.textContent == url) {
+          hasURL = true;
+          break;
+        }
+      }
 
-      hasTitle = titles.includes(title);
     } catch (ex) {
       Cu.reportError("Error in content: " + ex);
       Cu.reportError(ex.stack);
     } finally {
-      sendAsyncMessage("aboutperformance-test:hasItems", {hasPlatform, hasTitle});
+      sendAsyncMessage("aboutperformance-test:hasItems", {hasPlatform, hasURL});
     }
   });
 }
 
 
-add_task(function* go() {
-  info("Setting up about:performance");
-  let tabAboutPerformance = gBrowser.selectedTab = gBrowser.addTab("about:performance");
+add_task(function* test() {
+  let tabAboutPerformance = gBrowser.addTab("about:performance");
+  let tabContent = gBrowser.addTab(URL);
+
   yield ContentTask.spawn(tabAboutPerformance.linkedBrowser, null, frameScript);
 
-  info(`Setting up ${URL}`);
-  let tabContent = gBrowser.addTab(URL);
-  yield ContentTask.spawn(tabContent.linkedBrowser, null, frameScript);
-
-  let title = "Testing about:performance " + Math.random();
-  info(`Setting up title ${title}`);
   while (true) {
-    yield promiseContentResponse(tabContent.linkedBrowser, "aboutperformance-test:setTitle", title);
     yield new Promise(resolve => setTimeout(resolve, 100));
-    let {hasPlatform, hasTitle} = (yield promiseContentResponse(tabAboutPerformance.linkedBrowser, "aboutperformance-test:hasItems", title));
-    info(`Platform: ${hasPlatform}, title: ${hasTitle}`);
-    if (hasPlatform && hasTitle) {
-      Assert.ok(true, "Found a row for <platform> and a row for our page");
+    let {hasPlatform, hasURL} = (yield promiseContentResponse(tabAboutPerformance.linkedBrowser, "aboutperformance-test:hasItems", URL));
+    info(`Platform: ${hasPlatform}, url: ${hasURL}`);
+    if (hasPlatform && hasURL) {
+      Assert.ok(true, "Found a row for <platform> and a row for our URL");
       break;
     }
   }
 
   // Cleanup
-  info("Cleaning up");
-  yield promiseContentResponse(tabAboutPerformance.linkedBrowser, "aboutperformance-test:done", null);
-
-  info("Closing tabs");
-  for (let tab of gBrowser.tabs) {
-    yield BrowserTestUtils.removeTab(tab);
-  }
-
-  info("Done");
-  gBrowser.selectedTab = null;
+  gBrowser.removeTab(tabContent);
+  gBrowser.removeTab(tabAboutPerformance);
 });
--- a/toolkit/components/aboutperformance/tests/browser/browser_compartments.html
+++ b/toolkit/components/aboutperformance/tests/browser/browser_compartments.html
@@ -1,20 +1,25 @@
 <!DOCTYPE html>
 <html>
 <head>
   <title>
-    Main frame for test browser_aboutperformance.js
+    browser_compartments.html
   </title>
+  <script type="text/javascript">
+  // Use some CPU.
+  window.setInterval(() => {
+    // Compute an arbitrary value, print it out to make sure that the JS
+    // engine doesn't discard all our computation.
+    var date = Date.now();
+    var array = [];
+    var i = 0;
+    while (Date.now() - date <= 100) {
+      array[i%2] = i++;
+    }
+    console.log("Arbitrary value", array);
+  }, 300);
+  </script>
 </head>
 <body>
-Main frame.
-
-<iframe src="browser_compartments_frame.html?frame=1">
-  Subframe 1
-</iframe>
-
-<iframe src="browser_compartments_frame.html?frame=2">
-  Subframe 2.
-</iframe>
-
+browser_compartments.html
 </body>
 </html>
deleted file mode 100644
--- a/toolkit/components/aboutperformance/tests/browser/browser_compartments_frame.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>
-    Subframe for test browser_compartments.html (do not change this title)
-  </title>
-  <script src="browser_compartments_script.js"></script>
-</head>
-<body>
-Subframe loaded.
-</body>
-</html>
deleted file mode 100644
--- a/toolkit/components/aboutperformance/tests/browser/browser_compartments_script.js
+++ /dev/null
@@ -1,29 +0,0 @@
-
-var carryOn = true;
-
-window.addEventListener("message", e => {
-  console.log("frame content", "message", e);
-  if ("title" in e.data) {
-    document.title = e.data.title;
-  }
-  if ("stop" in e.data) {
-    carryOn = false;
-  }
-});
-
-// Use some CPU.
-var interval = window.setInterval(() => {
-  if (!carryOn) {
-    window.clearInterval(interval);
-    return;
-  }
-
-  // Compute an arbitrary value, print it out to make sure that the JS
-  // engine doesn't discard all our computation.
-  var date = Date.now();
-  var array = [];
-  var i = 0;
-  while (Date.now() - date <= 100) {
-    array[i%2] = i++;
-  }
-}, 300);
--- a/toolkit/components/perfmonitoring/PerformanceStats.jsm
+++ b/toolkit/components/perfmonitoring/PerformanceStats.jsm
@@ -20,42 +20,32 @@ const { classes: Cc, interfaces: Ci, uti
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
 let performanceStatsService =
   Cc["@mozilla.org/toolkit/performance-stats-service;1"].
   getService(Ci.nsIPerformanceStatsService);
 
 
 const PROPERTIES_NUMBERED = ["totalUserTime", "totalSystemTime", "totalCPOWTime", "ticks"];
-const PROPERTIES_META_IMMUTABLE = ["name", "addonId", "isSystem"];
-const PROPERTIES_META = [...PROPERTIES_META_IMMUTABLE, "windowId", "title"];
+const PROPERTIES_META = ["name", "addonId", "isSystem"];
 const PROPERTIES_FLAT = [...PROPERTIES_NUMBERED, ...PROPERTIES_META];
 
 /**
  * Information on a single component.
  *
  * This offers the following fields:
  *
  * @field {string} name The name of the component:
  * - for the process itself, "<process>";
  * - for platform code, "<platform>";
  * - for an add-on, the identifier of the addon (e.g. "myaddon@foo.bar");
  * - for a webpage, the url of the page.
  *
  * @field {string} addonId The identifier of the addon (e.g. "myaddon@foo.bar").
  *
- * @field {string|null} title The title of the webpage to which this code
- *  belongs. Note that this is the title of the entire webpage (i.e. the tab),
- *  even if the code is executed in an iframe. Also note that this title may
- *  change over time.
- *
- * @field {number} windowId The outer window ID of the top-level nsIDOMWindow
- *  to which this code belongs. May be 0 if the code doesn't belong to any
- *  nsIDOMWindow.
- *
  * @field {boolean} isSystem `true` if the component is a system component (i.e.
  *  an add-on or platform-code), `false` otherwise (i.e. a webpage).
  *
  * @field {number} totalUserTime The total amount of time spent executing code.
  *
  * @field {number} totalSystemTime The total amount of time spent executing
  *  system calls.
  *
--- a/toolkit/components/perfmonitoring/moz.build
+++ b/toolkit/components/perfmonitoring/moz.build
@@ -23,13 +23,9 @@ XPIDL_SOURCES += [
 UNIFIED_SOURCES += [
     'nsPerformanceStats.cpp'
 ]
 
 EXPORTS += [
     'nsPerformanceStats.h'
 ]
 
-LOCAL_INCLUDES += [
-    '/dom/base',
-]
-
 FINAL_LIBRARY = 'xul'
--- a/toolkit/components/perfmonitoring/nsIPerformanceStats.idl
+++ b/toolkit/components/perfmonitoring/nsIPerformanceStats.idl
@@ -1,33 +1,32 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-*/
 /* 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"
-#include "nsIDOMWindow.idl"
 
 /**
  * Mechanisms for querying the current process about performance
  * information.
  *
  * JavaScript clients should rather use PerformanceStats.jsm.
  */
 
 /**
  * Snapshot of the performance of a component, e.g. an add-on, a web
  * page, system built-ins, or the entire process itself.
  *
  * All values are monotonic and are updated only when
  * `nsIPerformanceStatsService.isStopwatchActive` is `true`.
  */
-[scriptable, uuid(7741ba2d-3171-42cd-9f36-e46a5ce6d5b1)]
+[scriptable, uuid(f015cbad-e16f-4982-a080-67e4e69a5b2e)]
 interface nsIPerformanceStats: nsISupports {
   /**
    * The name of the component:
    * - for the process itself, "<process>";
    * - for platform code, "<platform>";
    * - for an add-on, the identifier of the addon (e.g. "myaddon@foo.bar");
    * - for a webpage, the url of the page.
    */
@@ -35,28 +34,16 @@ interface nsIPerformanceStats: nsISuppor
 
   /**
    * If the component is an add-on, the ID of the addon,
    * otherwise an empty string.
    */
   readonly attribute AString addonId;
 
   /**
-   * If the component is code executed in a window, the ID of the topmost
-   * outer window (i.e. the tab), otherwise 0.
-   */
-  readonly attribute uint64_t windowId;
-
-  /**
-   * If the component is code executed in a window, the title of the topmost
-   * window (i.e. the tab), otherwise an empty string.
-   */
-  readonly attribute AString title;
-
-  /**
    * Total amount of time spent executing code in this group, in
    * microseconds.
    */
   readonly attribute unsigned long long totalUserTime;
   readonly attribute unsigned long long totalSystemTime;
   readonly attribute unsigned long long totalCPOWTime;
 
   /**
@@ -99,26 +86,26 @@ interface nsIPerformanceSnapshot: nsISup
    * This contains the total amount of time spent executing JS code,
    * the total amount of time spent waiting for system calls while
    * executing JS code, the total amount of time performing blocking
    * inter-process calls, etc.
    */
   nsIPerformanceStats getProcessData();
 };
 
-[scriptable, builtinclass, uuid(c989c220-6581-4252-9aad-358fdec9ec8c)]
+[scriptable, builtinclass, uuid(5795113a-39a1-4087-ba09-98b7d07d025a)]
 interface nsIPerformanceStatsService : nsISupports {
   /**
    * `true` if we should monitor performance, `false` otherwise.
    */
   [implicit_jscontext] attribute bool isStopwatchActive;
 
   /**
    * Capture a snapshot of the performance data.
    */
-  [implicit_jscontext] nsIPerformanceSnapshot getSnapshot();
+  nsIPerformanceSnapshot getSnapshot();
 };
 
 %{C++
 #define NS_TOOLKIT_PERFORMANCESTATSSERVICE_CID {0xfd7435d4, 0x9ec4, 0x4699, \
       {0xad, 0xd4, 0x1b, 0xe8, 0x3d, 0xd6, 0x8e, 0xf3} }
 #define NS_TOOLKIT_PERFORMANCESTATSSERVICE_CONTRACTID "@mozilla.org/toolkit/performance-stats-service;1"
 %}
--- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
+++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
@@ -1,44 +1,31 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "jsapi.h"
 #include "nsPerformanceStats.h"
-
 #include "nsMemory.h"
 #include "nsLiteralString.h"
 #include "nsCRTGlue.h"
+#include "nsIJSRuntimeService.h"
 #include "nsServiceManagerUtils.h"
-
 #include "nsCOMArray.h"
 #include "nsIMutableArray.h"
-
-#include "jsapi.h"
 #include "nsJSUtils.h"
 #include "xpcpublic.h"
 #include "jspubtd.h"
-#include "nsIJSRuntimeService.h"
-
-#include "nsIDOMWindow.h"
-#include "nsGlobalWindow.h"
 
 class nsPerformanceStats: public nsIPerformanceStats {
 public:
-  nsPerformanceStats(const nsAString& aName,
-                     const nsAString& aAddonId,
-                     const nsAString& aTitle,
-                     const uint64_t aWindowId,
-                     const bool aIsSystem,
-                     const js::PerformanceData& aPerformanceData)
+  nsPerformanceStats(nsAString& aName, nsAString& aAddonId, bool aIsSystem, js::PerformanceData& aPerformanceData)
     : mName(aName)
     , mAddonId(aAddonId)
-    , mTitle(aTitle)
-    , mWindowId(aWindowId)
     , mIsSystem(aIsSystem)
     , mPerformanceData(aPerformanceData)
   {
   }
   explicit nsPerformanceStats() {}
 
   NS_DECL_ISUPPORTS
 
@@ -49,34 +36,16 @@ public:
   };
 
   /* readonly attribute AString addon id; */
   NS_IMETHOD GetAddonId(nsAString& aAddonId) override {
     aAddonId.Assign(mAddonId);
     return NS_OK;
   };
 
-  /* readonly attribute uint64_t windowId; */
-  NS_IMETHOD GetWindowId(uint64_t *aWindowId) override {
-    *aWindowId = mWindowId;
-    return NS_OK;
-  }
-
-  /* readonly attribute AString title; */
-  NS_IMETHOD GetTitle(nsAString & aTitle) override {
-    aTitle.Assign(mTitle);
-    return NS_OK;
-  }
-
-  /* readonly attribute bool isSystem; */
-  NS_IMETHOD GetIsSystem(bool *_retval) override {
-    *_retval = mIsSystem;
-    return NS_OK;
-  }
-
   /* readonly attribute unsigned long long totalUserTime; */
   NS_IMETHOD GetTotalUserTime(uint64_t *aTotalUserTime) override {
     *aTotalUserTime = mPerformanceData.totalUserTime;
     return NS_OK;
   };
 
   /* readonly attribute unsigned long long totalSystemTime; */
   NS_IMETHOD GetTotalSystemTime(uint64_t *aTotalSystemTime) override {
@@ -104,204 +73,94 @@ public:
     }
     *aNumberOfOccurrences = new uint64_t[length];
     for (size_t i = 0; i < length; ++i) {
       (*aNumberOfOccurrences)[i] = mPerformanceData.durations[i];
     }
     return NS_OK;
   };
 
+  /* readonly attribute bool isSystem; */
+  NS_IMETHOD GetIsSystem(bool *_retval) override {
+    *_retval = mIsSystem;
+    return NS_OK;
+  }
+
 private:
   nsString mName;
   nsString mAddonId;
-  nsString mTitle;
-  uint64_t mWindowId;
   bool mIsSystem;
-
   js::PerformanceData mPerformanceData;
 
   virtual ~nsPerformanceStats() {}
 };
 
 NS_IMPL_ISUPPORTS(nsPerformanceStats, nsIPerformanceStats)
 
 
 class nsPerformanceSnapshot : public nsIPerformanceSnapshot
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERFORMANCESNAPSHOT
 
   nsPerformanceSnapshot();
-  nsresult Init(JSContext*);
+  nsresult Init();
 private:
   virtual ~nsPerformanceSnapshot();
 
   /**
-   * Import a `js::PerformanceStats` as a `nsIPerformanceStats`.
-   *
-   * Precondition: this method assumes that we have entered the JSCompartment for which data `c`
-   * has been collected.
-   *
-   * `cx` may be `nullptr` if we are importing the statistics for the
-   * entire process, rather than the statistics for a specific set of
-   * compartments.
+   * Import a `PerformanceStats` as a `nsIPerformanceStats`.
    */
-  already_AddRefed<nsIPerformanceStats> ImportStats(JSContext* cx, const js::PerformanceData& data);
-
-  /**
-   * Callbacks for iterating through the `PerformanceStats` of a runtime.
-   */
-  bool IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats);
-  static bool IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, void* self);
+  already_AddRefed<nsIPerformanceStats> ImportStats(js::PerformanceStats* c);
 
-  // If the context represents a window, extract the title and window ID.
-  // Otherwise, extract "" and 0.
-  static void GetWindowData(JSContext*,
-                            nsString& title,
-                            uint64_t* windowId);
-
-  // If the context presents an add-on, extract the addon ID.
-  // Otherwise, extract "".
-  static void GetAddonId(JSContext*,
-                         JS::Handle<JSObject*> global,
-                         nsAString& addonId);
-
-  // Determine whether a context is part of the system principals.
-  static bool GetIsSystem(JSContext*,
-                          JS::Handle<JSObject*> global);
-
-private:
   nsCOMArray<nsIPerformanceStats> mComponentsData;
   nsCOMPtr<nsIPerformanceStats> mProcessData;
 };
 
 NS_IMPL_ISUPPORTS(nsPerformanceSnapshot, nsIPerformanceSnapshot)
 
 nsPerformanceSnapshot::nsPerformanceSnapshot()
 {
 }
 
 nsPerformanceSnapshot::~nsPerformanceSnapshot()
 {
 }
 
-/* static */ void
-nsPerformanceSnapshot::GetWindowData(JSContext* cx,
-                                     nsString& title,
-                                     uint64_t* windowId)
-{
-  MOZ_ASSERT(windowId);
-
-  title.SetIsVoid(true);
-  *windowId = 0;
-
-  nsCOMPtr<nsPIDOMWindow> win = xpc::CurrentWindowOrNull(cx);
-  if (!win) {
-    return;
-  }
-
-  nsCOMPtr<nsIDOMWindow> top;
-  nsresult rv = win->GetTop(getter_AddRefs(top));
-  if (!top || NS_FAILED(rv)) {
-    return;
-  }
-
-  nsCOMPtr<nsPIDOMWindow> ptop = do_QueryInterface(top);
-  if (!ptop) {
-    return;
-  }
-
-  nsCOMPtr<nsIDocument> doc = ptop->GetExtantDoc();
-  if (!doc) {
-    return;
+already_AddRefed<nsIPerformanceStats>
+nsPerformanceSnapshot::ImportStats(js::PerformanceStats* c) {
+  nsString addonId;
+  if (c->addonId) {
+    AssignJSFlatString(addonId, (JSFlatString*)c->addonId);
   }
-
-  doc->GetTitle(title);
-  *windowId = ptop->WindowID();
-}
-
-/* static */ void
-nsPerformanceSnapshot::GetAddonId(JSContext*,
-                                  JS::Handle<JSObject*> global,
-                                  nsAString& addonId)
-{
-  addonId.AssignLiteral("");
-
-  JSAddonId* jsid = AddonIdOfObject(global);
-  if (!jsid) {
-    return;
-  }
-  AssignJSFlatString(addonId, (JSFlatString*)jsid);
-}
-
-/* static */ bool
-nsPerformanceSnapshot::GetIsSystem(JSContext*,
-                                   JS::Handle<JSObject*> global)
-{
-  return nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global));
-}
-
-already_AddRefed<nsIPerformanceStats>
-nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& performance) {
-  JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
-
-  if (!global) {
-    // While it is possible for a compartment to have no global
-    // (e.g. atoms), this compartment is not very interesting for us.
-    return nullptr;
-  }
-
-  nsString addonId;
-  GetAddonId(cx, global, addonId);
-
-  nsString title;
-  uint64_t windowId;
-  GetWindowData(cx, title, &windowId);
-
-  nsAutoString name;
-  nsAutoCString cname;
-  xpc::GetCurrentCompartmentName(cx, cname);
-  name.Assign(NS_ConvertUTF8toUTF16(cname));
-
-  bool isSystem = GetIsSystem(cx, global);
-
-  nsCOMPtr<nsIPerformanceStats> result =
-    new nsPerformanceStats(name, addonId, title, windowId, isSystem, performance);
+  nsCString cname(c->name);
+  NS_ConvertUTF8toUTF16 name(cname);
+  nsCOMPtr<nsIPerformanceStats> result = new nsPerformanceStats(name, addonId, c->isSystem, c->performance);
   return result.forget();
 }
 
-/*static*/ bool
-nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, void* self) {
-  return reinterpret_cast<nsPerformanceSnapshot*>(self)->IterPerformanceStatsCallbackInternal(cx, stats);
-}
-
-bool
-nsPerformanceSnapshot::IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats) {
-  nsCOMPtr<nsIPerformanceStats> result = ImportStats(cx, stats);
-  if (result) {
-    mComponentsData.AppendElement(result);
+nsresult
+nsPerformanceSnapshot::Init() {
+  JSRuntime* rt;
+  nsCOMPtr<nsIJSRuntimeService> svc(do_GetService("@mozilla.org/js/xpc/RuntimeService;1"));
+  NS_ENSURE_TRUE(svc, NS_ERROR_FAILURE);
+  svc->GetRuntime(&rt);
+  js::PerformanceStats processStats;
+  js::PerformanceStatsVector componentsStats;
+  if (!js::GetPerformanceStats(rt, componentsStats, processStats)) {
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  return true;
-}
-
-nsresult
-nsPerformanceSnapshot::Init(JSContext* cx) {
-  js::PerformanceData processStats;
-  if (!js::IterPerformanceStats(cx, nsPerformanceSnapshot::IterPerformanceStatsCallback, &processStats, this)) {
-    return NS_ERROR_UNEXPECTED;
+  size_t num = componentsStats.length();
+  for (size_t pos = 0; pos < num; pos++) {
+    nsCOMPtr<nsIPerformanceStats> stats = ImportStats(&componentsStats[pos]);
+    mComponentsData.AppendObject(stats);
   }
-
-  mProcessData = new nsPerformanceStats(NS_LITERAL_STRING("<process>"), // name
-                                        NS_LITERAL_STRING(""),          // add-on id
-                                        NS_LITERAL_STRING(""),          // title
-                                        0,                              // window id
-                                        true,                           // isSystem
-                                        processStats);
+  mProcessData = ImportStats(&processStats);
   return NS_OK;
 }
 
 
 /* void getComponentsData (out nsIArray aComponents); */
 NS_IMETHODIMP nsPerformanceSnapshot::GetComponentsData(nsIArray * *aComponents)
 {
   const size_t length = mComponentsData.Length();
@@ -345,20 +204,20 @@ NS_IMETHODIMP nsPerformanceStatsService:
   JSRuntime *runtime = JS_GetRuntime(cx);
   if (!js::SetStopwatchActive(runtime, aIsStopwatchActive)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   return NS_OK;
 }
 
 /* readonly attribute nsIPerformanceSnapshot snapshot; */
-NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot)
+NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(nsIPerformanceSnapshot * *aSnapshot)
 {
   nsRefPtr<nsPerformanceSnapshot> snapshot = new nsPerformanceSnapshot();
-  nsresult rv = snapshot->Init(cx);
+  nsresult rv = snapshot->Init();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   snapshot.forget(aSnapshot);
   return NS_OK;
 }
 
--- a/toolkit/components/perfmonitoring/tests/browser/browser.ini
+++ b/toolkit/components/perfmonitoring/tests/browser/browser.ini
@@ -1,9 +1,8 @@
 [DEFAULT]
 head = head.js
 support-files =
   browser_Addons_sample.xpi
   browser_compartments.html
-  browser_compartments_frame.html
 
 [browser_AddonWatcher.js]
 [browser_compartments.js]
--- a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html
+++ b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html
@@ -1,20 +1,25 @@
 <!DOCTYPE html>
 <html>
 <head>
   <title>
-    Main frame for test browser_compartments.js
+    browser_compartments.html
   </title>
+  <script type="text/javascript">
+  // Use some CPU.
+  window.setInterval(() => {
+    // Compute an arbitrary value, print it out to make sure that the JS
+    // engine doesn't discard all our computation.
+    var date = Date.now();
+    var array = [];
+    var i = 0;
+    while (Date.now() - date <= 100) {
+      array[i%2] = i++;
+    }
+    console.log("Arbitrary value", array);
+  }, 300);
+  </script>
 </head>
 <body>
-Main frame.
-
-<iframe src="browser_compartments_frame.html?frame=1">
-  Subframe 1
-</iframe>
-
-<iframe src="browser_compartments_frame.html?frame=2">
-  Subframe 2.
-</iframe>
-
+browser_compartments.html
 </body>
 </html>
--- a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js
+++ b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js
@@ -1,24 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "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://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()}`;
 
 // This function is injected as source as a frameScript
 function frameScript() {
   "use strict";
 
   const { utils: Cu, classes: Cc, interfaces: Ci } = Components;
   Cu.import("resource://gre/modules/PerformanceStats.jsm");
 
@@ -32,30 +25,16 @@ function frameScript() {
   addMessageListener("compartments-test:getStatistics", () => {
     try {
       sendAsyncMessage("compartments-test:getStatistics", PerformanceStats.getSnapshot());
     } catch (ex) {
       Cu.reportError("Error in content: " + ex);
       Cu.reportError(ex.stack);
     }
   });
-
-  addMessageListener("compartments-test:setTitles", titles => {
-    try {
-      content.document.title = titles.data.parent;
-      for (let i = 0; i < content.frames.length; ++i) {
-        content.frames[i].postMessage({title: titles.data.frames}, "*");
-      }
-      console.log("content", "Done setting titles", content.document.title);
-      sendAsyncMessage("compartments-test:setTitles");
-    } catch (ex) {
-      Cu.reportError("Error in content: " + ex);
-      Cu.reportError(ex.stack);
-    }
-  });
 }
 
 // A variant of `Assert` that doesn't spam the logs
 // in case of success.
 let SilentAssert = {
   equal: function(a, b, msg) {
     if (a == b) {
       return;
@@ -88,16 +67,17 @@ function monotinicity_tester(source, tes
   // - there is at most one component with a combination of `name` and `addonId`;
   // - types, etc.
   let previous = {
     processData: null,
     componentsMap: new Map(),
   };
 
   let sanityCheck = function(prev, next) {
+    info(`Sanity check: ${JSON.stringify(next, null, "\t")}`);
     if (prev == null) {
       return;
     }
     for (let k of ["name", "addonId", "isSystem"]) {
       SilentAssert.equal(prev[k], next[k], `Sanity check (${testName}): ${k} hasn't changed.`);
     }
     for (let k of ["totalUserTime", "totalSystemTime", "totalCPOWTime", "ticks"]) {
       SilentAssert.equal(typeof next[k], "number", `Sanity check (${testName}): ${k} is a number.`);
@@ -131,37 +111,28 @@ function monotinicity_tester(source, tes
     sanityCheck(previous.processData, snapshot.processData);
     SilentAssert.equal(snapshot.processData.isSystem, true);
     SilentAssert.equal(snapshot.processData.name, "<process>");
     SilentAssert.equal(snapshot.processData.addonId, "");
     previous.procesData = snapshot.processData;
 
     // Sanity check on components data.
     let set = new Set();
-    let map = new Map();
+    let keys = [];
     for (let item of snapshot.componentsData) {
+      let key = `{name: ${item.name}, addonId: ${item.addonId}, isSystem: ${item.isSystem}}`;
+      keys.push(key);
+      set.add(key);
+      sanityCheck(previous.componentsMap.get(key), item);
+      previous.componentsMap.set(key, item);
+
       for (let k of ["totalUserTime", "totalSystemTime", "totalCPOWTime"]) {
         SilentAssert.leq(item[k], snapshot.processData[k],
           `Sanity check (${testName}): component has a lower ${k} than process`);
       }
-
-      let key = `{name: ${item.name}, window: ${item.windowId}, addonId: ${item.addonId}, isSystem: ${item.isSystem}}`;
-      if (set.has(key)) {
-        // There are at least two components with the same name (e.g. about:blank).
-        // Don't perform sanity checks on that name until we know how to make
-        // the difference.
-        map.delete(key);
-        continue;
-      }
-      map.set(key, item);
-      set.add(key);
-    }
-    for (let [key, item] of map) {
-      sanityCheck(previous.componentsMap.get(key), item);
-      previous.componentsMap.set(key, item);
     }
     info(`Deactivating deduplication check (Bug 1150045)`);
     if (false) {
       SilentAssert.equal(set.size, snapshot.componentsData.length);
     }
   });
   let interval = window.setInterval(frameCheck, 300);
   registerCleanupFunction(() => {
@@ -171,17 +142,17 @@ function monotinicity_tester(source, tes
 
 add_task(function* test() {
   info("Extracting initial state");
   let stats0 = PerformanceStats.getSnapshot();
   Assert.notEqual(stats0.componentsData.length, 0, "There is more than one component");
   Assert.ok(!stats0.componentsData.find(stat => stat.name.indexOf(URL) != -1),
     "The url doesn't appear yet");
 
-  let newTab = gBrowser.selectedTab = gBrowser.addTab();
+  let newTab = gBrowser.addTab();
   let browser = newTab.linkedBrowser;
   // Setup monitoring in the tab
   info("Setting up monitoring in the tab");
   yield ContentTask.spawn(newTab.linkedBrowser, null, frameScript);
 
   info("Opening URL");
   newTab.linkedBrowser.loadURI(URL);
 
@@ -190,47 +161,24 @@ add_task(function* test() {
   } else {
     info("Setting up sanity checks");
     monotinicity_tester(() => PerformanceStats.getSnapshot(), "parent process");
     monotinicity_tester(() => promiseContentResponseOrNull(browser, "compartments-test:getStatistics", null), "content process" );
   }
 
   let skipTotalUserTime = hasLowPrecision();
 
-
   while (true) {
-    yield new Promise(resolve => setTimeout(resolve, 100));
-
-    // We may have race conditions with DOM loading.
-    // Don't waste too much brainpower here, let's just ask
-    // 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));
-
-    // While the webpage consists in three compartments, we should see only
-    // one `PerformanceData` in `componentsData`. Its `name` is undefined
-    // (could be either the main frame or one of its subframes), but its
-    // `title` should be the title of the main frame.
-    let frame = stats.componentsData.find(stat => stat.title == FRAME_TITLE);
-    Assert.equal(frame, null, "Searching by title, the frames don't show up in the list of components");
-
-    let parent = stats.componentsData.find(stat => stat.title == PARENT_TITLE);
-    if (!parent) {
-      info("Searching by title, we didn't find the main frame");
-      continue;
-    }
-    info("Searching by title, we found the main frame");
-
-    info(`Total user time: ${parent.totalUserTime}`);
-    if (skipTotalUserTime || parent.totalUserTime > 1000) {
+    let found = stats.componentsData.find(stat => {
+      return (stat.name.indexOf(URL) != -1)
+      && (skipTotalUserTime || stat.totalUserTime > 1000)
+    });
+    if (found) {
+      info(`Expected totalUserTime > 1000, got ${found.totalUserTime}`);
       break;
     }
+    yield new Promise(resolve => setTimeout(resolve, 100));
   }
 
   // Cleanup
   gBrowser.removeTab(newTab);
 });
deleted file mode 100644
--- a/toolkit/components/perfmonitoring/tests/browser/browser_compartments_frame.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>
-    Subframe for test browser_compartments.html (do not change this title)
-  </title>
-  <script src="browser_compartments_script.js"></script>
-</head>
-<body>
-Subframe loaded.
-</body>
-</html>