Bug 1520526 - Add categories to all profiler markers; r=mstange
authorGreg Tatum <gtatum@mozilla.com>
Fri, 18 Jan 2019 15:40:15 +0000
changeset 454549 e3a8a7245f627e6697056a18847f286c0a1d2bc9
parent 454548 9b729145045dd68311801c749f3e6fe5e3c741ba
child 454550 5fdaf215ab1242bf140b2139f516f940096ceebe
push id111289
push usercsabou@mozilla.com
push dateSat, 19 Jan 2019 10:14:46 +0000
treeherdermozilla-inbound@dbc87d86f411 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1520526
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1520526 - Add categories to all profiler markers; r=mstange This commit adds categories to all markers. This way the profiler's marker categories and frame label categories agree. There are a few duplicate category properties on some of the marker payloads, but this could be cleaned up in a follow-up if needed. Differential Revision: https://phabricator.services.mozilla.com/D16864
accessible/generic/Accessible.cpp
dom/base/nsDOMNavigationTiming.cpp
dom/base/nsDOMWindowUtils.cpp
dom/base/nsGlobalWindowOuter.cpp
dom/base/nsJSEnvironment.cpp
dom/events/EventDispatcher.cpp
dom/indexedDB/ProfilerHelpers.h
dom/performance/Performance.cpp
dom/vr/VRDisplay.cpp
gfx/layers/Layers.cpp
gfx/layers/ProfilerScreenshots.cpp
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/composite/CompositorScreenshotGrabber.cpp
gfx/layers/composite/ContainerLayerComposite.cpp
gfx/layers/ipc/CompositorBench.cpp
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
gfx/layers/ipc/LayerTransactionParent.cpp
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/vr/VRDisplayHost.cpp
gfx/vr/ipc/VRManagerChild.cpp
gfx/webrender_bindings/Moz2DImageRenderer.cpp
gfx/webrender_bindings/RenderThread.cpp
ipc/ipdl/ipdl/lower.py
layout/base/AutoProfilerStyleMarker.h
layout/base/PresShell.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsRefreshDriver.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/painting/nsDisplayList.cpp
toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
toolkit/components/startup/StartupTimeline.h
toolkit/components/startup/nsAppStartup.cpp
toolkit/xre/nsAppRunner.cpp
tools/profiler/core/ProfiledThreadData.cpp
tools/profiler/core/ProfilerMarker.h
tools/profiler/core/RegisteredThread.h
tools/profiler/core/platform.cpp
tools/profiler/gecko/ProfilerIOInterposeObserver.cpp
tools/profiler/gecko/nsProfiler.cpp
tools/profiler/public/GeckoProfiler.h
tools/profiler/tests/gtest/GeckoProfiler.cpp
xpcom/base/CycleCollectedJSRuntime.cpp
xpcom/base/Logging.cpp
xpcom/build/XPCOMInit.cpp
xpcom/threads/nsThread.cpp
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -799,17 +799,18 @@ nsresult Accessible::HandleAccEvent(AccE
 
 #ifdef MOZ_GECKO_PROFILER
   if (profiler_thread_is_being_profiled()) {
     nsAutoCString strEventType;
     GetAccService()->GetStringEventType(aEvent->GetEventType(), strEventType);
     nsAutoCString strMarker;
     strMarker.AppendLiteral("A11y Event - ");
     strMarker.Append(strEventType);
-    profiler_add_marker(strMarker.get());
+    profiler_add_marker(strMarker.get(),
+                        js::ProfilingStackFrame::Category::OTHER);
   }
 #endif
 
   if (IPCAccessibilityActive() && Document()) {
     DocAccessibleChild* ipcDoc = mDoc->IPCDoc();
     MOZ_ASSERT(ipcDoc);
     if (ipcDoc) {
       uint64_t id = aEvent->GetAccessible()->IsDoc()
--- a/dom/base/nsDOMNavigationTiming.cpp
+++ b/dom/base/nsDOMNavigationTiming.cpp
@@ -71,17 +71,17 @@ DOMTimeMilliSec nsDOMNavigationTiming::T
 }
 
 void nsDOMNavigationTiming::NotifyNavigationStart(
     DocShellState aDocShellState) {
   mNavigationStartHighRes = (double)PR_Now() / PR_USEC_PER_MSEC;
   mNavigationStart = TimeStamp::Now();
   mDocShellHasBeenActiveSinceNavigationStart =
       (aDocShellState == DocShellState::eActive);
-  PROFILER_ADD_MARKER("Navigation::Start");
+  PROFILER_ADD_MARKER("Navigation::Start", DOM);
 }
 
 void nsDOMNavigationTiming::NotifyFetchStart(nsIURI* aURI,
                                              Type aNavigationType) {
   mNavigationType = aNavigationType;
   // At the unload event time we don't really know the loading uri.
   // Need it for later check for unload timing access.
   mLoadedURI = aURI;
@@ -93,34 +93,34 @@ void nsDOMNavigationTiming::NotifyBefore
 
 void nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* aOldURI) {
   mUnloadStart = mBeforeUnloadStart;
   mUnloadedURI = aOldURI;
 }
 
 void nsDOMNavigationTiming::NotifyUnloadEventStart() {
   mUnloadStart = TimeStamp::Now();
-  PROFILER_TRACING_DOCSHELL("Navigation", "Unload", TRACING_INTERVAL_START,
-                            mDocShell);
+  PROFILER_TRACING_DOCSHELL("Navigation", "Unload", NETWORK,
+                            TRACING_INTERVAL_START, mDocShell);
 }
 
 void nsDOMNavigationTiming::NotifyUnloadEventEnd() {
   mUnloadEnd = TimeStamp::Now();
-  PROFILER_TRACING_DOCSHELL("Navigation", "Unload", TRACING_INTERVAL_END,
-                            mDocShell);
+  PROFILER_TRACING_DOCSHELL("Navigation", "Unload", NETWORK,
+                            TRACING_INTERVAL_END, mDocShell);
 }
 
 void nsDOMNavigationTiming::NotifyLoadEventStart() {
   if (!mLoadEventStart.IsNull()) {
     return;
   }
   mLoadEventStart = TimeStamp::Now();
 
-  PROFILER_TRACING_DOCSHELL("Navigation", "Load", TRACING_INTERVAL_START,
-                            mDocShell);
+  PROFILER_TRACING_DOCSHELL("Navigation", "Load", NETWORK,
+                            TRACING_INTERVAL_START, mDocShell);
 
   if (IsTopLevelContentDocumentInContentProcess()) {
     TimeStamp now = TimeStamp::Now();
 
     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_START_MS,
                                    mNavigationStart, now);
 
     if (mDocShellHasBeenActiveSinceNavigationStart) {
@@ -139,17 +139,17 @@ void nsDOMNavigationTiming::NotifyLoadEv
 }
 
 void nsDOMNavigationTiming::NotifyLoadEventEnd() {
   if (!mLoadEventEnd.IsNull()) {
     return;
   }
   mLoadEventEnd = TimeStamp::Now();
 
-  PROFILER_TRACING_DOCSHELL("Navigation", "Load", TRACING_INTERVAL_END,
+  PROFILER_TRACING_DOCSHELL("Navigation", "Load", NETWORK, TRACING_INTERVAL_END,
                             mDocShell);
 
   if (IsTopLevelContentDocumentInContentProcess()) {
 #ifdef MOZ_GECKO_PROFILER
     if (profiler_is_active() || PAGELOAD_LOG_ENABLED()) {
       TimeDuration elapsed = mLoadEventEnd - mNavigationStart;
       TimeDuration duration = mLoadEventEnd - mLoadEventStart;
       nsAutoCString spec;
@@ -157,17 +157,17 @@ void nsDOMNavigationTiming::NotifyLoadEv
         mLoadedURI->GetSpec(spec);
       }
       nsPrintfCString marker(
           "Document %s loaded after %dms, load event duration %dms", spec.get(),
           int(elapsed.ToMilliseconds()), int(duration.ToMilliseconds()));
       DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell);
       PAGELOAD_LOG(("%s", marker.get()));
       profiler_add_marker(
-          "DocumentLoad",
+          "DocumentLoad", js::ProfilingStackFrame::Category::DOM,
           MakeUnique<TextMarkerPayload>(marker, mNavigationStart, mLoadEventEnd,
                                         docShellId, docShellHistoryId));
     }
 #endif
     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_END_MS,
                                    mNavigationStart);
   }
 }
@@ -183,48 +183,48 @@ void nsDOMNavigationTiming::SetDOMLoadin
 
 void nsDOMNavigationTiming::NotifyDOMLoading(nsIURI* aURI) {
   if (!mDOMLoading.IsNull()) {
     return;
   }
   mLoadedURI = aURI;
   mDOMLoading = TimeStamp::Now();
 
-  PROFILER_ADD_MARKER("Navigation::DOMLoading");
+  PROFILER_ADD_MARKER("Navigation::DOMLoading", DOM);
 }
 
 void nsDOMNavigationTiming::NotifyDOMInteractive(nsIURI* aURI) {
   if (!mDOMInteractive.IsNull()) {
     return;
   }
   mLoadedURI = aURI;
   mDOMInteractive = TimeStamp::Now();
 
-  PROFILER_ADD_MARKER("Navigation::DOMInteractive");
+  PROFILER_ADD_MARKER("Navigation::DOMInteractive", DOM);
 }
 
 void nsDOMNavigationTiming::NotifyDOMComplete(nsIURI* aURI) {
   if (!mDOMComplete.IsNull()) {
     return;
   }
   mLoadedURI = aURI;
   mDOMComplete = TimeStamp::Now();
 
-  PROFILER_ADD_MARKER("Navigation::DOMComplete");
+  PROFILER_ADD_MARKER("Navigation::DOMComplete", DOM);
 }
 
 void nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI) {
   if (!mDOMContentLoadedEventStart.IsNull()) {
     return;
   }
 
   mLoadedURI = aURI;
   mDOMContentLoadedEventStart = TimeStamp::Now();
 
-  PROFILER_TRACING_DOCSHELL("Navigation", "DOMContentLoaded",
+  PROFILER_TRACING_DOCSHELL("Navigation", "DOMContentLoaded", NETWORK,
                             TRACING_INTERVAL_START, mDocShell);
 
   if (IsTopLevelContentDocumentInContentProcess()) {
     TimeStamp now = TimeStamp::Now();
 
     Telemetry::AccumulateTimeDelta(
         Telemetry::TIME_TO_DOM_CONTENT_LOADED_START_MS, mNavigationStart, now);
 
@@ -246,17 +246,17 @@ void nsDOMNavigationTiming::NotifyDOMCon
 void nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI) {
   if (!mDOMContentLoadedEventEnd.IsNull()) {
     return;
   }
 
   mLoadedURI = aURI;
   mDOMContentLoadedEventEnd = TimeStamp::Now();
 
-  PROFILER_TRACING_DOCSHELL("Navigation", "DOMContentLoaded",
+  PROFILER_TRACING_DOCSHELL("Navigation", "DOMContentLoaded", NETWORK,
                             TRACING_INTERVAL_END, mDocShell);
 
   if (IsTopLevelContentDocumentInContentProcess()) {
     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_CONTENT_LOADED_END_MS,
                                    mNavigationStart);
   }
 }
 
@@ -352,18 +352,19 @@ void nsDOMNavigationTiming::TTITimeout(n
       mLoadedURI->GetSpec(spec);
     }
     nsPrintfCString marker("TTFI after %dms (LongTask was at %dms) for URL %s",
                            int(elapsed.ToMilliseconds()),
                            int(elapsedLongTask.ToMilliseconds()), spec.get());
 
     DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell);
     profiler_add_marker(
-        "TTFI", MakeUnique<TextMarkerPayload>(marker, mNavigationStart, mTTFI,
-                                              docShellId, docShellHistoryId));
+        "TTFI", js::ProfilingStackFrame::Category::DOM,
+        MakeUnique<TextMarkerPayload>(marker, mNavigationStart, mTTFI,
+                                      docShellId, docShellHistoryId));
   }
 #endif
   return;
 }
 
 void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mNavigationStart.IsNull());
