Backed out changesets 49667763013b and 83fa8af8140b (bug 1147664) for test_compartments.js xpcshell failures.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 23 Jul 2015 11:35:55 -0400
changeset 254268 9dc4d517295721a22195d60332c9ff6299bc2894
parent 254267 b245058d67aa457e10ab8fa2e44e7065ce2f9ac1
child 254269 4207226366e9b90283183dd6b8108548858ea19a
push id14194
push userryanvm@gmail.com
push dateThu, 23 Jul 2015 15:36:31 +0000
treeherderfx-team@4207226366e9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1147664
milestone42.0a1
backs out49667763013bd66150d6ee6fb3b60b3959a7abae
83fa8af8140b4a3ab275313b77f4dee26312b7fa
Backed out changesets 49667763013b and 83fa8af8140b (bug 1147664) for test_compartments.js xpcshell failures.
js/src/jsapi.cpp
js/src/jsapi.h
js/src/vm/Interpreter.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
toolkit/components/perfmonitoring/PerformanceStats.jsm
toolkit/components/perfmonitoring/nsIPerformanceStats.idl
toolkit/components/perfmonitoring/nsPerformanceStats.cpp
toolkit/components/perfmonitoring/tests/browser/browser_compartments.js
toolkit/components/perfmonitoring/tests/xpcshell/test_compartments.js
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -312,82 +312,46 @@ IterPerformanceStats(JSContext* cx,
                     js::DefaultHasher<js::PerformanceGroup*>,
                     js::SystemAllocPolicy> Set;
     Set set;
     if (!set.init(100)) {
         return false;
     }
 
     JSRuntime* rt = JS_GetRuntime(cx);
-
-    // First report the shared groups
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
         JSCompartment* compartment = c.get();
-        if (!c->principals()) {
-            // Compartments without principals could show up here, but
-            // reporting them doesn't really make sense.
-            continue;
-        }
-        if (!c->performanceMonitoring.hasSharedGroup()) {
+        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.getSharedGroup(cx);
+        PerformanceGroup* group = compartment->performanceMonitoring.getGroup(cx);
+
         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, group->uid, nullptr,
-                       closure)) {
+        if (!(*walker)(cx, group->data, group->uid, closure)) {
             // Issue in callback
             return false;
         }
         if (!set.add(ptr, group)) {
             // Memory issue
             return false;
         }
     }
