Backed out changeset 33afbc6c4997 (bug 1208747)
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 21 Oct 2015 11:39:22 +0200
changeset 303855 10f906d69805a39c1f99fbc78f3a6b87a70225c0
parent 303854 237bc8ff140f148172c685e89e8d70c59d975063
child 303856 7e8966f583f3a9563239347176e91ccc96b5e1fd
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1208747
milestone44.0a1
backs out33afbc6c499777e07ae520cd83cbf9f57de252df
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 changeset 33afbc6c4997 (bug 1208747)
js/src/vm/Stopwatch.cpp
js/xpconnect/src/XPCJSRuntime.cpp
toolkit/components/build/nsToolkitCompsModule.cpp
toolkit/components/perfmonitoring/nsIPerformanceStats.idl
toolkit/components/perfmonitoring/nsPerformanceStats.cpp
toolkit/components/perfmonitoring/nsPerformanceStats.h
toolkit/components/telemetry/Histograms.json
--- a/js/src/vm/Stopwatch.cpp
+++ b/js/src/vm/Stopwatch.cpp
@@ -1,13 +1,13 @@
 #include "vm/Stopwatch.h"
 
-#include "mozilla/ArrayUtils.h"
-#include "mozilla/IntegerTypeTraits.h"
-#include "mozilla/unused.h"
+#include <mozilla/ArrayUtils.h>
+#include <mozilla/IntegerTypeTraits.h>
+#include <mozilla/unused.h>
 
 namespace js {
 
 bool
 PerformanceMonitoring::addRecentGroup(PerformanceGroup* group)
 {
   if (group->isUsedInThisIteration())
     return true;
@@ -333,31 +333,29 @@ bool
 AutoStopwatch::addToGroups(uint64_t cyclesDelta, uint64_t CPOWTimeDelta)
 {
   JSRuntime* runtime = cx_->runtime();
 
     for (auto group = groups_.begin(); group < groups_.end(); ++group) {
       if (!addToGroup(runtime, cyclesDelta, CPOWTimeDelta, *group))
         return false;
     }
-    return true;
 }
 
 bool
 AutoStopwatch::addToGroup(JSRuntime* runtime, uint64_t cyclesDelta, uint64_t CPOWTimeDelta, PerformanceGroup* group)
 {
     MOZ_ASSERT(group);
     MOZ_ASSERT(group->isAcquired(iteration_, this));
 
     if (!runtime->performanceMonitoring.addRecentGroup(group))
       return false;
     group->addRecentTicks(iteration_, 1);
     group->addRecentCycles(iteration_, cyclesDelta);
     group->addRecentCPOW(iteration_, CPOWTimeDelta);
-    return true;
 }
 
 uint64_t
 AutoStopwatch::getDelta(const uint64_t end, const uint64_t start) const
 {
     if (start >= end)
       return 0;
     return end - start;
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -3260,16 +3260,57 @@ 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.
+    RefPtr<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)),
@@ -3439,16 +3480,18 @@ 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/toolkit/components/build/nsToolkitCompsModule.cpp
+++ b/toolkit/components/build/nsToolkitCompsModule.cpp
@@ -62,17 +62,17 @@
 
 using namespace mozilla;
 
 /////////////////////////////////////////////////////////////////////////////
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsAppStartup, Init)
 
 #if defined(MOZ_HAS_PERFSTATS)
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPerformanceStatsService, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsPerformanceStatsService)
 #endif // defined (MOZ_HAS_PERFSTATS)
 
 #if defined(MOZ_HAS_TERMINATOR)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTerminator)
 #endif
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUserInfo)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFindService)
--- a/toolkit/components/perfmonitoring/nsIPerformanceStats.idl
+++ b/toolkit/components/perfmonitoring/nsIPerformanceStats.idl
@@ -17,44 +17,64 @@
 
 /**
  * Snapshot of the performance of a component, e.g. an add-on, a web
  * page, system built-ins, a module or the entire process itself.
  *
  * All values are monotonic and are updated only when
  * `nsIPerformanceStatsService.isStopwatchActive` is `true`.
  */
-[scriptable, uuid(f171c901-1888-4087-a002-c2751e510f92)]
+[scriptable, uuid(89440555-dd81-4a22-8747-049bcf0ab586)]
 interface nsIPerformanceStats: nsISupports {
   /**
    * An identifier unique to the component.
    *
    * This identifier is somewhat human-readable to aid with debugging,
    * but clients should not rely upon the format.
    */
   readonly attribute AString groupId;
 
   /**
-   * A somewhat human-readable name for the component.
+   * 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;
 
   /**
    * 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;
 
   /**
@@ -84,17 +104,17 @@ interface nsIPerformanceStats: nsISuppor
    */
   void getDurations([optional] out unsigned long aCount,
                     [retval, array, size_is(aCount)]out unsigned long long aNumberOfOccurrences);
 };
 
 /**
  * A snapshot of the performance data of the process.
  */