@@ -386,17 +387,17 @@ void nsDOMNavigationTiming::NotifyNonBla
         int(elapsed.ToMilliseconds()), spec.get(),
         mDocShellHasBeenActiveSinceNavigationStart
             ? "foreground tab"
             : "this tab was inactive some of the time between navigation start "
               "and first non-blank paint");
     PAGELOAD_LOG(("%s", marker.get()));
     DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell);
     profiler_add_marker(
-        "FirstNonBlankPaint",
+        "FirstNonBlankPaint", js::ProfilingStackFrame::Category::DOM,
         MakeUnique<TextMarkerPayload>(marker, mNavigationStart, mNonBlankPaint,
                                       docShellId, docShellHistoryId));
   }
 #endif
 
   if (mDocShellHasBeenActiveSinceNavigationStart) {
     if (net::nsHttp::IsBeforeLastActiveTabLoadOptimization(mNavigationStart)) {
       Telemetry::AccumulateTimeDelta(
@@ -434,20 +435,21 @@ void nsDOMNavigationTiming::NotifyConten
         "Contentful paint after %dms for URL %s, %s",
         int(elapsed.ToMilliseconds()), spec.get(),
         mDocShellHasBeenActiveSinceNavigationStart
             ? "foreground tab"
             : "this tab was inactive some of the time between navigation start "
               "and first non-blank paint");
     DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell);
     PAGELOAD_LOG(("%s", marker.get()));
-    profiler_add_marker("FirstContentfulPaint",
-                        MakeUnique<TextMarkerPayload>(
-                            marker, mNavigationStart, mContentfulPaint,
-                            docShellId, docShellHistoryId));
+    profiler_add_marker(
+        "FirstContentfulPaint", js::ProfilingStackFrame::Category::DOM,
+        MakeUnique<TextMarkerPayload>(marker, mNavigationStart,
+                                      mContentfulPaint, docShellId,
+                                      docShellHistoryId));
   }
 #endif
 
   if (!mTTITimer) {
     mTTITimer = NS_NewTimer();
   }
 
   // TTI is first checked 5 seconds after the FCP (non-blank-paint is very close
@@ -479,20 +481,21 @@ void nsDOMNavigationTiming::NotifyDOMCon
         "DOMContentFlushed after %dms for URL %s, %s",
         int(elapsed.ToMilliseconds()), spec.get(),
         mDocShellHasBeenActiveSinceNavigationStart
             ? "foreground tab"
             : "this tab was inactive some of the time between navigation start "
               "and DOMContentFlushed");
     DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell);
     PAGELOAD_LOG(("%s", marker.get()));
-    profiler_add_marker("DOMContentFlushed",
-                        MakeUnique<TextMarkerPayload>(
-                            marker, mNavigationStart, mDOMContentFlushed,
-                            docShellId, docShellHistoryId));
+    profiler_add_marker(
+        "DOMContentFlushed", js::ProfilingStackFrame::Category::DOM,
+        MakeUnique<TextMarkerPayload>(marker, mNavigationStart,
+                                      mDOMContentFlushed, docShellId,
+                                      docShellHistoryId));
   }
 #endif
 }
 
 void nsDOMNavigationTiming::NotifyDocShellStateChanged(
     DocShellState aDocShellState) {
   mDocShellHasBeenActiveSinceNavigationStart &=
       (aDocShellState == DocShellState::eActive);
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3017,17 +3017,17 @@ static void PrepareForFullscreenChange(n
       }
       viewManager->SetWindowDimensions(aSize.width, aSize.height);
     }
   }
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::HandleFullscreenRequests(bool* aRetVal) {
-  PROFILER_ADD_MARKER("Enter fullscreen");
+  PROFILER_ADD_MARKER("Enter fullscreen", DOM);
   nsCOMPtr<Document> doc = GetDocument();
   NS_ENSURE_STATE(doc);
 
   // Notify the pres shell that we are starting fullscreen change, and
   // set the window dimensions in advance. Since the resize message
   // comes after the fullscreen change call, doing so could avoid an
   // extra resize reflow after this point.
   nsRect screenRect;
@@ -3038,17 +3038,17 @@ nsDOMWindowUtils::HandleFullscreenReques
   PrepareForFullscreenChange(GetPresShell(), screenRect.Size(), &oldSize);
   OldWindowSize::Set(mWindow, oldSize);
 
   *aRetVal = Document::HandlePendingFullscreenRequests(doc);
   return NS_OK;
 }
 
 nsresult nsDOMWindowUtils::ExitFullscreen() {
-  PROFILER_ADD_MARKER("Exit fullscreen");
+  PROFILER_ADD_MARKER("Exit fullscreen", DOM);
   nsCOMPtr<Document> doc = GetDocument();
   NS_ENSURE_STATE(doc);
 
   // Although we would not use the old size if we have already exited
   // fullscreen, we still want to cleanup in case we haven't.
   nsSize oldSize = OldWindowSize::GetAndRemove(mWindow);
   if (!doc->GetFullscreenElement()) {
     return NS_OK;
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -3864,22 +3864,22 @@ FullscreenTransitionTask::Run() {
   mStage = Stage(mStage + 1);
   if (MOZ_UNLIKELY(mWidget->Destroyed())) {
     // If the widget has been destroyed before we get here, don't try to
     // do anything more. Just let it go and release ourselves.
     NS_WARNING("The widget to fullscreen has been destroyed");
     return NS_OK;
   }
   if (stage == eBeforeToggle) {
-    PROFILER_ADD_MARKER("Fullscreen transition start");
+    PROFILER_ADD_MARKER("Fullscreen transition start", DOM);
     mWidget->PerformFullscreenTransition(nsIWidget::eBeforeFullscreenToggle,
                                          mDuration.mFadeIn, mTransitionData,
                                          this);
   } else if (stage == eToggleFullscreen) {
-    PROFILER_ADD_MARKER("Fullscreen toggle start");
+    PROFILER_ADD_MARKER("Fullscreen toggle start", DOM);
     mFullscreenChangeStartTime = TimeStamp::Now();
     if (MOZ_UNLIKELY(mWindow->mFullscreen != mFullscreen)) {
       // This could happen in theory if several fullscreen requests in
       // different direction happen continuously in a short time. We
       // need to ensure the fullscreen state matches our target here,
       // otherwise the widget would change the window state as if we
       // toggle for Fullscreen Mode instead of Fullscreen API.
       NS_WARNING("The fullscreen state of the window does not match");
@@ -3913,17 +3913,17 @@ FullscreenTransitionTask::Run() {
                             nsITimer::TYPE_ONE_SHOT);
   } else if (stage == eAfterToggle) {
     Telemetry::AccumulateTimeDelta(Telemetry::FULLSCREEN_TRANSITION_BLACK_MS,
                                    mFullscreenChangeStartTime);
     mWidget->PerformFullscreenTransition(nsIWidget::eAfterFullscreenToggle,
                                          mDuration.mFadeOut, mTransitionData,
                                          this);
   } else if (stage == eEnd) {
-    PROFILER_ADD_MARKER("Fullscreen transition end");
+    PROFILER_ADD_MARKER("Fullscreen transition end", DOM);
     mWidget->CleanupFullscreenTransition();
   }
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(FullscreenTransitionTask::Observer, nsIObserver)
 
 NS_IMETHODIMP
@@ -3934,28 +3934,28 @@ FullscreenTransitionTask::Observer::Obse
   if (strcmp(aTopic, FullscreenTransitionTask::kPaintedTopic) == 0) {
     nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(aSubject));
     nsCOMPtr<nsIWidget> widget =
         win ? nsGlobalWindowInner::Cast(win)->GetMainWidget() : nullptr;
     if (widget == mTask->mWidget) {
       // The paint notification arrives first. Cancel the timer.
       mTask->mTimer->Cancel();
       shouldContinue = true;
-      PROFILER_ADD_MARKER("Fullscreen toggle end");
+      PROFILER_ADD_MARKER("Fullscreen toggle end", DOM);
     }
   } else {
 #ifdef DEBUG
     MOZ_ASSERT(strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0,
                "Should only get fullscreen-painted or timer-callback");
     nsCOMPtr<nsITimer> timer(do_QueryInterface(aSubject));
     MOZ_ASSERT(timer && timer == mTask->mTimer,
                "Should only trigger this with the timer the task created");
 #endif
     shouldContinue = true;
-    PROFILER_ADD_MARKER("Fullscreen toggle timeout");
+    PROFILER_ADD_MARKER("Fullscreen toggle timeout", DOM);
   }
   if (shouldContinue) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     obs->RemoveObserver(this, kPaintedTopic);
     mTask->mTimer = nullptr;
     mTask->Run();
   }
   return NS_OK;
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1141,17 +1141,18 @@ static void FinishAnyIncrementalGC() {
     JS::PrepareForIncrementalGC(jsapi.cx());
     JS::FinishIncrementalGC(jsapi.cx(), JS::gcreason::CC_FORCED);
   }
 }
 
 static void FireForgetSkippable(uint32_t aSuspected, bool aRemoveChildless,
                                 TimeStamp aDeadline) {
   AUTO_PROFILER_TRACING(
-      "CC", aDeadline.IsNull() ? "ForgetSkippable" : "IdleForgetSkippable");
+      "CC", aDeadline.IsNull() ? "ForgetSkippable" : "IdleForgetSkippable",
+      GCCC);
   PRTime startTime = PR_Now();
   TimeStamp startTimeStamp = TimeStamp::Now();
 
   static uint32_t sForgetSkippableCounter = 0;
   static TimeStamp sForgetSkippableFrequencyStartTime;
   static TimeStamp sLastForgetSkippableEndTime;
   static const TimeDuration minute = TimeDuration::FromSeconds(60.0f);
 
@@ -1416,17 +1417,18 @@ void nsJSContext::CycleCollectNow(nsICyc
 }
 
 // static
 void nsJSContext::RunCycleCollectorSlice(TimeStamp aDeadline) {
   if (!NS_IsMainThread()) {
     return;
   }
 
-  AUTO_PROFILER_TRACING("CC", aDeadline.IsNull() ? "CCSlice" : "IdleCCSlice");
+  AUTO_PROFILER_TRACING("CC", aDeadline.IsNull() ? "CCSlice" : "IdleCCSlice",
+                        GCCC);
 
   AUTO_PROFILER_LABEL("nsJSContext::RunCycleCollectorSlice", GCCC);
 
   gCCStats.PrepareForCycleCollectionSlice(aDeadline);
 
   // Decide how long we want to budget for this slice. By default,
   // use an unlimited budget.
   js::SliceBudget budget = js::SliceBudget::unlimited();
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -1020,28 +1020,29 @@ static bool ShouldClearTargets(WidgetEve
           postVisitor.mDOMEvent->GetType(typeStr);
           AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
               "EventDispatcher::Dispatch", OTHER, typeStr);
 
           nsCOMPtr<nsIDocShell> docShell;
           docShell = nsContentUtils::GetDocShellForEventTarget(aEvent->mTarget);
           DECLARE_DOCSHELL_AND_HISTORY_ID(docShell);
           profiler_add_marker(
-              "DOMEvent",
+              "DOMEvent", js::ProfilingStackFrame::Category::DOM,
               MakeUnique<DOMEventMarkerPayload>(
                   typeStr, aEvent->mTimeStamp, "DOMEvent",
                   TRACING_INTERVAL_START, docShellId, docShellHistoryId));
 
           EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
                                                        aCallback, cd);
 
-          profiler_add_marker("DOMEvent", MakeUnique<DOMEventMarkerPayload>(
-                                              typeStr, aEvent->mTimeStamp,
-                                              "DOMEvent", TRACING_INTERVAL_END,
-                                              docShellId, docShellHistoryId));
+          profiler_add_marker(
+              "DOMEvent", js::ProfilingStackFrame::Category::DOM,
+              MakeUnique<DOMEventMarkerPayload>(
+                  typeStr, aEvent->mTimeStamp, "DOMEvent", TRACING_INTERVAL_END,
+                  docShellId, docShellHistoryId));
         } else
 #endif
         {
           EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
                                                        aCallback, cd);
         }
         aEvent->mPath = nullptr;
 
--- a/dom/indexedDB/ProfilerHelpers.h
+++ b/dom/indexedDB/ProfilerHelpers.h
@@ -276,17 +276,17 @@ inline void MOZ_FORMAT_PRINTF(2, 3)
       message.AppendPrintf(aFmt, args);
 
       va_end(args);
     }
 
     MOZ_LOG(logModule, logLevel, ("%s", message.get()));
 
     if (aUseProfiler) {
-      PROFILER_ADD_MARKER(message.get());
+      PROFILER_ADD_MARKER(message.get(), DOM);
     }
   }
 }
 
 }  // namespace indexedDB
 }  // namespace dom
 }  // namespace mozilla
 
--- a/dom/performance/Performance.cpp
+++ b/dom/performance/Performance.cpp
@@ -219,19 +219,20 @@ void Performance::Mark(const nsAString& 
   InsertUserEntry(performanceMark);
 
 #ifdef MOZ_GECKO_PROFILER
   if (profiler_is_active()) {
     nsCOMPtr<EventTarget> et = do_QueryInterface(GetOwner());
     nsCOMPtr<nsIDocShell> docShell =
         nsContentUtils::GetDocShellForEventTarget(et);
     DECLARE_DOCSHELL_AND_HISTORY_ID(docShell);
-    profiler_add_marker("UserTiming", MakeUnique<UserTimingMarkerPayload>(
-                                          aName, TimeStamp::Now(), docShellId,
-                                          docShellHistoryId));
+    profiler_add_marker(
+        "UserTiming", js::ProfilingStackFrame::Category::DOM,
+        MakeUnique<UserTimingMarkerPayload>(aName, TimeStamp::Now(), docShellId,
+                                            docShellHistoryId));
   }
 #endif
 }
 
 void Performance::ClearMarks(const Optional<nsAString>& aName) {
   ClearUserEntries(aName, NS_LITERAL_STRING("mark"));
 }
 