-
-    // Then report the own groups
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
-        JSCompartment* compartment = c.get();
-        if (!c->principals()) {
-            // Compartments without principals could show up here, but
-            // reporting them doesn't really make sense.
-            continue;
-        }
-        if (!c->performanceMonitoring.hasOwnGroup()) {
-            // Don't report compartments that do not even have a PerformanceGroup.
-            continue;
-        }
-        js::AutoCompartment autoCompartment(cx, compartment);
-        PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx);
-        if (ownGroup->data.ticks == 0) {
-            // Don't report compartments that have never been used.
-            continue;
-        }
-        PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx);
-        if (!(*walker)(cx,
-                       ownGroup->data, ownGroup->uid, &sharedGroup->uid,
-                       closure)) {
-            // Issue in callback
-            return false;
-        }
-    }
-
-    // Finally, report the process stats
     *processStats = rt->stopwatch.performance;
     return true;
 }
 
 void
 AssertHeapIsIdle(JSRuntime* rt)
 {
     MOZ_ASSERT(!rt->isHeapBusy());
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5404,17 +5404,17 @@ BuildStackString(JSContext* cx, HandleOb
 
 } /* namespace JS */
 
 
 /* Stopwatch-based CPU monitoring. */
 
 namespace js {
 
-class AutoStopwatch;
+struct AutoStopwatch;
 
 // Container for performance data
 // All values are monotonic.
 struct PerformanceData {
     // Number of times we have spent at least 2^n consecutive
     // milliseconds executing code in this group.
     // durations[0] is increased whenever we spend at least 1 ms
     // executing code in this group
@@ -5535,67 +5535,55 @@ struct PerformanceGroup {
     friend struct PerformanceGroupHolder;
 
 private:
     // A reference counter. Maintained by PerformanceGroupHolder.
     uint64_t refCount_;
 };
 
 //
-// Each PerformanceGroupHolder handles:
-// - a reference-counted indirection towards a PerformanceGroup shared
-//   by several compartments
-// - a owned PerformanceGroup representing the performance of a single
-//   compartment.
+// Indirection towards a PerformanceGroup.
+// This structure handles reference counting for instances of PerformanceGroup.
 //
 struct PerformanceGroupHolder {
-    // Get the shared group.
+    // Get the group.
     // On first call, this causes a single Hashtable lookup.
     // Successive calls do not require further lookups.
-    js::PerformanceGroup* getSharedGroup(JSContext*);
-
-    // Get the own group.
-    js::PerformanceGroup* getOwnGroup(JSContext*);
-
-    // `true` if the this holder is currently associated to a shared
+    js::PerformanceGroup* getGroup(JSContext*);
+
+    // `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 hasSharedGroup() const {
-        return sharedGroup_ != nullptr;
-    }
-    inline bool hasOwnGroup() const {
-        return ownGroup_ != nullptr;
+    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)
       : runtime_(runtime)
-      , sharedGroup_(nullptr)
-      , ownGroup_(nullptr)
+      , group_(nullptr)
     {   }
     ~PerformanceGroupHolder();
-
-  private:
+private:
     // Return the key representing this PerformanceGroup in
     // Runtime::Stopwatch.
     // Do not deallocate the key.
     void* getHashKey(JSContext* cx);
 
     JSRuntime *runtime_;
 
-    // The PerformanceGroups held by this object.
-    // Initially set to `nullptr` until the first call to `getGroup`.
+    // 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* sharedGroup_;
-    js::PerformanceGroup* ownGroup_;
+    js::PerformanceGroup* group_;
 };
 
 /**
  * Reset any stopwatch currently measuring.
  *
  * This function is designed to be called when we process a new event.
  */
 extern JS_PUBLIC_API(void)
@@ -5611,34 +5599,28 @@ ResetStopwatches(JSRuntime*);
 extern JS_PUBLIC_API(bool)
 SetStopwatchIsMonitoringCPOW(JSRuntime*, bool);
 extern JS_PUBLIC_API(bool)
 GetStopwatchIsMonitoringCPOW(JSRuntime*);
 extern JS_PUBLIC_API(bool)
 SetStopwatchIsMonitoringJank(JSRuntime*, bool);
 extern JS_PUBLIC_API(bool)
 GetStopwatchIsMonitoringJank(JSRuntime*);
-extern JS_PUBLIC_API(bool)
-SetStopwatchIsMonitoringPerCompartment(JSRuntime*, bool);
-extern JS_PUBLIC_API(bool)
-GetStopwatchIsMonitoringPerCompartment(JSRuntime*);
 
 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, uint64_t uid,
-                         const uint64_t* parentId, void* closure);
+(PerformanceStatsWalker)(JSContext* cx, const PerformanceData& stats, uint64_t uid, void* closure);
 
 /**
  * Extract the performance statistics.
  *
  * Note that before calling `walker`, we enter the corresponding context.
  */
 extern JS_PUBLIC_API(bool)
 IterPerformanceStats(JSContext* cx, PerformanceStatsWalker* walker, js::PerformanceData* process, void* closure);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -367,259 +367,147 @@ InvokeState::pushInterpreterFrame(JSCont
 
 InterpreterFrame*
 ExecuteState::pushInterpreterFrame(JSContext* cx)
 {
     return cx->runtime()->interpreterStack().pushExecuteFrame(cx, script_, thisv_, newTargetValue_,
                                                               scopeChain_, type_, evalInFrame_);
 }
 namespace js {
+
 // Implementation of per-performance group performance measurement.
 //
 //
 // All mutable state is stored in `Runtime::stopwatch` (per-process
 // performance stats and logistics) and in `PerformanceGroup` (per
 // group performance stats).
-class AutoStopwatch final
-{
-    // The context with which this object was initialized.
-    // Non-null.
-    JSContext* const cx_;
-
-    // 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
-    // for a shared PerformanceGroup, `false` otherwise, i.e. if the
-    // stopwatch mechanism is off or if another stopwatch is already
-    // in charge of monitoring for the same PerformanceGroup.
-    bool isMonitoringForGroup_;
-
-    // `true` if this object is currently used to monitor performance
-    // for a single compartment, `false` otherwise, i.e. if the
-    // stopwatch mechanism is off or if another stopwatch is already
-    // in charge of monitoring for the same PerformanceGroup.
-    bool isMonitoringForSelf_;
-
-    // `true` if this stopwatch is the topmost stopwatch on the stack
-    // for this event, `false` otherwise.
-    bool isMonitoringForTop_;
-
-    // `true` if we are monitoring jank, `false` otherwise.
-    bool isMonitoringJank_;
-    // `true` if we are monitoring CPOW, `false` otherwise.
-    bool isMonitoringCPOW_;
-
-    // Timestamps captured while starting the stopwatch.
-    uint64_t userTimeStart_;
-    uint64_t systemTimeStart_;
-    uint64_t CPOWTimeStart_;
-
-   public:
+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)
       , iteration_(0)
-      , isMonitoringForGroup_(false)
-      , isMonitoringForSelf_(false)
-      , isMonitoringForTop_(false)
-      , isMonitoringJank_(false)
-      , isMonitoringCPOW_(false)
+      , isActive_(false)
+      , isTop_(false)
       , userTimeStart_(0)
       , systemTimeStart_(0)
       , CPOWTimeStart_(0)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
+        JSRuntime* runtime = JS_GetRuntime(cx_);
+        if (!runtime->stopwatch.isMonitoringJank())
+            return;
+
         JSCompartment* compartment = cx_->compartment();
         if (compartment->scheduledForDestruction)
             return;
 
-        JSRuntime* runtime = cx_->runtime();
         iteration_ = runtime->stopwatch.iteration;
 
-        PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx);
-        if (!sharedGroup) {
-            // Either this Runtime is not configured for Performance Monitoring, or we couldn't
-            // allocate the group, or there was a problem with the hashtable.
+        PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx);
+        MOZ_ASSERT(group);
+
+        if (group->hasStopwatch(iteration_)) {
+            // Someone is already monitoring this group during this
+            // tick, no need for further monitoring.
             return;
         }
 
-        if (!sharedGroup->hasStopwatch(iteration_)) {
-            // 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.
-            sharedGroup->acquireStopwatch(iteration_, this);
-            isMonitoringForGroup_ = true;
-        }
-
-        PerformanceGroup* ownGroup = nullptr;
-        if (runtime->stopwatch.isMonitoringPerCompartment()) {
-            // As above, but for the group representing just this compartment.
-            ownGroup = compartment->performanceMonitoring.getOwnGroup(cx);
-            if (!ownGroup->hasStopwatch(iteration_)) {
-                ownGroup->acquireStopwatch(iteration_, this);
-                isMonitoringForSelf_ = true;
-            }
-        }
+        // Start the stopwatch.
+        if (!this->getTimes(runtime, &userTimeStart_, &systemTimeStart_))
+            return;
+        isActive_ = true;
+        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) {
             // This is the topmost stopwatch on the stack.
             // It will be in charge of updating the per-process
             // performance data.
             runtime->stopwatch.isEmpty = false;
-            isMonitoringForTop_ = true;
-
-            MOZ_ASSERT(isMonitoringForGroup_);
+            runtime->stopwatch.performance.ticks++;
+            isTop_ = true;
         }
-
-        if (!isMonitoringForGroup_ && !isMonitoringForSelf_) {
+    }
+    inline ~AutoStopwatch() {
+        if (!isActive_) {
             // We are not in charge of monitoring anything.
-            // (isMonitoringForTop_ implies isMonitoringForGroup_,
-            // so we do not need to check it)
             return;
         }
 
-        enter();
-    }
-    ~AutoStopwatch() {
-        if (!isMonitoringForGroup_ && !isMonitoringForSelf_) {
-            // We are not in charge of monitoring anything.
-            // (isMonitoringForTop_ implies isMonitoringForGroup_,
-            // so we do not need to check it)
+        JSRuntime* runtime = JS_GetRuntime(cx_);
+        JSCompartment* compartment = cx_->compartment();
+
+        MOZ_ASSERT(!compartment->scheduledForDestruction);
+
+        if (!runtime->stopwatch.isMonitoringJank()) {
+            // Monitoring has been stopped while we were
+            // executing the code. Drop everything.
             return;
         }
 
-        JSCompartment* compartment = cx_->compartment();
-        if (compartment->scheduledForDestruction)
-            return;
-
-        JSRuntime* runtime = cx_->runtime();
         if (iteration_ != runtime->stopwatch.iteration) {
             // We have entered a nested event loop at some point.
             // Any information we may have is obsolete.
             return;
         }
 
-        // Finish and commit measures
-        exit();
-
-        // Now release groups.
-        if (isMonitoringForGroup_) {
-            PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_);
-            MOZ_ASSERT(sharedGroup);
-            sharedGroup->releaseStopwatch(iteration_, this);
-        }
-
-        if (isMonitoringForSelf_) {
-            PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx_);
-            MOZ_ASSERT(ownGroup);
-            ownGroup->releaseStopwatch(iteration_, this);
-        }
-
-        if (isMonitoringForTop_)
+        PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx_);
+        MOZ_ASSERT(group);
+
+        // Compute time spent.
+        group->releaseStopwatch(iteration_, this);
+        uint64_t userTimeEnd, systemTimeEnd;
+        if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd))
+            return;
+
+        uint64_t userTimeDelta = userTimeEnd - userTimeStart_;
+        uint64_t systemTimeDelta = systemTimeEnd - systemTimeStart_;
+        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;
-    }
-   private:
-    void enter() {
-        JSRuntime* runtime = cx_->runtime();
-
-        if (runtime->stopwatch.isMonitoringCPOW()) {
-            CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime;
-            isMonitoringCPOW_ = true;
-        }
-
-        if (runtime->stopwatch.isMonitoringJank()) {
-            if (this->getTimes(runtime, &userTimeStart_, &systemTimeStart_)) {
-                isMonitoringJank_ = true;
-            }
-        }
-
-    }
-
-    void exit() {
-        JSRuntime* runtime = cx_->runtime();
-
-        uint64_t userTimeDelta = 0;
-        uint64_t systemTimeDelta = 0;
-        if (isMonitoringJank_ && runtime->stopwatch.isMonitoringJank()) {
-            // We were monitoring jank when we entered and we still are.
-            uint64_t userTimeEnd, systemTimeEnd;
-            if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd)) {
-                // We make no attempt to recover from this error. If
-                // we bail out here, we lose nothing of value, plus
-                // I'm nearly sure that this error cannot happen in
-                // practice.
-                return;
-            }
-            userTimeDelta = userTimeEnd - userTimeStart_;
-            systemTimeDelta = systemTimeEnd - systemTimeStart_;
-        }
-
-        uint64_t CPOWTimeDelta = 0;
-        if (isMonitoringCPOW_ && runtime->stopwatch.isMonitoringCPOW()) {
-            // We were monitoring CPOW when we entered and we still are.
-            CPOWTimeDelta = runtime->stopwatch.performance.totalCPOWTime - CPOWTimeStart_;
-
-        }
-        commitDeltasToGroups(userTimeDelta, systemTimeDelta, CPOWTimeDelta);
-    }
-
-    void commitDeltasToGroups(uint64_t userTimeDelta,
-                              uint64_t systemTimeDelta,
-                              uint64_t CPOWTimeDelta)
-    {
-        JSCompartment* compartment = cx_->compartment();
-
-        PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_);
-        MOZ_ASSERT(sharedGroup);
-        applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, sharedGroup->data);
-
-        if (isMonitoringForSelf_) {
-            PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx_);
-            MOZ_ASSERT(ownGroup);
-            applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, ownGroup->data);
-        }
-
-        if (isMonitoringForTop_) {
-            JSRuntime* runtime = cx_->runtime();
-            applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, runtime->stopwatch.performance);
         }
     }
 