-[scriptable, uuid(0ac38e2a-2613-4e3f-9f21-95f085c177de)]
+[scriptable, uuid(2e0c50e2-3aff-4cc8-88a6-c0dc200da8fc)]
 interface nsIPerformanceSnapshot: nsISupports {
   /**
    * Data on all individual components.
    */
   nsIArray getComponentsData();
 
   /**
    * Information on the process itself.
@@ -102,17 +122,17 @@ 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(aad18f7c-9ff7-4e22-8cd1-60ab0b57c698)]
+[scriptable, builtinclass, uuid(60973d54-13e2-455c-a3c6-84dea5dfc8b9)]
 interface nsIPerformanceStatsService : nsISupports {
   /**
    * `true` if we should monitor CPOW, `false` otherwise.
    */
   [implicit_jscontext] attribute bool isMonitoringCPOW;
 
   /**
    * `true` if we should monitor jank, `false` otherwise.
--- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
+++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
@@ -7,610 +7,511 @@
 
 #include "nsMemory.h"
 #include "nsLiteralString.h"
 #include "nsCRTGlue.h"
 #include "nsServiceManagerUtils.h"
 
 #include "nsCOMArray.h"
 #include "nsIMutableArray.h"
-#include "nsReadableUtils.h"
 
 #include "jsapi.h"
 #include "nsJSUtils.h"
 #include "xpcpublic.h"
 #include "jspubtd.h"
 
 #include "nsIDOMWindow.h"
 #include "nsGlobalWindow.h"
 
 #include "mozilla/unused.h"
-#include "mozilla/ArrayUtils.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 
 #if defined(XP_WIN)
-#include <processthreadsapi.h>
-#include <windows.h>
+#include "windows.h"
 #else
 #include <unistd.h>
-#endif // defined(XP_WIN)
-
-#if defined(XP_MACOSX)
-#include <mach/mach_init.h>
-#include <mach/mach_interface.h>
-#include <mach/mach_port.h>
-#include <mach/mach_types.h>
-#include <mach/message.h>
-#include <mach/thread_info.h>
-#endif // defined(XP_MACOSX)
-
-#if defined(XP_LINUX)
-#include <sys/time.h>
-#include <sys/resource.h>
-#endif // defined(XP_LINUX)
-/* ------------------------------------------------------
- *
- * Utility functions.
- *
- */
-
-namespace {
-
-/**
- * Get the private window for the current compartment.
- *
- * @return null if the code is not executed in a window or in
- * case of error, a nsPIDOMWindow otherwise.
- */
-already_AddRefed<nsPIDOMWindow>
-GetPrivateWindow(JSContext* cx) {
-  nsCOMPtr<nsPIDOMWindow> win = xpc::CurrentWindowOrNull(cx);
-  if (!win) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIDOMWindow> top;
-  nsresult rv = win->GetTop(getter_AddRefs(top));
-  if (!top || NS_FAILED(rv)) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsPIDOMWindow> ptop = do_QueryInterface(top);
-  if (!ptop) {
-    return nullptr;
-  }
-  return ptop.forget();
-}
-
-bool
-URLForGlobal(JSContext* cx, JS::Handle<JSObject*> global, nsAString& url) {
-  nsCOMPtr<nsIPrincipal> principal = nsContentUtils::ObjectPrincipal(global);
-  if (!principal) {
-    return false;
-  }
-
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = principal->GetURI(getter_AddRefs(uri));
-  if (NS_FAILED(rv) || !uri) {
-    return false;
-  }
-
-  nsAutoCString spec;
-  rv = uri->GetSpec(spec);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-
-  url.Assign(NS_ConvertUTF8toUTF16(spec));
-  return true;
-}
-
-/**
- * Extract a somewhat human-readable name from the current context.
- */
-void
-CompartmentName(JSContext* cx, JS::Handle<JSObject*> global, nsAString& name) {
-  // Attempt to use the URL as name.
-  if (URLForGlobal(cx, global, name)) {
-    return;
-  }
-
-  // Otherwise, fallback to XPConnect's less readable but more
-  // complete naming scheme.
-  nsAutoCString cname;
-  xpc::GetCurrentCompartmentName(cx, cname);
-  name.Assign(NS_ConvertUTF8toUTF16(cname));
-}
+#endif
 
-/**
- * Generate a unique-to-the-application identifier for a group.
- */
-void
-GenerateUniqueGroupId(const JSRuntime* rt, uint64_t uid, uint64_t processId, nsAString& groupId) {
-  uint64_t runtimeId = reinterpret_cast<uintptr_t>(rt);
-
-  groupId.AssignLiteral("process: ");
-  groupId.AppendInt(processId);
-  groupId.AppendLiteral(", thread: ");
-  groupId.AppendInt(runtimeId);
-  groupId.AppendLiteral(", group: ");
-  groupId.AppendInt(uid);
-}
-
-} // namespace
-
-
-
-/* ------------------------------------------------------
- *
- * struct PerformanceData
- *
- */
-
-PerformanceData::PerformanceData()
-  : mTotalUserTime(0)
-  , mTotalSystemTime(0)
-  , mTotalCPOWTime(0)
-  , mTicks(0)
-{
-  mozilla::PodArrayZero(mDurations);
-}
-
-/* ------------------------------------------------------
- *
- * class nsPerformanceGroupDetails
- *
- */
-
-const nsAString&
-nsPerformanceGroupDetails::Name() const {
-  return mName;
-}
-
-const nsAString&
-nsPerformanceGroupDetails::GroupId() const {
-  return mGroupId;
-}
-
-const nsAString&
-nsPerformanceGroupDetails::AddonId() const {
-  return mAddonId;
-}
-
-uint64_t
-nsPerformanceGroupDetails::WindowId() const {
-  return mWindowId;
-}
-
-uint64_t
-nsPerformanceGroupDetails::ProcessId() const {
-  return mProcessId;
-}
-
-bool
-nsPerformanceGroupDetails::IsSystem() const {
-  return mIsSystem;
-}
-
-bool
-nsPerformanceGroupDetails::IsAddon() const {
-  return mAddonId.Length() != 0;
-}
-
-bool
-nsPerformanceGroupDetails::IsWindow() const {
-  return mWindowId != 0;
-}
-
-/* ------------------------------------------------------
- *
- * struct nsPerformanceStats
- *
- */
-
-class nsPerformanceStats: public nsIPerformanceStats,
-                          public nsPerformanceGroupDetails
-{
+class nsPerformanceStats: public nsIPerformanceStats {
 public:
   nsPerformanceStats(const nsAString& aName,
+                     nsIPerformanceStats* aParent,
                      const nsAString& aGroupId,
                      const nsAString& aAddonId,
+                     const nsAString& aTitle,
                      const uint64_t aWindowId,
                      const uint64_t aProcessId,
                      const bool aIsSystem,
-                     const PerformanceData& aPerformanceData)
-    : nsPerformanceGroupDetails(aName, aGroupId, aAddonId, aWindowId, aProcessId, aIsSystem)
+                     const js::PerformanceData& aPerformanceData)
+    : mName(aName)
+    , mGroupId(aGroupId)
+    , mAddonId(aAddonId)
+    , mTitle(aTitle)
+    , mWindowId(aWindowId)
+    , mProcessId(aProcessId)
+    , mIsSystem(aIsSystem)
     , mPerformanceData(aPerformanceData)
   {
+    if (aParent) {
+      mozilla::DebugOnly<nsresult> rv = aParent->GetGroupId(mParentId);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
+    }
   }
-  nsPerformanceStats(const nsPerformanceGroupDetails& item,
-                     const PerformanceData& aPerformanceData)
-    : nsPerformanceGroupDetails(item)
-    , mPerformanceData(aPerformanceData)
-  {
-  }
+  explicit nsPerformanceStats() {}
 
   NS_DECL_ISUPPORTS
 
   /* readonly attribute AString name; */
   NS_IMETHOD GetName(nsAString& aName) override {
-    aName.Assign(nsPerformanceGroupDetails::Name());
+    aName.Assign(mName);
     return NS_OK;
   };
 
   /* readonly attribute AString groupId; */
   NS_IMETHOD GetGroupId(nsAString& aGroupId) override {
-    aGroupId.Assign(nsPerformanceGroupDetails::GroupId());
+    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(nsPerformanceGroupDetails::AddonId());
+    aAddonId.Assign(mAddonId);
     return NS_OK;
   };
 
   /* readonly attribute uint64_t windowId; */
   NS_IMETHOD GetWindowId(uint64_t *aWindowId) override {
-    *aWindowId = nsPerformanceGroupDetails::WindowId();
+    *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 = nsPerformanceGroupDetails::IsSystem();
+    *_retval = mIsSystem;
     return NS_OK;
   }
 
   /* readonly attribute unsigned long long totalUserTime; */
   NS_IMETHOD GetTotalUserTime(uint64_t *aTotalUserTime) override {
-    *aTotalUserTime = mPerformanceData.mTotalUserTime;
+    *aTotalUserTime = mPerformanceData.totalUserTime;
     return NS_OK;
   };
 
   /* readonly attribute unsigned long long totalSystemTime; */
   NS_IMETHOD GetTotalSystemTime(uint64_t *aTotalSystemTime) override {
-    *aTotalSystemTime = mPerformanceData.mTotalSystemTime;
+    *aTotalSystemTime = mPerformanceData.totalSystemTime;
     return NS_OK;
   };
 
   /* readonly attribute unsigned long long totalCPOWTime; */
   NS_IMETHOD GetTotalCPOWTime(uint64_t *aCpowTime) override {
-    *aCpowTime = mPerformanceData.mTotalCPOWTime;
+    *aCpowTime = mPerformanceData.totalCPOWTime;
     return NS_OK;
   };
 
   /* readonly attribute unsigned long long ticks; */
   NS_IMETHOD GetTicks(uint64_t *aTicks) override {
-    *aTicks = mPerformanceData.mTicks;
+    *aTicks = mPerformanceData.ticks;
     return NS_OK;
   };
 
   /* void getDurations (out unsigned long aCount, [array, size_is (aCount), retval] out unsigned long long aNumberOfOccurrences); */
   NS_IMETHOD GetDurations(uint32_t *aCount, uint64_t **aNumberOfOccurrences) override {
-    const size_t length = mozilla::ArrayLength(mPerformanceData.mDurations);
+    const size_t length = mozilla::ArrayLength(mPerformanceData.durations);
     if (aCount) {
       *aCount = length;
     }
     *aNumberOfOccurrences = new uint64_t[length];
     for (size_t i = 0; i < length; ++i) {
-      (*aNumberOfOccurrences)[i] = mPerformanceData.mDurations[i];
+      (*aNumberOfOccurrences)[i] = mPerformanceData.durations[i];
     }
     return NS_OK;
   };
 
   /*
     readonly attribute unsigned long long processId;
   */
   NS_IMETHODIMP GetProcessId(uint64_t* processId) override {
-    *processId = nsPerformanceGroupDetails::ProcessId();
+    *processId = mProcessId;
     return NS_OK;
   }
 
 private:
-  PerformanceData mPerformanceData;
+  nsString mName;
+  nsString mParentId;
+  nsString mGroupId;
+  nsString mAddonId;
+  nsString mTitle;
+  uint64_t mWindowId;
+  uint64_t mProcessId;
+  bool mIsSystem;
+
+  js::PerformanceData mPerformanceData;
 
   virtual ~nsPerformanceStats() {}
 };
 
 NS_IMPL_ISUPPORTS(nsPerformanceStats, nsIPerformanceStats)
 
 
-/* ------------------------------------------------------
- *
- * struct nsPerformanceSnapshot
- *
- */
-
 class nsPerformanceSnapshot : public nsIPerformanceSnapshot
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERFORMANCESNAPSHOT
 
-  nsPerformanceSnapshot() {}
+  nsPerformanceSnapshot();
+  nsresult Init(JSContext*, uint64_t processId);
+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.
+   */
+  already_AddRefed<nsIPerformanceStats> ImportStats(JSContext* cx, const js::PerformanceData& data, uint64_t uid, nsIPerformanceStats* parent);
 
   /**
-   * Append statistics to the list of components data.
+   * Callbacks for iterating through the `PerformanceStats` of a runtime.
    */
-  void AppendComponentsStats(nsIPerformanceStats* stats);
+  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);
 