@@ -315,17 +316,17 @@ void Performance::Measure(const nsAStrin
     if (aEndMark.WasPassed()) {
       endMark.emplace(aEndMark.Value());
     }
 
     nsCOMPtr<EventTarget> et = do_QueryInterface(GetOwner());
     nsCOMPtr<nsIDocShell> docShell =
         nsContentUtils::GetDocShellForEventTarget(et);
     DECLARE_DOCSHELL_AND_HISTORY_ID(docShell);
-    profiler_add_marker("UserTiming",
+    profiler_add_marker("UserTiming", js::ProfilingStackFrame::Category::DOM,
                         MakeUnique<UserTimingMarkerPayload>(
                             aName, startMark, endMark, startTimeStamp,
                             endTimeStamp, docShellId, docShellHistoryId));
   }
 #endif
 }
 
 void Performance::ClearMeasures(const Optional<nsAString>& aName) {
--- a/dom/vr/VRDisplay.cpp
+++ b/dom/vr/VRDisplay.cpp
@@ -581,17 +581,17 @@ void VRDisplay::GetLayers(nsTArray<VRLay
   if (mPresentation) {
     mPresentation->GetDOMLayers(result);
   } else {
     result = nsTArray<VRLayer>();
   }
 }
 
 void VRDisplay::SubmitFrame() {
-  AUTO_PROFILER_TRACING("VR", "SubmitFrameAtVRDisplay");
+  AUTO_PROFILER_TRACING("VR", "SubmitFrameAtVRDisplay", OTHER);
 
   if (mClient && !mClient->IsPresentationGenerationCurrent()) {
     mPresentation = nullptr;
     mClient->MakePresentationGenerationCurrent();
   }
 
   if (mPresentation) {
     mPresentation->SubmitFrame();
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -159,17 +159,18 @@ void LayerManager::PayloadPresented() {
     TimeStamp presented = TimeStamp::Now();
     for (CompositionPayload& payload : mPayload) {
 #if MOZ_GECKO_PROFILER
       if (profiler_is_active()) {
         nsPrintfCString marker(
             "Payload Presented, type: %d latency: %dms\n",
             int32_t(payload.mType),
             int32_t((presented - payload.mTimeStamp).ToMilliseconds()));
-        profiler_add_marker(marker.get());
+        profiler_add_marker(marker.get(),
+                            js::ProfilingStackFrame::Category::GRAPHICS);
       }
 #endif
 
       if (payload.mType == CompositionPayloadType::eKeyPress) {
         Telemetry::AccumulateTimeDelta(
             mozilla::Telemetry::KEYPRESS_PRESENT_LATENCY, payload.mTimeStamp,
             presented);
       }
--- a/gfx/layers/ProfilerScreenshots.cpp
+++ b/gfx/layers/ProfilerScreenshots.cpp
@@ -54,27 +54,29 @@ void ProfilerScreenshots::SubmitScreensh
   }
 
   MOZ_RELEASE_ASSERT(aScaledSize <= backingSurface->GetSize());
 
   bool succeeded = aPopulateSurface(backingSurface);
 
   if (!succeeded) {
     PROFILER_ADD_MARKER(
-        "NoCompositorScreenshot because aPopulateSurface callback failed");
+        "NoCompositorScreenshot because aPopulateSurface callback failed",
+        GRAPHICS);
     ReturnSurface(backingSurface);
     return;
   }
 
   if (!mThread) {
     nsresult rv = NS_NewNamedThread("ProfScreenshot", getter_AddRefs(mThread));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       PROFILER_ADD_MARKER(
           "NoCompositorScreenshot because ProfilerScreenshots thread creation "
-          "failed");
+          "failed",
+          DOM);
       ReturnSurface(backingSurface);
       return;
     }
   }
 
   int sourceThread = profiler_current_thread_id();
   uintptr_t windowIdentifier = aWindowIdentifier;
   IntSize originalSize = aOriginalSize;
@@ -98,17 +100,18 @@ void ProfilerScreenshots::SubmitScreensh
           // Encode surf to a JPEG data URL.
           nsCString dataURL;
           nsresult rv = gfxUtils::EncodeSourceSurface(
               surf, ImageType::JPEG, NS_LITERAL_STRING("quality=85"),
               gfxUtils::eDataURIEncode, nullptr, &dataURL);
           if (NS_SUCCEEDED(rv)) {
             // Add a marker with the data URL.
             profiler_add_marker_for_thread(
-                sourceThread, "CompositorScreenshot",
+                sourceThread, js::ProfilingStackFrame::Category::GRAPHICS,
+                "CompositorScreenshot",
                 MakeUnique<ScreenshotPayload>(timeStamp, std::move(dataURL),
                                               originalSize, windowIdentifier));
           }
         }
 
         // Return backingSurface back to the surface pool.
         ReturnSurface(backingSurface);
       }));
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -272,17 +272,17 @@ bool ClientLayerManager::EndTransactionI
   // count as time spent rasterizing.
   {
     PaintTelemetry::AutoRecord record(
         PaintTelemetry::Metric::FlushRasterization);
     FlushAsyncPaints();
   }
 
   PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Rasterization);
-  AUTO_PROFILER_TRACING("Paint", "Rasterize");
+  AUTO_PROFILER_TRACING("Paint", "Rasterize", GRAPHICS);
 
   Maybe<TimeStamp> startTime;
   if (gfxPrefs::LayersDrawFPS()) {
     startTime = Some(TimeStamp::Now());
   }
 
   AUTO_PROFILER_LABEL("ClientLayerManager::EndTransactionInternal", GRAPHICS);
 
@@ -646,17 +646,17 @@ void ClientLayerManager::StopFrameTimeRe
     uint32_t aStartIndex, nsTArray<float>& aFrameIntervals) {
   CompositorBridgeChild* renderer = GetRemoteRenderer();
   if (renderer) {
     renderer->SendStopFrameTimeRecording(aStartIndex, &aFrameIntervals);
   }
 }
 
 void ClientLayerManager::ForwardTransaction(bool aScheduleComposite) {
-  AUTO_PROFILER_TRACING("Paint", "ForwardTransaction");
+  AUTO_PROFILER_TRACING("Paint", "ForwardTransaction", GRAPHICS);
   TimeStamp start = TimeStamp::Now();
 
   // Skip the synchronization for buffer since we also skip the painting during
   // device-reset status. With OMTP, we have to wait for async paints
   // before we synchronize and it's done on the paint thread.
   RefPtr<SyncObjectClient> syncObject = nullptr;
   if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
     if (mForwarder->GetSyncObject() &&
--- a/gfx/layers/composite/CompositorScreenshotGrabber.cpp
+++ b/gfx/layers/composite/CompositorScreenshotGrabber.cpp
@@ -82,17 +82,18 @@ void CompositorScreenshotGrabber::MaybeP
     mImpl->ProcessQueue();
   } else if (mImpl) {
     Destroy();
   }
 }
 
 void CompositorScreenshotGrabber::NotifyEmptyFrame() {
 #ifdef MOZ_GECKO_PROFILER
-  profiler_add_marker("NoCompositorScreenshot because nothing changed");
+  profiler_add_marker("NoCompositorScreenshot because nothing changed",
+                      js::ProfilingStackFrame::Category::GRAPHICS);
 #endif
 }
 
 void CompositorScreenshotGrabber::Destroy() { mImpl = nullptr; }
 
 CompositorScreenshotGrabberImpl::CompositorScreenshotGrabberImpl(
     const IntSize& aBufferSize)
     : mBufferSize(aBufferSize) {}
@@ -141,40 +142,43 @@ void CompositorScreenshotGrabberImpl::Gr
       aCompositor->GetCurrentRenderTarget();
 
   RefPtr<CompositingRenderTarget> windowTarget =
       aCompositor->GetWindowRenderTarget();
 
   if (!windowTarget) {
     PROFILER_ADD_MARKER(
         "NoCompositorScreenshot because of unsupported compositor "
-        "configuration");
+        "configuration",
+        GRAPHICS);
     return;
   }
 
   Size windowSize(windowTarget->GetSize());
   float scale = std::min(mBufferSize.width / windowSize.width,
                          mBufferSize.height / windowSize.height);
   IntSize scaledSize = IntSize::Round(windowSize * scale);
   RefPtr<CompositingRenderTarget> scaledTarget =
       ScaleDownWindowTargetToSize(aCompositor, scaledSize, windowTarget, 0);
 
   // Restore the old render target.
   aCompositor->SetRenderTarget(previousTarget);
 
   if (!scaledTarget) {
     PROFILER_ADD_MARKER(
-        "NoCompositorScreenshot because ScaleDownWindowTargetToSize failed");
+        "NoCompositorScreenshot because ScaleDownWindowTargetToSize failed",
+        GRAPHICS);
     return;
   }
 
   RefPtr<AsyncReadbackBuffer> buffer = TakeNextBuffer(aCompositor);
   if (!buffer) {
     PROFILER_ADD_MARKER(
-        "NoCompositorScreenshot because AsyncReadbackBuffer creation failed");
+        "NoCompositorScreenshot because AsyncReadbackBuffer creation failed",
+        GRAPHICS);
     return;
   }
 
   aCompositor->ReadbackRenderTarget(scaledTarget, buffer);
 
   // This QueueItem will be added to the queue at the end of the next call to
   // ProcessQueue(). This ensures that the buffer isn't mapped into main memory
   // until the next frame. If we did it in this frame, we'd block on the GPU.
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -96,16 +96,17 @@ static void PrintUniformityInfo(Layer* a
 
   Matrix4x4 transform = aLayer->AsHostLayer()->GetShadowBaseTransform();
   if (!transform.Is2D()) {
     return;
   }
 
   Point translation = transform.As2D().GetTranslation();
   profiler_add_marker("LayerTranslation",
+                      js::ProfilingStackFrame::Category::GRAPHICS,
                       MakeUnique<LayerTranslationMarkerPayload>(
                           aLayer, translation, TimeStamp::Now()));
 #endif
 }
 
 static Maybe<gfx::Polygon> SelectLayerGeometry(
     const Maybe<gfx::Polygon>& aParentGeometry,
     const Maybe<gfx::Polygon>& aChildGeometry) {
--- a/gfx/layers/ipc/CompositorBench.cpp
+++ b/gfx/layers/ipc/CompositorBench.cpp
@@ -287,17 +287,17 @@ static void RunCompositorBench(Composito
   tests.push_back(new EffectSolidColorStressBench());
   tests.push_back(new TrivialTexturedQuadBench());
   tests.push_back(new StressTexturedQuadBench());
 
   for (size_t i = 0; i < tests.size(); i++) {
     BenchTest* test = tests[i];
     std::vector<TimeDuration> results;
     int testsOverThreshold = 0;
-    PROFILER_ADD_MARKER(test->ToString());
+    PROFILER_ADD_MARKER(test->ToString(), GRAPHICS);
     for (size_t j = 0; j < TEST_STEPS; j++) {
       test->Setup(aCompositor, j);
 
       TimeStamp start = TimeStamp::Now();
       IntRect screenRect(aScreenRect.x, aScreenRect.y, aScreenRect.width,
                          aScreenRect.height);
       aCompositor->BeginFrame(IntRect(screenRect.x, screenRect.y,
                                       screenRect.width, screenRect.height),
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -899,17 +899,17 @@ void CompositorBridgeParent::ScheduleCom
     }
     layerCompositor->SetShadowVisibleRegion(layer->GetVisibleRegion());
     layerCompositor->SetShadowClipRect(layer->GetClipRect());
   });
 }
 
 void CompositorBridgeParent::CompositeToTarget(VsyncId aId, DrawTarget* aTarget,
                                                const gfx::IntRect* aRect) {
-  AUTO_PROFILER_TRACING("Paint", "Composite");
+  AUTO_PROFILER_TRACING("Paint", "Composite", GRAPHICS);
   AUTO_PROFILER_LABEL("CompositorBridgeParent::CompositeToTarget", GRAPHICS);
 
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
              "Composite can only be called on the compositor thread");
   TimeStamp start = TimeStamp::Now();
 
   if (!CanComposite()) {
     TimeStamp end = TimeStamp::Now();
@@ -1898,16 +1898,17 @@ CompositorBridgeParent::GetAPZCTreeManag
   return apzctm.forget();
 }
 
 #if defined(MOZ_GECKO_PROFILER)
 static void InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (profiler_thread_is_being_profiled()) {
     profiler_add_marker("VsyncTimestamp",
+                        js::ProfilingStackFrame::Category::GRAPHICS,
                         MakeUnique<VsyncMarkerPayload>(aVsyncTimestamp));
   }
 }
 #endif
 
 /*static */ void CompositorBridgeParent::PostInsertVsyncProfilerMarker(
     TimeStamp aVsyncTimestamp) {
 #if defined(MOZ_GECKO_PROFILER)
@@ -2446,17 +2447,18 @@ int32_t RecordContentFrameTime(
       virtual void StreamPayload(SpliceableJSONWriter& aWriter,
                                  const TimeStamp& aProcessStartTime,
                                  UniqueStacks& aUniqueStacks) override {
         StreamCommonProps("CONTENT_FRAME_TIME", aWriter, aProcessStartTime,
                           aUniqueStacks);
       }
     };
     profiler_add_marker_for_thread(
-        profiler_current_thread_id(), "CONTENT_FRAME_TIME",
+        profiler_current_thread_id(),
+        js::ProfilingStackFrame::Category::GRAPHICS, "CONTENT_FRAME_TIME",
         MakeUnique<ContentFramePayload>(aTxnStart, aCompositeEnd));
   }
 #endif
 
   Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME, fracLatencyNorm);
 
   if (!(aTxnId == VsyncId()) && aVsyncStart) {
     latencyMs = (aCompositeEnd - aVsyncStart).ToMilliseconds();
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -375,17 +375,18 @@ void CrossProcessCompositorBridgeParent:
       virtual void StreamPayload(SpliceableJSONWriter& aWriter,
                                  const TimeStamp& aProcessStartTime,
                                  UniqueStacks& aUniqueStacks) override {
         StreamCommonProps("CONTENT_FULL_PAINT_TIME", aWriter, aProcessStartTime,
                           aUniqueStacks);
       }
     };
     profiler_add_marker_for_thread(
-        profiler_current_thread_id(), "CONTENT_FULL_PAINT_TIME",
+        profiler_current_thread_id(),
+        js::ProfilingStackFrame::Category::GRAPHICS, "CONTENT_FULL_PAINT_TIME",
         MakeUnique<ContentBuildPayload>(aInfo.transactionStart(), endTime));
   }
 #endif
   Telemetry::Accumulate(
       Telemetry::CONTENT_FULL_PAINT_TIME,
       static_cast<uint32_t>(
           (endTime - aInfo.transactionStart()).ToMilliseconds()));
 
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -146,17 +146,17 @@ mozilla::ipc::IPCResult LayerTransaction
 mozilla::ipc::IPCResult LayerTransactionParent::RecvUpdate(
     const TransactionInfo& aInfo) {
   auto guard = MakeScopeExit([&] {
     if (recordreplay::IsRecordingOrReplaying()) {
       recordreplay::child::NotifyPaintComplete();
     }
   });
 
-  AUTO_PROFILER_TRACING("Paint", "LayerTransaction");
+  AUTO_PROFILER_TRACING("Paint", "LayerTransaction", GRAPHICS);
   AUTO_PROFILER_LABEL("LayerTransactionParent::RecvUpdate", GRAPHICS);
 
   TimeStamp updateStart = TimeStamp::Now();
 
   MOZ_LAYERS_LOG(
       ("[ParentSide] received txn with %zu edits", aInfo.cset().Length()));
 
   UpdateFwdTransactionId(aInfo.fwdTransactionId());
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -51,23 +51,27 @@ bool is_in_compositor_thread() {
 }
 
 bool is_in_render_thread() {
   return mozilla::wr::RenderThread::IsInRenderThread();
 }
 
 void gecko_profiler_start_marker(const char* name) {
 #ifdef MOZ_GECKO_PROFILER
-  profiler_tracing("WebRender", name, TRACING_INTERVAL_START);
+  profiler_tracing("WebRender", name,
+                   js::ProfilingStackFrame::Category::GRAPHICS,
+                   TRACING_INTERVAL_START);
 #endif
 }
 
 void gecko_profiler_end_marker(const char* name) {
 #ifdef MOZ_GECKO_PROFILER
-  profiler_tracing("WebRender", name, TRACING_INTERVAL_END);
+  profiler_tracing("WebRender", name,
+                   js::ProfilingStackFrame::Category::GRAPHICS,
+                   TRACING_INTERVAL_END);
 #endif
 }
 
 bool is_glcontext_egl(void* glcontext_ptr) {
   MOZ_ASSERT(glcontext_ptr);
 
   mozilla::gl::GLContext* glcontext =
       reinterpret_cast<mozilla::gl::GLContext*>(glcontext_ptr);
@@ -200,17 +204,19 @@ class SceneBuiltNotification : public wr
                                          const TimeStamp& aProcessStartTime,
                                          UniqueStacks& aUniqueStacks) override {
                 StreamCommonProps("CONTENT_FULL_PAINT_TIME", aWriter,
                                   aProcessStartTime, aUniqueStacks);
               }
             };
 
             profiler_add_marker_for_thread(
-                profiler_current_thread_id(), "CONTENT_FULL_PAINT_TIME",
+                profiler_current_thread_id(),
+                js::ProfilingStackFrame::Category::GRAPHICS,
+                "CONTENT_FULL_PAINT_TIME",
                 MakeUnique<ContentFullPaintPayload>(startTime, endTime));
           }
 #endif
           Telemetry::Accumulate(
               Telemetry::CONTENT_FULL_PAINT_TIME,
               static_cast<uint32_t>((endTime - startTime).ToMilliseconds()));
           parent->NotifySceneBuiltForEpoch(epoch, endTime);
         }));