-
-    void applyDeltas(uint64_t userTimeDelta,
-                     uint64_t systemTimeDelta,
-                     uint64_t CPOWTimeDelta,
-                     PerformanceData& data) const {
-
-        data.ticks++;
-
-        uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta;
-        data.totalUserTime += userTimeDelta;
-        data.totalSystemTime += systemTimeDelta;
-        data.totalCPOWTime += CPOWTimeDelta;
-
-        // 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.
-
+ 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.
+    template<int N>
+    void updateDurations(uint64_t totalTimeDelta, uint64_t (&array)[N]) const {
         // Duration of one frame, i.e. 16ms in museconds
         size_t i = 0;
         uint64_t duration = 1000;
         for (i = 0, duration = 1000;
-             i < ArrayLength(data.durations) && duration < totalTimeDelta;
-             ++i, duration *= 2)
-        {
-            data.durations[i]++;
+             i < N && duration < totalTimeDelta;
+             ++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 {
         MOZ_ASSERT(userTime);
@@ -692,19 +580,41 @@ class AutoStopwatch final
         // Convert 100 ns to 1 us, make sure that the result is monotonic
         *userTime = runtime->stopwatch.userTimeFix.monotonize(userTimeInt.QuadPart / 10);
 
 #endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN)
 
         return true;
     }
 
-
-private:
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
+  private:
+    // The context with which this object was initialized.
+    // Non-null.
+    JSContext* const cx_;
+
+    // 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
+    // same PerformanceGroup.
+    bool isActive_;
+
+    // `true` if this stopwatch is the topmost stopwatch on the stack
+    // for this event, `false` otherwise.
+    bool isTop_;
+
+    // Timestamps captured while starting the stopwatch.
+    uint64_t userTimeStart_;
+    uint64_t systemTimeStart_;
+    uint64_t CPOWTimeStart_;
+
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 } // namespace js
 
 // MSVC with PGO inlines a lot of functions in RunScript, resulting in large
 // stack frames and stack overflow issues, see bug 1167883. Turn off PGO to
 // avoid this.
 #ifdef _MSC_VER
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -894,27 +894,16 @@ js::SetStopwatchIsMonitoringCPOW(JSRunti
     return rt->stopwatch.setIsMonitoringCPOW(value);
 }
 bool
 js::GetStopwatchIsMonitoringCPOW(JSRuntime* rt)
 {
     return rt->stopwatch.isMonitoringCPOW();
 }
 
-bool
-js::SetStopwatchIsMonitoringPerCompartment(JSRuntime* rt, bool value)
-{
-    return rt->stopwatch.setIsMonitoringPerCompartment(value);
-}
-bool
-js::GetStopwatchIsMonitoringPerCompartment(JSRuntime* rt)
-{
-    return rt->stopwatch.isMonitoringPerCompartment();
-}
-
 js::PerformanceGroupHolder::~PerformanceGroupHolder()
 {
     unlink();
 }
 
 void*
 js::PerformanceGroupHolder::getHashKey(JSContext* cx)
 {
@@ -924,81 +913,57 @@ js::PerformanceGroupHolder::getHashKey(J
 
     // As a fallback, put everything in the same PerformanceGroup.
     return nullptr;
 }
 
 void
 js::PerformanceGroupHolder::unlink()
 {
-    if (ownGroup_) {
-        js_delete(ownGroup_);
-        ownGroup_ = nullptr;
-    }
-
-    if (!sharedGroup_) {
+    if (!group_) {
         // The group has never been instantiated.
         return;
     }
 
-    js::PerformanceGroup* group = sharedGroup_;
-    sharedGroup_ = nullptr;
+    js::PerformanceGroup* group = group_;
+    group_ = nullptr;
 
     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(group->key_);
     MOZ_ASSERT(ptr);
-    runtime_->stopwatch.groups().remove(ptr);
+    runtime_->stopwatch.groups_.remove(ptr);
     js_delete(group);
 }
 
 PerformanceGroup*
-js::PerformanceGroupHolder::getOwnGroup(JSContext* cx)
+js::PerformanceGroupHolder::getGroup(JSContext* cx)
 {
-    if (ownGroup_)
-        return ownGroup_;
-
-    ownGroup_ = runtime_->new_<PerformanceGroup>(cx, nullptr);
-    return ownGroup_;
-}
-
-PerformanceGroup*
-js::PerformanceGroupHolder::getSharedGroup(JSContext* cx)
-{
-    if (sharedGroup_)
-        return sharedGroup_;
-
-    if (!runtime_->stopwatch.groups().initialized())
-        return nullptr;
+    if (group_)
+        return group_;
 
     void* key = getHashKey(cx);
-    JSRuntime::Stopwatch::Groups::AddPtr ptr = runtime_->stopwatch.groups().lookupForAdd(key);
+    JSRuntime::Stopwatch::Groups::AddPtr ptr =
+        runtime_->stopwatch.groups_.lookupForAdd(key);
     if (ptr) {
-        sharedGroup_ = ptr->value();
-        MOZ_ASSERT(sharedGroup_);
+        group_ = ptr->value();
+        MOZ_ASSERT(group_);
     } else {
-        sharedGroup_ = runtime_->new_<PerformanceGroup>(cx, key);
-        if (!sharedGroup_)
-            return nullptr;
-
-        if (!runtime_->stopwatch.groups().add(ptr, key, sharedGroup_)) {
-            js_delete(sharedGroup_);
-            sharedGroup_ = nullptr;
-            return nullptr;
-        }
+        group_ = runtime_->new_<PerformanceGroup>(cx, key);
+        runtime_->stopwatch.groups_.add(ptr, key, group_);
     }
 
-    sharedGroup_->incRefCount();
+    group_->incRefCount();
 
-    return sharedGroup_;
+    return group_;
 }
 
 PerformanceData*
 js::GetPerformanceData(JSRuntime* rt)
 {
     return &rt->stopwatch.performance;
 }
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1479,39 +1479,16 @@ struct JSRuntime : public JS::shadow::Ru
 
   public:
 
     /* ------------------------------------------
        Performance measurements
        ------------------------------------------ */
     struct Stopwatch {
         /**
-         * A map used to collapse compartments belonging to the same
-         * add-on (respectively to the same webpage, to the platform)
-         * into a single group.
-         *
-         * Keys: for system compartments, a `JSAddonId*` (which may be
-         * `nullptr`), and for webpages, a `JSPrincipals*` (which may
-         * not). Note that compartments may start as non-system
-         * compartments and become compartments later during their
-         * lifetime, which requires an invalidation.
-         *
-         * This map is meant to be accessed only by instances of
-         * PerformanceGroupHolder, which handle both reference-counting
-         * of the values and invalidation of the key/value pairs.
-         */
-        typedef js::HashMap<void*, js::PerformanceGroup*,
-                            js::DefaultHasher<void*>,
-                            js::SystemAllocPolicy> Groups;
-
-        Groups& groups() {
-            return groups_;
-        }
-
-        /**
          * The number of times we have entered the event loop.
          * Used to reset counters whenever we enter the loop,
          * which may be caused either by having completed the
          * previous run of the event loop, or by entering a
          * nested loop.
          *
          * Always incremented by 1, may safely overflow.
          */
@@ -1541,17 +1518,16 @@ struct JSRuntime : public JS::shadow::Ru
         JSCurrentPerfGroupCallback currentPerfGroupCallback;
 
         Stopwatch()
           : iteration(0)
           , isEmpty(true)
           , currentPerfGroupCallback(nullptr)
           , isMonitoringJank_(false)
           , isMonitoringCPOW_(false)
-          , isMonitoringPerCompartment_(false)
           , idCounter_(0)
         { }
 
         /**
          * 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
@@ -1583,31 +1559,16 @@ struct JSRuntime : public JS::shadow::Ru
 
             isMonitoringJank_ = value;
             return true;
         }
         bool isMonitoringJank() const {
             return isMonitoringJank_;
         }
 
-        bool setIsMonitoringPerCompartment(bool value) {
-            if (isMonitoringPerCompartment_ != value)
-                reset();
-
-            if (value && !groups_.initialized()) {
-                if (!groups_.init(128))
-                    return false;
-            }
-
-            isMonitoringPerCompartment_ = value;
-            return true;
-        }
-        bool isMonitoringPerCompartment() const {
-            return isMonitoringPerCompartment_;
-        }
 
         /**
          * Activate/deactivate stopwatch measurement of CPOW.
          */
         bool setIsMonitoringCPOW(bool value) {
             isMonitoringCPOW_ = value;
             return true;
         }
@@ -1641,25 +1602,43 @@ struct JSRuntime : public JS::shadow::Ru
             }
           private:
             uint64_t latestGood_;
         };
         MonotonicTimeStamp systemTimeFix;
         MonotonicTimeStamp userTimeFix;
 
     private:
+        /**
+         * A map used to collapse compartments belonging to the same
+         * add-on (respectively to the same webpage, to the platform)
+         * into a single group.
+         *
+         * Keys: for system compartments, a `JSAddonId*` (which may be
+         * `nullptr`), and for webpages, a `JSPrincipals*` (which may
+         * not). Note that compartments may start as non-system
+         * compartments and become compartments later during their
+         * lifetime, which requires an invalidation.
+         *
+         * This map is meant to be accessed only by instances of
+         * PerformanceGroupHolder, which handle both reference-counting
+         * of the values and invalidation of the key/value pairs.
+         */
+        typedef js::HashMap<void*, js::PerformanceGroup*,
+                            js::DefaultHasher<void*>,
+                            js::SystemAllocPolicy> Groups;
+
         Groups groups_;
         friend struct js::PerformanceGroupHolder;
 
         /**
          * `true` if stopwatch monitoring is active, `false` otherwise.
          */
         bool isMonitoringJank_;
         bool isMonitoringCPOW_;
-        bool isMonitoringPerCompartment_;
 
         /**
          * A counter used to generate unique identifiers for groups.
          */
         uint64_t idCounter_;
     };
     Stopwatch stopwatch;
 };
--- a/toolkit/components/perfmonitoring/PerformanceStats.jsm
+++ b/toolkit/components/perfmonitoring/PerformanceStats.jsm
@@ -141,26 +141,16 @@ Probe.prototype = {
       throw new TypeError();
     }
     if (b == null) {
       return a;
     }
     return this._impl.subtract(a, b);
   },
 