-  /**
-   * Set the statistics attached to process data.
-   */
-  void SetProcessStats(nsIPerformanceStats* group);
+  // 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:
-  virtual ~nsPerformanceSnapshot() {}
-
-private:
-  /**
-   * The data for all components.
-   */
   nsCOMArray<nsIPerformanceStats> mComponentsData;
-
-  /**
-   * The data for the process.
-   */
   nsCOMPtr<nsIPerformanceStats> mProcessData;
+  nsBaseHashtable<nsUint64HashKey, nsCOMPtr<nsIPerformanceStats>, nsCOMPtr<nsIPerformanceStats> > mCachedStats;
+  uint64_t mProcessId;
 };
 
 NS_IMPL_ISUPPORTS(nsPerformanceSnapshot, nsIPerformanceSnapshot)
 
+nsPerformanceSnapshot::nsPerformanceSnapshot()
+{
+}
 
-/* nsIArray getComponentsData (); */
-NS_IMETHODIMP
-nsPerformanceSnapshot::GetComponentsData(nsIArray * *aComponents)
+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;
+  }
+
+  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);
+}
+
+void
+nsPerformanceSnapshot::GetGroupId(JSContext* cx,
+                                  uint64_t uid,
+                                  nsString& groupId)
+{
+  JSRuntime* rt = JS_GetRuntime(cx);
+  uint64_t runtimeId = reinterpret_cast<uintptr_t>(rt);
+
+  groupId.AssignLiteral("process: ");
+  groupId.AppendInt(mProcessId);
+  groupId.AppendLiteral(", thread: ");
+  groupId.AppendInt(runtimeId);
+  groupId.AppendLiteral(", group: ");
+  groupId.AppendInt(uid);
+}
+
+/* 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;
+  }
+  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 groupId;
+  GetGroupId(cx, uid, groupId);
+
+  nsString addonId;
+  GetAddonId(cx, global, addonId);
+
+  nsString title;
+  uint64_t windowId;
+  GetWindowData(cx, title, &windowId);
+
+  nsString name;
+  GetName(cx, global, name);
+
+  bool isSystem = GetIsSystem(cx, global);
+
+  nsCOMPtr<nsIPerformanceStats> result =
+    new nsPerformanceStats(name, parent, groupId, addonId, title, windowId, mProcessId, isSystem, performance);
+  return result.forget();
+}
+
+/*static*/ bool
+nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx,
+                                                    const js::PerformanceData& stats, const uint64_t id,
+                                                    const uint64_t* parentId,
+                                                    void* self) {
+  return reinterpret_cast<nsPerformanceSnapshot*>(self)->IterPerformanceStatsCallbackInternal(cx, stats, id, parentId);
+}
+
+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);
+  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(""),          // add-on id
+                                        NS_LITERAL_STRING(""),          // title
+                                        0,                              // window id
+                                        mProcessId,                     // process id
+                                        true,                           // isSystem
+                                        processStats);
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP nsPerformanceSnapshot::GetComponentsData(nsIArray * *aComponents)
 {
   const size_t length = mComponentsData.Length();
   nsCOMPtr<nsIMutableArray> components = do_CreateInstance(NS_ARRAY_CONTRACTID);
   for (size_t i = 0; i < length; ++i) {
     nsCOMPtr<nsIPerformanceStats> stats = mComponentsData[i];
     mozilla::DebugOnly<nsresult> rv = components->AppendElement(stats, false);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
   components.forget(aComponents);
   return NS_OK;
 }
 
-/* nsIPerformanceStats getProcessData (); */
-NS_IMETHODIMP
-nsPerformanceSnapshot::GetProcessData(nsIPerformanceStats * *aProcess)
+NS_IMETHODIMP nsPerformanceSnapshot::GetProcessData(nsIPerformanceStats * *aProcess)
 {
   NS_IF_ADDREF(*aProcess = mProcessData);
   return NS_OK;
 }
 
-void
-nsPerformanceSnapshot::AppendComponentsStats(nsIPerformanceStats* stats)
-{
-  mComponentsData.AppendElement(stats);
-}
 
-void
-nsPerformanceSnapshot::SetProcessStats(nsIPerformanceStats* stats)
-{
-  mProcessData = stats;
-}
-
-/* ------------------------------------------------------
- *
- * class nsPerformanceStatsService
- *
- */
-
-NS_IMPL_ISUPPORTS(nsPerformanceStatsService, nsIPerformanceStatsService, nsIObserver)
+NS_IMPL_ISUPPORTS(nsPerformanceStatsService, nsIPerformanceStatsService)
 
 nsPerformanceStatsService::nsPerformanceStatsService()
 #if defined(XP_WIN)
   : mProcessId(GetCurrentProcessId())
 #else
   : mProcessId(getpid())
 #endif
-  , mRuntime(xpc::GetJSRuntime())
-  , mUIdCounter(0)
-  , mTopGroup(nsPerformanceGroup::Make(mRuntime,
-                                       this,
-                                       NS_LITERAL_STRING("<process>"), // name
-                                       NS_LITERAL_STRING(""),          // addonid
-                                       0,    // windowId
-                                       mProcessId,
-                                       true, // isSystem
-                                       nsPerformanceGroup::GroupScope::RUNTIME // scope
-                                     ))
   , mProcessStayed(0)
   , mProcessMoved(0)
   , mProcessUpdateCounter(0)
-  , mIsMonitoringPerCompartment(false)
-
-{ }
+{
+}
 
 nsPerformanceStatsService::~nsPerformanceStatsService()
-{ }
-
-/**
- * Clean up the service.
- *
- * Called during shutdown. Idempotent.
- */
-void
-nsPerformanceStatsService::Dispose()
 {
-  // Make sure that we do not accidentally destroy `this` while we are
-  // cleaning up back references.
-  RefPtr<nsPerformanceStatsService> kungFuDeathGrip(this);
-
-  // Disconnect from nsIObserverService.
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (obs) {
-    obs->RemoveObserver(this, "profile-before-change");
-    obs->RemoveObserver(this, "quit-application");
-    obs->RemoveObserver(this, "quit-application-granted");
-    obs->RemoveObserver(this, "content-child-shutdown");
-  }
-
-  // Clear up and disconnect from JSAPI.
-  js::DisposePerformanceMonitoring(mRuntime);
-
-  mozilla::unused << js::SetStopwatchIsMonitoringCPOW(mRuntime, false);
-  mozilla::unused << js::SetStopwatchIsMonitoringJank(mRuntime, false);
-
-  mozilla::unused << js::SetStopwatchStartCallback(mRuntime, nullptr, nullptr);
-  mozilla::unused << js::SetStopwatchCommitCallback(mRuntime, nullptr, nullptr);
-  mozilla::unused << js::SetGetPerformanceGroupsCallback(mRuntime, nullptr, nullptr);
-
-  // At this stage, the JS VM may still be holding references to
-  // instances of PerformanceGroup on the stack. To let the service be
-  // collected, we need to break the references from these groups to
-  // `this`.
-  mTopGroup->Dispose();
-
-  // Copy references to the groups to a vector to ensure that we do
-  // not modify the hashtable while iterating it.
-  GroupVector groups;
-  for (auto iter = mGroups.Iter(); !iter.Done(); iter.Next()) {
-    groups.append(iter.Get()->GetKey());
-  }
-  for (auto iter = groups.begin(); iter < groups.end(); iter++) {
-    RefPtr<nsPerformanceGroup> group = *iter;
-    group->Dispose();
-  }
-
-  // Any remaining references to PerformanceGroup will be released as
-  // the VM unrolls the stack. If there are any nested event loops,
-  // this may take time.
 }
 
-nsresult
-nsPerformanceStatsService::Init()
-{
-  nsresult rv = InitInternal();
-  if (NS_FAILED(rv)) {
-    // Attempt to clean up.
-    Dispose();
-  }
-  return rv;
-}
-
-nsresult
-nsPerformanceStatsService::InitInternal()
-{
-  // Make sure that we release everything during shutdown.
-  // We are a bit defensive here, as we know that some strange behavior can break the
-  // regular shutdown order.
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (obs) {
-    obs->AddObserver(this, "profile-before-change", false);
-    obs->AddObserver(this, "quit-application-granted", false);
-    obs->AddObserver(this, "quit-application", false);
-    obs->AddObserver(this, "content-child-shutdown", false);
-  }
-
-  // Connect to JSAPI.
-  if (!js::SetStopwatchStartCallback(mRuntime, StopwatchStartCallback, this)) {
-    return NS_ERROR_UNEXPECTED;
-  }
-  if (!js::SetStopwatchCommitCallback(mRuntime, StopwatchCommitCallback, this)) {
-    return NS_ERROR_UNEXPECTED;
-  }
-  if (!js::SetGetPerformanceGroupsCallback(mRuntime, GetPerformanceGroupsCallback, this)) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  mTopGroup->setIsActive(true);
-  return NS_OK;
-}
-
-// Observe shutdown events.
-NS_IMETHODIMP
-nsPerformanceStatsService::Observe(nsISupports *aSubject, const char *aTopic,
-                                   const char16_t *aData)
-{
-  MOZ_ASSERT(strcmp(aTopic, "profile-before-change") == 0
-             || strcmp(aTopic, "quit-application") == 0
-             || strcmp(aTopic, "quit-application-granted") == 0
-             || strcmp(aTopic, "content-child-shutdown") == 0);
-
-  Dispose();
-  return NS_OK;
-}
-
-/* [implicit_jscontext] attribute bool isMonitoringCPOW; */
-NS_IMETHODIMP
-nsPerformanceStatsService::GetIsMonitoringCPOW(JSContext* cx, bool *aIsStopwatchActive)
+//[implicit_jscontext] attribute bool isMonitoringCPOW;
+NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringCPOW(JSContext* cx, bool *aIsStopwatchActive)
 {
   JSRuntime *runtime = JS_GetRuntime(cx);
   *aIsStopwatchActive = js::GetStopwatchIsMonitoringCPOW(runtime);
   return NS_OK;
 }
-NS_IMETHODIMP
-nsPerformanceStatsService::SetIsMonitoringCPOW(JSContext* cx, bool aIsStopwatchActive)
+NS_IMETHODIMP nsPerformanceStatsService::SetIsMonitoringCPOW(JSContext* cx, bool aIsStopwatchActive)
 {
   JSRuntime *runtime = JS_GetRuntime(cx);
   if (!js::SetStopwatchIsMonitoringCPOW(runtime, aIsStopwatchActive)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   return NS_OK;
 }
-
-/* [implicit_jscontext] attribute bool isMonitoringJank; */
-NS_IMETHODIMP
-nsPerformanceStatsService::GetIsMonitoringJank(JSContext* cx, bool *aIsStopwatchActive)
+NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringJank(JSContext* cx, bool *aIsStopwatchActive)
 {
   JSRuntime *runtime = JS_GetRuntime(cx);
   *aIsStopwatchActive = js::GetStopwatchIsMonitoringJank(runtime);
   return NS_OK;
 }
-NS_IMETHODIMP
-nsPerformanceStatsService::SetIsMonitoringJank(JSContext* cx, bool aIsStopwatchActive)
+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;
 }
-
-/* [implicit_jscontext] attribute bool isMonitoringPerCompartment; */
-NS_IMETHODIMP
-nsPerformanceStatsService::GetIsMonitoringPerCompartment(JSContext*, bool *aIsMonitoringPerCompartment)
+NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringPerCompartment(JSContext* cx, bool *aIsStopwatchActive)
 {
-  *aIsMonitoringPerCompartment = mIsMonitoringPerCompartment;
+  JSRuntime *runtime = JS_GetRuntime(cx);
+  *aIsStopwatchActive = js::GetStopwatchIsMonitoringPerCompartment(runtime);
   return NS_OK;
 }
-NS_IMETHODIMP
-nsPerformanceStatsService::SetIsMonitoringPerCompartment(JSContext*, bool aIsMonitoringPerCompartment)
+NS_IMETHODIMP nsPerformanceStatsService::SetIsMonitoringPerCompartment(JSContext* cx, bool aIsStopwatchActive)
 {
-  if (aIsMonitoringPerCompartment == mIsMonitoringPerCompartment) {
-    return NS_OK;
+  JSRuntime *runtime = JS_GetRuntime(cx);
+  if (!js::SetStopwatchIsMonitoringPerCompartment(runtime, aIsStopwatchActive)) {
+    return NS_ERROR_OUT_OF_MEMORY;
   }
-
-  // Relatively slow update: walk the entire lost of performance groups,
-  // update the active flag of those that have changed.
-  //
-  // Alternative strategies could be envisioned to make the update
-  // much faster, at the expense of the speed of calling `isActive()`,
-  // (e.g. deferring `isActive()` to the nsPerformanceStatsService),
-  // but we expect that `isActive()` can be called thousands of times
-  // per second, while `SetIsMonitoringPerCompartment` is not called
-  // at all during most Firefox runs.
-
-  for (auto iter = mGroups.Iter(); !iter.Done(); iter.Next()) {
-    RefPtr<nsPerformanceGroup> group = iter.Get()->GetKey();
-    if (group->Scope() == nsPerformanceGroup::GroupScope::COMPARTMENT) {
-      group->setIsActive(aIsMonitoringPerCompartment);
-    }
-  }
-  mIsMonitoringPerCompartment = aIsMonitoringPerCompartment;
   return NS_OK;
 }
 
-nsresult
-nsPerformanceStatsService::UpdateTelemetry()
+nsresult nsPerformanceStatsService::UpdateTelemetry()
 {
   // Promote everything to floating-point explicitly before dividing.
   const double processStayed = mProcessStayed;
   const double processMoved = mProcessMoved;
 
   if (processStayed <= 0 || processMoved <= 0 || processStayed + processMoved <= 0) {
     // Overflow/underflow/nothing to report
     return NS_OK;
@@ -621,398 +522,28 @@ nsPerformanceStatsService::UpdateTelemet
     // Overflow/underflow
     return NS_OK;
   }
 
   mozilla::Telemetry::Accumulate(mozilla::Telemetry::PERF_MONITORING_TEST_CPU_RESCHEDULING_PROPORTION_MOVED, (uint32_t)proportion);
   return NS_OK;
 }
 
-
-/* static */ nsIPerformanceStats*
-nsPerformanceStatsService::GetStatsForGroup(const js::PerformanceGroup* group)
-{
-  return GetStatsForGroup(nsPerformanceGroup::Get(group));
-}
-
-/* static */ nsIPerformanceStats*
-nsPerformanceStatsService::GetStatsForGroup(const nsPerformanceGroup* group)
-{
-  return new nsPerformanceStats(*group, group->data);
-}
-
-/* [implicit_jscontext] nsIPerformanceSnapshot getSnapshot (); */
-NS_IMETHODIMP
-nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot)
+NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot)
 {
   RefPtr<nsPerformanceSnapshot> snapshot = new nsPerformanceSnapshot();
-  snapshot->SetProcessStats(GetStatsForGroup(mTopGroup));
-
-  for (auto iter = mGroups.Iter(); !iter.Done(); iter.Next()) {
-    auto* entry = iter.Get();
-    nsPerformanceGroup* group = entry->GetKey();
-    if (group->isActive()) {
-      snapshot->AppendComponentsStats(GetStatsForGroup(group));
-    }
+  nsresult rv = snapshot->Init(cx, mProcessId);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
   js::GetPerfMonitoringTestCpuRescheduling(JS_GetRuntime(cx), &mProcessStayed, &mProcessMoved);
 
   if (++mProcessUpdateCounter % 10 == 0) {
     mozilla::unused << UpdateTelemetry();
   }
 
   snapshot.forget(aSnapshot);
 
   return NS_OK;
 }
 
-uint64_t
-nsPerformanceStatsService::GetNextId() {
-  return ++mUIdCounter;
-}
 
-/* static*/ bool
-nsPerformanceStatsService::GetPerformanceGroupsCallback(JSContext* cx, JSGroupVector& out, void* closure) {
-  RefPtr<nsPerformanceStatsService> self = reinterpret_cast<nsPerformanceStatsService*>(closure);
-  return self->GetPerformanceGroups(cx, out);
-}
-
-bool
-nsPerformanceStatsService::GetPerformanceGroups(JSContext* cx, JSGroupVector& out) {
-  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 true;
-  }
-
-  // All compartments belong to the top group.
-  out.append(mTopGroup);
-
-  nsAutoString name;
-  CompartmentName(cx, global, name);
-  bool isSystem = nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global));
-
-  // Find out if the compartment is executed by an add-on. If so, its
-  // duration should count towards the total duration of the add-on.
-  JSAddonId* jsaddonId = AddonIdOfObject(global);
-  nsString addonId;
-  if (jsaddonId) {
-    AssignJSFlatString(addonId, (JSFlatString*)jsaddonId);
-    auto entry = mAddonIdToGroup.PutEntry(addonId);
-    if (!entry->mGroup) {
-      nsString addonName = name;
-      addonName.AppendLiteral(" (as addon ");
-      addonName.Append(addonId);
-      addonName.AppendLiteral(")");
-      entry->mGroup =
-        nsPerformanceGroup::Make(mRuntime, this,
-                                 addonName, addonId, 0,
-                                 mProcessId, isSystem,
-                                 nsPerformanceGroup::GroupScope::ADDON);
-    }
-    out.append(entry->mGroup);
-  }
-
-  // Find out if the compartment is executed by a window. If so, its
-  // duration should count towards the total duration of the window.
-  nsCOMPtr<nsPIDOMWindow> ptop = GetPrivateWindow(cx);
-  uint64_t windowId = 0;
-  if (ptop) {
-    windowId = ptop->WindowID();
-    auto entry = mWindowIdToGroup.PutEntry(windowId);
-    if (!entry->mGroup) {
-      nsString windowName = name;
-      windowName.AppendLiteral(" (as window ");
-      windowName.AppendInt(windowId);
-      windowName.AppendLiteral(")");
-      entry->mGroup =
-        nsPerformanceGroup::Make(mRuntime, this,
-                                 windowName, EmptyString(), windowId,
-                                 mProcessId, isSystem,
-                                 nsPerformanceGroup::GroupScope::WINDOW);
-    }
-    out.append(entry->mGroup);
-  }
-
-  // All compartments have their own group.
-  auto group =
-    nsPerformanceGroup::Make(mRuntime, this,
-                             name, addonId, windowId,
-                             mProcessId, isSystem,
-                             nsPerformanceGroup::GroupScope::COMPARTMENT);
-  out.append(group);
-
-  return true;
-}
-
-/*static*/ bool
-nsPerformanceStatsService::StopwatchStartCallback(uint64_t iteration, void* closure) {
-  RefPtr<nsPerformanceStatsService> self = reinterpret_cast<nsPerformanceStatsService*>(closure);
-  return self->StopwatchStart(iteration);
-}
-
-bool
-nsPerformanceStatsService::StopwatchStart(uint64_t iteration) {
-  mIteration = iteration;
-
-  nsresult rv = GetResources(&mUserTimeStart, &mSystemTimeStart);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-
-  return true;
-}
-
-/*static*/ bool
-nsPerformanceStatsService::StopwatchCommitCallback(uint64_t iteration, JSGroupVector& recentGroups, void* closure) {
-  RefPtr<nsPerformanceStatsService> self = reinterpret_cast<nsPerformanceStatsService*>(closure);
-  return self->StopwatchCommit(iteration, recentGroups);
-}
-
-bool
-nsPerformanceStatsService::StopwatchCommit(uint64_t iteration, JSGroupVector& recentGroups)
-{
-  MOZ_ASSERT(iteration == mIteration);
-  MOZ_ASSERT(recentGroups.length() > 0);
-
-  uint64_t userTimeStop, systemTimeStop;
-  nsresult rv = GetResources(&userTimeStop, &systemTimeStop);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-
-  // `GetResources` is not guaranteed to be monotonic, so round up
-  // any negative result to 0 milliseconds.
-  uint64_t userTimeDelta = 0;
-  if (userTimeStop > mUserTimeStart)
-    userTimeDelta = userTimeStop - mUserTimeStart;
-
-  uint64_t systemTimeDelta = 0;
-  if (systemTimeStop > mSystemTimeStart)
-    systemTimeDelta = systemTimeStop - mSystemTimeStart;
-
-  MOZ_ASSERT(mTopGroup->isUsedInThisIteration());
-  const uint64_t totalRecentCycles = mTopGroup->recentCycles(iteration);
-
-  // We should only reach this stage if `group` has had some activity.
-  MOZ_ASSERT(mTopGroup->recentTicks(iteration) > 0);
-  for (auto iter = recentGroups.begin(); iter != recentGroups.end(); ++iter) {
-    RefPtr<nsPerformanceGroup> group = nsPerformanceGroup::Get(*iter);
-    CommitGroup(iteration, userTimeDelta, systemTimeDelta, totalRecentCycles, group);
-  }
-
-  // Make sure that `group` was treated along with the other items of `recentGroups`.
-  MOZ_ASSERT(!mTopGroup->isUsedInThisIteration());
-  MOZ_ASSERT(mTopGroup->recentTicks(iteration) == 0);
-  return true;
-}
-
-void
-nsPerformanceStatsService::CommitGroup(uint64_t iteration,
-                                       uint64_t totalUserTimeDelta, uint64_t totalSystemTimeDelta,
-                                       uint64_t totalCyclesDelta, nsPerformanceGroup* group) {
-
-  MOZ_ASSERT(group->isUsedInThisIteration());
-
-  const uint64_t ticksDelta = group->recentTicks(iteration);
-  const uint64_t cpowTimeDelta = group->recentCPOW(iteration);
-  const uint64_t cyclesDelta = group->recentCycles(iteration);
-  group->resetRecentData();
-
-  // We have now performed all cleanup and may `return` at any time without fear of leaks.
-
-  if (group->iteration() != iteration) {
-    // Stale data, don't commit.
-    return;
-  }
-
-  // When we add a group as changed, we immediately set its
-  // `recentTicks` from 0 to 1.  If we have `ticksDelta == 0` at
-  // this stage, we have already called `resetRecentData` but we
-  // haven't removed it from the list.
-  MOZ_ASSERT(ticksDelta != 0);
-  MOZ_ASSERT(cyclesDelta <= totalCyclesDelta);
-  if (cyclesDelta == 0 || totalCyclesDelta == 0) {
-    // Nothing useful, don't commit.
-    return;
-  }
-
-  double proportion = (double)cyclesDelta / (double)totalCyclesDelta;
-  MOZ_ASSERT(proportion <= 1);
-
-  const uint64_t userTimeDelta = proportion * totalUserTimeDelta;
-  const uint64_t systemTimeDelta = proportion * totalSystemTimeDelta;
-
-  group->data.mTotalUserTime += userTimeDelta;
-  group->data.mTotalSystemTime += systemTimeDelta;
-  group->data.mTotalCPOWTime += cpowTimeDelta;
-  group->data.mTicks += ticksDelta;
-
-  const uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta;
-  uint64_t duration = 1000;   // 1ms in ┬Ás
-  for (size_t i = 0;
-       i < mozilla::ArrayLength(group->data.mDurations) && duration < totalTimeDelta;
-       ++i, duration *= 2) {
-    group->data.mDurations[i]++;
-  }
-}
-
-nsresult
-nsPerformanceStatsService::GetResources(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;
-  thread_basic_info_data_t info;
-  mach_port_t port = mach_thread_self();
-  kern_return_t err =
-    thread_info(/* [in] targeted thread*/ port,
-                /* [in] nature of information*/ THREAD_BASIC_INFO,
-                /* [out] thread information */  (thread_info_t)&info,
-                /* [inout] number of items */   &count);
-
-  // We do not need ability to communicate with the thread, so
-  // let's release the port.
-  mach_port_deallocate(mach_task_self(), port);
-
-  if (err != KERN_SUCCESS)
-    return NS_ERROR_FAILURE;
-
-  *userTime = info.user_time.microseconds + info.user_time.seconds * 1000000;
-  *systemTime = info.system_time.microseconds + info.system_time.seconds * 1000000;
-
-#elif defined(XP_UNIX)
-  struct rusage rusage;
-#if defined(RUSAGE_THREAD)
-  // Under Linux, we can obtain per-thread statistics
-  int err = getrusage(RUSAGE_THREAD, &rusage);
-#else
-  // Under other Unices, we need to do with more noisy
-  // per-process statistics.
-  int err = getrusage(RUSAGE_SELF, &rusage);
-#endif // defined(RUSAGE_THREAD)
-
-  if (err)
-    return NS_ERROR_FAILURE;
-
-  *userTime = rusage.ru_utime.tv_usec + rusage.ru_utime.tv_sec * 1000000;
-  *systemTime = rusage.ru_stime.tv_usec + rusage.ru_stime.tv_sec * 1000000;
-
-#elif defined(XP_WIN)
-  // Under Windows, we can obtain per-thread statistics. Experience
-  // seems to suggest that they are not very accurate under Windows
-  // XP, though.
-  FILETIME creationFileTime; // Ignored
-  FILETIME exitFileTime; // Ignored
-  FILETIME kernelFileTime;
-  FILETIME userFileTime;
-  BOOL success = GetThreadTimes(GetCurrentThread(),
-                                &creationFileTime, &exitFileTime,
-                                &kernelFileTime, &userFileTime);
-
-  if (!success)
-    return NS_ERROR_FAILURE;
-
-  ULARGE_INTEGER kernelTimeInt;
-  kernelTimeInt.LowPart = kernelFileTime.dwLowDateTime;
-  kernelTimeInt.HighPart = kernelFileTime.dwHighDateTime;
-  // Convert 100 ns to 1 us.
-  *systemTime = kernelTimeInt.QuadPart / 10;
-
-  ULARGE_INTEGER userTimeInt;
-  userTimeInt.LowPart = userFileTime.dwLowDateTime;
-  userTimeInt.HighPart = userFileTime.dwHighDateTime;
-  // Convert 100 ns to 1 us.
-  *userTime = userTimeInt.QuadPart / 10;
-
-#endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN)
-
-  return NS_OK;
-}
-
-
-/* ------------------------------------------------------
- *
- * Class nsPerformanceGroup
- *
- */
-
-/*static*/ nsPerformanceGroup*
-nsPerformanceGroup::Make(JSRuntime* rt,
-                         nsPerformanceStatsService* service,
-                         const nsAString& name,
-                         const nsAString& addonId,
-                         uint64_t windowId,
-                         uint64_t processId,
-                         bool isSystem,
-                         GroupScope scope)
-{
-  nsString groupId;
-  ::GenerateUniqueGroupId(rt, service->GetNextId(), processId, groupId);
-  return new nsPerformanceGroup(service, name, groupId, addonId, windowId, processId, isSystem, scope);
-}
-
-nsPerformanceGroup::nsPerformanceGroup(nsPerformanceStatsService* service,
-                                       const nsAString& name,
-                                       const nsAString& groupId,
-                                       const nsAString& addonId,
-                                       uint64_t windowId,
-                                       uint64_t processId,
-                                       bool isSystem,
-                                       GroupScope scope)
-  : nsPerformanceGroupDetails(name, groupId, addonId, windowId, processId, isSystem)
-  , mService(service)
-  , mScope(scope)
-{
-  mozilla::unused << mService->mGroups.PutEntry(this);
-
-#if defined(DEBUG)
-  if (scope == GroupScope::ADDON) {
-    MOZ_ASSERT(IsAddon());
-    MOZ_ASSERT(!IsWindow());
-  } else if (scope == GroupScope::WINDOW) {
-    MOZ_ASSERT(IsWindow());
-    MOZ_ASSERT(!IsAddon());
-  } else if (scope == GroupScope::RUNTIME) {
-    MOZ_ASSERT(!IsWindow());
-    MOZ_ASSERT(!IsAddon());
-  }
-#endif // defined(DEBUG)
-  setIsActive(mScope != GroupScope::COMPARTMENT || mService->mIsMonitoringPerCompartment);
-}
-
-void
-nsPerformanceGroup::Dispose() {
-  if (!mService) {
-    // We have already called `Dispose()`.
-    return;
-  }
-
-  // Remove any reference to the service
-  RefPtr<nsPerformanceStatsService> service;
-  service.swap(mService);
-
-  service->mGroups.RemoveEntry(this);
-
-  if (mScope == GroupScope::ADDON) {
-    MOZ_ASSERT(IsAddon());
-    service->mAddonIdToGroup.RemoveEntry(AddonId());
-  } else if (mScope == GroupScope::WINDOW) {
-    MOZ_ASSERT(IsWindow());
-    service->mWindowIdToGroup.RemoveEntry(WindowId());
-  }
-}
-
-nsPerformanceGroup::~nsPerformanceGroup() {
-  Dispose();
-}
-
-nsPerformanceGroup::GroupScope
-nsPerformanceGroup::Scope() const {
-  return mScope;
-}
--- a/toolkit/components/perfmonitoring/nsPerformanceStats.h
+++ b/toolkit/components/perfmonitoring/nsPerformanceStats.h
@@ -1,492 +1,32 @@
 /* -*- 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/. */
 
 #ifndef nsPerformanceStats_h
 #define nsPerformanceStats_h
 