@@ -912,17 +918,17 @@ mozilla::ipc::IPCResult WebRenderBridgeP
     }
     return IPC_OK();
   }
 
   if (!IsRootWebRenderBridgeParent()) {
     CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, aTxnURL);
   }
 
-  AUTO_PROFILER_TRACING("Paint", "SetDisplayList");
+  AUTO_PROFILER_TRACING("Paint", "SetDisplayList", GRAPHICS);
   UpdateFwdTransactionId(aFwdTransactionId);
 
   // This ensures that destroy operations are always processed. It is not safe
   // to early-return from RecvDPEnd without doing so.
   AutoWebRenderBridgeParentAsyncMessageSender autoAsyncMessageSender(
       this, &aToDestroy);
 
   wr::Epoch wrEpoch = GetNextWrEpoch();
@@ -1038,17 +1044,17 @@ mozilla::ipc::IPCResult WebRenderBridgeP
     }
     return IPC_OK();
   }
 
   if (!IsRootWebRenderBridgeParent()) {
     CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, aTxnURL);
   }
 
-  AUTO_PROFILER_TRACING("Paint", "EmptyTransaction");
+  AUTO_PROFILER_TRACING("Paint", "EmptyTransaction", GRAPHICS);
   UpdateFwdTransactionId(aFwdTransactionId);
 
   // This ensures that destroy operations are always processed. It is not safe
   // to early-return without doing so.
   AutoWebRenderBridgeParentAsyncMessageSender autoAsyncMessageSender(
       this, &aToDestroy);
 
   bool scheduleComposite = false;
@@ -1715,17 +1721,17 @@ void WebRenderBridgeParent::CompositeToT
   // This function should only get called in the root WRBP
   MOZ_ASSERT(IsRootWebRenderBridgeParent());
 
   // The two arguments are part of the CompositorVsyncSchedulerOwner API but in
   // this implementation they should never be non-null.
   MOZ_ASSERT(aTarget == nullptr);
   MOZ_ASSERT(aRect == nullptr);
 
-  AUTO_PROFILER_TRACING("Paint", "CompositeToTarget");
+  AUTO_PROFILER_TRACING("Paint", "CompositeToTarget", GRAPHICS);
   if (mPaused || !mReceivedDisplayList) {
     mPreviousFrameTimeStamp = TimeStamp();
     return;
   }
 
   if (mSkippedComposite ||
       wr::RenderThread::Get()->TooManyPendingFrames(mApi->GetId())) {
     // Render thread is busy, try next time.
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -243,17 +243,17 @@ void WebRenderLayerManager::EndTransacti
   // EndTransactionWithoutLayer instead.
   MOZ_ASSERT(false);
 }
 
 void WebRenderLayerManager::EndTransactionWithoutLayer(
     nsDisplayList* aDisplayList, nsDisplayListBuilder* aDisplayListBuilder,
     const nsTArray<wr::FilterOp>& aFilters,
     WebRenderBackgroundData* aBackground) {
-  AUTO_PROFILER_TRACING("Paint", "RenderLayers");
+  AUTO_PROFILER_TRACING("Paint", "RenderLayers", GRAPHICS);
 
   // Since we don't do repeat transactions right now, just set the time
   mAnimationReadyTime = TimeStamp::Now();
 
   WrBridge()->BeginTransaction();
 
   LayoutDeviceIntSize size = mWidget->GetClientSize();
   wr::LayoutSize contentSize{(float)size.width, (float)size.height};
@@ -348,17 +348,17 @@ void WebRenderLayerManager::EndTransacti
     }
   }
 
   wr::BuiltDisplayList dl;
   builder.Finalize(contentSize, dl);
   mLastDisplayListSize = dl.dl.inner.capacity;
 
   {
-    AUTO_PROFILER_TRACING("Paint", "ForwardDPTransaction");
+    AUTO_PROFILER_TRACING("Paint", "ForwardDPTransaction", GRAPHICS);
     WrBridge()->EndTransaction(contentSize, dl, resourceUpdates,
                                size.ToUnknownSize(), mLatestTransactionId,
                                mScrollData, containsSVGGroup,
                                mTransactionIdAllocator->GetVsyncId(),
                                mTransactionIdAllocator->GetVsyncStart(),
                                refreshStart, mTransactionStart, mURL);
   }
 