-  importChildren: function(parent, children) {
-    if (!Array.isArray(children)) {
-      throw new TypeError();
-    }
-    if (!parent || !(parent instanceof PerformanceData)) {
-      throw new TypeError();
-    }
-    return this._impl.importChildren(parent, children);
-  },
-
   /**
    * The name of the probe.
    */
   get name() {
     return this._name;
   }
 };
 
@@ -233,18 +223,17 @@ let Probes = {
         durations: [],
         longestDuration: -1,
       };
       for (let i = 0; i < a.durations.length; ++i) {
         result.durations[i] = a.durations[i] - b.durations[i];
       }
       result.longestDuration = lastNonZero(result.durations);
       return result;
-    },
-    importChildren: function() { /* nothing to do */ },
+    }
   }),
 
   /**
    * A probe measuring CPOW activity.
    *
    * Data provided by this probe uses the following format:
    *
    * @field {number} totalCPOWTime The amount of wallclock time
@@ -264,18 +253,17 @@ let Probes = {
     },
     isEqual: function(a, b) {
       return a.totalCPOWTime == b.totalCPOWTime;
     },
     subtract: function(a, b) {
       return {
         totalCPOWTime: a.totalCPOWTime - b.totalCPOWTime
       };
-    },
-    importChildren: function() { /* nothing to do */ },
+    }
   }),
 
   /**
    * A probe measuring activations, i.e. the number
    * of times code execution has entered a given
    * PerformanceGroup.
    *
    * Note that this probe is always active.
@@ -294,29 +282,29 @@ let Probes = {
     },
     isEqual: function(a, b) {
       return a.ticks == b.ticks;
     },
     subtract: function(a, b) {
       return {
         ticks: a.ticks - b.ticks
       };
-    },
-    importChildren: function() { /* nothing to do */ },
+    }
   }),
 
   "jank-content": new Probe("jank-content", {
     _isActive: false,
     set isActive(x) {
       this._isActive = x;
       if (x) {
         Process.broadcast("acquire", ["jank"]);
       } else {
         Process.broadcast("release", ["jank"]);
       }
+    },
     get isActive() {
       return this._isActive;
     },
     extract: function(xpcom) {
       return {};
     },
     isEqual: function(a, b) {
       return true;
@@ -330,73 +318,48 @@ let Probes = {
     _isActive: false,
     set isActive(x) {
       this._isActive = x;
       if (x) {
         Process.broadcast("acquire", ["cpow"]);
       } else {
         Process.broadcast("release", ["cpow"]);
       }
+    },
     get isActive() {
       return this._isActive;
     },
     extract: function(xpcom) {
       return {};
     },
     isEqual: function(a, b) {
       return true;
     },
     subtract: function(a, b) {
       return null;
     }
   }),
 
   "ticks-content": new Probe("ticks-content", {
-    _isActive: false,
     set isActive(x) {
-      this._isActive = x;
-      if (x) {
-        Process.broadcast("acquire", ["ticks"]);
-      } else {
-        Process.broadcast("release", ["ticks"]);
-      }
+      // Ignore: This probe is always active.
+    },
     get isActive() {
-      return this._isActive;
+      return true;
     },
     extract: function(xpcom) {
       return {};
     },
     isEqual: function(a, b) {
       return true;
     },
     subtract: function(a, b) {
       return null;
     }
   }),
-
-  compartments: new Probe("compartments", {
-    set isActive(x) {
-      performanceStatsService.isMonitoringPerCompartment = x;
-    },
-    get isActive() {
-      return performanceStatsService.isMonitoringPerCompartment;
-    },
-    extract: function(xpcom) {
-      return null;
-    },
-    isEqual: function(a, b) {
-      return true;
-    },
-    subtract: function(a, b) {
-      return true;
-    },
-    importChildren: function(parent, children) {
-      parent.children = children;
-    },
-  })
 };
 
 
 /**
  * A monitor for a set of probes.
  *
  * Keeping probes active when they are unused is often a bad
  * idea for performance reasons. Upon destruction, or whenever
@@ -689,42 +652,26 @@ function PerformanceDiff(current, old = 
  *
  * @param {nsIPerformanceSnapshot} xpcom The data acquired from this process.
  * @param {Array<Object>} childProcesses The data acquired from children processes.
  * @param {Array<Probe>} probes The active probes.
  */
 function Snapshot({xpcom, childProcesses, probes}) {
   this.componentsData = [];
 
-  // Current process
+  // Parent process
   if (xpcom) {
-    let children = new Map();
     let enumeration = xpcom.getComponentsData().enumerate();
     while (enumeration.hasMoreElements()) {
       let xpcom = enumeration.getNext().QueryInterface(Ci.nsIPerformanceStats);
-      let stat = new PerformanceData({xpcom, probes});
-      if (!stat.parentId) {
-        this.componentsData.push(stat);
-      } else {
-        let siblings = children.get(stat.parentId);
-        if (!siblings) {
-          children.set(stat.parentId, (siblings = []));
-        }
-        siblings.push(stat);
-      }
-    }
-
-    for (let parent of this.componentsData) {
-      for (let probe of probes) {
-        probe.importChildren(parent, children.get(parent.groupId) || []);
-      }
+      this.componentsData.push(new PerformanceData({xpcom, probes}));
     }
   }
 
-  // Child processes
+  // Content processes
   if (childProcesses) {
     for (let {componentsData} of childProcesses) {
       // We are only interested in `componentsData` for the time being.
       for (let json of componentsData) {
         this.componentsData.push(new PerformanceData({json, probes}));
       }
     }
   }
--- a/toolkit/components/perfmonitoring/nsIPerformanceStats.idl
+++ b/toolkit/components/perfmonitoring/nsIPerformanceStats.idl
@@ -12,42 +12,32 @@
  * 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, a module or the entire process itself.
+ * page, system built-ins, or the entire process itself.
  *
  * All values are monotonic and are updated only when
  * `nsIPerformanceStatsService.isStopwatchActive` is `true`.
  */
-[scriptable, uuid(1bc2d016-e9ae-4186-97c6-9478eddda245)]
+[scriptable, uuid(47f8d36d-1d67-43cb-befd-d2f4720ac568)]
 interface nsIPerformanceStats: nsISupports {
   /**
    * An identifier unique to the component.
    *
    * This identifier is somewhat human-readable to aid with debugging,
    * but clients should not rely upon the format.
    */
   readonly attribute AString groupId;
 
   /**
-   * If this component is part of a larger component, the larger
-   * component. Otherwise, null.
-   *
-   * As of this writing, there can be at most two levels of components:
-   * - compartments (a single module, iframe, etc.);
-   * - groups (an entire add-on, an entire webpage, etc.).
-   */
-  readonly attribute AString parentId;
-
-  /**
    * 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.
    */
   readonly attribute AString name;
 
@@ -117,36 +107,29 @@ 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(60973d54-13e2-455c-a3c6-84dea5dfc8b9)]
+[scriptable, builtinclass, uuid(0469e6af-95c3-4961-a385-4bc009128939)]
 interface nsIPerformanceStatsService : nsISupports {
   /**
    * `true` if we should monitor CPOW, `false` otherwise.
    */
   [implicit_jscontext] attribute bool isMonitoringCPOW;
 
   /**
    * `true` if we should monitor jank, `false` otherwise.
    */
   [implicit_jscontext] attribute bool isMonitoringJank;
 
   /**
-   * `true` if all compartments need to be monitored individually,
-   * `false` if only performance groups (i.e. entire add-ons, entire
-   * webpages, etc.) need to be monitored.
-   */
-  [implicit_jscontext] attribute bool isMonitoringPerCompartment;
-
-  /**
    * Capture a snapshot of the performance data.
    */
   [implicit_jscontext] nsIPerformanceSnapshot getSnapshot();
 };
 
 %{C++
 #define NS_TOOLKIT_PERFORMANCESTATSSERVICE_CID {0xfd7435d4, 0x9ec4, 0x4699, \
       {0xad, 0xd4, 0x1b, 0xe8, 0x3d, 0xd6, 0x8e, 0xf3} }
--- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
+++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
@@ -25,35 +25,30 @@
 #include "windows.h"
 #else
 #include <unistd.h>
 #endif
 
 class nsPerformanceStats: public nsIPerformanceStats {
 public:
   nsPerformanceStats(const nsAString& aName,
-                     nsIPerformanceStats* aParent,
                      const nsAString& aGroupId,
                      const nsAString& aAddonId,
                      const nsAString& aTitle,
                      const uint64_t aWindowId,
                      const bool aIsSystem,
                      const js::PerformanceData& aPerformanceData)
     : mName(aName)
     , mGroupId(aGroupId)
     , mAddonId(aAddonId)
     , mTitle(aTitle)
     , mWindowId(aWindowId)
     , mIsSystem(aIsSystem)
     , mPerformanceData(aPerformanceData)
   {
-    if (aParent) {
-      mozilla::DebugOnly<nsresult> rv = aParent->GetGroupId(mParentId);
-      MOZ_ASSERT(NS_SUCCEEDED(rv));
-    }
   }
   explicit nsPerformanceStats() {}
 
   NS_DECL_ISUPPORTS
 
   /* readonly attribute AString name; */
   NS_IMETHOD GetName(nsAString& aName) override {
     aName.Assign(mName);
@@ -61,22 +56,16 @@ public:
   };
 
   /* readonly attribute AString groupId; */
   NS_IMETHOD GetGroupId(nsAString& aGroupId) override {
     aGroupId.Assign(mGroupId);
     return NS_OK;
   };
 
-  /* readonly attribute AString parentId; */
-  NS_IMETHOD GetParentId(nsAString& aParentId) override {
-    aParentId.Assign(mParentId);
-    return NS_OK;
-  };
-
   /* readonly attribute AString addonId; */
   NS_IMETHOD GetAddonId(nsAString& aAddonId) override {
     aAddonId.Assign(mAddonId);
     return NS_OK;
   };
 
   /* readonly attribute uint64_t windowId; */
   NS_IMETHOD GetWindowId(uint64_t *aWindowId) override {
@@ -130,17 +119,16 @@ public:
     for (size_t i = 0; i < length; ++i) {
       (*aNumberOfOccurrences)[i] = mPerformanceData.durations[i];
     }
     return NS_OK;
   };
 
 private:
   nsString mName;
-  nsString mParentId;
   nsString mGroupId;
   nsString mAddonId;
   nsString mTitle;
   uint64_t mWindowId;
   bool mIsSystem;
 
   js::PerformanceData mPerformanceData;
 
@@ -166,56 +154,45 @@ private:
    *
    * 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.
    */
-  already_AddRefed<nsIPerformanceStats> ImportStats(JSContext* cx, const js::PerformanceData& data, uint64_t uid, nsIPerformanceStats* parent);
+  already_AddRefed<nsIPerformanceStats> ImportStats(JSContext* cx, const js::PerformanceData& data, uint64_t uid);
 
   /**
    * Callbacks for iterating through the `PerformanceStats` of a runtime.
    */
-  bool IterPerformanceStatsCallbackInternal(JSContext* cx,
-                                            const js::PerformanceData& ownStats, const uint64_t ownId,
-                                            const uint64_t* parentId);
-  static bool IterPerformanceStatsCallback(JSContext* cx,
-                                           const js::PerformanceData& ownStats, const uint64_t ownId,
-                                           const uint64_t* parentId,
-                                           void* self);
+  bool IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats, uint64_t uid);
+  static bool IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, uint64_t uid, void* self);
 
   // 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);
   void GetGroupId(JSContext*,
                   uint64_t uid,
                   nsString& groupId);
-
-  static void GetName(JSContext*,
-                      JS::Handle<JSObject*> global,
-                      nsString& name);
-
   // 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;
-  nsBaseHashtable<nsUint64HashKey, nsCOMPtr<nsIPerformanceStats>, nsCOMPtr<nsIPerformanceStats> > mCachedStats;
   uint64_t mProcessId;
 };
 
 NS_IMPL_ISUPPORTS(nsPerformanceSnapshot, nsIPerformanceSnapshot)
 
 nsPerformanceSnapshot::nsPerformanceSnapshot()
 {
 }