-#include "jsapi.h"
-
-#include "nsHashKeys.h"
-#include "nsTHashtable.h"
-
 #include "nsIObserver.h"
-#include "nsPIDOMWindow.h"
 
 #include "nsIPerformanceStats.h"
 
-class nsPerformanceGroup;
-
-typedef mozilla::Vector<RefPtr<js::PerformanceGroup>> JSGroupVector;
-typedef mozilla::Vector<RefPtr<nsPerformanceGroup>> GroupVector;
-
-/**
- * An implementation of the nsIPerformanceStatsService.
- *
- * Note that this implementation is not thread-safe.
- */
-class nsPerformanceStatsService : public nsIPerformanceStatsService,
-                                  public nsIObserver
+class nsPerformanceStatsService : public nsIPerformanceStatsService
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERFORMANCESTATSSERVICE
-  NS_DECL_NSIOBSERVER
 
   nsPerformanceStatsService();
-  nsresult Init();
 
 private:
-  nsresult InitInternal();
-  void Dispose();
+  nsresult UpdateTelemetry();
   virtual ~nsPerformanceStatsService();
 
-protected:
-  friend nsPerformanceGroup;
-
-  /**
-   * A unique identifier for the process.
-   *
-   * Process HANDLE under Windows, pid under Unix.
-   */
   const uint64_t mProcessId;