--- a/gfx/vr/VRDisplayHost.cpp
+++ b/gfx/vr/VRDisplayHost.cpp
@@ -170,17 +170,17 @@ void VRDisplayHost::RemoveLayer(VRLayerP
   }
 
   // Ensure that the content process receives the change immediately
   VRManager* vm = VRManager::Get();
   vm->RefreshVRDisplays();
 }
 
 void VRDisplayHost::StartFrame() {
-  AUTO_PROFILER_TRACING("VR", "GetSensorState");
+  AUTO_PROFILER_TRACING("VR", "GetSensorState", OTHER);
 
   TimeStamp now = TimeStamp::Now();
 #if defined(MOZ_WIDGET_ANDROID)
   const TimeStamp lastFrameStart =
       mDisplayInfo.mLastFrameStart[mDisplayInfo.mFrameId % kVRMaxLatencyFrames];
   const bool isPresenting = mLastUpdateDisplayInfo.GetPresentingGroups() != 0;
   double duration =
       lastFrameStart.IsNull() ? 0.0 : (now - lastFrameStart).ToMilliseconds();
@@ -278,17 +278,17 @@ void VRDisplayHost::Run100msTasks() {
 }
 
 void VRDisplayHost::SubmitFrameInternal(
     const layers::SurfaceDescriptor& aTexture, uint64_t aFrameId,
     const gfx::Rect& aLeftEyeRect, const gfx::Rect& aRightEyeRect) {
 #if !defined(MOZ_WIDGET_ANDROID)
   MOZ_ASSERT(mSubmitThread->GetThread() == NS_GetCurrentThread());
 #endif  // !defined(MOZ_WIDGET_ANDROID)
-  AUTO_PROFILER_TRACING("VR", "SubmitFrameAtVRDisplayHost");
+  AUTO_PROFILER_TRACING("VR", "SubmitFrameAtVRDisplayHost", OTHER);
 
   if (!SubmitFrame(aTexture, aFrameId, aLeftEyeRect, aRightEyeRect)) {
     return;
   }
 
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
 
   /**
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -369,17 +369,17 @@ mozilla::ipc::IPCResult VRManagerChild::
   } else {
     p->MaybeResolve(new VRMockController(aID, aDeviceID));
   }
   mPromiseList.Remove(aPromiseID);
   return IPC_OK();
 }
 
 void VRManagerChild::RunFrameRequestCallbacks() {
-  AUTO_PROFILER_TRACING("VR", "RunFrameRequestCallbacks");
+  AUTO_PROFILER_TRACING("VR", "RunFrameRequestCallbacks", GRAPHICS);
 
   TimeStamp nowTime = TimeStamp::Now();
   mozilla::TimeDuration duration = nowTime - mStartTimeStamp;
   DOMHighResTimeStamp timeStamp = duration.ToMilliseconds();
 
   nsTArray<FrameRequest> callbacks;
   callbacks.AppendElements(mFrameRequestCallbacks);
   mFrameRequestCallbacks.Clear();
--- a/gfx/webrender_bindings/Moz2DImageRenderer.cpp
+++ b/gfx/webrender_bindings/Moz2DImageRenderer.cpp
@@ -306,17 +306,17 @@ static RefPtr<ScaledFont> GetScaledFont(
 }
 
 static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
                                 gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                 const uint16_t* aTileSize,
                                 const mozilla::wr::TileOffset* aTileOffset,
                                 const mozilla::wr::LayoutIntRect* aDirtyRect,
                                 Range<uint8_t> aOutput) {
-  AUTO_PROFILER_TRACING("WebRender", "RasterizeSingleBlob");
+  AUTO_PROFILER_TRACING("WebRender", "RasterizeSingleBlob", GRAPHICS);
   MOZ_ASSERT(aSize.width > 0 && aSize.height > 0);
   if (aSize.width <= 0 || aSize.height <= 0) {
     return false;
   }
 
   auto stride = aSize.width * gfx::BytesPerPixel(aFormat);
 
   if (aOutput.length() < static_cast<size_t>(aSize.height * stride)) {
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -339,17 +339,17 @@ static void NotifyDidStartRender(layers:
 }
 
 void RenderThread::UpdateAndRender(wr::WindowId aWindowId,
                                    const VsyncId& aStartId,
                                    const TimeStamp& aStartTime, bool aRender,
                                    const Maybe<gfx::IntSize>& aReadbackSize,
                                    const Maybe<Range<uint8_t>>& aReadbackBuffer,
                                    bool aHadSlowFrame) {
-  AUTO_PROFILER_TRACING("Paint", "Composite");
+  AUTO_PROFILER_TRACING("Paint", "Composite", GRAPHICS);
   MOZ_ASSERT(IsInRenderThread());
   MOZ_ASSERT(aRender || aReadbackBuffer.isNothing());
 
   auto it = mRenderers.find(aWindowId);
   MOZ_ASSERT(it != mRenderers.end());
   if (it == mRenderers.end()) {
     return;
   }
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -4544,17 +4544,18 @@ class _GenerateProtocolActorCode(ipdl.as
               self.profilerLabel(md)]
              + self.transition(md, actor, errorfn=errfnUnreachable)
              + [Whitespace.NL,
                 StmtDecl(Decl(Type.BOOL, sendok.name)),
                 StmtBlock([
                     StmtExpr(ExprCall(ExprVar('AUTO_PROFILER_TRACING'),
                                       [ExprLiteral.String("IPC"),
                                        ExprLiteral.String(self.protocol.name + "::" +
-                                                          md.prettyMsgName())])),
+                                                          md.prettyMsgName()),
+                                       ExprVar('OTHER')])),
                     StmtExpr(ExprAssn(sendok,
                                       ExprCall(
                                           ExprSelect(self.protocol.callGetChannel(actor),
                                                      '->',
                                                      _sendPrefix(md.decl.type)),
                                           args=[msgexpr, ExprAddrOf(replyexpr)]))),
                 ])
                 ])
--- a/layout/base/AutoProfilerStyleMarker.h
+++ b/layout/base/AutoProfilerStyleMarker.h
@@ -34,17 +34,17 @@ class MOZ_RAII AutoProfilerStyleMarker {
     ServoTraversalStatistics::sActive = true;
   }
 
   ~AutoProfilerStyleMarker() {
     if (!mActive) {
       return;
     }
     ServoTraversalStatistics::sActive = false;
-    profiler_add_marker("Styles",
+    profiler_add_marker("Styles", js::ProfilingStackFrame::Category::LAYOUT,
                         MakeUnique<StyleMarkerPayload>(
                             mStartTime, TimeStamp::Now(), std::move(mCause),
                             ServoTraversalStatistics::sSingleton, mDocShellId,
                             mDocShellHistoryId));
   }
 
  private:
   bool mActive;
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -3699,17 +3699,17 @@ void PresShell::ScheduleViewManagerFlush
   nsPresContext* presContext = GetPresContext();
   if (presContext) {
     presContext->RefreshDriver()->ScheduleViewManagerFlush();
   }
   SetNeedLayoutFlush();
 }
 
 void nsIPresShell::DispatchSynthMouseMove(WidgetGUIEvent* aEvent) {
-  AUTO_PROFILER_TRACING_DOCSHELL("Paint", "DispatchSynthMouseMove",
+  AUTO_PROFILER_TRACING_DOCSHELL("Paint", "DispatchSynthMouseMove", GRAPHICS,
                                  mPresContext->GetDocShell());
   nsEventStatus status = nsEventStatus_eIgnore;
   nsView* targetView = nsView::GetViewFor(aEvent->mWidget);
   if (!targetView) return;
   targetView->GetViewManager()->DispatchEvent(aEvent, targetView, &status);
 }
 
 void PresShell::ClearMouseCaptureOnView(nsView* aView) {
@@ -8511,19 +8511,19 @@ bool PresShell::DoReflow(nsIFrame* targe
 
   if (isTimelineRecording) {
     timelines->AddMarkerForDocShell(docShell, "Reflow",
                                     MarkerTracingType::START);
   }
 
 #ifdef MOZ_GECKO_PROFILER
   DECLARE_DOCSHELL_AND_HISTORY_ID(docShell);
-  AutoProfilerTracing tracingLayoutFlush("Paint", "Reflow",
-                                         std::move(mReflowCause), docShellId,
-                                         docShellHistoryId);
+  AutoProfilerTracing tracingLayoutFlush(
+      "Paint", "Reflow", js::ProfilingStackFrame::Category::LAYOUT,
+      std::move(mReflowCause), docShellId, docShellHistoryId);
   mReflowCause = nullptr;
 #endif
 
   FlushPendingScrollAnchorSelections();
 
   if (mReflowContinueTimer) {
     mReflowContinueTimer->Cancel();
     mReflowContinueTimer = nullptr;
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -8297,17 +8297,17 @@ static nsIFrame* FindPreviousNonWhitespa
   } while (f && IsWhitespaceFrame(f));
   return f;
 }
 
 bool nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(
     nsIFrame* aFrame) {
 #define TRACE(reason)                                                          \
   PROFILER_TRACING("Layout", "MaybeRecreateContainerForFrameRemoval: " reason, \
-                   TRACING_EVENT)
+                   LAYOUT, TRACING_EVENT)
   MOZ_ASSERT(aFrame, "Must have a frame");
   MOZ_ASSERT(aFrame->GetParent(), "Frame shouldn't be root");
   MOZ_ASSERT(aFrame == aFrame->FirstContinuation(),
              "aFrame not the result of GetPrimaryFrame()?");
 
   nsIFrame* inFlowFrame = (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
                               ? aFrame->GetPlaceholderFrame()
                               : aFrame;
@@ -11183,18 +11183,19 @@ static bool IsSafeToAppendToIBSplitInlin
 
   return true;
 }
 
 bool nsCSSFrameConstructor::WipeContainingBlock(
     nsFrameConstructorState& aState, nsIFrame* aContainingBlock,
     nsIFrame* aFrame, FrameConstructionItemList& aItems, bool aIsAppend,
     nsIFrame* aPrevSibling) {
-#define TRACE(reason) \
-  PROFILER_TRACING("Layout", "WipeContainingBlock: " reason, TRACING_EVENT)
+#define TRACE(reason)                                                \
+  PROFILER_TRACING("Layout", "WipeContainingBlock: " reason, LAYOUT, \
+                   TRACING_EVENT)
 
   if (aItems.IsEmpty()) {
     return false;
   }
 
   // Before we go and append the frames, we must check for several
   // special situations.
 
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -3544,17 +3544,17 @@ nsresult nsLayoutUtils::PaintFrame(gfxCo
     MaybeCreateDisplayPortInFirstScrollFrameEncountered(aFrame, builder);
   }
 
   nsRect visibleRect = visibleRegion.GetBounds();
   PartialUpdateResult updateState = PartialUpdateResult::Failed;
 
   {
     AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame:BuildDisplayList", GRAPHICS);
-    AUTO_PROFILER_TRACING("Paint", "DisplayList");
+    AUTO_PROFILER_TRACING("Paint", "DisplayList", GRAPHICS);
 
     PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::DisplayList);
     TimeStamp dlStart = TimeStamp::Now();
 
     {
       // If a scrollable container layer is created in
       // nsDisplayList::PaintForFrame, it will be the scroll parent for display
       // items that are built in the BuildDisplayListForStackingContext call
@@ -3897,17 +3897,17 @@ nsresult nsLayoutUtils::PaintFrame(gfxCo
     // Disable partial updates for the following paint as well, as we now have
     // a plugin-specific display list.
     builder.SetDisablePartialUpdates(true);
   }
 
   builder.Check();
 
   {
-    AUTO_PROFILER_TRACING("Paint", "DisplayListResources");
+    AUTO_PROFILER_TRACING("Paint", "DisplayListResources", GRAPHICS);
 
     // Flush the list so we don't trigger the IsEmpty-on-destruction assertion
     if (!useRetainedBuilder) {
       list.DeleteAll(&builder);
     }
 
     builder.EndFrame();
   }
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -311,17 +311,17 @@ class RefreshDriverTimer {
    */
   void Tick(VsyncId aId, TimeStamp now) {
     ScheduleNextTick(now);
 
     mLastFireTime = now;
 
     LOG("[%p] ticking drivers...", this);
     // RD is short for RefreshDriver
-    AUTO_PROFILER_TRACING("Paint", "RefreshDriverTick");
+    AUTO_PROFILER_TRACING("Paint", "RefreshDriverTick", GRAPHICS);
 
     TickRefreshDrivers(aId, now, mContentRefreshDrivers);
     TickRefreshDrivers(aId, now, mRootRefreshDrivers);
 
     LOG("[%p] done.", this);
   }
 
   static void TickDriver(nsRefreshDriver* driver, VsyncId aId, TimeStamp now) {
@@ -1608,17 +1608,17 @@ void nsRefreshDriver::RunFrameRequestCal
   for (Document* doc : mFrameRequestCallbackDocs) {
     TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
   }
 
   // Reset mFrameRequestCallbackDocs so they can be readded as needed.
   mFrameRequestCallbackDocs.Clear();
 
   if (!frameRequestCallbacks.IsEmpty()) {
-    AUTO_PROFILER_TRACING_DOCSHELL("Paint", "Scripts",
+    AUTO_PROFILER_TRACING_DOCSHELL("Paint", "Scripts", GRAPHICS,
                                    GetDocShell(mPresContext));
     for (const DocumentFrameCallbacks& docCallbacks : frameRequestCallbacks) {
       // XXXbz Bug 863140: GetInnerWindow can return the outer
       // window in some cases.
       nsPIDOMWindowInner* innerWindow =
           docCallbacks.mDocument->GetInnerWindow();
       DOMHighResTimeStamp timeStamp = 0;
       if (innerWindow) {
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -4910,17 +4910,18 @@ ScrollFrameHelper::ScrollEndEvent::Run()
   return NS_OK;
 }
 
 void ScrollFrameHelper::FireScrollEvent() {
   nsIContent* content = mOuter->GetContent();
   nsPresContext* prescontext = mOuter->PresContext();
 #ifdef MOZ_GECKO_PROFILER
   nsCOMPtr<nsIDocShell> docShell = prescontext->GetDocShell();
-  AUTO_PROFILER_TRACING_DOCSHELL("Paint", "FireScrollEvent", docShell);
+  AUTO_PROFILER_TRACING_DOCSHELL("Paint", "FireScrollEvent", GRAPHICS,
+                                 docShell);
 #endif
 
   MOZ_ASSERT(mScrollEvent);
   mScrollEvent->Revoke();
   mScrollEvent = nullptr;
 
   // If event handling is suppressed, keep posting the scroll event to the
   // refresh driver until it is unsuppressed. The event is marked as delayed so
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -2464,17 +2464,18 @@ FrameLayerBuilder* nsDisplayList::BuildL
   if (aFlags & PAINT_COMPRESSED) {
     layerBuilder->SetLayerTreeCompressionMode();
   }
 
   RefPtr<ContainerLayer> root;
   {
 #ifdef MOZ_GECKO_PROFILER
     nsCOMPtr<nsIDocShell> docShell = presContext->GetDocShell();
-    AUTO_PROFILER_TRACING_DOCSHELL("Paint", "LayerBuilding", docShell);
+    AUTO_PROFILER_TRACING_DOCSHELL("Paint", "LayerBuilding", GRAPHICS,
+                                   docShell);
 #endif
 
     if (XRE_IsContentProcess() && gfxPrefs::AlwaysPaint()) {
       FrameLayerBuilder::InvalidateAllLayers(aLayerManager);
     }
 
     if (aIsWidgetTransaction) {
       layerBuilder->DidBeginRetainedLayerTransaction(aLayerManager);
--- a/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
+++ b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
@@ -493,18 +493,18 @@ void BackgroundHangThread::ReportHang(Ti
   }
 
   // If the profiler is enabled, add a marker.
 #ifdef MOZ_GECKO_PROFILER
   if (profiler_is_active()) {
     TimeStamp endTime = TimeStamp::Now();
     TimeStamp startTime = endTime - aHangTime;
     profiler_add_marker_for_thread(
-        mStackHelper.GetThreadId(), "BHR-detected hang",
-        MakeUnique<HangMarkerPayload>(startTime, endTime));
+        mStackHelper.GetThreadId(), js::ProfilingStackFrame::Category::OTHER,
+        "BHR-detected hang", MakeUnique<HangMarkerPayload>(startTime, endTime));
   }
 #endif
 }
 
 void BackgroundHangThread::ReportPermaHang() {
   // Permanently hanged; called on the monitor thread
   // mManager->mLock IS locked
 
--- a/toolkit/components/startup/StartupTimeline.h
+++ b/toolkit/components/startup/StartupTimeline.h
@@ -62,17 +62,17 @@ class StartupTimeline {
   };
 
   static TimeStamp Get(Event ev) { return sStartupTimeline[ev]; }
 
   static const char* Describe(Event ev) { return sStartupTimelineDesc[ev]; }
 
 #ifdef MOZILLA_INTERNAL_API
   static void Record(Event ev) {
-    PROFILER_ADD_MARKER(Describe(ev));
+    PROFILER_ADD_MARKER(Describe(ev), OTHER);
     Record(ev, TimeStamp::Now());
   }
 
   static void Record(Event ev, TimeStamp when) { sStartupTimeline[ev] = when; }
 
   static void RecordOnce(Event ev);
 #endif
 
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -357,17 +357,17 @@ nsAppStartup::Quit(uint32_t aMode) {
           if (domWindow) {
             if (!domWindow->CanClose()) return NS_OK;
           }
           windowEnumerator->HasMoreElements(&more);
         }
       }
     }
 
-    PROFILER_ADD_MARKER("Shutdown start");
+    PROFILER_ADD_MARKER("Shutdown start", OTHER);
     mozilla::RecordShutdownStartTimeStamp();
     mShuttingDown = true;
     if (!mRestart) {
       mRestart = (aMode & eRestart) != 0;
     }
 
     if (!mRestartNotSameProfile) {
       mRestartNotSameProfile = (aMode & eRestartNotSameProfile) != 0;
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1297,17 +1297,17 @@ ScopedXPCOMStartup::~ScopedXPCOMStartup(
     // during teardown.
     mozilla::MacAutoreleasePool pool;
 #endif
 
     nsCOMPtr<nsIAppStartup> appStartup(do_GetService(NS_APPSTARTUP_CONTRACTID));
     if (appStartup) appStartup->DestroyHiddenWindow();
 
     gDirServiceProvider->DoShutdown();
-    PROFILER_ADD_MARKER("Shutdown early");
+    PROFILER_ADD_MARKER("Shutdown early", OTHER);
 
     WriteConsoleLog();
 
     NS_ShutdownXPCOM(mServiceManager);
     mServiceManager = nullptr;
   }
 }
 
--- a/tools/profiler/core/ProfiledThreadData.cpp
+++ b/tools/profiler/core/ProfiledThreadData.cpp
@@ -260,16 +260,17 @@ void StreamSamplesAndMarkers(const char*
   aWriter.EndObject();
 
   aWriter.StartObjectProperty("markers");
   {
     {
       JSONSchemaWriter schema(aWriter);
       schema.WriteField("name");
       schema.WriteField("time");
+      schema.WriteField("category");
       schema.WriteField("data");
     }
 
     aWriter.StartArrayProperty("data");
     {
       aBuffer.StreamMarkersToJSON(aWriter, aThreadId, aProcessStartTime,
                                   aSinceTime, aUniqueStacks);
     }
--- a/tools/profiler/core/ProfilerMarker.h
+++ b/tools/profiler/core/ProfilerMarker.h
@@ -16,25 +16,27 @@ class ProfilerLinkedList;
 class SpliceableJSONWriter;
 class UniqueStacks;
 
 class ProfilerMarker {
   friend class ProfilerLinkedList<ProfilerMarker>;
 
  public:
   explicit ProfilerMarker(
-      const char* aMarkerName, int aThreadId,
+      const char* aMarkerName, js::ProfilingStackFrame::Category aCategory,
+      int aThreadId,
       mozilla::UniquePtr<ProfilerMarkerPayload> aPayload = nullptr,
       double aTime = 0)
       : mMarkerName(strdup(aMarkerName)),
         mPayload(std::move(aPayload)),
         mNext{nullptr},
         mTime(aTime),
         mPositionInBuffer{0},
-        mThreadId{aThreadId} {}
+        mThreadId{aThreadId},
+        mCategory{aCategory} {}
 
   void SetPositionInBuffer(uint64_t aPosition) {
     mPositionInBuffer = aPosition;
   }
 
   bool HasExpired(uint64_t aBufferRangeStart) const {
     return mPositionInBuffer < aBufferRangeStart;
   }
@@ -42,22 +44,23 @@ class ProfilerMarker {
   double GetTime() const { return mTime; }
 
   int GetThreadId() const { return mThreadId; }
 
   void StreamJSON(SpliceableJSONWriter& aWriter,
                   const mozilla::TimeStamp& aProcessStartTime,
                   UniqueStacks& aUniqueStacks) const {
     // Schema:
-    //   [name, time, data]
+    //   [name, time, category, data]
 
     aWriter.StartArrayElement();
     {
       aUniqueStacks.mUniqueStrings->WriteElement(aWriter, mMarkerName.get());
       aWriter.DoubleElement(mTime);
+      aWriter.IntElement(unsigned(mCategory));
       // TODO: Store the callsite for this marker if available:
       // if have location data
       //   b.NameValue(marker, "location", ...);
       if (mPayload) {
         aWriter.StartObjectElement(SpliceableJSONWriter::SingleLineStyle);
         { mPayload->StreamPayload(aWriter, aProcessStartTime, aUniqueStacks); }
         aWriter.EndObject();
       }
@@ -67,16 +70,17 @@ class ProfilerMarker {
 
  private:
   mozilla::UniqueFreePtr<char> mMarkerName;
   mozilla::UniquePtr<ProfilerMarkerPayload> mPayload;
   ProfilerMarker* mNext;
   double mTime;
   uint64_t mPositionInBuffer;
   int mThreadId;
+  js::ProfilingStackFrame::Category mCategory;
 };
 
 template <typename T>
 class ProfilerLinkedList {
  public:
   ProfilerLinkedList() : mHead(nullptr), mTail(nullptr) {}
 
   void insert(T* aElem) {
--- a/tools/profiler/core/RegisteredThread.h
+++ b/tools/profiler/core/RegisteredThread.h
@@ -29,22 +29,23 @@ class RacyRegisteredThread final {
 
   void SetIsBeingProfiled(bool aIsBeingProfiled) {
     mIsBeingProfiled = aIsBeingProfiled;
   }
 
   bool IsBeingProfiled() const { return mIsBeingProfiled; }
 
   void AddPendingMarker(const char* aMarkerName,
+                        js::ProfilingStackFrame::Category aCategory,
                         mozilla::UniquePtr<ProfilerMarkerPayload> aPayload,
                         double aTime) {
     // Note: We don't assert on mIsBeingProfiled, because it could have changed
     // between the check in the caller and now.
-    ProfilerMarker* marker =
-        new ProfilerMarker(aMarkerName, mThreadId, std::move(aPayload), aTime);
+    ProfilerMarker* marker = new ProfilerMarker(
+        aMarkerName, aCategory, mThreadId, std::move(aPayload), aTime);
     mPendingMarkers.insert(marker);
   }
 
   // Called within signal. Function must be reentrant.
   ProfilerMarkerLinkedList* GetPendingMarkers() {
     // The profiled thread is interrupted, so we can access the list safely.
     // Unless the profiled thread was in the middle of changing the list when
     // we interrupted it - in that case, accessList() will return null.
@@ -240,17 +241,18 @@ class RegisteredThread final {
         mJSSampling = ACTIVE;
         js::EnableContextProfilingStack(mContext, true);
         JS_SetGlobalJitCompilerOption(mContext,
                                       JSJITCOMPILER_TRACK_OPTIMIZATIONS,
                                       TrackOptimizationsEnabled());
         if (JSTracerEnabled()) {
           JS::StartTraceLogger(mContext);
         }
-        js::RegisterContextProfilingEventMarker(mContext, profiler_add_marker);
+        js::RegisterContextProfilingEventMarker(mContext,
+                                                profiler_add_js_marker);
 
       } else if (mJSSampling == INACTIVE_REQUESTED) {
         mJSSampling = INACTIVE;
         js::EnableContextProfilingStack(mContext, false);
         if (JSTracerEnabled()) {
           JS::StopTraceLogger(mContext);
         }
       }
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -3623,17 +3623,18 @@ UniqueProfilerBacktrace profiler_get_bac
       new ProfilerBacktrace("SyncProfile", tid, std::move(buffer)));
 }
 
 void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) {
   delete aBacktrace;
 }
 
 static void racy_profiler_add_marker(
-    const char* aMarkerName, UniquePtr<ProfilerMarkerPayload> aPayload) {
+    const char* aMarkerName, js::ProfilingStackFrame::Category aCategory,
+    UniquePtr<ProfilerMarkerPayload> aPayload) {
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
   // We don't assert that RacyFeatures::IsActiveWithoutPrivacy() or
   // RacyRegisteredThread::IsBeingProfiled() is true here, because it's
   // possible that the result has changed since we tested it in the caller.
   //
   // Because of this imprecision it's possible to miss a marker or record one
   // we shouldn't. Either way is not a big deal.
@@ -3643,34 +3644,43 @@ static void racy_profiler_add_marker(
   if (!racyRegisteredThread || !racyRegisteredThread->IsBeingProfiled()) {
     return;
   }
 
   TimeStamp origin = (aPayload && !aPayload->GetStartTime().IsNull())
                          ? aPayload->GetStartTime()
                          : TimeStamp::Now();
   TimeDuration delta = origin - CorePS::ProcessStartTime();
-  racyRegisteredThread->AddPendingMarker(aMarkerName, std::move(aPayload),
-                                         delta.ToMilliseconds());
+  racyRegisteredThread->AddPendingMarker(
+      aMarkerName, aCategory, std::move(aPayload), delta.ToMilliseconds());
 }
 
 void profiler_add_marker(const char* aMarkerName,
+                         js::ProfilingStackFrame::Category aCategory,
                          UniquePtr<ProfilerMarkerPayload> aPayload) {
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
   // This function is hot enough that we use RacyFeatures, not ActivePS.
   if (!RacyFeatures::IsActiveWithoutPrivacy()) {
     return;
   }
 
-  racy_profiler_add_marker(aMarkerName, std::move(aPayload));
+  racy_profiler_add_marker(aMarkerName, aCategory, std::move(aPayload));
+}
+
+void profiler_add_marker(const char* aMarkerName,
+                         js::ProfilingStackFrame::Category aCategory) {
+  profiler_add_marker(aMarkerName, aCategory, nullptr);
 }
 
-void profiler_add_marker(const char* aMarkerName) {
-  profiler_add_marker(aMarkerName, nullptr);
+// This is a simplified version of profiler_add_marker that can be easily passed
+// into the JS engine.
+void profiler_add_js_marker(const char* aMarkerName) {
+  profiler_add_marker(aMarkerName, js::ProfilingStackFrame::Category::JS,
+                      nullptr);
 }
 
 void profiler_add_network_marker(
     nsIURI* aURI, int32_t aPriority, uint64_t aChannelId, NetworkLoadType aType,
     mozilla::TimeStamp aStart, mozilla::TimeStamp aEnd, int64_t aCount,
     mozilla::net::CacheDisposition aCacheDisposition,
     const mozilla::net::TimingStruct* aTimings, nsIURI* aRedirectURI) {
   if (!profiler_is_active()) {
@@ -3685,41 +3695,44 @@ void profiler_add_network_marker(
   if (aRedirectURI) {
     aRedirectURI->GetAsciiSpec(redirect_spec);
   }
   // top 32 bits are process id of the load
   uint32_t id = static_cast<uint32_t>(aChannelId & 0xFFFFFFFF);
   char name[2048];
   SprintfLiteral(name, "Load %d: %s", id, PromiseFlatCString(spec).get());
   profiler_add_marker(
-      name,
+      name, js::ProfilingStackFrame::Category::NETWORK,
       MakeUnique<NetworkMarkerPayload>(
           static_cast<int64_t>(aChannelId), PromiseFlatCString(spec).get(),
           aType, aStart, aEnd, aPriority, aCount, aCacheDisposition, aTimings,
           PromiseFlatCString(redirect_spec).get()));
 }
 
 // This logic needs to add a marker for a different thread, so we actually need
 // to lock here.
-void profiler_add_marker_for_thread(int aThreadId, const char* aMarkerName,
+void profiler_add_marker_for_thread(int aThreadId,
+                                    js::ProfilingStackFrame::Category aCategory,
+                                    const char* aMarkerName,
                                     UniquePtr<ProfilerMarkerPayload> aPayload) {
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
   PSAutoLock lock(gPSMutex);
   if (!ActivePS::Exists(lock)) {
     return;
   }
 
   // Create the ProfilerMarker which we're going to store.
   TimeStamp origin = (aPayload && !aPayload->GetStartTime().IsNull())
                          ? aPayload->GetStartTime()
                          : TimeStamp::Now();
   TimeDuration delta = origin - CorePS::ProcessStartTime();
-  ProfilerMarker* marker = new ProfilerMarker(
-      aMarkerName, aThreadId, std::move(aPayload), delta.ToMilliseconds());
+  ProfilerMarker* marker =
+      new ProfilerMarker(aMarkerName, aCategory, aThreadId, std::move(aPayload),
+                         delta.ToMilliseconds());
 
 #ifdef DEBUG
   // Assert that our thread ID makes sense
   bool realThread = false;
   const nsTArray<UniquePtr<RegisteredThread>>& registeredThreads =
       CorePS::RegisteredThreads(lock);
   for (auto& thread : registeredThreads) {
     RefPtr<ThreadInfo> info = thread->Info();
@@ -3732,49 +3745,52 @@ void profiler_add_marker_for_thread(int 
 #endif
 
   // Insert the marker into the buffer
   ProfileBuffer& buffer = ActivePS::Buffer(lock);
   buffer.AddStoredMarker(marker);
   buffer.AddEntry(ProfileBufferEntry::Marker(marker));
 }
 
-void profiler_tracing(const char* aCategory, const char* aMarkerName,
+void profiler_tracing(const char* aCategoryString, const char* aMarkerName,
+                      js::ProfilingStackFrame::Category aCategory,
                       TracingKind aKind, const Maybe<nsID>& aDocShellId,
                       const Maybe<uint32_t>& aDocShellHistoryId) {
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
   VTUNE_TRACING(aMarkerName, aKind);
 
   // This function is hot enough that we use RacyFeatures, notActivePS.
   if (!RacyFeatures::IsActiveWithoutPrivacy()) {
     return;
   }
 
-  auto payload = MakeUnique<TracingMarkerPayload>(aCategory, aKind, aDocShellId,
-                                                  aDocShellHistoryId);
-  racy_profiler_add_marker(aMarkerName, std::move(payload));
+  auto payload = MakeUnique<TracingMarkerPayload>(
+      aCategoryString, aKind, aDocShellId, aDocShellHistoryId);
+  racy_profiler_add_marker(aMarkerName, aCategory, std::move(payload));
 }
 
-void profiler_tracing(const char* aCategory, const char* aMarkerName,
+void profiler_tracing(const char* aCategoryString, const char* aMarkerName,
+                      js::ProfilingStackFrame::Category aCategory,
                       TracingKind aKind, UniqueProfilerBacktrace aCause,
                       const Maybe<nsID>& aDocShellId,
                       const Maybe<uint32_t>& aDocShellHistoryId) {
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
   VTUNE_TRACING(aMarkerName, aKind);
 
   // This function is hot enough that we use RacyFeatures, notActivePS.
   if (!RacyFeatures::IsActiveWithoutPrivacy()) {
     return;
   }
 
-  auto payload = MakeUnique<TracingMarkerPayload>(
-      aCategory, aKind, aDocShellId, aDocShellHistoryId, std::move(aCause));
-  racy_profiler_add_marker(aMarkerName, std::move(payload));
+  auto payload =
+      MakeUnique<TracingMarkerPayload>(aCategoryString, aKind, aDocShellId,
+                                       aDocShellHistoryId, std::move(aCause));
+  racy_profiler_add_marker(aMarkerName, aCategory, std::move(payload));
 }
 
 void profiler_set_js_context(JSContext* aCx) {
   MOZ_ASSERT(aCx);
 
   PSAutoLock lock(gPSMutex);
 
   RegisteredThread* registeredThread =
--- a/tools/profiler/gecko/ProfilerIOInterposeObserver.cpp
+++ b/tools/profiler/gecko/ProfilerIOInterposeObserver.cpp
@@ -14,12 +14,13 @@ void ProfilerIOInterposeObserver::Observ
   }
 
   UniqueProfilerBacktrace stack = profiler_get_backtrace();
 
   nsString filename;
   aObservation.Filename(filename);
   profiler_add_marker(
       aObservation.ObservedOperationString(),
+      js::ProfilingStackFrame::Category::OTHER,
       MakeUnique<IOMarkerPayload>(
           aObservation.Reference(), NS_ConvertUTF16toUTF8(filename).get(),
           aObservation.Start(), aObservation.End(), std::move(stack)));
 }
--- a/tools/profiler/gecko/nsProfiler.cpp
+++ b/tools/profiler/gecko/nsProfiler.cpp
@@ -149,17 +149,17 @@ nsProfiler::PauseSampling() {
 NS_IMETHODIMP
 nsProfiler::ResumeSampling() {
   profiler_resume();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::AddMarker(const char* aMarker) {
-  profiler_add_marker(aMarker);
+  profiler_add_marker(aMarker, js::ProfilingStackFrame::Category::OTHER);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::GetProfile(double aSinceTime, char** aProfile) {
   mozilla::UniquePtr<char[]> profile = profiler_get_profile(aSinceTime);
   *aProfile = profile.release();
   return NS_OK;
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -44,25 +44,27 @@
 #define AUTO_PROFILER_LABEL(label, category)
 #define AUTO_PROFILER_LABEL_DYNAMIC_CSTR(label, category, cStr)
 #define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(label, category, nsCStr)
 #define AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(label, category, nsStr)
 #define AUTO_PROFILER_LABEL_FAST(label, category, ctx)
 #define AUTO_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString, category, ctx, \
                                          flags)
 
-#define PROFILER_ADD_MARKER(markerName)
+#define PROFILER_ADD_MARKER(markerName, category)
 #define PROFILER_ADD_NETWORK_MARKER(uri, pri, channel, type, start, end, \
                                     count, cache, timings, redirect)
 
 #define DECLARE_DOCSHELL_AND_HISTORY_ID(docShell)
-#define PROFILER_TRACING(category, markerName, kind)
-#define PROFILER_TRACING_DOCSHELL(category, markerName, kind, docshell)
-#define AUTO_PROFILER_TRACING(category, markerName)
-#define AUTO_PROFILER_TRACING_DOCSHELL(category, markerName, docShell)
+#define PROFILER_TRACING(categoryString, markerName, category, kind)
+#define PROFILER_TRACING_DOCSHELL(categoryString, markerName, category, kind, \
+                                  docshell)
+#define AUTO_PROFILER_TRACING(categoryString, markerName, category)
+#define AUTO_PROFILER_TRACING_DOCSHELL(categoryString, markerName, category, \
+                                       docShell)
 
 #else  // !MOZ_GECKO_PROFILER
 
 #include <functional>
 #include <signal.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -602,24 +604,29 @@ mozilla::Maybe<ProfilerBufferInfo> profi
 
 // Insert a marker in the profile timeline. This is useful to delimit something
 // important happening such as the first paint. Unlike labels, which are only
 // recorded in the profile buffer if a sample is collected while the label is
 // on the label stack, markers will always be recorded in the profile buffer.
 // aMarkerName is copied, so the caller does not need to ensure it lives for a
 // certain length of time. A no-op if the profiler is inactive or in privacy
 // mode.
-#define PROFILER_ADD_MARKER(markerName) profiler_add_marker(markerName)
-void profiler_add_marker(const char* aMarkerName);
+#define PROFILER_ADD_MARKER(markerName, category) \
+  profiler_add_marker(markerName, js::ProfilingStackFrame::Category::category)
 void profiler_add_marker(const char* aMarkerName,
+                         js::ProfilingStackFrame::Category aCategory);
+void profiler_add_marker(const char* aMarkerName,
+                         js::ProfilingStackFrame::Category aCategory,
                          mozilla::UniquePtr<ProfilerMarkerPayload> aPayload);
+void profiler_add_js_marker(const char* aMarkerName);
 
 // Insert a marker in the profile timeline for a specified thread.
 void profiler_add_marker_for_thread(
-    int aThreadId, const char* aMarkerName,
+    int aThreadId, js::ProfilingStackFrame::Category aCategory,
+    const char* aMarkerName,
     mozilla::UniquePtr<ProfilerMarkerPayload> aPayload);
 
 enum class NetworkLoadType { LOAD_START, LOAD_STOP, LOAD_REDIRECT };
 
 #define PROFILER_ADD_NETWORK_MARKER(uri, pri, channel, type, start, end,  \
                                     count, cache, timings, redirect)      \
   profiler_add_network_marker(uri, pri, channel, type, start, end, count, \
                               cache, timings, redirect)
@@ -652,39 +659,48 @@ enum TracingKind {
     }                                             \
   } else {                                        \
     docShellId = Nothing();                       \
     docShellHistoryId = Nothing();                \
   }
 
 // Adds a tracing marker to the profile. A no-op if the profiler is inactive or
 // in privacy mode.
-#define PROFILER_TRACING(category, markerName, kind) \
-  profiler_tracing(category, markerName, kind)
-#define PROFILER_TRACING_DOCSHELL(category, markerName, kind, docShell) \
-  DECLARE_DOCSHELL_AND_HISTORY_ID(docShell);                            \
-  profiler_tracing(category, markerName, kind, docShellId, docShellHistoryId)
+#define PROFILER_TRACING(categoryString, markerName, category, kind) \
+  profiler_tracing(categoryString, markerName,                       \
+                   js::ProfilingStackFrame::Category::category, kind)
+#define PROFILER_TRACING_DOCSHELL(categoryString, markerName, category, kind, \
+                                  docShell)                                   \
+  DECLARE_DOCSHELL_AND_HISTORY_ID(docShell);                                  \
+  profiler_tracing(categoryString, markerName,                                \
+                   js::ProfilingStackFrame::Category::category, kind,         \
+                   docShellId, docShellHistoryId)
 void profiler_tracing(
-    const char* aCategory, const char* aMarkerName, TracingKind aKind,
+    const char* aCategoryString, const char* aMarkerName,
+    js::ProfilingStackFrame::Category aCategory, TracingKind aKind,
     const mozilla::Maybe<nsID>& aDocShellId = mozilla::Nothing(),
     const mozilla::Maybe<uint32_t>& aDocShellHistoryId = mozilla::Nothing());
 void profiler_tracing(
-    const char* aCategory, const char* aMarkerName, TracingKind aKind,
+    const char* aCategoryString, const char* aMarkerName,
+    js::ProfilingStackFrame::Category aCategory, TracingKind aKind,
     UniqueProfilerBacktrace aCause,
     const mozilla::Maybe<nsID>& aDocShellId = mozilla::Nothing(),
     const mozilla::Maybe<uint32_t>& aDocShellHistoryId = mozilla::Nothing());
 
 // Adds a START/END pair of tracing markers.
-#define AUTO_PROFILER_TRACING(category, markerName) \
-  mozilla::AutoProfilerTracing PROFILER_RAII(       \
-      category, markerName, mozilla::Nothing(), mozilla::Nothing())
-#define AUTO_PROFILER_TRACING_DOCSHELL(category, markerName, docShell)         \
+#define AUTO_PROFILER_TRACING(categoryString, markerName, category)            \
+  mozilla::AutoProfilerTracing PROFILER_RAII(                                  \
+      categoryString, markerName, js::ProfilingStackFrame::Category::category, \
+      mozilla::Nothing(), mozilla::Nothing())
+#define AUTO_PROFILER_TRACING_DOCSHELL(categoryString, markerName, category,   \
+                                       docShell)                               \
   DECLARE_DOCSHELL_AND_HISTORY_ID(docShell);                                   \
-  mozilla::AutoProfilerTracing PROFILER_RAII(category, markerName, docShellId, \
-                                             docShellHistoryId)
+  mozilla::AutoProfilerTracing PROFILER_RAII(                                  \
+      categoryString, markerName, js::ProfilingStackFrame::Category::category, \
+      docShellId, docShellHistoryId)
 
 //---------------------------------------------------------------------------
 // Output profiles
 //---------------------------------------------------------------------------
 
 // Get the profile encoded as a JSON string. A no-op (returning nullptr) if the
 // profiler is inactive.
 // If aIsShuttingDown is true, the current time is included as the process
@@ -840,52 +856,58 @@ class MOZ_RAII AutoProfilerLabel {
 
  public:
   // See the comment on the definition in platform.cpp for details about this.
   static MOZ_THREAD_LOCAL(ProfilingStack*) sProfilingStack;
 };
 
 class MOZ_RAII AutoProfilerTracing {
  public:
-  AutoProfilerTracing(const char* aCategory, const char* aMarkerName,
+  AutoProfilerTracing(const char* aCategoryString, const char* aMarkerName,
+                      js::ProfilingStackFrame::Category aCategory,
                       const mozilla::Maybe<nsID>& aDocShellId,
                       const mozilla::Maybe<uint32_t>& aDocShellHistoryId
                           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : mCategory(aCategory),
+      : mCategoryString(aCategoryString),
         mMarkerName(aMarkerName),
+        mCategory(aCategory),
         mDocShellId(aDocShellId),
         mDocShellHistoryId(aDocShellHistoryId) {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    profiler_tracing(mCategory, mMarkerName, TRACING_INTERVAL_START,
-                     mDocShellId, mDocShellHistoryId);
+    profiler_tracing(mCategoryString, mMarkerName, aCategory,
+                     TRACING_INTERVAL_START, mDocShellId, mDocShellHistoryId);
   }
 
-  AutoProfilerTracing(const char* aCategory, const char* aMarkerName,
+  AutoProfilerTracing(const char* aCategoryString, const char* aMarkerName,
+                      js::ProfilingStackFrame::Category aCategory,
                       UniqueProfilerBacktrace aBacktrace,
                       const mozilla::Maybe<nsID>& aDocShellId,
                       const mozilla::Maybe<uint32_t>& aDocShellHistoryId
                           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : mCategory(aCategory),
+      : mCategoryString(aCategoryString),
         mMarkerName(aMarkerName),
+        mCategory(aCategory),
         mDocShellId(aDocShellId),
         mDocShellHistoryId(aDocShellHistoryId) {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    profiler_tracing(mCategory, mMarkerName, TRACING_INTERVAL_START,
-                     std::move(aBacktrace), mDocShellId, mDocShellHistoryId);
+    profiler_tracing(mCategoryString, mMarkerName, aCategory,
+                     TRACING_INTERVAL_START, std::move(aBacktrace), mDocShellId,
+                     mDocShellHistoryId);
   }
 
   ~AutoProfilerTracing() {
-    profiler_tracing(mCategory, mMarkerName, TRACING_INTERVAL_END, mDocShellId,
-                     mDocShellHistoryId);
+    profiler_tracing(mCategoryString, mMarkerName, mCategory,
+                     TRACING_INTERVAL_END, mDocShellId, mDocShellHistoryId);
   }
 
  protected:
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-  const char* mCategory;
+  const char* mCategoryString;
   const char* mMarkerName;
+  const js::ProfilingStackFrame::Category mCategory;
   const mozilla::Maybe<nsID> mDocShellId;
   const mozilla::Maybe<uint32_t> mDocShellHistoryId;
 };
 
 // Get the MOZ_PROFILER_STARTUP* environment variables that should be
 // supplied to a child process that is about to be launched, in order
 // to make that child process start with the same profiler settings as
 // in the current process.  The given function is invoked once for
--- a/tools/profiler/tests/gtest/GeckoProfiler.cpp
+++ b/tools/profiler/tests/gtest/GeckoProfiler.cpp
@@ -452,35 +452,39 @@ int GTestMarkerPayload::sNumDestroyed = 
 
 TEST(GeckoProfiler, Markers) {
   uint32_t features = ProfilerFeature::StackWalk;
   const char* filters[] = {"GeckoMain"};
 
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL, features,
                  filters, MOZ_ARRAY_LENGTH(filters));
 
-  profiler_tracing("A", "B", TRACING_EVENT);
-  PROFILER_TRACING("A", "C", TRACING_INTERVAL_START);
-  PROFILER_TRACING("A", "C", TRACING_INTERVAL_END);
+  profiler_tracing("A", "B", js::ProfilingStackFrame::Category::OTHER,
+                   TRACING_EVENT);
+  PROFILER_TRACING("A", "C", OTHER, TRACING_INTERVAL_START);
+  PROFILER_TRACING("A", "C", OTHER, TRACING_INTERVAL_END);
 
   UniqueProfilerBacktrace bt = profiler_get_backtrace();
-  profiler_tracing("B", "A", TRACING_EVENT, std::move(bt));
+  profiler_tracing("B", "A", js::ProfilingStackFrame::Category::OTHER,
+                   TRACING_EVENT, std::move(bt));
 
-  { AUTO_PROFILER_TRACING("C", "A"); }
+  { AUTO_PROFILER_TRACING("C", "A", OTHER); }
 
-  profiler_add_marker("M1");
-  profiler_add_marker("M2",
+  profiler_add_marker("M1", js::ProfilingStackFrame::Category::OTHER);
+  profiler_add_marker("M2", js::ProfilingStackFrame::Category::OTHER,
                       MakeUnique<TracingMarkerPayload>("C", TRACING_EVENT));
-  PROFILER_ADD_MARKER("M3");
-  profiler_add_marker("M4", MakeUnique<TracingMarkerPayload>(
-                                "C", TRACING_EVENT, mozilla::Nothing(),
-                                mozilla::Nothing(), profiler_get_backtrace()));
+  PROFILER_ADD_MARKER("M3", OTHER);
+  profiler_add_marker("M4", js::ProfilingStackFrame::Category::OTHER,
+                      MakeUnique<TracingMarkerPayload>(
+                          "C", TRACING_EVENT, mozilla::Nothing(),
+                          mozilla::Nothing(), profiler_get_backtrace()));
 
   for (int i = 0; i < 10; i++) {
-    profiler_add_marker("M5", MakeUnique<GTestMarkerPayload>(i));
+    profiler_add_marker("M5", js::ProfilingStackFrame::Category::OTHER,
+                        MakeUnique<GTestMarkerPayload>(i));
   }
 
   // Create two strings: one that is the maximum allowed length, and one that
   // is one char longer.
   static const size_t kMax = ProfileBuffer::kMaxFrameKeyLength;
   UniquePtr<char[]> okstr1 = MakeUnique<char[]>(kMax);
   UniquePtr<char[]> okstr2 = MakeUnique<char[]>(kMax);
   UniquePtr<char[]> longstr = MakeUnique<char[]>(kMax + 1);
@@ -528,17 +532,18 @@ TEST(GeckoProfiler, Markers) {
   ASSERT_TRUE(strstr(profile.get(), "(too long)"));
 
   profiler_stop();
 
   // The GTestMarkerPayloads should have been destroyed.
   ASSERT_TRUE(GTestMarkerPayload::sNumDestroyed == 10);
 
   for (int i = 0; i < 10; i++) {
-    profiler_add_marker("M5", MakeUnique<GTestMarkerPayload>(i));
+    profiler_add_marker("M5", js::ProfilingStackFrame::Category::OTHER,
+                        MakeUnique<GTestMarkerPayload>(i));
   }
 
   // Warning: this could be racy
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL, features,
                  filters, MOZ_ARRAY_LENGTH(filters));
 
   ASSERT_TRUE(profiler_stream_json_for_this_process(w));
 
@@ -557,19 +562,21 @@ TEST(GeckoProfiler, DurationLimit) {
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL, features,
                  filters, MOZ_ARRAY_LENGTH(filters), Some(1.5));
 
   // Clear up the counters after the last test.
   GTestMarkerPayload::sNumCreated = 0;
   GTestMarkerPayload::sNumStreamed = 0;
   GTestMarkerPayload::sNumDestroyed = 0;
 
-  profiler_add_marker("M1", MakeUnique<GTestMarkerPayload>(1));
+  profiler_add_marker("M1", js::ProfilingStackFrame::Category::OTHER,
+                      MakeUnique<GTestMarkerPayload>(1));
   PR_Sleep(PR_MillisecondsToInterval(1100));
-  profiler_add_marker("M2", MakeUnique<GTestMarkerPayload>(2));
+  profiler_add_marker("M2", js::ProfilingStackFrame::Category::OTHER,
+                      MakeUnique<GTestMarkerPayload>(2));
   PR_Sleep(PR_MillisecondsToInterval(500));
 
   SpliceableChunkedJSONWriter w;
   ASSERT_TRUE(profiler_stream_json_for_this_process(w));
 
   // The first marker should be destroyed.
   ASSERT_TRUE(GTestMarkerPayload::sNumCreated == 2);
   ASSERT_TRUE(GTestMarkerPayload::sNumStreamed == 1);
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -793,24 +793,26 @@ void CycleCollectedJSRuntime::TraverseNa
     const JS::GCDescription& aDesc) {
   CycleCollectedJSRuntime* self = CycleCollectedJSRuntime::Get();
   MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext);
 
 #ifdef MOZ_GECKO_PROFILER
   if (profiler_thread_is_being_profiled()) {
     if (aProgress == JS::GC_CYCLE_END) {
       profiler_add_marker(
-          "GCMajor", MakeUnique<GCMajorMarkerPayload>(
-                         aDesc.startTime(aContext), aDesc.endTime(aContext),
-                         aDesc.formatJSONProfiler(aContext)));
+          "GCMajor", js::ProfilingStackFrame::Category::GCCC,
+          MakeUnique<GCMajorMarkerPayload>(aDesc.startTime(aContext),
+                                           aDesc.endTime(aContext),
+                                           aDesc.formatJSONProfiler(aContext)));
     } else if (aProgress == JS::GC_SLICE_END) {
-      profiler_add_marker("GCSlice", MakeUnique<GCSliceMarkerPayload>(
-                                         aDesc.lastSliceStart(aContext),
-                                         aDesc.lastSliceEnd(aContext),
-                                         aDesc.sliceToJSONProfiler(aContext)));
+      profiler_add_marker(
+          "GCSlice", js::ProfilingStackFrame::Category::GCCC,
+          MakeUnique<GCSliceMarkerPayload>(
+              aDesc.lastSliceStart(aContext), aDesc.lastSliceEnd(aContext),
+              aDesc.sliceToJSONProfiler(aContext)));
     }
   }
 #endif
 
   if (aProgress == JS::GC_CYCLE_END &&
       JS::dbg::FireOnGarbageCollectionHookRequired(aContext)) {
     JS::gcreason::Reason reason = aDesc.reason_;
     Unused << NS_WARN_IF(
@@ -878,20 +880,20 @@ class MinorGCMarker : public TimelineMar
   }
 
   if (aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START) {
     self->mLatestNurseryCollectionStart = TimeStamp::Now();
   }
 #ifdef MOZ_GECKO_PROFILER
   else if (aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END &&
            profiler_thread_is_being_profiled()) {
-    profiler_add_marker(
-        "GCMinor", MakeUnique<GCMinorMarkerPayload>(
-                       self->mLatestNurseryCollectionStart, TimeStamp::Now(),
-                       JS::MinorGcToJSON(aContext)));
+    profiler_add_marker("GCMinor", js::ProfilingStackFrame::Category::GCCC,
+                        MakeUnique<GCMinorMarkerPayload>(
+                            self->mLatestNurseryCollectionStart,
+                            TimeStamp::Now(), JS::MinorGcToJSON(aContext)));
   }
 #endif
 
   if (self->mPrevGCNurseryCollectionCallback) {
     self->mPrevGCNurseryCollectionCallback(aContext, aProgress, aReason);
   }
 }
 
--- a/xpcom/base/Logging.cpp
+++ b/xpcom/base/Logging.cpp
@@ -407,17 +407,17 @@ class LogModuleManager {
       allocatedBuff = mozilla::Vsmprintf(aFmt, aArgs);
       buffToWrite = allocatedBuff.get();
       charsWritten = strlen(buffToWrite);
     }
 
 #ifdef MOZ_GECKO_PROFILER
     if (mAddProfilerMarker && profiler_is_active()) {
       profiler_add_marker(
-          "LogMessages",
+          "LogMessages", js::ProfilingStackFrame::Category::OTHER,
           MakeUnique<LogMarkerPayload>(aName, buffToWrite, TimeStamp::Now()));
     }
 #endif
 
     // Determine if a newline needs to be appended to the message.
     const char* newline = "";
     if (charsWritten == 0 || buffToWrite[charsWritten - 1] != '\n') {
       newline = "\n";
--- a/xpcom/build/XPCOMInit.cpp
+++ b/xpcom/build/XPCOMInit.cpp
@@ -915,17 +915,17 @@ nsresult ShutdownXPCOM(nsIServiceManager
   bool shutdownCollect;
 #ifdef NS_FREE_PERMANENT_DATA
   shutdownCollect = true;
 #else
   shutdownCollect = !!PR_GetEnv("MOZ_CC_RUN_DURING_SHUTDOWN");
 #endif
   nsCycleCollector_shutdown(shutdownCollect);
 
-  PROFILER_ADD_MARKER("Shutdown xpcom");
+  PROFILER_ADD_MARKER("Shutdown xpcom", OTHER);
   // If we are doing any shutdown checks, poison writes.
   if (gShutdownChecks != SCM_NOTHING) {
 #ifdef XP_MACOSX
     mozilla::OnlyReportDirtyWrites();
 #endif /* XP_MACOSX */
     mozilla::BeginLateWriteChecks();
   }
 
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -1176,16 +1176,17 @@ nsThread::ProcessNextEvent(bool aMayWait
           if (priority != EventPriority::Idle) {
             mLastLongNonIdleTaskEnd = now;
           }
           mLastLongTaskEnd = now;
 #ifdef MOZ_GECKO_PROFILER
           if (profiler_thread_is_being_profiled()) {
             profiler_add_marker(
                 (priority != EventPriority::Idle) ? "LongTask" : "LongIdleTask",
+                js::ProfilingStackFrame::Category::OTHER,
                 MakeUnique<LongTaskMarkerPayload>(mCurrentEventStart, now));
           }
 #endif
         }
       }
 
       // End of execution, we can send the duration for the group
       if (schedulerLoggingEnabled) {