@@ -291,54 +268,18 @@ nsPerformanceSnapshot::GetGroupId(JSCont
 
 /* static */ bool
 nsPerformanceSnapshot::GetIsSystem(JSContext*,
                                    JS::Handle<JSObject*> global)
 {
   return nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global));
 }
 
-/* static */
-void
-nsPerformanceSnapshot::GetName(JSContext* cx,
-                               JS::Handle<JSObject*> global,
-                               nsString& name)
-{
-  nsAutoCString cname;
-  do {
-    // Attempt to use the URL as name.
-    nsCOMPtr<nsIPrincipal> principal = nsContentUtils::ObjectPrincipal(global);
-    if (!principal) {
-      break;
-    }
-
-    nsCOMPtr<nsIURI> uri;
-    nsresult rv = principal->GetURI(getter_AddRefs(uri));
-    if (NS_FAILED(rv) || !uri) {
-      break;
-    }
-
-    rv = uri->GetSpec(cname);
-    if (NS_FAILED(rv)) {
-      break;
-    }
-
-    name.Assign(NS_ConvertUTF8toUTF16(cname));
-    return;
-  } while(false);
-  xpc::GetCurrentCompartmentName(cx, cname);
-  name.Assign(NS_ConvertUTF8toUTF16(cname));
-}
-
 already_AddRefed<nsIPerformanceStats>
-nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& performance, const uint64_t uid, nsIPerformanceStats* parent) {
-  if (performance.ticks == 0) {
-    // Ignore compartments with no activity.
-    return nullptr;
-  }
+nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& performance, const uint64_t uid) {
   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;
   }
 
@@ -347,63 +288,53 @@ nsPerformanceSnapshot::ImportStats(JSCon
 
   nsString addonId;
   GetAddonId(cx, global, addonId);
 
   nsString title;
   uint64_t windowId;
   GetWindowData(cx, title, &windowId);
 
-  nsString name;
-  GetName(cx, global, name);
+  nsAutoString name;
+  nsAutoCString cname;
+  xpc::GetCurrentCompartmentName(cx, cname);
+  name.Assign(NS_ConvertUTF8toUTF16(cname));
 
   bool isSystem = GetIsSystem(cx, global);
 
   nsCOMPtr<nsIPerformanceStats> result =
-    new nsPerformanceStats(name, parent, groupId, addonId, title, windowId, isSystem, performance);
+    new nsPerformanceStats(name, groupId, addonId, title, windowId, isSystem, performance);
   return result.forget();
 }
 
 /*static*/ bool
-nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx,
-                                                    const js::PerformanceData& stats, const uint64_t id,
-                                                    const uint64_t* parentId,
-                                                    void* self) {
-  return reinterpret_cast<nsPerformanceSnapshot*>(self)->IterPerformanceStatsCallbackInternal(cx, stats, id, parentId);
+nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, const uint64_t uid, void* self) {
+  return reinterpret_cast<nsPerformanceSnapshot*>(self)->IterPerformanceStatsCallbackInternal(cx, stats, uid);
 }
 
 bool