-
-  /**
-   * The JS Runtime for the main thread.
-   */
-  JSRuntime* const mRuntime;
-
-  /**
-   * Generate unique identifiers.
-   */
-  uint64_t GetNextId();
-  uint64_t mUIdCounter;
-
-
-
-  /**
-   * Extract a snapshot of performance statistics from a performance group.
-   */
-  static nsIPerformanceStats* GetStatsForGroup(const js::PerformanceGroup* group);
-  static nsIPerformanceStats* GetStatsForGroup(const nsPerformanceGroup* group);
-
-
-
-  /**
-   * Get the performance groups associated to a given JS compartment.
-   *
-   * A compartment is typically associated to the following groups:
-   * - the top group, shared by the entire process;
-   * - the window group, if the code is executed in a window, shared
-   *     by all compartments for that window (typically, all frames);
-   * - the add-on group, if the code is executed as an add-on, shared
-   *     by all compartments for that add-on (typically, all modules);
-   * - the compartment's own group.
-   *
-   * Pre-condition: the VM must have entered the JS compartment.
-   *
-   * The caller is expected to cache the results of this method, as
-   * calling it more than once may not return the same instances of
-   * performance groups.
-   */
-  bool GetPerformanceGroups(JSContext* cx, JSGroupVector&);
-  static bool GetPerformanceGroupsCallback(JSContext* cx, JSGroupVector&, void* closure);
-
-
-
-  /**********************************************************
-   *
-   * Sets of all performance groups, indexed by several keys.
-   *
-   * These sets do not keep the performance groups alive. Rather, a
-   * performance group is inserted in the relevant sets upon
-   * construction and removed from the sets upon destruction or when
-   * we Dispose() of the service.
-   *
-   * A `nsPerformanceGroup` is typically kept alive (as a
-   * `js::PerformanceGroup`) by the JSCompartment to which it is
-   * associated. It may also temporarily be kept alive by the JS
-   * stack, in particular in case of nested event loops.
-   */
-
-  /**
-   * Set of performance groups associated to add-ons, indexed
-   * by add-on id. Each item is shared by all the compartments
-   * that belong to the add-on.
-   */
-  struct AddonIdToGroup: public nsStringHashKey {
-    explicit AddonIdToGroup(const nsAString* key)
-      : nsStringHashKey(key)
-      , mGroup(nullptr)
-    { }
-    nsPerformanceGroup* mGroup;
-  };
-  nsTHashtable<AddonIdToGroup> mAddonIdToGroup;
-
-  /**
-   * Set of performance groups associated to windows, indexed by outer
-   * window id. Each item is shared by all the compartments that
-   * belong to the window.
-   */
-  struct WindowIdToGroup: public nsUint64HashKey {
-    explicit WindowIdToGroup(const uint64_t* key)
-      : nsUint64HashKey(key)
-      , mGroup(nullptr)
-    {}
-    nsPerformanceGroup* mGroup;
-  };
-  nsTHashtable<WindowIdToGroup> mWindowIdToGroup;
-
-  /**
-   * Set of all performance groups.
-   */
-  struct Groups: public nsPtrHashKey<nsPerformanceGroup> {
-    explicit Groups(const nsPerformanceGroup* key)
-      : nsPtrHashKey<nsPerformanceGroup>(key)
-    {}
-  };
-  nsTHashtable<Groups> mGroups;
-
-  /**
-   * The performance group representing the runtime itself.  All
-   * compartments are associated to this group.
-   */
-  RefPtr<nsPerformanceGroup> mTopGroup;
-
-  /**********************************************************
-   *
-   * Measuring and recording the CPU use of the system.
-   *
-   */
-
-  /**
-   * 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.
-   *
-   * Data is not guaranteed to be monotonic.
-   */
-  nsresult GetResources(uint64_t* userTime, uint64_t* systemTime) const;
-
-  /**
-   * Amount of user/system CPU time used by the thread (or process,
-   * for platforms that don't support per-thread measure) since start.
-   * Updated by `StopwatchStart` at most once per event.
-   *
-   * Unit: microseconds.
-   */
-  uint64_t mUserTimeStart;
-  uint64_t mSystemTimeStart;
-
-
-  /**********************************************************
-   *
-   * Callbacks triggered by the JS VM when execution of JavaScript
-   * code starts/completes.
-   *
-   * As measures of user CPU time/system CPU time have low resolution
-   * (and are somewhat slow), we measure both only during the calls to
-   * `StopwatchStart`/`StopwatchCommit` and we make the assumption
-   * that each group's user/system CPU time is proportional to the
-   * number of clock cycles spent executing code in the group between
-   * `StopwatchStart`/`StopwatchCommit`.
-   *
-   * The results may be skewed by the thread being rescheduled to a
-   * different CPU during the measure, but we expect that on average,
-   * the skew will have limited effects, and will generally tend to
-   * make already-slow executions appear slower.
-   */
-
-  /**
-   * Execution of JavaScript code has started. This may happen several
-   * times in succession if the JavaScript code contains nested event
-   * loops, in which case only the innermost call will receive
-   * `StopwatchCommitCallback`.
-   *
-   * @param iteration The number of times we have started executing
-   * JavaScript code.
-   */
-  static bool StopwatchStartCallback(uint64_t iteration, void* closure);
-  bool StopwatchStart(uint64_t iteration);
-
-  /**
-   * Execution of JavaScript code has reached completion (including
-   * enqueued microtasks). In cse of tested event loops, any ongoing
-   * measurement on outer loops is silently cancelled without any call
-   * to this method.
-   *
-   * @param iteration The number of times we have started executing
-   * JavaScript code.
-   * @param recentGroups The groups that have seen activity during this
-   * event.
-   */
-  static bool StopwatchCommitCallback(uint64_t iteration, JSGroupVector& recentGroups, void* closure);
-  bool StopwatchCommit(uint64_t iteration, JSGroupVector& recentGroups);
-
-  /**
-   * The number of times we have started executing JavaScript code.
-   */
-  uint64_t mIteration;
-
-  /**
-   * Commit performance measures of a single group.
-   *
-   * Data is transfered from `group->recent*` to `group->data`.
-   *
-   *
-   * @param iteration The current iteration.
-   * @param userTime The total user CPU time for this thread (or
-   *   process, if per-thread data is not available) between the
-   *   calls to `StopwatchStart` and `StopwatchCommit`.
-   * @param systemTime The total system CPU time for this thread (or
-   *   process, if per-thread data is not available) between the
-   *   calls to `StopwatchStart` and `StopwatchCommit`.
-   * @param cycles The total number of cycles for this thread
-   *   between the calls to `StopwatchStart` and `StopwatchCommit`.
-   * @param group The group containing the data to commit.
-   */
-  void CommitGroup(uint64_t iteration,
-                   uint64_t userTime, uint64_t systemTime,  uint64_t cycles,
-                   nsPerformanceGroup* group);
-
-
-
-
-  /**********************************************************
-   *
-   * To check whether our algorithm makes sense, we keep count of the
-   * number of times the process has been rescheduled to another CPU
-   * while we were monitoring the performance of a group and we upload
-   * this data through Telemetry.
-   */
-  nsresult UpdateTelemetry();
-
   uint64_t mProcessStayed;
   uint64_t mProcessMoved;
   uint32_t mProcessUpdateCounter;