-nsPerformanceSnapshot::IterPerformanceStatsCallbackInternal(JSContext* cx,
-                                                            const js::PerformanceData& stats, const uint64_t id,
-                                                            const uint64_t* parentId) {
-
-  nsCOMPtr<nsIPerformanceStats> parent = parentId ? mCachedStats.Get(*parentId) : nullptr;
-  nsCOMPtr<nsIPerformanceStats> result = ImportStats(cx, stats, id, parent);
+nsPerformanceSnapshot::IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats, const uint64_t uid) {
+  nsCOMPtr<nsIPerformanceStats> result = ImportStats(cx, stats, uid);
   if (result) {
     mComponentsData.AppendElement(result);
-    mCachedStats.Put(id, result);
   }
 
   return true;
 }
 
 nsresult
 nsPerformanceSnapshot::Init(JSContext* cx, uint64_t processId) {
   mProcessId = processId;
   js::PerformanceData processStats;
   if (!js::IterPerformanceStats(cx, nsPerformanceSnapshot::IterPerformanceStatsCallback, &processStats, this)) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  nsAutoString processGroupId;
-  processGroupId.AssignLiteral("process: ");
-  processGroupId.AppendInt(processId);
   mProcessData = new nsPerformanceStats(NS_LITERAL_STRING("<process>"), // name
-                                        nullptr,                        // parent
-                                        processGroupId,                 // group id
+                                        NS_LITERAL_STRING("<process:?>"), // group id
                                         NS_LITERAL_STRING(""),          // add-on id
                                         NS_LITERAL_STRING(""),          // title
                                         0,                              // window id
                                         true,                           // isSystem
                                         processStats);
   return NS_OK;
 }
 