-
-  /**********************************************************
-   *
-   * Options controlling measurements.
-   */
-
-  /**
-   * Determine if we are measuring the performance of every individual
-   * compartment (in particular, every individual module, frame,
-   * sandbox). Note that this makes measurements noticeably slower.
-   */
-  bool mIsMonitoringPerCompartment;
-};
-
-
-
-/**
- * Container for performance data.
- *
- * All values are monotonic.
- *
- * All values are updated after running to completion.
- */
-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
-   * durations[1] whenever we spend 2ms+
-   * ...
-   * durations[i] whenever we spend 2^ims+
-   */
-  uint64_t mDurations[10];
-
-  /**
-   * Total amount of time spent executing code in this group, in
-   * microseconds.
-   */
-  uint64_t mTotalUserTime;
-  uint64_t mTotalSystemTime;
-  uint64_t mTotalCPOWTime;
-
-  /**
-   * Total number of times code execution entered this group, since
-   * process launch. This may be greater than the number of times we
-   * have entered the event loop.
-   */
-  uint64_t mTicks;
-
-  PerformanceData();
-  PerformanceData(const PerformanceData& from) = default;
-  PerformanceData& operator=(const PerformanceData& from) = default;
-};
-
-
-
-/**
- * Identification information for an item that can hold performance
- * data.
- */
-class nsPerformanceGroupDetails {
-public:
-  nsPerformanceGroupDetails(const nsAString& aName,
-                            const nsAString& aGroupId,
-                            const nsAString& aAddonId,
-                            const uint64_t aWindowId,
-                            const uint64_t aProcessId,
-                            const bool aIsSystem)
-    : mName(aName)
-    , mGroupId(aGroupId)
-    , mAddonId(aAddonId)
-    , mWindowId(aWindowId)
-    , mProcessId(aProcessId)
-    , mIsSystem(aIsSystem)
-  { }
-public:
-  const nsAString& Name() const;
-  const nsAString& GroupId() const;
-  const nsAString& AddonId() const;
-  uint64_t WindowId() const;
-  uint64_t ProcessId() const;
-  bool IsAddon() const;
-  bool IsWindow() const;
-  bool IsSystem() const;
-private:
-  const nsString mName;
-  const nsString mGroupId;
-  const nsString mAddonId;
-  const uint64_t mWindowId;
-  const uint64_t mProcessId;
-  const bool mIsSystem;
-};
-
-/**
- * The kind of compartments represented by this group.
- */
-enum class PerformanceGroupScope {
-  /**
-   * This group represents the entire runtime (i.e. the thread).
-   */
-  RUNTIME,
-
-  /**
-   * This group represents all the compartments executed in a window.
-   */
-  WINDOW,
-
-  /**
-   * This group represents all the compartments provided by an addon.
-   */
-  ADDON,
-
-  /**
-   * This group represents a single compartment.
-   */
-  COMPARTMENT,
-};
-
-/**
- * A concrete implementation of `js::PerformanceGroup`, also holding
- * performance data. Instances may represent individual compartments,
- * windows, addons or the entire runtime.
- *
- * This class is intended to be the sole implementation of
- * `js::PerformanceGroup`.
- */
-class nsPerformanceGroup final: public js::PerformanceGroup,
-                                public nsPerformanceGroupDetails
-{
-public:
-
-  // Ideally, we would define the enum class in nsPerformanceGroup,
-  // but this seems to choke some versions of gcc.
-  typedef PerformanceGroupScope GroupScope;
-
-  /**
-   * Construct a performance group.
-   *
-   * @param rt The container runtime. Used to generate a unique identifier.
-   * @param service The performance service. Used during destruction to
-   *   cleanup the hash tables.
-   * @param name A name for the group, designed mostly for debugging purposes,
-   *   so it should be at least somewhat human-readable.
-   * @param addonId The identifier of the add-on. Should be "" when the
-   *   group is not part of an add-on,
-   * @param windowId The identifier of the window. Should be 0 when the
-   *   group is not part of a window.
-   * @param processId A unique identifier for the process.
-   * @param isSystem `true` if the code of the group is executed with
-   *   system credentials, `false` otherwise.
-   * @param scope the scope of this group.
-   */
-  static nsPerformanceGroup*
-    Make(JSRuntime* rt,
-         nsPerformanceStatsService* service,
-         const nsAString& name,
-         const nsAString& addonId,
-         uint64_t windowId,
-         uint64_t processId,
-         bool isSystem,
-         GroupScope scope);
-
-  /**
-   * Utility: type-safer conversion from js::PerformanceGroup to nsPerformanceGroup.
-   */
-  static inline nsPerformanceGroup* Get(js::PerformanceGroup* self) {
-    return static_cast<nsPerformanceGroup*>(self);
-  }
-  static inline const nsPerformanceGroup* Get(const js::PerformanceGroup* self) {
-    return static_cast<const nsPerformanceGroup*>(self);
-  }
-
-  /**
-   * The performance data committed to this group.
-   */
-  PerformanceData data;
-
-  /**
-   * The scope of this group. Used to determine whether the group
-   * should be (de)activated.
-   */
-  GroupScope Scope() const;
-
-  /**
-   * Cleanup any references.
-   */
-  void Dispose();
 protected:
-  nsPerformanceGroup(nsPerformanceStatsService* service,
-                     const nsAString& name,
-                     const nsAString& groupId,
-                     const nsAString& addonId,
-                     uint64_t windowId,
-                     uint64_t processId,
-                     bool isSystem,
-                     GroupScope scope);
-
-
-  /**
-   * Virtual implementation of `delete`, to make sure that objects are
-   * destoyed with an implementation of `delete` compatible with the
-   * implementation of `new` used to allocate them.
-   *
-   * Called by SpiderMonkey.
-   */
-  virtual void Delete() override {
-    delete this;
-  }
-  virtual ~nsPerformanceGroup();
-
-private:
-  /**
-   * The stats service. Used to perform cleanup during destruction.
-   */
-  RefPtr<nsPerformanceStatsService> mService;
-
-  /**
-   * The scope of this group. Used to determine whether the group
-   * should be (de)activated.
-   */
-  const GroupScope mScope;
 };
 
 #endif
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -9572,17 +9572,17 @@
   "LOOP_CALL_TYPE": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": "8",
     "description": "Type of call: (Bitmask) Audio = 1, Video = 2, DataChannels = 4"
   },
   "PERF_MONITORING_TEST_CPU_RESCHEDULING_PROPORTION_MOVED": {
     "alert_emails": ["dteller@mozilla.com"],
-    "expires_in_version": "48",
+    "expires_in_version": "45",
     "kind": "linear",
     "high": "100",
     "n_buckets": "20",
     "description": "Proportion (%) of reschedulings of the main process to another CPU during the execution of code inside a JS compartment. Updated while we are measuring jank."
   },
   "VIDEO_EME_ADOBE_INSTALL_FAILED_REASON": {
     "alert_emails": ["edwin@mozilla.com"],
     "expires_in_version": "50",