@@ -469,30 +400,16 @@ NS_IMETHODIMP nsPerformanceStatsService:
 NS_IMETHODIMP nsPerformanceStatsService::SetIsMonitoringJank(JSContext* cx, bool aIsStopwatchActive)
 {
   JSRuntime *runtime = JS_GetRuntime(cx);
   if (!js::SetStopwatchIsMonitoringJank(runtime, aIsStopwatchActive)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   return NS_OK;
 }
-NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringPerCompartment(JSContext* cx, bool *aIsStopwatchActive)
-{
-  JSRuntime *runtime = JS_GetRuntime(cx);
-  *aIsStopwatchActive = js::GetStopwatchIsMonitoringPerCompartment(runtime);
-  return NS_OK;
-}
-NS_IMETHODIMP nsPerformanceStatsService::SetIsMonitoringPerCompartment(JSContext* cx, bool aIsStopwatchActive)
-{
-  JSRuntime *runtime = JS_GetRuntime(cx);
-  if (!js::SetStopwatchIsMonitoringPerCompartment(runtime, aIsStopwatchActive)) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-  return NS_OK;
-}
 
 /* readonly attribute nsIPerformanceSnapshot snapshot; */
 NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot)
 {
   nsRefPtr<nsPerformanceSnapshot> snapshot = new nsPerformanceSnapshot();
   nsresult rv = snapshot->Init(cx, mProcessId);
   if (NS_FAILED(rv)) {
     return rv;
--- a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js
+++ b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js
@@ -23,17 +23,17 @@ function frameScript() {
     const { utils: Cu, classes: Cc, interfaces: Ci } = Components;
     Cu.import("resource://gre/modules/PerformanceStats.jsm");
 
     let performanceStatsService =
       Cc["@mozilla.org/toolkit/performance-stats-service;1"].
       getService(Ci.nsIPerformanceStatsService);
 
     // Make sure that the stopwatch is now active.
-    let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks", "compartments"]);
+    let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks"]);
 
     addMessageListener("compartments-test:getStatistics", () => {
       try {
         monitor.promiseSnapshot().then(snapshot => {
           sendAsyncMessage("compartments-test:getStatistics", snapshot);
         });
       } catch (ex) {
         Cu.reportError("Error in content (getStatistics): " + ex);
@@ -153,47 +153,27 @@ function monotinicity_tester(source, tes
     // Sanity check on components data.
     let map = new Map();
     for (let item of snapshot.componentsData) {
       for (let [probe, k] of [
         ["jank", "totalUserTime"],
         ["jank", "totalSystemTime"],
         ["cpow", "totalCPOWTime"]
       ]) {
-        // Note that we cannot expect components data to be always smaller
-        // than process data, as `getrusage` & co are not monotonic.
-        SilentAssert.leq(item[probe][k], 2 * snapshot.processData[probe][k],
-          `Sanity check (${testName}): ${k} of component is not impossibly larger than that of process`);
+        SilentAssert.leq(item[probe][k], snapshot.processData[probe][k],
+          `Sanity check (${testName}): component has a lower ${k} than process`);
       }
 
       let key = item.groupId;
       if (map.has(key)) {
         let old = map.get(key);
         Assert.ok(false, `Component ${key} has already been seen. Latest: ${item.title||item.addonId||item.name}, previous: ${old.title||old.addonId||old.name}`);
       }
       map.set(key, item);
     }
-    for (let item of snapshot.componentsData) {
-      if (!item.parentId) {
-        continue;
-      }
-      let parent = map.get(item.parentId);
-      SilentAssert.ok(parent, `The parent exists ${item.parentId}`);
-
-      for (let [probe, k] of [
-        ["jank", "totalUserTime"],
-        ["jank", "totalSystemTime"],
-        ["cpow", "totalCPOWTime"]
-      ]) {
-        // Note that we cannot expect components data to be always smaller
-        // than parent data, as `getrusage` & co are not monotonic.
-        SilentAssert.leq(item[probe][k], 2 * parent[probe][k],
-          `Sanity check (${testName}): ${k} of component is not impossibly larger than that of parent`);
-      }
-    }
     for (let [key, item] of map) {
       sanityCheck(previous.componentsMap.get(key), item);
       previous.componentsMap.set(key, item);
     }
   });
   let interval = window.setInterval(frameCheck, 300);
   registerCleanupFunction(() => {
     window.clearInterval(interval);
--- a/toolkit/components/perfmonitoring/tests/xpcshell/test_compartments.js
+++ b/toolkit/components/perfmonitoring/tests/xpcshell/test_compartments.js
@@ -14,47 +14,37 @@ let promiseStatistics = Task.async(funct
   yield Promise.resolve(); // Make sure that we wait until
   // statistics have been updated.
   let service = Cc["@mozilla.org/toolkit/performance-stats-service;1"].
     getService(Ci.nsIPerformanceStatsService);
   let snapshot = service.getSnapshot();
   let componentsData = [];
   let componentsEnum = snapshot.getComponentsData().enumerate();
   while (componentsEnum.hasMoreElements()) {
-    let data = componentsEnum.getNext().QueryInterface(Ci.nsIPerformanceStats);
-    let normalized = JSON.parse(JSON.stringify(data));
-    componentsData.push(data);
+    componentsData.push(componentsEnum.getNext().QueryInterface(Ci.nsIPerformanceStats));
   }
   return {
-    processData: JSON.parse(JSON.stringify(snapshot.getProcessData())),
+    processData: snapshot.getProcessData(),
     componentsData
   };
 });
 
 let promiseSetMonitoring = Task.async(function*(to) {
   let service = Cc["@mozilla.org/toolkit/performance-stats-service;1"].
     getService(Ci.nsIPerformanceStatsService);
   service.isMonitoringJank = to;
   service.isMonitoringCPOW = to;
   yield Promise.resolve();
 });
 
-let promiseSetPerCompartment = Task.async(function*(to) {
-  let service = Cc["@mozilla.org/toolkit/performance-stats-service;1"].
-    getService(Ci.nsIPerformanceStatsService);
-  service.isMonitoringPerCompartment = to;
-  yield Promise.resolve();
-});
-
 function getBuiltinStatistics(name, snapshot) {
   let stats = snapshot.componentsData.find(stats =>
     stats.isSystem && !stats.addonId
   );
   do_print(`Built-in statistics for ${name} were ${stats?"":"not "}found`);
-  do_print(JSON.stringify(snapshot.componentsData, null, "\t"));
   return stats;
 }
 
 function burnCPU(ms) {
   do_print("Burning CPU");
   let counter = 0;
   let ignored = [];
   let start = Date.now();
@@ -62,35 +52,27 @@ function burnCPU(ms) {
     ignored.push(0);
     ignored.shift();
     ++counter;
   }
   do_print("Burning CPU over, after " + counter + " iterations");
 }
 
 function ensureEquals(snap1, snap2, name) {
-  for (let k of Object.keys(snap1.processData)) {
-    if (k == "ticks") {
-      // Ticks monitoring cannot be deactivated
-      continue;
-    }
-    Assert.equal(snap1.processData[k], snap2.processData[k], `Same process data value ${k} (${name})`)
-  }
+  Assert.equal(
+    JSON.stringify(snap1.processData),
+    JSON.stringify(snap2.processData),
+    "Same process data: " + name);
   let stats1 = snap1.componentsData.sort((a, b) => a.name <= b.name);
   let stats2 = snap2.componentsData.sort((a, b) => a.name <= b.name);
-  Assert.equal(stats1.length, stats2.length, `Same number of components (${name})`);
-  for (let i = 0; i < stats1.length; ++i) {
-    for (let k of Object.keys(stats1[i])) {
-      if (k == "ticks") {
-        // Ticks monitoring cannot be deactivated
-        continue;
-      }
-      Assert.equal(stats1[i][k], stats1[i][k], `Same component data value ${i} ${k} (${name})`)
-    }
-  }
+  Assert.equal(
+    JSON.stringify(stats1),
+    JSON.stringify(stats2),
+    "Same components data: " + name
+  );
 }
 
 function hasLowPrecision() {
   let [sysName, sysVersion] = [Services.sysinfo.getPropertyAsAString("name"), Services.sysinfo.getPropertyAsDouble("version")];
   do_print(`Running ${sysName} version ${sysVersion}`);
 
   if (sysName == "Windows_NT" && sysVersion < 6) {
     do_print("Running old Windows, need to deactivate tests due to bad precision.");
@@ -101,17 +83,16 @@ function hasLowPrecision() {
     return true;
   }
   do_print("This platform has good precision.")
   return false;
 }
 
 add_task(function* test_measure() {
   let skipPrecisionTests = hasLowPrecision();
-  yield promiseSetPerCompartment(false);
 
   do_print("Burn CPU without the stopwatch");
   yield promiseSetMonitoring(false);
   let stats0 = yield promiseStatistics("Initial state");
   burnCPU(300);
   let stats1 = yield promiseStatistics("Initial state + burn, without stopwatch");
 
   do_print("Burn CPU with the stopwatch");
@@ -151,19 +132,9 @@ add_task(function* test_measure() {
     do_print("Skipping totalUserTime check under Windows XP, as timer is not always updated by the OS.")
   } else {
     Assert.ok(builtin2.totalUserTime - builtin1.totalUserTime >= 10000, `At least 10ms counted for built-in statistics (${builtin2.totalUserTime - builtin1.totalUserTime})`);
   }
   Assert.equal(builtin2.totalCPOWTime, builtin1.totalCPOWTime, "We haven't used any CPOW time during the first burn for the built-in");
   Assert.equal(builtin2.totalCPOWTime, builtin1.totalCPOWTime, "No CPOW for built-in statistics");
   Assert.equal(builtin4.totalUserTime, builtin3.totalUserTime, "After deactivating the stopwatch, we didn't count any time for the built-in");
   Assert.equal(builtin4.totalCPOWTime, builtin3.totalCPOWTime, "After deactivating the stopwatch, we didn't count any CPOW time for the built-in");
-
-  // Ideally, we should be able to look for test_compartments.js, but
-  // it doesn't have its own compartment.
-  for (let stats of [stats1, stats2, stats3, stats4]) {
-    Assert.ok(!stats.componentsData.find(x => x.name.includes("Task.jsm")), "At this stage, Task.jsm doesn't show up in the components data");
-  }
-  yield promiseSetPerCompartment(true);
-  burnCPU(300);
-  let stats5 = yield promiseStatistics("With per-compartment monitoring");
-  Assert.ok(stats5.componentsData.find(x => x.name.includes("Task.jsm")), "With per-compartment monitoring, test_compartments.js shows up");
 });