Backed out 24 changesets (bug 1666566, bug 1675409) for causing hazard failures in profiler/core/platform.cpp
authorMihai Alexandru Michis <malexandru@mozilla.com>
Tue, 17 Nov 2020 19:31:28 +0200
changeset 557576 ba3967802e66c1c3f90170d8f417b0182c514e1d
parent 557575 16f1f6c388364b38118d1dc2b8be6bd3c025a154
child 557577 0463e97f6980f55f1016009263052e86c5513802
push id37959
push userbtara@mozilla.com
push dateTue, 17 Nov 2020 21:55:29 +0000
treeherdermozilla-central@9dd0b13d77b9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1666566, 1675409
milestone85.0a1
backs out4d8af8533fd4fb98d225387bdc21fd3237e3ed6c
f031a3a8a20f84f36177562b72b7142ce5458f06
2b7e1a0319219e1d433cb26240e3375f6ae85a0b
bda5a24b2d0aecf544fd851601a90050eef8bea9
4282e2284314bf1330e9fa0c12188a976444ce84
0637f1b26e9f1013f490c0d0f08e60bf292f4caa
67ae04c8f6075165df34e893992eec907d984379
6c7b3f3618ef8f0d127c0d51828ded907bb22ece
2f325c22d169f09c83422fad043cabace2785c3e
1e48ff70ad8f51f9b03d9b924a4b196bf8714ce2
1dfc32d6871dcad3e5620db07b2e253dea34d9a2
4f1f218a777b8f866e20a92a76e8083628aa544a
e6ac8722b38e7ea237144fe4bf78fef44648823f
cf132e15fb57039464318ae2d34a6adb1cd7b5e5
a126e6b00ba96a03f8ba026ecb2b333a3f5eae22
fbc7fbb04f339d374c0f47daef2957154e3bf343
554c69681474046080077bacc7a4045eee31244e
44d0521c701f70dee0cdb69bc4f9c5b02f46a2ac
04653dfe47202a42590ba38c17d5021a875e4640
41ca2c043a009511fcc41a68740437283861f7e8
264ae4c805d411e584cf875671d8808b82f19d38
5f3bbdac0d5220524c382fc7d040a448571b28e9
11311c11a6e8cbec2c8363cf32d847de5cf28c65
0355fbc44baf93758d04edc32b322b41e30976c1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 24 changesets (bug 1666566, bug 1675409) for causing hazard failures in profiler/core/platform.cpp CLOSED TREE Backed out changeset 4d8af8533fd4 (bug 1666566) Backed out changeset f031a3a8a20f (bug 1675409) Backed out changeset 2b7e1a031921 (bug 1675409) Backed out changeset bda5a24b2d0a (bug 1675409) Backed out changeset 4282e2284314 (bug 1675409) Backed out changeset 0637f1b26e9f (bug 1675409) Backed out changeset 67ae04c8f607 (bug 1675409) Backed out changeset 6c7b3f3618ef (bug 1675409) Backed out changeset 2f325c22d169 (bug 1675409) Backed out changeset 1e48ff70ad8f (bug 1675409) Backed out changeset 1dfc32d6871d (bug 1675409) Backed out changeset 4f1f218a777b (bug 1675409) Backed out changeset e6ac8722b38e (bug 1675409) Backed out changeset cf132e15fb57 (bug 1675409) Backed out changeset a126e6b00ba9 (bug 1675409) Backed out changeset fbc7fbb04f33 (bug 1675409) Backed out changeset 554c69681474 (bug 1675409) Backed out changeset 44d0521c701f (bug 1675409) Backed out changeset 04653dfe4720 (bug 1675409) Backed out changeset 41ca2c043a00 (bug 1675409) Backed out changeset 264ae4c805d4 (bug 1675409) Backed out changeset 5f3bbdac0d52 (bug 1675409) Backed out changeset 11311c11a6e8 (bug 1675409) Backed out changeset 0355fbc44baf (bug 1675409)
docshell/base/nsDocShell.cpp
dom/base/ChromeUtils.cpp
dom/base/TimeoutManager.cpp
dom/base/nsDOMNavigationTiming.cpp
dom/events/EventDispatcher.cpp
dom/ipc/ContentParent.cpp
dom/media/AsyncLogger.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaFormatReader.cpp
dom/media/mediasink/AudioSink.cpp
dom/media/mediasink/VideoSink.cpp
dom/media/platforms/wmf/WMFDecoderModule.cpp
dom/media/platforms/wmf/WMFMediaDataDecoder.cpp
dom/media/platforms/wrappers/MediaChangeMonitor.cpp
dom/performance/Performance.cpp
dom/script/ScriptLoader.cpp
dom/xhr/XMLHttpRequestMainThread.cpp
dom/xhr/XMLHttpRequestMainThread.h
dom/xhr/XMLHttpRequestWorker.cpp
gfx/layers/ProfilerScreenshots.cpp
gfx/layers/composite/ContainerLayerComposite.cpp
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/ContentCompositorBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/webrender_bindings/RenderCompositorNative.cpp
ipc/chromium/src/chrome/common/ipc_channel_utils.cc
ipc/glue/MessageChannel.cpp
ipc/glue/MessageChannel.h
ipc/mscom/ProfilerMarkers.cpp
js/xpconnect/src/XPCJSContext.cpp
layout/base/AutoProfilerStyleMarker.h
layout/base/PresShell.cpp
layout/base/PresShell.h
layout/base/PresShellInlines.h
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsRefreshDriver.cpp
modules/libpref/Preferences.cpp
mozglue/baseprofiler/core/ProfileBufferEntry.cpp
mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h
mozglue/baseprofiler/public/BaseProfilerMarkers.h
mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h
mozglue/baseprofiler/public/ProfileBufferEntryKinds.h
mozglue/baseprofiler/public/ProfileBufferEntrySerialization.h
mozglue/tests/TestBaseProfiler.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsIHttpChannel.idl
toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
tools/profiler/core/ProfileBufferEntry.cpp
tools/profiler/core/ProfilerMarkerPayload.cpp
tools/profiler/core/RegisteredThread.cpp
tools/profiler/core/memory_hooks.cpp
tools/profiler/core/memory_hooks.h
tools/profiler/core/platform.cpp
tools/profiler/moz.build
tools/profiler/public/GeckoProfiler.h
tools/profiler/public/ProfilerMarkerPayload.h
tools/profiler/public/ProfilerMarkerTypes.h
tools/profiler/public/ProfilerMarkers.h
tools/profiler/tests/gtest/GeckoProfiler.cpp
xpcom/base/CycleCollectedJSRuntime.cpp
xpcom/base/Logging.cpp
xpcom/threads/nsThread.cpp
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -239,16 +239,20 @@
 #  include "mozIPlacesPendingOperation.h"
 #endif
 
 #if NS_PRINT_PREVIEW
 #  include "nsIDocumentViewerPrint.h"
 #  include "nsIWebBrowserPrint.h"
 #endif
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::net;
 
 using mozilla::ipc::Endpoint;
 
 // Threshold value in ms for META refresh based redirects
 #define REFRESH_REDIRECT_TIMER 15000
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -46,16 +46,19 @@
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "mozilla/net/SocketProcessHost.h"
 #include "IOActivityMonitor.h"
 #include "nsIOService.h"
 #include "nsThreadUtils.h"
 #include "mozJSComponentLoader.h"
 #include "GeckoProfiler.h"
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
 #include "nsIException.h"
 
 namespace mozilla::dom {
 
 /* static */
 void ChromeUtils::NondeterministicGetWeakMapKeys(
     GlobalObject& aGlobal, JS::Handle<JS::Value> aMap,
     JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) {
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -17,16 +17,19 @@
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/PopupBlocker.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/TimeoutHandler.h"
 #include "TimeoutExecutor.h"
 #include "TimeoutBudgetManager.h"
 #include "mozilla/net/WebSocketEventService.h"
 #include "mozilla/MediaManager.h"
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 LazyLogModule gTimeoutLog("Timeout");
 
 static int32_t gRunningTimeoutDepth = 0;
 
@@ -146,23 +149,20 @@ void TimeoutManager::MoveIdleToActive() 
       TimeDuration target = timeout->When() - timeout->SubmitTime();
       TimeDuration delta = now - timeout->When();
       nsPrintfCString marker(
           "Releasing deferred setTimeout() for %dms (original target time was "
           "%dms (%dms delta))",
           int(elapsed.ToMilliseconds()), int(target.ToMilliseconds()),
           int(delta.ToMilliseconds()));
       // don't have end before start...
-      PROFILER_MARKER_TEXT(
-          "setTimeout deferred release", DOM,
-          MarkerOptions(
-              MarkerTiming::Interval(
-                  delta.ToMilliseconds() >= 0 ? timeout->When() : now, now),
-              MarkerInnerWindowId(mWindow.WindowID())),
-          marker);
+      PROFILER_ADD_MARKER_WITH_PAYLOAD(
+          "setTimeout deferred release", DOM, TextMarkerPayload,
+          (marker, delta.ToMilliseconds() >= 0 ? timeout->When() : now, now,
+           Some(mWindow.WindowID())));
     }
 #endif
     num++;
   }
   if (num > 0) {
     MOZ_ALWAYS_SUCCEEDS(MaybeSchedule(when));
     mIdleExecutor->Cancel();
   }
@@ -900,23 +900,20 @@ void TimeoutManager::RunTimeout(const Ti
           nsPrintfCString marker(
               "%sset%s() for %dms (original target time was %dms (%dms "
               "delta)); runtime = %dms",
               aProcessIdle ? "Deferred " : "",
               timeout->mIsInterval ? "Interval" : "Timeout",
               int(elapsed.ToMilliseconds()), int(target.ToMilliseconds()),
               int(delta.ToMilliseconds()), int(runtime.ToMilliseconds()));
           // don't have end before start...
-          PROFILER_MARKER_TEXT(
-              "setTimeout", DOM,
-              MarkerOptions(
-                  MarkerTiming::Interval(
-                      delta.ToMilliseconds() >= 0 ? timeout->When() : now, now),
-                  MarkerInnerWindowId(mWindow.WindowID())),
-              marker);
+          PROFILER_ADD_MARKER_WITH_PAYLOAD(
+              "setTimeout", DOM, TextMarkerPayload,
+              (marker, delta.ToMilliseconds() >= 0 ? timeout->When() : now, now,
+               Some(mWindow.WindowID())));
         }
 #endif
 
         MOZ_LOG(gTimeoutLog, LogLevel::Debug,
                 ("Run%s(TimeoutManager=%p, timeout=%p) returned %d\n",
                  timeout->mIsInterval ? "Interval" : "Timeout", this,
                  timeout.get(), !!timeout_was_cleared));
 
--- a/dom/base/nsDOMNavigationTiming.cpp
+++ b/dom/base/nsDOMNavigationTiming.cpp
@@ -20,17 +20,17 @@
 #include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsHttp.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIURI.h"
 #include "nsPrintfCString.h"
 #include "prtime.h"
 #ifdef MOZ_GECKO_PROFILER
-#  include "mozilla/ProfilerMarkerTypes.h"
+#  include "ProfilerMarkerPayload.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 LazyLogModule gPageLoadLog("PageLoad");
@@ -111,40 +111,34 @@ void nsDOMNavigationTiming::NotifyBefore
 
 void nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* aOldURI) {
   mUnloadStart = mBeforeUnloadStart;
   mUnloadedURI = aOldURI;
 }
 
 void nsDOMNavigationTiming::NotifyUnloadEventStart() {
   mUnloadStart = TimeStamp::Now();
-  PROFILER_MARKER("Unload", NETWORK,
-                  MarkerOptions(MarkerTiming::IntervalStart(),
-                                MarkerInnerWindowIdFromDocShell(mDocShell)),
-                  Tracing, "Navigation");
+  PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "Unload", NETWORK,
+                                   TRACING_INTERVAL_START, mDocShell);
 }
 
 void nsDOMNavigationTiming::NotifyUnloadEventEnd() {
   mUnloadEnd = TimeStamp::Now();
-  PROFILER_MARKER("Unload", NETWORK,
-                  MarkerOptions(MarkerTiming::IntervalEnd(),
-                                MarkerInnerWindowIdFromDocShell(mDocShell)),
-                  Tracing, "Navigation");
+  PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "Unload", NETWORK,
+                                   TRACING_INTERVAL_END, mDocShell);
 }
 
 void nsDOMNavigationTiming::NotifyLoadEventStart() {
   if (!mLoadEventStart.IsNull()) {
     return;
   }
   mLoadEventStart = TimeStamp::Now();
 
-  PROFILER_MARKER("Load", NETWORK,
-                  MarkerOptions(MarkerTiming::IntervalStart(),
-                                MarkerInnerWindowIdFromDocShell(mDocShell)),
-                  Tracing, "Navigation");
+  PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "Load", NETWORK,
+                                   TRACING_INTERVAL_START, mDocShell);
 
   if (IsTopLevelContentDocumentInContentProcess()) {
     mLoadEventStartForTelemetry = TimeStamp::Now();
 
     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_START_MS,
                                    mNavigationStart,
                                    mLoadEventStartForTelemetry);
 
@@ -164,41 +158,36 @@ void nsDOMNavigationTiming::NotifyLoadEv
 }
 
 void nsDOMNavigationTiming::NotifyLoadEventEnd() {
   if (!mLoadEventEnd.IsNull()) {
     return;
   }
   mLoadEventEnd = TimeStamp::Now();
 
-  PROFILER_MARKER("Load", NETWORK,
-                  MarkerOptions(MarkerTiming::IntervalEnd(),
-                                MarkerInnerWindowIdFromDocShell(mDocShell)),
-                  Tracing, "Navigation");
+  PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "Load", NETWORK,
+                                   TRACING_INTERVAL_END, mDocShell);
 
   if (IsTopLevelContentDocumentInContentProcess()) {
 #ifdef MOZ_GECKO_PROFILER
     if (profiler_can_accept_markers() || PAGELOAD_LOG_ENABLED()) {
       TimeDuration elapsed = mLoadEventEnd - mNavigationStart;
       TimeDuration duration = mLoadEventEnd - mLoadEventStart;
       nsAutoCString spec;
       if (mLoadedURI) {
         mLoadedURI->GetSpec(spec);
       }
       nsPrintfCString marker(
           "Document %s loaded after %dms, load event duration %dms", spec.get(),
           int(elapsed.ToMilliseconds()), int(duration.ToMilliseconds()));
       PAGELOAD_LOG(("%s", marker.get()));
-      PROFILER_MARKER_TEXT(
-          "DocumentLoad", DOM,
-          MarkerOptions(
-              MarkerTiming::Interval(mNavigationStart, mLoadEventEnd),
-              MarkerInnerWindowId(
-                  profiler_get_inner_window_id_from_docshell(mDocShell))),
-          marker);
+      PROFILER_ADD_MARKER_WITH_PAYLOAD(
+          "DocumentLoad", DOM, TextMarkerPayload,
+          (marker, mNavigationStart, mLoadEventEnd,
+           profiler_get_inner_window_id_from_docshell(mDocShell)));
     }
 #endif
     TimeStamp loadEventEnd = TimeStamp::Now();
 
     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_END_MS,
                                    mNavigationStart, loadEventEnd);
 
     MaybeSubmitTimeToLoadEventPreloadTelemetry(loadEventEnd);
@@ -247,20 +236,18 @@ void nsDOMNavigationTiming::NotifyDOMCom
 void nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI) {
   if (!mDOMContentLoadedEventStart.IsNull()) {
     return;
   }
 
   mLoadedURI = aURI;
   mDOMContentLoadedEventStart = TimeStamp::Now();
 
-  PROFILER_MARKER("DOMContentLoaded", NETWORK,
-                  MarkerOptions(MarkerTiming::IntervalStart(),
-                                MarkerInnerWindowIdFromDocShell(mDocShell)),
-                  Tracing, "Navigation");
+  PROFILER_TRACING_MARKER_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);
 
     if (mDocShellHasBeenActiveSinceNavigationStart) {
@@ -281,20 +268,18 @@ void nsDOMNavigationTiming::NotifyDOMCon
 void nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI) {
   if (!mDOMContentLoadedEventEnd.IsNull()) {
     return;
   }
 
   mLoadedURI = aURI;
   mDOMContentLoadedEventEnd = TimeStamp::Now();
 
-  PROFILER_MARKER("DOMContentLoaded", NETWORK,
-                  MarkerOptions(MarkerTiming::IntervalEnd(),
-                                MarkerInnerWindowIdFromDocShell(mDocShell)),
-                  Tracing, "Navigation");
+  PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "DOMContentLoaded", NETWORK,
+                                   TRACING_INTERVAL_END, mDocShell);
 
   if (IsTopLevelContentDocumentInContentProcess()) {
     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_CONTENT_LOADED_END_MS,
                                    mNavigationStart);
   }
 }
 
 // static
@@ -387,23 +372,20 @@ void nsDOMNavigationTiming::TTITimeout(n
     nsAutoCString spec;
     if (mLoadedURI) {
       mLoadedURI->GetSpec(spec);
     }
     nsPrintfCString marker("TTFI after %dms (LongTask was at %dms) for URL %s",
                            int(elapsed.ToMilliseconds()),
                            int(elapsedLongTask.ToMilliseconds()), spec.get());
 
-    PROFILER_MARKER_TEXT(
-        "TimeToFirstInteractive (TTFI)", DOM,
-        MarkerOptions(
-            MarkerTiming::Interval(mNavigationStart, mTTFI),
-            MarkerInnerWindowId(
-                profiler_get_inner_window_id_from_docshell(mDocShell))),
-        marker);
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(
+        "TimeToFirstInteractive (TTFI)", DOM, TextMarkerPayload,
+        (marker, mNavigationStart, mTTFI,
+         profiler_get_inner_window_id_from_docshell(mDocShell)));
   }
 #endif
 }
 
 void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mNavigationStart.IsNull());
 
@@ -423,23 +405,20 @@ void nsDOMNavigationTiming::NotifyNonBla
     nsPrintfCString marker(
         "Non-blank 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");
     PAGELOAD_LOG(("%s", marker.get()));
-    PROFILER_MARKER_TEXT(
-        "FirstNonBlankPaint", DOM,
-        MarkerOptions(
-            MarkerTiming::Interval(mNavigationStart, mNonBlankPaint),
-            MarkerInnerWindowId(
-                profiler_get_inner_window_id_from_docshell(mDocShell))),
-        marker);
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(
+        "FirstNonBlankPaint", DOM, TextMarkerPayload,
+        (marker, mNavigationStart, mNonBlankPaint,
+         profiler_get_inner_window_id_from_docshell(mDocShell)));
   }
 #endif
 
   if (mDocShellHasBeenActiveSinceNavigationStart) {
     if (net::nsHttp::IsBeforeLastActiveTabLoadOptimization(mNavigationStart)) {
       Telemetry::AccumulateTimeDelta(
           Telemetry::TIME_TO_NON_BLANK_PAINT_NETOPT_MS, mNavigationStart,
           mNonBlankPaint);
@@ -475,23 +454,20 @@ void nsDOMNavigationTiming::NotifyConten
     nsPrintfCString marker(
         "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");
     PAGELOAD_LOG(("%s", marker.get()));
-    PROFILER_MARKER_TEXT(
-        "FirstContentfulPaint", DOM,
-        MarkerOptions(
-            MarkerTiming::Interval(mNavigationStart, mContentfulPaint),
-            MarkerInnerWindowId(
-                profiler_get_inner_window_id_from_docshell(mDocShell))),
-        marker);
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(
+        "FirstContentfulPaint", DOM, TextMarkerPayload,
+        (marker, mNavigationStart, mContentfulPaint,
+         profiler_get_inner_window_id_from_docshell(mDocShell)));
   }
 #endif
 
   if (!mTTITimer) {
     mTTITimer = NS_NewTimer();
   }
 
   // TTI is first checked 5 seconds after the FCP (non-blank-paint is very close
@@ -527,23 +503,20 @@ void nsDOMNavigationTiming::NotifyDOMCon
     nsPrintfCString marker(
         "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");
     PAGELOAD_LOG(("%s", marker.get()));
-    PROFILER_MARKER_TEXT(
-        "DOMContentFlushed", DOM,
-        MarkerOptions(
-            MarkerTiming::Interval(mNavigationStart, mDOMContentFlushed),
-            MarkerInnerWindowId(
-                profiler_get_inner_window_id_from_docshell(mDocShell))),
-        marker);
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(
+        "DOMContentFlushed", DOM, TextMarkerPayload,
+        (marker, mNavigationStart, mDOMContentFlushed,
+         profiler_get_inner_window_id_from_docshell(mDocShell)));
   }
 #endif
 }
 
 void nsDOMNavigationTiming::NotifyDocShellStateChanged(
     DocShellState aDocShellState) {
   mDocShellHasBeenActiveSinceNavigationStart &=
       (aDocShellState == DocShellState::eActive);
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -1034,16 +1034,20 @@ nsresult EventDispatcher::Dispatch(nsISu
           MarkerInnerWindowId innerWindowId;
           if (nsCOMPtr<nsPIDOMWindowInner> inner =
                   do_QueryInterface(aEvent->mTarget->GetOwnerGlobal())) {
             innerWindowId = MarkerInnerWindowId{inner->WindowID()};
           }
 
           struct DOMEventMarker {
             static constexpr Span<const char> MarkerTypeName() {
+              // Note: DOMEventMarkerPayload was originally a sub-class of
+              // TracingMarkerPayload, so it uses the same payload type.
+              // TODO: Change to its own distinct type, but this will require
+              // front-end changes.
               return MakeStringSpan("DOMEvent");
             }
             static void StreamJSONMarkerData(
                 baseprofiler::SpliceableJSONWriter& aWriter,
                 const ProfilerString16View& aEventType,
                 const TimeStamp& aStartTime, const TimeStamp& aEventTimeStamp) {
               aWriter.StringProperty(
                   "eventType", NS_ConvertUTF16toUTF8(aEventType.Data(),
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -16,16 +16,19 @@
 #include "chrome/common/process_watcher.h"
 #include "mozilla/Result.h"
 #include "nsIBrowserDOMWindow.h"
 
 #ifdef ACCESSIBILITY
 #  include "mozilla/a11y/PDocAccessible.h"
 #endif
 #include "GeckoProfiler.h"
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
 #include "GMPServiceParent.h"
 #include "HandlerServiceParent.h"
 #include "IHistory.h"
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
 #  include "mozilla/a11y/AccessibleWrap.h"
 #  include "mozilla/a11y/Compatibility.h"
 #endif
 #include <utility>
@@ -910,17 +913,19 @@ already_AddRefed<ContentParent> ContentP
                                               aMaxContentParents, &index))) {
     // If the provider returned an existing ContentParent, use that one.
     if (0 <= index && static_cast<uint32_t>(index) <= aMaxContentParents) {
       RefPtr<ContentParent> retval = aContentParents[index];
 #ifdef MOZ_GECKO_PROFILER
       if (profiler_thread_is_being_profiled()) {
         nsPrintfCString marker("Reused process %u",
                                (unsigned int)retval->ChildID());
-        PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
+        TimeStamp now = TimeStamp::Now();
+        PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload,
+                                         (marker, now, now));
       }
 #endif
       MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
               ("GetUsedProcess: Reused process %p (%u) for %s", retval.get(),
                (unsigned int)retval->ChildID(),
                PromiseFlatCString(aRemoteType).get()));
       retval->AssertAlive();
       retval->StopRecycling();
@@ -949,17 +954,19 @@ already_AddRefed<ContentParent> ContentP
     MOZ_DIAGNOSTIC_ASSERT(recycled->GetRemoteType() == DEFAULT_REMOTE_TYPE);
     recycled->AssertAlive();
     recycled->StopRecycling();
 
 #ifdef MOZ_GECKO_PROFILER
     if (profiler_thread_is_being_profiled()) {
       nsPrintfCString marker("Recycled process %u (%p)",
                              (unsigned int)recycled->ChildID(), recycled.get());
-      PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
+      TimeStamp now = TimeStamp::Now();
+      PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload,
+                                       (marker, now, now));
     }
 #endif
     MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
             ("Recycled process %p", recycled.get()));
 
     return recycled.forget();
   }
 
@@ -972,17 +979,19 @@ already_AddRefed<ContentParent> ContentP
                           PREALLOC_REMOTE_TYPE);
     MOZ_DIAGNOSTIC_ASSERT(sRecycledE10SProcess != preallocated);
     preallocated->AssertAlive();
 
 #ifdef MOZ_GECKO_PROFILER
     if (profiler_thread_is_being_profiled()) {
       nsPrintfCString marker("Assigned preallocated process %u",
                              (unsigned int)preallocated->ChildID());
-      PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
+      TimeStamp now = TimeStamp::Now();
+      PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload,
+                                       (marker, now, now));
     }
 #endif
     MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
             ("Adopted preallocated process %p for type %s", preallocated.get(),
              PromiseFlatCString(aRemoteType).get()));
 
     // Specialize this process for the appropriate remote type, and activate it.
     preallocated->mActivateTS = TimeStamp::Now();
@@ -2409,20 +2418,19 @@ bool ContentParent::LaunchSubprocessReso
   mPrefSerializer = nullptr;
 
   const auto launchResumeTS = TimeStamp::Now();
 #ifdef MOZ_GECKO_PROFILER
   if (profiler_thread_is_being_profiled()) {
     nsPrintfCString marker("Process start%s for %u",
                            mIsAPreallocBlocker ? " (immediate)" : "",
                            (unsigned int)ChildID());
-    PROFILER_MARKER_TEXT(
-        mIsAPreallocBlocker ? ProfilerString8View("Process Immediate Launch")
-                            : ProfilerString8View("Process Launch"),
-        DOM, MarkerTiming::Interval(mLaunchTS, launchResumeTS), marker);
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(
+        mIsAPreallocBlocker ? "Process Immediate Launch" : "Process Launch",
+        DOM, TextMarkerPayload, (marker, mLaunchTS, launchResumeTS));
   }
 #endif
 
   if (!sCreatedFirstContentProcess) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     obs->NotifyObservers(nullptr, "ipc:first-content-process-created", nullptr);
     sCreatedFirstContentProcess = true;
   }
--- a/dom/media/AsyncLogger.h
+++ b/dom/media/AsyncLogger.h
@@ -11,16 +11,19 @@
 #include <thread>
 #include <cinttypes>
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Logging.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Sprintf.h"
 #include "GeckoProfiler.h"
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
 #include "MPSCQueue.h"
 
 #if defined(_WIN32)
 #  include <process.h>
 #  define getpid() _getpid()
 #else
 #  include <unistd.h>
 #endif
@@ -191,51 +194,34 @@ class AsyncLogger {
           TextPayload message;
           while (mMessageQueueLog.Pop(&message) && mRunning) {
             MOZ_LOG(mLogModule, mozilla::LogLevel::Verbose,
                     ("%s", message.mPayload));
           }
         }
 #ifdef MOZ_GECKO_PROFILER
         {
-          struct Budget {
-            static constexpr Span<const char> MarkerTypeName() {
-              return MakeStringSpan("Budget");
-            }
-            static void StreamJSONMarkerData(
-                baseprofiler::SpliceableJSONWriter& aWriter) {}
-            static MarkerSchema MarkerTypeDisplay() {
-              using MS = MarkerSchema;
-              MS schema{MS::Location::markerChart, MS::Location::markerTable};
-              // Nothing outside the defaults.
-              return schema;
-            }
-          };
-
           TracePayload message;
           while (mMessageQueueProfiler.Pop(&message) && mRunning) {
             if (message.mPhase != TracingPhase::COMPLETE) {
-              profiler_add_marker(
-                  ProfilerString8View::WrapNullTerminatedString(message.mName),
-                  geckoprofiler::category::MEDIA_RT,
-                  {MarkerThreadId(message.mTID),
-                   (message.mPhase == TracingPhase::BEGIN)
-                       ? MarkerTiming::IntervalStart(message.mTimestamp)
-                       : MarkerTiming::IntervalEnd(message.mTimestamp)},
-                  Budget{});
+              TracingKind kind = message.mPhase == TracingPhase::BEGIN
+                                     ? TracingKind::TRACING_INTERVAL_START
+                                     : TracingKind::TRACING_INTERVAL_END;
+              TracingMarkerPayload payload("media", kind, message.mTimestamp);
+              profiler_add_marker_for_thread(
+                  message.mTID, JS::ProfilingCategoryPair::MEDIA_RT,
+                  message.mName, payload);
             } else {
-              profiler_add_marker(
-                  ProfilerString8View::WrapNullTerminatedString(message.mName),
-                  geckoprofiler::category::MEDIA_RT,
-                  {MarkerThreadId(message.mTID),
-                   MarkerTiming::Interval(
-                       message.mTimestamp,
-                       message.mTimestamp + TimeDuration::FromMicroseconds(
-                                                message.mDurationUs))},
-                  Budget{});
+              mozilla::TimeStamp end =
+                  message.mTimestamp +
+                  TimeDuration::FromMicroseconds(message.mDurationUs);
+              BudgetMarkerPayload payload(message.mTimestamp, end);
+              profiler_add_marker_for_thread(
+                  message.mTID, JS::ProfilingCategoryPair::MEDIA_RT,
+                  message.mName, payload);
             }
           }
         }
 #endif
         Sleep();
       }
       PROFILER_UNREGISTER_THREAD();
     }));
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -22,30 +22,38 @@
 #include "mozilla/TaskQueue.h"
 #include "mozilla/Tuple.h"
 #include "nsIMemoryReporter.h"
 #include "nsPrintfCString.h"
 #include "nsTArray.h"
 #include "AudioSegment.h"
 #include "DOMMediaStream.h"
 #include "ImageContainer.h"
-#include "GeckoProfiler.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderStateMachine.h"
 #include "MediaShutdownManager.h"
 #include "MediaTrackGraph.h"
 #include "MediaTimer.h"
 #include "ReaderProxy.h"
 #include "TimeUnits.h"
 #include "VideoSegment.h"
 #include "VideoUtils.h"
 
 #ifdef MOZ_GECKO_PROFILER
-#  include "mozilla/ProfilerMarkerTypes.h"
-#endif  // MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#  define MDSM_ERROR_MARKER(tag, error, markerTime)                          \
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \
+                                     (error, markerTime))
+#  define MDSM_SAMPLE_MARKER(tag, startTime, endTime) \
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(                 \
+        tag, MEDIA_PLAYBACK, MediaSampleMarkerPayload, (startTime, endTime))
+#else
+#  define MDSM_ERROR_MARKER(tag, error, markerTime)
+#  define MDSM_SAMPLE_MARKER(tag, startTime, endTime)
+#endif
 
 namespace mozilla {
 
 using namespace mozilla::media;
 
 #define NS_DispatchToMainThread(...) \
   CompileError_UseAbstractThreadDispatchInstead
 
@@ -2846,29 +2854,27 @@ bool MediaDecoderStateMachine::HaveEnoug
   MOZ_ASSERT(OnTaskQueue());
   return VideoQueue().GetSize() >= GetAmpleVideoFrames() * mPlaybackRate + 1;
 }
 
 void MediaDecoderStateMachine::PushAudio(AudioData* aSample) {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(aSample);
   AudioQueue().Push(aSample);
-  PROFILER_MARKER("MDSM::PushAudio", MEDIA_PLAYBACK, {}, MediaSampleMarker,
-                  aSample->mTime.ToMicroseconds(),
-                  aSample->GetEndTime().ToMicroseconds());
+  MDSM_SAMPLE_MARKER("MDSM::PushAudio", aSample->mTime.ToMicroseconds(),
+                     aSample->GetEndTime().ToMicroseconds());
 }
 
 void MediaDecoderStateMachine::PushVideo(VideoData* aSample) {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(aSample);
   aSample->mFrameID = ++mCurrentFrameID;
   VideoQueue().Push(aSample);
-  PROFILER_MARKER("MDSM::PushVideo", MEDIA_PLAYBACK, {}, MediaSampleMarker,
-                  aSample->mTime.ToMicroseconds(),
-                  aSample->GetEndTime().ToMicroseconds());
+  MDSM_SAMPLE_MARKER("MDSM::PushVideo", aSample->mTime.ToMicroseconds(),
+                     aSample->GetEndTime().ToMicroseconds());
 }
 
 void MediaDecoderStateMachine::OnAudioPopped(const RefPtr<AudioData>& aSample) {
   MOZ_ASSERT(OnTaskQueue());
   mPlaybackOffset = std::max(mPlaybackOffset, aSample->mOffset);
 }
 
 void MediaDecoderStateMachine::OnVideoPopped(const RefPtr<VideoData>& aSample) {
@@ -3458,18 +3464,18 @@ bool MediaDecoderStateMachine::HasLowBuf
   }
   media::TimeInterval interval(start, end);
   return !mBuffered.Ref().Contains(interval);
 }
 
 void MediaDecoderStateMachine::DecodeError(const MediaResult& aError) {
   MOZ_ASSERT(OnTaskQueue());
   LOGE("Decode error: %s", aError.Description().get());
-  PROFILER_MARKER_TEXT("MDSM::DecodeError", MEDIA_PLAYBACK, {},
-                       aError.Description());
+  MDSM_ERROR_MARKER("MDSM::DecodeError", aError.Description(),
+                    TimeStamp::NowUnfuzzed());
   // Notify the decode error and MediaDecoder will shut down MDSM.
   mOnPlaybackErrorEvent.Notify(aError);
 }
 
 void MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent() {
   MOZ_ASSERT(OnTaskQueue());
   // Track value of mSentFirstFrameLoadedEvent from before updating it
   bool firstFrameBeenLoaded = mSentFirstFrameLoadedEvent;
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -37,16 +37,25 @@ mozilla::LazyLogModule gMediaDemuxerLog(
 
 #define LOG(arg, ...)                                                  \
   DDMOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Debug, "::%s: " arg, \
             __func__, ##__VA_ARGS__)
 #define LOGV(arg, ...)                                                   \
   DDMOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Verbose, "::%s: " arg, \
             __func__, ##__VA_ARGS__)
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#  define MEDIA_FORMAT_READER_STATUS_MARKER(tag, text, markerTime)           \
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \
+                                     (text, markerTime))
+#else
+#  define MEDIA_FORMAT_READER_STATUS_MARKER(tag, text, markerTime)
+#endif
+
 #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
 
 namespace mozilla {
 
 typedef void* MediaDataDecoderID;
 
 /**
  * This class tracks shutdown promises to ensure all decoders are shut down
@@ -1919,17 +1928,18 @@ void MediaFormatReader::HandleDemuxedSam
       } else if (decoder.HasWaitingPromise()) {
         decoder.Flush();
       }
     }
 
     nsPrintfCString markerString(
         "%s stream id changed from:%" PRIu32 " to:%" PRIu32,
         TrackTypeToStr(aTrack), decoder.mLastStreamSourceID, info->GetID());
-    PROFILER_MARKER_TEXT("StreamID Change", MEDIA_PLAYBACK, {}, markerString);
+    MEDIA_FORMAT_READER_STATUS_MARKER("StreamID Change", markerString,
+                                      TimeStamp::NowUnfuzzed());
     LOG("%s", markerString.get());
 
     if (aTrack == TrackInfo::kVideoTrack) {
       // We are about to create a new decoder thus the benchmark,
       // up to this point, is stored.
       NotifyDecoderBenchmarkStore();
     }
     decoder.mNextStreamSourceID.reset();
@@ -3137,10 +3147,11 @@ void MediaFormatReader::OnFirstDemuxFail
   MOZ_ASSERT(decoder.mFirstDemuxedSampleTime.isNothing());
   decoder.mFirstDemuxedSampleTime.emplace(TimeUnit::FromInfinity());
   MaybeResolveMetadataPromise();
 }
 
 }  // namespace mozilla
 
 #undef NS_DispatchToMainThread
+#undef MEDIA_FORMAT_READER_STATUS_MARKER
 #undef LOGV
 #undef LOG
--- a/dom/media/mediasink/AudioSink.cpp
+++ b/dom/media/mediasink/AudioSink.cpp
@@ -7,21 +7,38 @@
 #include "AudioSink.h"
 #include "AudioConverter.h"
 #include "AudioDeviceInfo.h"
 #include "MediaQueue.h"
 #include "VideoUtils.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/IntegerPrintfMacros.h"
-#include "mozilla/ProfilerMarkerTypes.h"
 #include "mozilla/StaticPrefs_media.h"
 #include "mozilla/StaticPrefs_dom.h"
 #include "nsPrintfCString.h"
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#  define PROFILER_AUDIO_MARKER(tag, sample)                              \
+    do {                                                                  \
+      uint64_t startTime = (sample)->mTime.ToMicroseconds();              \
+      uint64_t endTime = (sample)->GetEndTime().ToMicroseconds();         \
+      auto profilerTag = (tag);                                           \
+      mOwnerThread->Dispatch(NS_NewRunnableFunction(                      \
+          "AudioSink:AddMarker", [profilerTag, startTime, endTime] {      \
+            PROFILER_ADD_MARKER_WITH_PAYLOAD(profilerTag, MEDIA_PLAYBACK, \
+                                             MediaSampleMarkerPayload,    \
+                                             (startTime, endTime));       \
+          }));                                                            \
+    } while (0)
+#else
+#  define PROFILER_AUDIO_MARKER(tag, sample)
+#endif
+
 namespace mozilla {
 
 extern LazyLogModule gMediaDecoderLog;
 #define SINK_LOG(msg, ...)                   \
   MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, \
           ("AudioSink=%p " msg, this, ##__VA_ARGS__))
 #define SINK_LOG_V(msg, ...)                   \
   MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, \
@@ -249,26 +266,17 @@ UniquePtr<AudioStream::Chunk> AudioSink:
         FramesToUsecs(mCurrentData->Frames(), mOutputRate).value();
   }
 
   auto framesToPop = std::min(aFrames, mCursor->Available());
 
   SINK_LOG_V("playing audio at time=%" PRId64 " offset=%u length=%u",
              mCurrentData->mTime.ToMicroseconds(),
              mCurrentData->Frames() - mCursor->Available(), framesToPop);
-
-#ifdef MOZ_GECKO_PROFILER
-  mOwnerThread->Dispatch(NS_NewRunnableFunction(
-      "AudioSink:AddMarker",
-      [startTime = mCurrentData->mTime.ToMicroseconds(),
-       endTime = mCurrentData->GetEndTime().ToMicroseconds()] {
-        PROFILER_MARKER("PlayAudio", MEDIA_PLAYBACK, {}, MediaSampleMarker,
-                        startTime, endTime);
-      }));
-#endif  // MOZ_GECKO_PROFILER
+  PROFILER_AUDIO_MARKER("PlayAudio", mCurrentData);
 
   UniquePtr<AudioStream::Chunk> chunk =
       MakeUnique<Chunk>(mCurrentData, framesToPop, mCursor->Ptr());
 
   {
     MonitorAutoLock mon(mMonitor);
     mWritten += framesToPop;
     mCursor->Advance(framesToPop);
--- a/dom/media/mediasink/VideoSink.cpp
+++ b/dom/media/mediasink/VideoSink.cpp
@@ -11,30 +11,38 @@
 #endif
 
 #include "VideoSink.h"
 
 #include "MediaQueue.h"
 #include "VideoUtils.h"
 
 #include "mozilla/IntegerPrintfMacros.h"
-#include "mozilla/ProfilerMarkerTypes.h"
 #include "mozilla/StaticPrefs_browser.h"
 #include "mozilla/StaticPrefs_media.h"
 
 extern mozilla::LazyLogModule gMediaDecoderLog;
 
 #undef FMT
 
 #define FMT(x, ...) "VideoSink=%p " x, this, ##__VA_ARGS__
 #define VSINK_LOG(x, ...) \
   MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, (FMT(x, ##__VA_ARGS__)))
 #define VSINK_LOG_V(x, ...) \
   MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, (FMT(x, ##__VA_ARGS__)))
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#  define VSINK_ADD_PROFILER_MARKER(tag, startTime, endTime) \
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(                        \
+        tag, MEDIA_PLAYBACK, MediaSampleMarkerPayload, (startTime, endTime))
+#else
+#  define VSINK_ADD_PROFILER_MARKER(tag, startTime, endTime)
+#endif
+
 namespace mozilla {
 
 using namespace mozilla::layers;
 
 // Minimum update frequency is 1/120th of a second, i.e. half the
 // duration of a 60-fps frame.
 static const int64_t MIN_UPDATE_INTERVAL_US = 1000000 / (60 * 2);
 
@@ -450,19 +458,18 @@ void VideoSink::RenderVideoFrames(int32_
     }
     img->mFrameID = frame->mFrameID;
     img->mProducerID = mProducerID;
 
     VSINK_LOG_V("playing video frame %" PRId64 " (id=%x) (vq-queued=%zu)",
                 frame->mTime.ToMicroseconds(), frame->mFrameID,
                 VideoQueue().GetSize());
     if (!wasSent) {
-      PROFILER_MARKER("PlayVideo", MEDIA_PLAYBACK, {}, MediaSampleMarker,
-                      frame->mTime.ToMicroseconds(),
-                      frame->GetEndTime().ToMicroseconds());
+      VSINK_ADD_PROFILER_MARKER("PlayVideo", frame->mTime.ToMicroseconds(),
+                                frame->GetEndTime().ToMicroseconds());
     }
   }
 
   if (images.Length() > 0) {
     mContainer->SetCurrentFrames(frames[0]->mDisplay, images);
 
     if (mSecondaryContainer) {
       mSecondaryContainer->SetCurrentFrames(frames[0]->mDisplay, images);
@@ -491,19 +498,18 @@ void VideoSink::UpdateRenderedVideoFrame
     lastFrameEndTime = frame->GetEndTime();
     if (frame->IsSentToCompositor()) {
       sentToCompositorCount++;
     } else {
       droppedInSink++;
       VSINK_LOG_V("discarding video frame mTime=%" PRId64
                   " clock_time=%" PRId64,
                   frame->mTime.ToMicroseconds(), clockTime.ToMicroseconds());
-      PROFILER_MARKER("DiscardVideo", MEDIA_PLAYBACK, {}, MediaSampleMarker,
-                      frame->mTime.ToMicroseconds(),
-                      frame->GetEndTime().ToMicroseconds());
+      VSINK_ADD_PROFILER_MARKER("DiscardVideo", frame->mTime.ToMicroseconds(),
+                                frame->GetEndTime().ToMicroseconds());
     }
   }
 
   if (droppedInSink || sentToCompositorCount) {
     uint32_t totalCompositorDroppedCount = mContainer->GetDroppedImageCount();
     uint32_t droppedInCompositor =
         totalCompositorDroppedCount - mOldCompositorDroppedCount;
     if (droppedInCompositor > 0) {
--- a/dom/media/platforms/wmf/WMFDecoderModule.cpp
+++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp
@@ -22,39 +22,47 @@
 #include "WMFVideoMFTManager.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPrefs_media.h"
 #include "mozilla/WindowsVersion.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/mscom/EnsureMTA.h"
-#include "mozilla/ProfilerMarkers.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIXULRuntime.h"
 #include "nsIXULRuntime.h"  // for BrowserTabsRemoteAutostart
 #include "nsServiceManagerUtils.h"
 #include "nsWindowsHelpers.h"
 #include "prsystem.h"
 
 #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#  define WFM_DECODER_MODULE_STATUS_MARKER(tag, text, markerTime)            \
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \
+                                     (text, markerTime))
+#else
+#  define WFM_DECODER_MODULE_STATUS_MARKER(tag, text, markerTime)
+#endif
+
 extern const GUID CLSID_WebmMfVpxDec;
 
 namespace mozilla {
 
 // Helper function to add a profile marker and log at the same time.
 static void MOZ_FORMAT_PRINTF(2, 3)
-    WmfDeocderModuleMarkerAndLog(const ProfilerString8View& aMarkerTag,
-                                 const char* aFormat, ...) {
+    WmfDeocderModuleMarkerAndLog(const char* aTag, const char* aFormat, ...) {
   va_list ap;
   va_start(ap, aFormat);
   const nsVprintfCString markerString(aFormat, ap);
   va_end(ap);
-  PROFILER_MARKER_TEXT(aMarkerTag, MEDIA_PLAYBACK, {}, markerString);
+  WFM_DECODER_MODULE_STATUS_MARKER(aTag, markerString,
+                                   TimeStamp::NowUnfuzzed());
   LOG("%s", markerString.get());
 }
 
 static Atomic<bool> sDXVAEnabled(false);
 static Atomic<bool> sUsableVPXMFT(false);
 
 /* static */
 already_AddRefed<PlatformDecoderModule> WMFDecoderModule::Create() {
--- a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp
+++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp
@@ -4,24 +4,32 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WMFMediaDataDecoder.h"
 
 #include "VideoUtils.h"
 #include "WMFUtils.h"
 #include "mozilla/Logging.h"
-#include "mozilla/ProfilerMarkers.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/TaskQueue.h"
 #include "mozilla/Telemetry.h"
 #include "nsTArray.h"
 
 #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#  define WFM_MEDIA_DATA_DECODER_STATUS_MARKER(tag, text, markerTime)        \
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \
+                                     (text, markerTime))
+#else
+#  define WFM_MEDIA_DATA_DECODER_STATUS_MARKER(tag, text, markerTime)
+#endif
+
 namespace mozilla {
 
 WMFMediaDataDecoder::WMFMediaDataDecoder(MFTManager* aMFTManager)
     : mTaskQueue(
           new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
                         "WMFMediaDataDecoder")),
       mMFTManager(aMFTManager) {}
 
@@ -89,17 +97,18 @@ RefPtr<MediaDataDecoder::DecodePromise> 
     mRecordedError = true;
   }
 
   nsPrintfCString markerString(
       "WMFMediaDataDecoder::ProcessError for decoder with description %s with "
       "reason: %s",
       GetDescriptionName().get(), aReason);
   LOG(markerString.get());
-  PROFILER_MARKER_TEXT("WMFDecoder Error", MEDIA_PLAYBACK, {}, markerString);
+  WFM_MEDIA_DATA_DECODER_STATUS_MARKER("WMFDecoder Error", markerString,
+                                       TimeStamp::NowUnfuzzed());
 
   // TODO: For the error DXGI_ERROR_DEVICE_RESET, we could return
   // NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER to get the latest device. Maybe retry
   // up to 3 times.
   return DecodePromise::CreateAndReject(
       MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
                   RESULT_DETAIL("%s:%x", aReason, aError)),
       __func__);
--- a/dom/media/platforms/wrappers/MediaChangeMonitor.cpp
+++ b/dom/media/platforms/wrappers/MediaChangeMonitor.cpp
@@ -8,20 +8,28 @@
 
 #include "AnnexB.h"
 #include "H264.h"
 #include "ImageContainer.h"
 #include "MP4Decoder.h"
 #include "MediaInfo.h"
 #include "PDMFactory.h"
 #include "VPXDecoder.h"
-#include "mozilla/ProfilerMarkers.h"
 #include "mozilla/StaticPrefs_media.h"
 #include "mozilla/TaskQueue.h"
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#  define MEDIA_CHANGE_MONITOR_STATUS_MARKER(tag, text, markerTime)          \
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \
+                                     (text, markerTime))
+#else
+#  define MEDIA_CHANGE_MONITOR_STATUS_MARKER(tag, text, markerTime)
+#endif
+
 namespace mozilla {
 
 // H264ChangeMonitor is used to ensure that only AVCC or AnnexB is fed to the
 // underlying MediaDataDecoder. The H264ChangeMonitor allows playback of content
 // where the SPS NAL may not be provided in the init segment (e.g. AVC3 or Annex
 // B) H264ChangeMonitor will monitor the input data, and will delay creation of
 // the MediaDataDecoder until a SPS and PPS NALs have been extracted.
 
@@ -85,19 +93,21 @@ class H264ChangeMonitor : public MediaCh
       return NS_OK;
     }
 
     // Store the sample's extradata so we don't trigger a false positive
     // with the out of band test on the next sample.
     mPreviousExtraData = aSample->mExtraData;
     UpdateConfigFromExtraData(extra_data);
 
-    PROFILER_MARKER_TEXT("H264 Stream Change", MEDIA_PLAYBACK, {},
-                         "H264ChangeMonitor::CheckForChange has detected a "
-                         "change in the stream and will request a new decoder");
+    MEDIA_CHANGE_MONITOR_STATUS_MARKER(
+        "H264 Stream Change",
+        "H264ChangeMonitor::CheckForChange has detected a change in the "
+        "stream and will request a new decoder"_ns,
+        TimeStamp::NowUnfuzzed());
     return NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER;
   }
 
   const TrackInfo& Config() const override { return mCurrentConfig; }
 
   MediaResult PrepareSample(MediaDataDecoder::ConversionRequired aConversion,
                             MediaRawData* aSample,
                             bool aNeedKeyFrame) override {
@@ -189,20 +199,21 @@ class VPXChangeMonitor : public MediaCha
       if (mInfo.ref().IsCompatible(info)) {
         return rv;
       }
       mCurrentConfig.mImage = info.mImage;
       mCurrentConfig.mDisplay = info.mDisplay;
       mCurrentConfig.SetImageRect(
           gfx::IntRect(0, 0, info.mImage.width, info.mImage.height));
 
-      PROFILER_MARKER_TEXT(
-          "VPX Stream Change", MEDIA_PLAYBACK, {},
+      MEDIA_CHANGE_MONITOR_STATUS_MARKER(
+          "VPX Stream Change",
           "VPXChangeMonitor::CheckForChange has detected a change in the "
-          "stream and will request a new decoder");
+          "stream and will request a new decoder"_ns,
+          TimeStamp::NowUnfuzzed());
       rv = NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER;
     }
     mInfo = Some(info);
     // For the first frame, we leave the mDisplay/mImage untouched as they
     // contain aspect ratio (AR) information set by the demuxer.
     // The AR data isn't found in the VP8/VP9 bytestream.
     mCurrentConfig.mColorDepth = gfx::ColorDepthForBitDepth(info.mBitDepth);
     mCurrentConfig.mColorSpace = info.ColorSpace();
@@ -739,8 +750,10 @@ void MediaChangeMonitor::FlushThenShutdo
               return;
             }
             mDecodePromise.Reject(aError, __func__);
           })
       ->Track(mFlushRequest);
 }
 
 }  // namespace mozilla
+
+#undef MEDIA_CHANGE_MONITOR_STATUS_MARKER
--- a/dom/performance/Performance.cpp
+++ b/dom/performance/Performance.cpp
@@ -23,16 +23,20 @@
 #include "mozilla/dom/PerformanceNavigationBinding.h"
 #include "mozilla/dom/PerformanceObserverBinding.h"
 #include "mozilla/dom/PerformanceNavigationTiming.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
+
 #define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__)
 
 namespace mozilla::dom {
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Performance)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(Performance, DOMEventTargetHelper,
@@ -207,63 +211,16 @@ void Performance::ClearUserEntries(const
     return (!aEntryName.WasPassed() ||
             entry->GetName().Equals(aEntryName.Value())) &&
            (aEntryType.IsEmpty() || entry->GetEntryType().Equals(aEntryType));
   });
 }
 
 void Performance::ClearResourceTimings() { mResourceEntries.Clear(); }
 
-#ifdef MOZ_GECKO_PROFILER
-struct UserTimingMarker {
-  static constexpr Span<const char> MarkerTypeName() {
-    return MakeStringSpan("UserTiming");
-  }
-  static void StreamJSONMarkerData(
-      baseprofiler::SpliceableJSONWriter& aWriter,
-      const ProfilerString16View& aName, bool aIsMeasure,
-      const Maybe<ProfilerString16View>& aStartMark,
-      const Maybe<ProfilerString16View>& aEndMark) {
-    aWriter.StringProperty("name", NS_ConvertUTF16toUTF8(aName.Data()));
-    if (aIsMeasure) {
-      aWriter.StringProperty("entryType", "measure");
-    } else {
-      aWriter.StringProperty("entryType", "mark");
-    }
-
-    if (aStartMark.isSome()) {
-      aWriter.StringProperty("startMark",
-                             NS_ConvertUTF16toUTF8(aStartMark->Data()));
-    } else {
-      aWriter.NullProperty("startMark");
-    }
-    if (aEndMark.isSome()) {
-      aWriter.StringProperty("endMark",
-                             NS_ConvertUTF16toUTF8(aEndMark->Data()));
-    } else {
-      aWriter.NullProperty("endMark");
-    }
-  }
-  static MarkerSchema MarkerTypeDisplay() {
-    using MS = MarkerSchema;
-    MS schema{MS::Location::markerChart, MS::Location::markerTable};
-    schema.SetAllLabels("{marker.data.name}");
-    schema.AddStaticLabelValue("Marker", "UserTiming");
-    schema.AddKeyLabelFormat("entryType", "Entry Type", MS::Format::string);
-    schema.AddKeyLabelFormat("name", "Name", MS::Format::string);
-    schema.AddKeyLabelFormat("startMark", "Start Mark", MS::Format::string);
-    schema.AddKeyLabelFormat("endMark", "End Mark", MS::Format::string);
-    schema.AddStaticLabelValue("Description",
-                               "UserTimingMeasure is created using the DOM API "
-                               "performance.measure().");
-    return schema;
-  }
-};
-#endif
-
 void Performance::Mark(const nsAString& aName, ErrorResult& aRv) {
   // We add nothing when 'privacy.resistFingerprinting' is on.
   if (nsContentUtils::ShouldResistFingerprinting()) {
     return;
   }
 
   if (IsPerformanceTimingAttribute(aName)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
@@ -275,19 +232,18 @@ void Performance::Mark(const nsAString& 
   InsertUserEntry(performanceMark);
 
 #ifdef MOZ_GECKO_PROFILER
   if (profiler_can_accept_markers()) {
     Maybe<uint64_t> innerWindowId;
     if (GetOwner()) {
       innerWindowId = Some(GetOwner()->WindowID());
     }
-    profiler_add_marker("UserTiming", geckoprofiler::category::DOM,
-                        MarkerInnerWindowId(innerWindowId), UserTimingMarker{},
-                        aName, /* aIsMeasure */ false, Nothing{}, Nothing{});
+    PROFILER_ADD_MARKER_WITH_PAYLOAD("UserTiming", DOM, UserTimingMarkerPayload,
+                                     (aName, TimeStamp::Now(), innerWindowId));
   }
 #endif
 }
 
 void Performance::ClearMarks(const Optional<nsAString>& aName) {
   ClearUserEntries(aName, u"mark"_ns);
 }
 
@@ -371,21 +327,19 @@ void Performance::Measure(const nsAStrin
     if (aEndMark.WasPassed()) {
       endMark.emplace(aEndMark.Value());
     }
 
     Maybe<uint64_t> innerWindowId;
     if (GetOwner()) {
       innerWindowId = Some(GetOwner()->WindowID());
     }
-    profiler_add_marker("UserTiming", geckoprofiler::category::DOM,
-                        {MarkerTiming::Interval(startTimeStamp, endTimeStamp),
-                         MarkerInnerWindowId(innerWindowId)},
-                        UserTimingMarker{}, aName, /* aIsMeasure */ true,
-                        startMark, endMark);
+    PROFILER_ADD_MARKER_WITH_PAYLOAD("UserTiming", DOM, UserTimingMarkerPayload,
+                                     (aName, startMark, endMark, startTimeStamp,
+                                      endTimeStamp, innerWindowId));
   }
 #endif
 }
 
 void Performance::ClearMeasures(const Optional<nsAString>& aName) {
   ClearUserEntries(aName, u"measure"_ns);
 }
 
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -19,17 +19,16 @@
 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
 #include "js/MemoryFunctions.h"
 #include "js/Modules.h"  // JS::FinishDynamicModuleImport, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{DynamicImport,Metadata}Hook
 #include "js/OffThreadScriptCompilation.h"
 #include "js/Realm.h"
 #include "js/SourceText.h"
 #include "js/Utility.h"
 #include "xpcpublic.h"
-#include "GeckoProfiler.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIContent.h"
 #include "nsJSUtils.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ScriptDecoding.h"  // mozilla::dom::ScriptDecoding
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/SRILogHelper.h"
@@ -79,16 +78,20 @@
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 #include "nsIScriptError.h"
 #include "nsIAsyncOutputStream.h"
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
+
 using JS::SourceText;
 
 using mozilla::Telemetry::LABELS_DOM_SCRIPT_PRELOAD_RESULT;
 
 namespace mozilla {
 namespace dom {
 
 LazyLogModule ScriptLoader::gCspPRLog("CSP");
@@ -2172,33 +2175,32 @@ NotifyOffThreadScriptLoadCompletedRunnab
   // function.
   RefPtr<ScriptLoadRequest> request = std::move(mRequest);
 
   // Runnable pointer should have been cleared in the offthread callback.
   MOZ_ASSERT(!request->mRunnable);
 
 #ifdef MOZ_GECKO_PROFILER
   if (profiler_is_active()) {
-    ProfilerString8View scriptSourceString;
+    const char* scriptSourceString;
     if (request->IsTextSource()) {
       scriptSourceString = "ScriptCompileOffThread";
     } else if (request->IsBinASTSource()) {
       scriptSourceString = "BinASTDecodeOffThread";
     } else {
       MOZ_ASSERT(request->IsBytecode());
       scriptSourceString = "BytecodeDecodeOffThread";
     }
 
     nsAutoCString profilerLabelString;
     GetProfilerLabelForRequest(request, profilerLabelString);
-    PROFILER_MARKER_TEXT(
-        scriptSourceString, JS,
-        MarkerTiming::Interval(request->mOffThreadParseStartTime,
-                               request->mOffThreadParseStopTime),
-        profilerLabelString);
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(
+        scriptSourceString, JS, TextMarkerPayload,
+        (profilerLabelString, request->mOffThreadParseStartTime,
+         request->mOffThreadParseStopTime));
   }
 #endif
 
   RefPtr<ScriptLoader> loader = std::move(mLoader);
 
   // Request was already cancelled at some earlier point.
   if (!request->mOffThreadToken) {
     return NS_OK;
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -2453,17 +2453,17 @@ nsresult XMLHttpRequestMainThread::Creat
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
   if (httpChannel) {
     rv = httpChannel->SetRequestMethod(mRequestMethod);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    httpChannel->SetSource(profiler_capture_backtrace());
+    httpChannel->SetSource(profiler_get_backtrace());
 
     // Set the initiator type
     nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
     if (timedChannel) {
       timedChannel->SetInitiatorType(u"xmlhttprequest"_ns);
     }
   }
 
@@ -3235,18 +3235,17 @@ void XMLHttpRequestMainThread::SetMozBac
   SetMozBackgroundRequest(aMozBackgroundRequest);
 }
 
 void XMLHttpRequestMainThread::SetOriginStack(
     UniquePtr<SerializedStackHolder> aOriginStack) {
   mOriginStack = std::move(aOriginStack);
 }
 
-void XMLHttpRequestMainThread::SetSource(
-    UniquePtr<ProfileChunkedBuffer> aSource) {
+void XMLHttpRequestMainThread::SetSource(UniqueProfilerBacktrace aSource) {
   if (!mChannel) {
     return;
   }
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
 
   if (httpChannel) {
     httpChannel->SetSource(std::move(aSource));
   }
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -391,17 +391,17 @@ class XMLHttpRequestMainThread final : p
 
   nsresult SetMozBackgroundRequest(bool aMozBackgroundRequest);
 
   virtual void SetMozBackgroundRequest(bool aMozBackgroundRequest,
                                        ErrorResult& aRv) override;
 
   void SetOriginStack(UniquePtr<SerializedStackHolder> aOriginStack);
 
-  void SetSource(UniquePtr<ProfileChunkedBuffer> aSource);
+  void SetSource(UniqueProfilerBacktrace aSource);
 
   virtual uint16_t ErrorCode() const override {
     return static_cast<uint16_t>(mErrorLoad);
   }
 
   virtual bool MozAnon() const override;
 
   virtual bool MozSystem() const override;
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -648,28 +648,28 @@ class OpenRunnable final : public Worker
   const nsString mMimeTypeOverride;
 
   // Remember the worker thread's stack when the XHR was opened, so that it can
   // be passed on to the net monitor.
   UniquePtr<SerializedStackHolder> mOriginStack;
 
   // Remember the worker thread's stack when the XHR was opened for profiling
   // purposes.
-  UniquePtr<ProfileChunkedBuffer> mSource;
+  UniqueProfilerBacktrace mSource;
 
  public:
   OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
                const nsACString& aMethod, const nsAString& aURL,
                const Optional<nsAString>& aUser,
                const Optional<nsAString>& aPassword, bool aBackgroundRequest,
                bool aWithCredentials, uint32_t aTimeout,
                XMLHttpRequestResponseType aResponseType,
                const nsString& aMimeTypeOverride,
                UniquePtr<SerializedStackHolder> aOriginStack,
-               UniquePtr<ProfileChunkedBuffer> aSource = nullptr)
+               UniqueProfilerBacktrace aSource = nullptr)
       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
         mMethod(aMethod),
         mURL(aURL),
         mBackgroundRequest(aBackgroundRequest),
         mWithCredentials(aWithCredentials),
         mTimeout(aTimeout),
         mResponseType(aResponseType),
         mMimeTypeOverride(aMimeTypeOverride),
@@ -1736,17 +1736,17 @@ void XMLHttpRequestWorker::Open(const ns
       stack = GetCurrentStackForNetMonitor(cx);
     }
   }
 
   RefPtr<OpenRunnable> runnable = new OpenRunnable(
       mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
       mBackgroundRequest, mWithCredentials, mTimeout, mResponseType,
       alsoOverrideMimeType ? mMimeTypeOverride : VoidString(), std::move(stack),
-      profiler_capture_backtrace());
+      profiler_get_backtrace());
 
   ++mProxy->mOpenCount;
   runnable->Dispatch(Canceling, aRv);
   if (aRv.Failed()) {
     if (mProxy && !--mProxy->mOpenCount) {
       ReleaseProxy();
     }
 
--- a/gfx/layers/ProfilerScreenshots.cpp
+++ b/gfx/layers/ProfilerScreenshots.cpp
@@ -6,16 +6,19 @@
 
 #include "mozilla/layers/ProfilerScreenshots.h"
 
 #include "mozilla/TimeStamp.h"
 
 #include "GeckoProfiler.h"
 #include "gfxUtils.h"
 #include "nsThreadUtils.h"
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 ProfilerScreenshots::ProfilerScreenshots()
     : mMutex("ProfilerScreenshots::mMutex"), mLiveSurfaceCount(0) {}
 
@@ -76,42 +79,22 @@ void ProfilerScreenshots::SubmitScreensh
 
           // Encode surf to a JPEG data URL.
           nsCString dataURL;
           nsresult rv = gfxUtils::EncodeSourceSurface(
               surf, ImageType::JPEG, u"quality=85"_ns, gfxUtils::eDataURIEncode,
               nullptr, &dataURL);
           if (NS_SUCCEEDED(rv)) {
             // Add a marker with the data URL.
-            struct ScreenshotMarker {
-              static constexpr mozilla::Span<const char> MarkerTypeName() {
-                return mozilla::MakeStringSpan("CompositorScreenshot");
-              }
-              static void StreamJSONMarkerData(
-                  mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
-                  const mozilla::ProfilerString8View& aScreenshotDataURL,
-                  const mozilla::gfx::IntSize& aWindowSize,
-                  uintptr_t aWindowIdentifier) {
-                aWriter.UniqueStringProperty("url", aScreenshotDataURL);
-
-                char hexWindowID[32];
-                SprintfLiteral(hexWindowID, "0x%" PRIXPTR, aWindowIdentifier);
-                aWriter.StringProperty("windowID", hexWindowID);
-                aWriter.DoubleProperty("windowWidth", aWindowSize.width);
-                aWriter.DoubleProperty("windowHeight", aWindowSize.height);
-              }
-              static mozilla::MarkerSchema MarkerTypeDisplay() {
-                return mozilla::MarkerSchema::SpecialFrontendLocation{};
-              }
-            };
-            profiler_add_marker(
-                "CompositorScreenshot", geckoprofiler::category::GRAPHICS,
-                {MarkerThreadId(sourceThread),
-                 MarkerTiming::InstantAt(timeStamp)},
-                ScreenshotMarker{}, dataURL, originalSize, windowIdentifier);
+            AUTO_PROFILER_STATS(add_marker_with_ScreenshotPayload);
+            profiler_add_marker_for_thread(
+                sourceThread, JS::ProfilingCategoryPair::GRAPHICS,
+                "CompositorScreenshot",
+                ScreenshotPayload(timeStamp, std::move(dataURL), originalSize,
+                                  windowIdentifier));
           }
         }
 
         // Return backingSurface back to the surface pool.
         self->ReturnSurface(backingSurface);
       }));
 #endif
 }
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -35,16 +35,20 @@
 #include "nsISupportsUtils.h"  // for NS_ADDREF, NS_RELEASE
 #include "nsRegion.h"          // for nsIntRegion
 #include "nsTArray.h"          // for AutoTArray
 #include <stack>
 #include "TextRenderer.h"  // for TextRenderer
 #include <vector>
 #include "GeckoProfiler.h"  // for GeckoProfiler
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"  // for LayerTranslationMarkerPayload
+#endif
+
 static mozilla::LazyLogModule sGfxCullLog("gfx.culling");
 #define CULLING_LOG(...) MOZ_LOG(sGfxCullLog, LogLevel::Debug, (__VA_ARGS__))
 
 #define DUMP(...)                 \
   do {                            \
     if (gfxEnv::DumpDebug()) {    \
       printf_stderr(__VA_ARGS__); \
     }                             \
@@ -94,47 +98,19 @@ static void PrintUniformityInfo(Layer* a
   }
 
   Matrix4x4 transform = aLayer->AsHostLayer()->GetShadowBaseTransform();
   if (!transform.Is2D()) {
     return;
   }
 
   Point translation = transform.As2D().GetTranslation();
-
-  // Contains the translation applied to a 2d layer so we can track the layer
-  // position at each frame.
-  struct LayerTranslationMarker {
-    static constexpr Span<const char> MarkerTypeName() {
-      return MakeStringSpan("LayerTranslation");
-    }
-    static void StreamJSONMarkerData(
-        baseprofiler::SpliceableJSONWriter& aWriter,
-        ProfileBufferRawPointer<layers::Layer> aLayer, gfx::Point aPoint) {
-      const size_t bufferSize = 32;
-      char buffer[bufferSize];
-      SprintfLiteral(buffer, "%p", aLayer.mRawPointer);
-
-      aWriter.StringProperty("layer", buffer);
-      aWriter.IntProperty("x", aPoint.x);
-      aWriter.IntProperty("y", aPoint.y);
-    }
-    static MarkerSchema MarkerTypeDisplay() {
-      using MS = MarkerSchema;
-      MS schema{MS::Location::markerChart, MS::Location::markerTable};
-      schema.AddKeyLabelFormat("layer", "Layer", MS::Format::string);
-      schema.AddKeyLabelFormat("x", "X", MS::Format::integer);
-      schema.AddKeyLabelFormat("y", "Y", MS::Format::integer);
-      return schema;
-    }
-  };
-
-  profiler_add_marker("LayerTranslation", geckoprofiler::category::GRAPHICS, {},
-                      LayerTranslationMarker{},
-                      WrapProfileBufferRawPointer(aLayer), translation);
+  PROFILER_ADD_MARKER_WITH_PAYLOAD("LayerTranslation", GRAPHICS,
+                                   LayerTranslationMarkerPayload,
+                                   (aLayer, translation, TimeStamp::Now()));
 #endif
 }
 
 static Maybe<gfx::Polygon> SelectLayerGeometry(
     const Maybe<gfx::Polygon>& aParentGeometry,
     const Maybe<gfx::Polygon>& aChildGeometry) {
   // Both the parent and the child layer were split.
   if (aParentGeometry && aChildGeometry) {
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -85,16 +85,19 @@
 #endif
 #include "GeckoProfiler.h"
 #include "mozilla/ipc/ProtocolTypes.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Hal.h"
 #include "mozilla/HalTypes.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
 #include "mozilla/VsyncDispatcher.h"
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
 #  include "VsyncSource.h"
 #endif
 #include "mozilla/widget/CompositorWidget.h"
 #ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
 #  include "mozilla/widget/CompositorWidgetParent.h"
 #endif
@@ -2168,33 +2171,18 @@ already_AddRefed<IAPZCTreeManager> Compo
       lts->mParent ? lts->mParent->mApzcTreeManager.get() : nullptr;
   return apzctm.forget();
 }
 
 #if defined(MOZ_GECKO_PROFILER)
 static void InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (profiler_thread_is_being_profiled()) {
-    // Tracks when a vsync occurs according to the HardwareComposer.
-    struct VsyncMarker {
-      static constexpr mozilla::Span<const char> MarkerTypeName() {
-        return mozilla::MakeStringSpan("VsyncTimestamp");
-      }
-      static void StreamJSONMarkerData(
-          baseprofiler::SpliceableJSONWriter& aWriter) {}
-      static MarkerSchema MarkerTypeDisplay() {
-        using MS = MarkerSchema;
-        MS schema{MS::Location::markerChart, MS::Location::markerTable};
-        // Nothing outside the defaults.
-        return schema;
-      }
-    };
-    profiler_add_marker("VsyncTimestamp", geckoprofiler::category::GRAPHICS,
-                        MarkerTiming::InstantAt(aVsyncTimestamp),
-                        VsyncMarker{});
+    PROFILER_ADD_MARKER_WITH_PAYLOAD("VsyncTimestamp", GRAPHICS,
+                                     VsyncMarkerPayload, (aVsyncTimestamp));
   }
 }
 #endif
 
 /*static */
 void CompositorBridgeParent::PostInsertVsyncProfilerMarker(
     TimeStamp aVsyncTimestamp) {
 #if defined(MOZ_GECKO_PROFILER)
@@ -2749,33 +2737,52 @@ int32_t RecordContentFrameTime(
     const TimeDuration& aVsyncRate, bool aContainsSVGGroup,
     bool aRecordUploadStats, wr::RendererStats* aStats /* = nullptr */) {
   double latencyMs = (aCompositeEnd - aTxnStart).ToMilliseconds();
   double latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
   int32_t fracLatencyNorm = lround(latencyNorm * 100.0);
 
 #ifdef MOZ_GECKO_PROFILER
   if (profiler_can_accept_markers()) {
-    struct ContentFrameMarker {
-      static constexpr Span<const char> MarkerTypeName() {
-        return MakeStringSpan("CONTENT_FRAME_TIME");
+    class ContentFramePayload : public ProfilerMarkerPayload {
+     public:
+      ContentFramePayload(const mozilla::TimeStamp& aStartTime,
+                          const mozilla::TimeStamp& aEndTime)
+          : ProfilerMarkerPayload(aStartTime, aEndTime) {}
+      mozilla::ProfileBufferEntryWriter::Length TagAndSerializationBytes()
+          const override {
+        return CommonPropsTagAndSerializationBytes();
+      }
+      void SerializeTagAndPayload(
+          mozilla::ProfileBufferEntryWriter& aEntryWriter) const override {
+        static const DeserializerTag tag = TagForDeserializer(Deserialize);
+        SerializeTagAndCommonProps(tag, aEntryWriter);
       }
-      static void StreamJSONMarkerData(
-          baseprofiler::SpliceableJSONWriter& aWriter) {}
-      static MarkerSchema MarkerTypeDisplay() {
-        using MS = MarkerSchema;
-        MS schema{MS::Location::markerChart, MS::Location::markerTable};
-        // Nothing outside the defaults.
-        return schema;
+      void StreamPayload(mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+                         const TimeStamp& aProcessStartTime,
+                         UniqueStacks& aUniqueStacks) const override {
+        StreamCommonProps("CONTENT_FRAME_TIME", aWriter, aProcessStartTime,
+                          aUniqueStacks);
+      }
+
+     private:
+      explicit ContentFramePayload(CommonProps&& aCommonProps)
+          : ProfilerMarkerPayload(std::move(aCommonProps)) {}
+      static mozilla::UniquePtr<ProfilerMarkerPayload> Deserialize(
+          mozilla::ProfileBufferEntryReader& aEntryReader) {
+        ProfilerMarkerPayload::CommonProps props =
+            DeserializeCommonProps(aEntryReader);
+        return UniquePtr<ProfilerMarkerPayload>(
+            new ContentFramePayload(std::move(props)));
       }
     };
-
-    profiler_add_marker("CONTENT_FRAME_TIME", geckoprofiler::category::GRAPHICS,
-                        MarkerTiming::Interval(aTxnStart, aCompositeEnd),
-                        ContentFrameMarker{});
+    AUTO_PROFILER_STATS(add_marker_with_ContentFramePayload);
+    profiler_add_marker_for_thread(
+        profiler_current_thread_id(), JS::ProfilingCategoryPair::GRAPHICS,
+        "CONTENT_FRAME_TIME", ContentFramePayload(aTxnStart, aCompositeEnd));
   }
 #endif
 
   Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME, fracLatencyNorm);
 
   if (!(aTxnId == VsyncId()) && aVsyncStart) {
     latencyMs = (aCompositeEnd - aVsyncStart).ToMilliseconds();
     latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
--- a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
@@ -34,17 +34,17 @@
 #include "mozilla/mozalloc.h"  // for operator new, etc
 #include "nsDebug.h"           // for NS_ASSERTION, etc
 #include "nsTArray.h"          // for nsTArray
 #include "nsXULAppAPI.h"       // for XRE_GetIOMessageLoop
 #include "mozilla/Unused.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #ifdef MOZ_GECKO_PROFILER
-#  include "mozilla/BaseProfilerMarkerTypes.h"
+#  include "ProfilerMarkerPayload.h"
 #endif
 
 namespace mozilla {
 
 namespace layers {
 
 // defined in CompositorBridgeParent.cpp
 typedef std::map<LayersId, CompositorBridgeParent::LayerTreeState> LayerTreeMap;
@@ -367,20 +367,53 @@ void ContentCompositorBridgeParent::Shad
     // to reach the widget owning the tab.
     Unused << state->mParent->SendObserveLayersUpdate(
         id, aLayerTree->GetChildEpoch(), true);
   }
 
   auto endTime = TimeStamp::Now();
 #ifdef MOZ_GECKO_PROFILER
   if (profiler_can_accept_markers()) {
-    profiler_add_marker(
-        "CONTENT_FULL_PAINT_TIME", geckoprofiler::category::GRAPHICS,
-        MarkerTiming::Interval(aInfo.transactionStart(), endTime),
-        baseprofiler::markers::ContentBuildMarker{});
+    class ContentBuildPayload : public ProfilerMarkerPayload {
+     public:
+      ContentBuildPayload(const mozilla::TimeStamp& aStartTime,
+                          const mozilla::TimeStamp& aEndTime)
+          : ProfilerMarkerPayload(aStartTime, aEndTime) {}
+      mozilla::ProfileBufferEntryWriter::Length TagAndSerializationBytes()
+          const override {
+        return CommonPropsTagAndSerializationBytes();
+      }
+      void SerializeTagAndPayload(
+          mozilla::ProfileBufferEntryWriter& aEntryWriter) const override {
+        static const DeserializerTag tag = TagForDeserializer(Deserialize);
+        SerializeTagAndCommonProps(tag, aEntryWriter);
+      }
+      void StreamPayload(mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+                         const TimeStamp& aProcessStartTime,
+                         UniqueStacks& aUniqueStacks) const override {
+        StreamCommonProps("CONTENT_FULL_PAINT_TIME", aWriter, aProcessStartTime,
+                          aUniqueStacks);
+      }
+
+     private:
+      explicit ContentBuildPayload(CommonProps&& aCommonProps)
+          : ProfilerMarkerPayload(std::move(aCommonProps)) {}
+      static mozilla::UniquePtr<ProfilerMarkerPayload> Deserialize(
+          mozilla::ProfileBufferEntryReader& aEntryReader) {
+        ProfilerMarkerPayload::CommonProps props =
+            DeserializeCommonProps(aEntryReader);
+        return UniquePtr<ProfilerMarkerPayload>(
+            new ContentBuildPayload(std::move(props)));
+      }
+    };
+    AUTO_PROFILER_STATS(add_marker_with_ContentBuildPayload);
+    profiler_add_marker_for_thread(
+        profiler_current_thread_id(), JS::ProfilingCategoryPair::GRAPHICS,
+        "CONTENT_FULL_PAINT_TIME",
+        ContentBuildPayload(aInfo.transactionStart(), endTime));
   }
 #endif
   Telemetry::Accumulate(
       Telemetry::CONTENT_FULL_PAINT_TIME,
       static_cast<uint32_t>(
           (endTime - aInfo.transactionStart()).ToMilliseconds()));
 
   RegisterPayloads(aLayerTree, aInfo.payload());
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -40,44 +40,50 @@
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/widget/CompositorWidget.h"
 
 #ifdef XP_WIN
 #  include "mozilla/widget/WinCompositorWidget.h"
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
-#  include "mozilla/ProfilerMarkerTypes.h"
+#  include "ProfilerMarkerPayload.h"
 #endif
 
 bool is_in_main_thread() { return NS_IsMainThread(); }
 
 bool is_in_compositor_thread() {
   return mozilla::layers::CompositorThreadHolder::IsInCompositorThread();
 }
 
 bool is_in_render_thread() {
   return mozilla::wr::RenderThread::IsInRenderThread();
 }
 
 void gecko_profiler_start_marker(const char* name) {
-  PROFILER_MARKER(mozilla::ProfilerString8View::WrapNullTerminatedString(name),
-                  GRAPHICS, mozilla::MarkerTiming::IntervalStart(), Tracing,
-                  "WebRender");
+#ifdef MOZ_GECKO_PROFILER
+  profiler_tracing_marker("WebRender", name,
+                          JS::ProfilingCategoryPair::GRAPHICS,
+                          TRACING_INTERVAL_START);
+#endif
 }
 
 void gecko_profiler_end_marker(const char* name) {
-  PROFILER_MARKER(mozilla::ProfilerString8View::WrapNullTerminatedString(name),
-                  GRAPHICS, mozilla::MarkerTiming::IntervalEnd(), Tracing,
-                  "WebRender");
+#ifdef MOZ_GECKO_PROFILER
+  profiler_tracing_marker("WebRender", name,
+                          JS::ProfilingCategoryPair::GRAPHICS,
+                          TRACING_INTERVAL_END);
+#endif
 }
 
 void gecko_profiler_event_marker(const char* name) {
-  PROFILER_MARKER(mozilla::ProfilerString8View::WrapNullTerminatedString(name),
-                  GRAPHICS, {}, Tracing, "WebRender");
+#ifdef MOZ_GECKO_PROFILER
+  profiler_tracing_marker("WebRender", name,
+                          JS::ProfilingCategoryPair::GRAPHICS, TRACING_EVENT);
+#endif
 }
 
 void gecko_profiler_add_text_marker(const char* name, const char* text_bytes,
                                     size_t text_len, uint64_t microseconds) {
 #ifdef MOZ_GECKO_PROFILER
   if (profiler_thread_is_being_profiled()) {
     auto now = mozilla::TimeStamp::NowUnfuzzed();
     auto start = now - mozilla::TimeDuration::FromMicroseconds(microseconds);
@@ -215,20 +221,56 @@ class SceneBuiltNotification : public wr
     auto startTime = this->mTxnStartTime;
     RefPtr<WebRenderBridgeParent> parent = mParent;
     wr::Epoch epoch = mEpoch;
     CompositorThread()->Dispatch(NS_NewRunnableFunction(
         "SceneBuiltNotificationRunnable", [parent, epoch, startTime]() {
           auto endTime = TimeStamp::Now();
 #ifdef MOZ_GECKO_PROFILER
           if (profiler_can_accept_markers()) {
-            profiler_add_marker("CONTENT_FULL_PAINT_TIME",
-                                geckoprofiler::category::GRAPHICS,
-                                MarkerTiming::Interval(startTime, endTime),
-                                baseprofiler::markers::ContentBuildMarker{});
+            class ContentFullPaintPayload : public ProfilerMarkerPayload {
+             public:
+              ContentFullPaintPayload(const mozilla::TimeStamp& aStartTime,
+                                      const mozilla::TimeStamp& aEndTime)
+                  : ProfilerMarkerPayload(aStartTime, aEndTime) {}
+              mozilla::ProfileBufferEntryWriter::Length
+              TagAndSerializationBytes() const override {
+                return CommonPropsTagAndSerializationBytes();
+              }
+              void SerializeTagAndPayload(mozilla::ProfileBufferEntryWriter&
+                                              aEntryWriter) const override {
+                static const DeserializerTag tag =
+                    TagForDeserializer(Deserialize);
+                SerializeTagAndCommonProps(tag, aEntryWriter);
+              }
+              void StreamPayload(
+                  mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+                  const TimeStamp& aProcessStartTime,
+                  UniqueStacks& aUniqueStacks) const override {
+                StreamCommonProps("CONTENT_FULL_PAINT_TIME", aWriter,
+                                  aProcessStartTime, aUniqueStacks);
+              }
+
+             private:
+              explicit ContentFullPaintPayload(CommonProps&& aCommonProps)
+                  : ProfilerMarkerPayload(std::move(aCommonProps)) {}
+              static mozilla::UniquePtr<ProfilerMarkerPayload> Deserialize(
+                  mozilla::ProfileBufferEntryReader& aEntryReader) {
+                ProfilerMarkerPayload::CommonProps props =
+                    DeserializeCommonProps(aEntryReader);
+                return UniquePtr<ProfilerMarkerPayload>(
+                    new ContentFullPaintPayload(std::move(props)));
+              }
+            };
+
+            AUTO_PROFILER_STATS(add_marker_with_ContentFullPaintPayload);
+            profiler_add_marker_for_thread(
+                profiler_current_thread_id(),
+                JS::ProfilingCategoryPair::GRAPHICS, "CONTENT_FULL_PAINT_TIME",
+                ContentFullPaintPayload(startTime, endTime));
           }
 #endif
           Telemetry::Accumulate(
               Telemetry::CONTENT_FULL_PAINT_TIME,
               static_cast<uint32_t>((endTime - startTime).ToMilliseconds()));
           parent->NotifySceneBuiltForEpoch(epoch, endTime);
         }));
   }
--- a/gfx/webrender_bindings/RenderCompositorNative.cpp
+++ b/gfx/webrender_bindings/RenderCompositorNative.cpp
@@ -12,16 +12,20 @@
 #include "GLContextProvider.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/CompositionRecorder.h"
 #include "mozilla/layers/SurfacePool.h"
 #include "mozilla/StaticPrefs_gfx.h"
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/widget/CompositorWidget.h"
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
+
 namespace mozilla {
 namespace wr {
 
 class RenderCompositorRecordedFrame final : public layers::RecordedFrame {
  public:
   RenderCompositorRecordedFrame(
       const TimeStamp& aTimeStamp,
       RefPtr<layers::profiler_screenshots::AsyncReadbackBuffer>&& aBuffer)
--- a/ipc/chromium/src/chrome/common/ipc_channel_utils.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_utils.cc
@@ -3,32 +3,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "chrome/common/ipc_channel_utils.h"
 
 #include "chrome/common/ipc_message.h"
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
+
 namespace IPC {
 
 void AddIPCProfilerMarker(const Message& aMessage, int32_t aOtherPid,
                           mozilla::ipc::MessageDirection aDirection,
                           mozilla::ipc::MessagePhase aPhase) {
 #ifdef MOZ_GECKO_PROFILER
   if (aMessage.routing_id() != MSG_ROUTING_NONE &&
       profiler_feature_active(ProfilerFeature::IPCMessages)) {
     if (aOtherPid == -1) {
       DLOG(WARNING) << "Unable to record IPC profile marker, other PID not set";
       return;
     }
 
-    // The current timestamp must be given to the `IPCMarker` payload.
-    const mozilla::TimeStamp now = mozilla::TimeStamp::NowUnfuzzed();
-    PROFILER_MARKER("IPC", IPC, mozilla::MarkerTiming::InstantAt(now),
-                    IPCMarker, now, now, aOtherPid, aMessage.seqno(),
-                    aMessage.type(), mozilla::ipc::UnknownSide, aDirection,
-                    aPhase, aMessage.is_sync());
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(
+        "IPC", IPC, IPCMarkerPayload,
+        (aOtherPid, aMessage.seqno(), aMessage.type(),
+         mozilla::ipc::UnknownSide, aDirection, aPhase, aMessage.is_sync(),
+         mozilla::TimeStamp::NowUnfuzzed()));
   }
 #endif
 }
 
 }  // namespace IPC
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -32,16 +32,20 @@
 #include "nsISupportsImpl.h"
 #include "nsPrintfCString.h"
 
 #ifdef MOZ_TASK_TRACER
 #  include "GeckoTaskTracer.h"
 using namespace mozilla::tasktracer;
 #endif
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
+
 // Undo the damage done by mozzconf.h
 #undef compress
 
 static mozilla::LazyLogModule sLogModule("ipc");
 #define IPC_LOG(...) MOZ_LOG(sLogModule, LogLevel::Debug, (__VA_ARGS__))
 
 /*
  * IPC design:
@@ -2780,21 +2784,21 @@ void MessageChannel::DumpInterruptStack(
 
 void MessageChannel::AddProfilerMarker(const IPC::Message& aMessage,
                                        MessageDirection aDirection) {
   mMonitor->AssertCurrentThreadOwns();
 #ifdef MOZ_GECKO_PROFILER
   if (profiler_feature_active(ProfilerFeature::IPCMessages)) {
     int32_t pid = mListener->OtherPidMaybeInvalid();
     if (pid != kInvalidProcessId) {
-      // The current timestamp must be given to the `IPCMarker` payload.
-      const TimeStamp now = TimeStamp::NowUnfuzzed();
-      PROFILER_MARKER("IPC", IPC, MarkerTiming::InstantAt(now), IPCMarker, now,
-                      now, pid, aMessage.seqno(), aMessage.type(), mSide,
-                      aDirection, MessagePhase::Endpoint, aMessage.is_sync());
+      PROFILER_ADD_MARKER_WITH_PAYLOAD(
+          "IPC", IPC, IPCMarkerPayload,
+          (pid, aMessage.seqno(), aMessage.type(), mSide, aDirection,
+           MessagePhase::Endpoint, aMessage.is_sync(),
+           TimeStamp::NowUnfuzzed()));
     }
   }
 #endif
 }
 
 int32_t MessageChannel::GetTopmostMessageRoutingId() const {
   AssertWorkerThread();
 
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -24,20 +24,16 @@
 #include <functional>
 #include <map>
 #include <stack>
 #include <vector>
 
 #include "MessageLink.h"
 #include "mozilla/ipc/Transport.h"
 
-#ifdef MOZ_GECKO_PROFILER
-#  include "mozilla/ProfilerMarkersPrerequisites.h"
-#endif
-
 class nsIEventTarget;
 
 namespace mozilla {
 namespace ipc {
 
 class MessageChannel;
 class IToplevelProtocol;
 class ActorLifecycleProxy;
@@ -878,79 +874,9 @@ namespace IPC {
 template <>
 struct ParamTraits<mozilla::ipc::ResponseRejectReason>
     : public ContiguousEnumSerializer<
           mozilla::ipc::ResponseRejectReason,
           mozilla::ipc::ResponseRejectReason::SendError,
           mozilla::ipc::ResponseRejectReason::EndGuard_> {};
 }  // namespace IPC
 
-#ifdef MOZ_GECKO_PROFILER
-namespace geckoprofiler::markers {
-
-struct IPCMarker {
-  static constexpr mozilla::Span<const char> MarkerTypeName() {
-    return mozilla::MakeStringSpan("IPC");
-  }
-  static void StreamJSONMarkerData(
-      mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
-      mozilla::TimeStamp aStart, mozilla::TimeStamp aEnd, int32_t aOtherPid,
-      int32_t aMessageSeqno, IPC::Message::msgid_t aMessageType,
-      mozilla::ipc::Side aSide, mozilla::ipc::MessageDirection aDirection,
-      mozilla::ipc::MessagePhase aPhase, bool aSync) {
-    using namespace mozilla::ipc;
-    // This payload still streams a startTime and endTime property because it
-    // made the migration to MarkerTiming on the front-end easier.
-    mozilla::baseprofiler::WritePropertyTime(aWriter, "startTime", aStart);
-    mozilla::baseprofiler::WritePropertyTime(aWriter, "endTime", aEnd);
-
-    aWriter.IntProperty("otherPid", aOtherPid);
-    aWriter.IntProperty("messageSeqno", aMessageSeqno);
-    aWriter.StringProperty(
-        "messageType",
-        mozilla::MakeStringSpan(IPC::StringFromIPCMessageType(aMessageType)));
-    aWriter.StringProperty("side", IPCSideToString(aSide));
-    aWriter.StringProperty("direction",
-                           aDirection == MessageDirection::eSending
-                               ? mozilla::MakeStringSpan("sending")
-                               : mozilla::MakeStringSpan("receiving"));
-    aWriter.StringProperty("phase", IPCPhaseToString(aPhase));
-    aWriter.BoolProperty("sync", aSync);
-  }
-  static mozilla::MarkerSchema MarkerTypeDisplay() {
-    return mozilla::MarkerSchema::SpecialFrontendLocation{};
-  }
-
- private:
-  static mozilla::Span<const char> IPCSideToString(mozilla::ipc::Side aSide) {
-    switch (aSide) {
-      case mozilla::ipc::ParentSide:
-        return mozilla::MakeStringSpan("parent");
-      case mozilla::ipc::ChildSide:
-        return mozilla::MakeStringSpan("child");
-      case mozilla::ipc::UnknownSide:
-        return mozilla::MakeStringSpan("unknown");
-      default:
-        MOZ_ASSERT_UNREACHABLE("Invalid IPC side");
-        return mozilla::MakeStringSpan("<invalid IPC side>");
-    }
-  }
-
-  static mozilla::Span<const char> IPCPhaseToString(
-      mozilla::ipc::MessagePhase aPhase) {
-    switch (aPhase) {
-      case mozilla::ipc::MessagePhase::Endpoint:
-        return mozilla::MakeStringSpan("endpoint");
-      case mozilla::ipc::MessagePhase::TransferStart:
-        return mozilla::MakeStringSpan("transferStart");
-      case mozilla::ipc::MessagePhase::TransferEnd:
-        return mozilla::MakeStringSpan("transferEnd");
-      default:
-        MOZ_ASSERT_UNREACHABLE("Invalid IPC phase");
-        return mozilla::MakeStringSpan("<invalid IPC phase>");
-    }
-  }
-};
-
-}  // namespace geckoprofiler::markers
-#endif
-
 #endif  // ifndef ipc_glue_MessageChannel_h
--- a/ipc/mscom/ProfilerMarkers.cpp
+++ b/ipc/mscom/ProfilerMarkers.cpp
@@ -18,20 +18,16 @@
 #include "nsIObserverService.h"
 #include "nsISupportsImpl.h"
 #include "nsString.h"
 #include "nsXULAppAPI.h"
 
 #include <objbase.h>
 #include <objidlbase.h>
 
-#ifdef MOZ_GECKO_PROFILER
-#  include "mozilla/ProfilerMarkerTypes.h"
-#endif
-
 // {9DBE6B28-E5E7-4FDE-AF00-9404604E74DC}
 static const GUID GUID_MozProfilerMarkerExtension = {
     0x9dbe6b28, 0xe5e7, 0x4fde, {0xaf, 0x0, 0x94, 0x4, 0x60, 0x4e, 0x74, 0xdc}};
 
 namespace {
 
 class ProfilerMarkerChannelHook final : public IChannelHook {
   ~ProfilerMarkerChannelHook() = default;
@@ -132,35 +128,35 @@ void ProfilerMarkerChannelHook::BuildMar
 }
 
 void ProfilerMarkerChannelHook::ClientGetSize(REFGUID aExtensionId, REFIID aIid,
                                               ULONG* aOutDataSize) {
   if (aExtensionId == GUID_MozProfilerMarkerExtension) {
     if (NS_IsMainThread()) {
       nsAutoCString markerName;
       BuildMarkerName(aIid, markerName);
-      PROFILER_MARKER(markerName, IPC, mozilla::MarkerTiming::IntervalStart(),
-                      Tracing, "MSCOM");
+      PROFILER_TRACING_MARKER("MSCOM", markerName.get(), IPC,
+                              TRACING_INTERVAL_START);
     }
 
     if (aOutDataSize) {
       // We don't add any payload data to the channel
       *aOutDataSize = 0UL;
     }
   }
 }
 
 void ProfilerMarkerChannelHook::ClientNotify(REFGUID aExtensionId, REFIID aIid,
                                              ULONG aDataSize, void* aDataBuffer,
                                              DWORD aDataRep, HRESULT aFault) {
   if (NS_IsMainThread() && aExtensionId == GUID_MozProfilerMarkerExtension) {
     nsAutoCString markerName;
     BuildMarkerName(aIid, markerName);
-    PROFILER_MARKER(markerName, IPC, mozilla::MarkerTiming::IntervalEnd(),
-                    Tracing, "MSCOM");
+    PROFILER_TRACING_MARKER("MSCOM", markerName.get(), IPC,
+                            TRACING_INTERVAL_END);
   }
 }
 
 }  // anonymous namespace
 
 static void RegisterChannelHook() {
   RefPtr<ProfilerMarkerChannelHook> hook(new ProfilerMarkerChannelHook());
   mozilla::DebugOnly<HRESULT> hr =
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -58,16 +58,19 @@
 #include "AccessCheck.h"
 #include "nsGlobalWindow.h"
 #include "nsAboutProtocolUtils.h"
 
 #include "GeckoProfiler.h"
 #include "nsIXULRuntime.h"
 #include "nsJSPrincipals.h"
 #include "ExpandedPrincipal.h"
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
 
 #if defined(XP_LINUX) && !defined(ANDROID)
 // For getrlimit and min/max.
 #  include <algorithm>
 #  include <sys/resource.h>
 #endif
 
 #ifdef XP_WIN
@@ -583,17 +586,19 @@ bool XPCJSContext::InterruptCallback(JSC
   nsDependentCString filename("unknown file");
   JS::AutoFilename scriptFilename;
   // Computing the line number can be very expensive (see bug 1330231 for
   // example), so don't request it here.
   if (JS::DescribeScriptedCaller(cx, &scriptFilename)) {
     if (const char* file = scriptFilename.get()) {
       filename.Assign(file, strlen(file));
     }
-    PROFILER_MARKER_TEXT("JS::InterruptCallback", JS, {}, filename);
+    PROFILER_ADD_MARKER_WITH_PAYLOAD("JS::InterruptCallback", JS,
+                                     TextMarkerPayload,
+                                     (filename, TimeStamp::Now()));
   }
 #endif
 
   // Normally we record mSlowScriptCheckpoint when we start to process an
   // event. However, we can run JS outside of event handlers. This code takes
   // care of that case.
   if (self->mSlowScriptCheckpoint.IsNull()) {
     self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
--- a/layout/base/AutoProfilerStyleMarker.h
+++ b/layout/base/AutoProfilerStyleMarker.h
@@ -3,25 +3,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_AutoProfilerStyleMarker_h
 #define mozilla_AutoProfilerStyleMarker_h
 
 #include "mozilla/Attributes.h"
-#include "mozilla/ProfilerMarkers.h"
 #include "mozilla/ServoTraversalStatistics.h"
 #include "mozilla/TimeStamp.h"
+#include "GeckoProfiler.h"
+#include "ProfilerMarkerPayload.h"
 
 namespace mozilla {
 
 class MOZ_RAII AutoProfilerStyleMarker {
  public:
-  explicit AutoProfilerStyleMarker(UniquePtr<ProfileChunkedBuffer> aCause,
+  explicit AutoProfilerStyleMarker(UniqueProfilerBacktrace aCause,
                                    const Maybe<uint64_t>& aInnerWindowID)
       : mActive(profiler_can_accept_markers()),
         mStartTime(TimeStamp::Now()),
         mCause(std::move(aCause)),
         mInnerWindowID(aInnerWindowID) {
     if (!mActive) {
       return;
     }
@@ -30,65 +31,25 @@ class MOZ_RAII AutoProfilerStyleMarker {
     ServoTraversalStatistics::sSingleton = ServoTraversalStatistics();
     ServoTraversalStatistics::sActive = true;
   }
 
   ~AutoProfilerStyleMarker() {
     if (!mActive) {
       return;
     }
-
-    struct StyleMarker {
-      static constexpr mozilla::Span<const char> MarkerTypeName() {
-        return mozilla::MakeStringSpan("Styles");
-      }
-      static void StreamJSONMarkerData(
-          baseprofiler::SpliceableJSONWriter& aWriter,
-          uint32_t aElementsTraversed, uint32_t aElementsStyled,
-          uint32_t aElementsMatched, uint32_t aStylesShared,
-          uint32_t aStylesReused) {
-        aWriter.IntProperty("elementsTraversed", aElementsTraversed);
-        aWriter.IntProperty("elementsStyled", aElementsStyled);
-        aWriter.IntProperty("elementsMatched", aElementsMatched);
-        aWriter.IntProperty("stylesShared", aStylesShared);
-        aWriter.IntProperty("stylesReused", aStylesReused);
-      }
-      static MarkerSchema MarkerTypeDisplay() {
-        using MS = MarkerSchema;
-        MS schema{MS::Location::markerChart, MS::Location::markerTable,
-                  MS::Location::timelineOverview};
-        schema.AddKeyLabelFormat("elementsTraversed", "Elements traversed",
-                                 MS::Format::integer);
-        schema.AddKeyLabelFormat("elementsStyled", "Elements styled",
-                                 MS::Format::integer);
-        schema.AddKeyLabelFormat("elementsMatched", "Elements matched",
-                                 MS::Format::integer);
-        schema.AddKeyLabelFormat("stylesShared", "Styles shared",
-                                 MS::Format::integer);
-        schema.AddKeyLabelFormat("stylesReused", "Styles reused",
-                                 MS::Format::integer);
-        return schema;
-      }
-    };
-
     ServoTraversalStatistics::sActive = false;
-    profiler_add_marker("Styles", geckoprofiler::category::LAYOUT,
-                        {MarkerTiming::IntervalUntilNowFrom(mStartTime),
-                         MarkerStack::TakeBacktrace(std::move(mCause)),
-                         MarkerInnerWindowId(mInnerWindowID)},
-                        StyleMarker{},
-                        ServoTraversalStatistics::sSingleton.mElementsTraversed,
-                        ServoTraversalStatistics::sSingleton.mElementsStyled,
-                        ServoTraversalStatistics::sSingleton.mElementsMatched,
-                        ServoTraversalStatistics::sSingleton.mStylesShared,
-                        ServoTraversalStatistics::sSingleton.mStylesReused);
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(
+        "Styles", LAYOUT, StyleMarkerPayload,
+        (mStartTime, TimeStamp::Now(), std::move(mCause),
+         ServoTraversalStatistics::sSingleton, mInnerWindowID));
   }
 
  private:
   bool mActive;
   TimeStamp mStartTime;
-  UniquePtr<ProfileChunkedBuffer> mCause;
+  UniqueProfilerBacktrace mCause;
   Maybe<uint64_t> mInnerWindowID;
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_AutoProfilerStyleMarker_h
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -9560,17 +9560,17 @@ bool PresShell::DoReflow(nsIFrame* targe
   }
 
 #ifdef MOZ_GECKO_PROFILER
   Maybe<uint64_t> innerWindowID;
   if (auto* window = mDocument->GetInnerWindow()) {
     innerWindowID = Some(window->WindowID());
   }
   AutoProfilerTracing tracingLayoutFlush(
-      "Paint", "Reflow", geckoprofiler::category::LAYOUT,
+      "Paint", "Reflow", JS::ProfilingCategoryPair::LAYOUT,
       std::move(mReflowCause), innerWindowID);
   mReflowCause = nullptr;
 #endif
 
   FlushPendingScrollAnchorSelections();
 
   if (mReflowContinueTimer) {
     mReflowContinueTimer->Cancel();
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -2856,18 +2856,18 @@ class PresShell final : public nsStubDoc
 
   // Reflow roots that need to be reflowed.
   DirtyRootsList mDirtyRoots;
 
 #ifdef MOZ_GECKO_PROFILER
   // These two fields capture call stacks of any changes that require a restyle
   // or a reflow. Only the first change per restyle / reflow is recorded (the
   // one that caused a call to SetNeedStyleFlush() / SetNeedLayoutFlush()).
-  UniquePtr<ProfileChunkedBuffer> mStyleCause;
-  UniquePtr<ProfileChunkedBuffer> mReflowCause;
+  UniqueProfilerBacktrace mStyleCause;
+  UniqueProfilerBacktrace mReflowCause;
 #endif
 
   nsTArray<UniquePtr<DelayedEvent>> mDelayedEvents;
 
   nsRevocableEventPtr<nsSynthMouseMoveEvent> mSynthMouseMoveEvent;
 
   TouchManager mTouchManager;
 
--- a/layout/base/PresShellInlines.h
+++ b/layout/base/PresShellInlines.h
@@ -18,34 +18,34 @@ void PresShell::SetNeedLayoutFlush() {
   if (dom::Document* doc = mDocument->GetDisplayDocument()) {
     if (PresShell* presShell = doc->GetPresShell()) {
       presShell->mNeedLayoutFlush = true;
     }
   }
 
 #ifdef MOZ_GECKO_PROFILER
   if (!mReflowCause) {
-    mReflowCause = profiler_capture_backtrace();
+    mReflowCause = profiler_get_backtrace();
   }
 #endif
 
   mLayoutTelemetry.IncReqsPerFlush(FlushType::Layout);
 }
 
 void PresShell::SetNeedStyleFlush() {
   mNeedStyleFlush = true;
   if (dom::Document* doc = mDocument->GetDisplayDocument()) {
     if (PresShell* presShell = doc->GetPresShell()) {
       presShell->mNeedStyleFlush = true;
     }
   }
 
 #ifdef MOZ_GECKO_PROFILER
   if (!mStyleCause) {
-    mStyleCause = profiler_capture_backtrace();
+    mStyleCause = profiler_get_backtrace();
   }
 #endif
 
   mLayoutTelemetry.IncReqsPerFlush(FlushType::Layout);
 }
 
 void PresShell::EnsureStyleFlush() {
   SetNeedStyleFlush();
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -124,20 +124,16 @@
 #include "mozilla/dom/SVGTests.h"
 #include "mozilla/SVGGradientFrame.h"
 #include "mozilla/SVGUtils.h"
 
 #include "nsRefreshDriver.h"
 #include "nsTextNode.h"
 #include "ActiveLayerTracker.h"
 
-#ifdef MOZ_GECKO_PROFILER
-#  include "mozilla/ProfilerMarkerTypes.h"
-#endif
-
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // An alias for convenience.
 static const nsIFrame::ChildListID kPrincipalList = nsIFrame::kPrincipalList;
 
 nsIFrame* NS_NewHTMLCanvasFrame(PresShell* aPresShell, ComputedStyle* aStyle);
 
@@ -8193,18 +8189,19 @@ static nsIFrame* FindPreviousNonWhitespa
     f = f->GetPrevSibling();
   } while (f && IsWhitespaceFrame(f));
   return f;
 }
 
 bool nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(
     nsIFrame* aFrame) {
 #define TRACE(reason)                                                       \
-  PROFILER_MARKER("MaybeRecreateContainerForFrameRemoval: " reason, LAYOUT, \
-                  {}, Tracing, "Layout")
+  PROFILER_TRACING_MARKER("Layout",                                         \
+                          "MaybeRecreateContainerForFrameRemoval: " reason, \
+                          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->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)
                               ? aFrame->GetPlaceholderFrame()
                               : aFrame;
@@ -10717,18 +10714,19 @@ bool nsCSSFrameConstructor::MaybeRecreat
     // -moz-column-span-wrapper frame, we need to reframe the multi-column
     // containing block.
     //
     // We can only be here if none of the new inserted nsIContent* nodes (via
     // ContentAppended or ContentRangeInserted) have column-span:all style, yet
     // some of them have column-span:all descendants. Sadly, there's no way to
     // detect this by checking FrameConstructionItems in WipeContainingBlock().
     // Otherwise, we would have already wiped the multi-column containing block.
-    PROFILER_MARKER("Reframe multi-column after constructing frame list",
-                    LAYOUT, {}, Tracing, "Layout");
+    PROFILER_TRACING_MARKER(
+        "Layout", "Reframe multi-column after constructing frame list", LAYOUT,
+        TRACING_EVENT);
 
     // aFrameList can contain placeholder frames. In order to destroy their
     // associated out-of-flow frames properly, we need to manually flush all the
     // out-of-flow frames in aState to their container frames.
     aState.ProcessFrameInsertionsForAllLists();
     aFrameList.DestroyFrames();
     RecreateFramesForContent(
         GetMultiColumnContainingBlockFor(aParentFrame)->GetContent(),
@@ -11043,19 +11041,19 @@ static bool IsSafeToAppendToIBSplitInlin
     aNextSibling = aParentFrame->GetNextSibling();
     aParentFrame = aParentFrame->GetParent();
   } while (IsInlineFrame(aParentFrame));
 
   return true;
 }
 
 bool nsCSSFrameConstructor::WipeInsertionParent(nsContainerFrame* aFrame) {
-#define TRACE(reason)                                                  \
-  PROFILER_MARKER("WipeInsertionParent: " reason, LAYOUT, {}, Tracing, \
-                  "Layout");
+#define TRACE(reason)                                                       \
+  PROFILER_TRACING_MARKER("Layout", "WipeInsertionParent: " reason, LAYOUT, \
+                          TRACING_EVENT)
 
   const LayoutFrameType frameType = aFrame->Type();
 
   // FIXME(emilio): This looks terribly inefficient if you insert elements deep
   // in a MathML subtree.
   if (aFrame->IsFrameOfType(nsIFrame::eMathML)) {
     TRACE("MathML");
     RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
@@ -11105,19 +11103,19 @@ bool nsCSSFrameConstructor::WipeInsertio
 
 #undef TRACE
 }
 
 bool nsCSSFrameConstructor::WipeContainingBlock(
     nsFrameConstructorState& aState, nsIFrame* aContainingBlock,
     nsIFrame* aFrame, FrameConstructionItemList& aItems, bool aIsAppend,
     nsIFrame* aPrevSibling) {
-#define TRACE(reason)                                                  \
-  PROFILER_MARKER("WipeContainingBlock: " reason, LAYOUT, {}, Tracing, \
-                  "Layout");
+#define TRACE(reason)                                                       \
+  PROFILER_TRACING_MARKER("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/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -2061,18 +2061,19 @@ void nsRefreshDriver::Tick(VsyncId aId, 
     // removed, because sometimes observers can be added and removed
     // often depending on what other things are going on and in that
     // situation we don't want to thrash our timer.  So instead we
     // wait until we get a Notify() call when we have no observers
     // before stopping the timer.
     // On top level content pages keep the timer running initially so that we
     // paint the page soon enough.
     if (ShouldKeepTimerRunningWhileWaitingForFirstContentfulPaint()) {
-      PROFILER_MARKER("RefreshDriver waiting for first contentful paint",
-                      GRAPHICS, {}, Tracing, "Paint");
+      PROFILER_TRACING_MARKER(
+          "Paint", "RefreshDriver waiting for first contentful paint", GRAPHICS,
+          TRACING_EVENT);
     } else {
       StopTimer();
     }
     return;
   }
 
   AUTO_PROFILER_LABEL("nsRefreshDriver::Tick", LAYOUT);
 
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -9,16 +9,19 @@
 #include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "SharedPrefMap.h"
 
 #include "base/basictypes.h"
 #include "GeckoProfiler.h"
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
 #include "MainThreadUtils.h"
 #include "mozilla/ArenaAllocatorExtensions.h"
 #include "mozilla/ArenaAllocator.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Components.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/HashFunctions.h"
@@ -4272,106 +4275,51 @@ static nsCString PrefValueToString(const
 #endif
 
 // These preference getter wrappers allow us to look up the value for static
 // preferences based on their native types, rather than manually mapping them to
 // the appropriate Preferences::Get* functions.
 // We define these methods in a struct which is made friend of Preferences in
 // order to access private members.
 struct Internals {
-#ifdef MOZ_GECKO_PROFILER
-  struct PreferenceReadMarker {
-    static constexpr Span<const char> MarkerTypeName() {
-      return MakeStringSpan("PreferenceRead");
-    }
-    static void StreamJSONMarkerData(
-        baseprofiler::SpliceableJSONWriter& aWriter,
-        const ProfilerString8View& aPrefName,
-        const Maybe<PrefValueKind>& aPrefKind, PrefType aPrefType,
-        const ProfilerString8View& aPrefValue) {
-      aWriter.StringProperty("prefName", aPrefName);
-      aWriter.StringProperty("prefKind", PrefValueKindToString(aPrefKind));
-      aWriter.StringProperty("prefType", PrefTypeToString(aPrefType));
-      aWriter.StringProperty("prefValue", aPrefValue);
-    }
-    static MarkerSchema MarkerTypeDisplay() {
-      using MS = MarkerSchema;
-      MS schema{MS::Location::markerChart, MS::Location::markerTable};
-      schema.AddKeyLabelFormat("prefName", "Name", MS::Format::string);
-      schema.AddKeyLabelFormat("prefKind", "Kind", MS::Format::string);
-      schema.AddKeyLabelFormat("prefType", "Type", MS::Format::string);
-      schema.AddKeyLabelFormat("prefValue", "Value", MS::Format::string);
-      return schema;
-    }
-
-   private:
-    static Span<const char> PrefValueKindToString(
-        const Maybe<PrefValueKind>& aKind) {
-      if (aKind) {
-        return *aKind == PrefValueKind::Default ? MakeStringSpan("Default")
-                                                : MakeStringSpan("User");
-      }
-      return "Shared";
-    }
-
-    static Span<const char> PrefTypeToString(PrefType type) {
-      switch (type) {
-        case PrefType::None:
-          return "None";
-        case PrefType::Int:
-          return "Int";
-        case PrefType::Bool:
-          return "Bool";
-        case PrefType::String:
-          return "String";
-        default:
-          MOZ_ASSERT_UNREACHABLE("Unknown preference type.");
-      }
-    }
-  };
-#endif  // MOZ_GECKO_PROFILER
-
   template <typename T>
   static nsresult GetPrefValue(const char* aPrefName, T&& aResult,
                                PrefValueKind aKind) {
     nsresult rv = NS_ERROR_UNEXPECTED;
     NS_ENSURE_TRUE(Preferences::InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
 
     if (Maybe<PrefWrapper> pref = pref_Lookup(aPrefName)) {
       rv = pref->GetValue(aKind, std::forward<T>(aResult));
 
 #ifdef MOZ_GECKO_PROFILER
       if (profiler_feature_active(ProfilerFeature::PreferenceReads)) {
-        profiler_add_marker(
-            "PreferenceRead", baseprofiler::category::OTHER_PreferenceRead, {},
-            PreferenceReadMarker{},
-            ProfilerString8View::WrapNullTerminatedString(aPrefName),
-            Some(aKind), pref->Type(), PrefValueToString(aResult));
+        PROFILER_ADD_MARKER_WITH_PAYLOAD(
+            "PreferenceRead", OTHER_PreferenceRead, PrefMarkerPayload,
+            (aPrefName, Some(aKind), Some(pref->Type()),
+             PrefValueToString(aResult), TimeStamp::Now()));
       }
 #endif
     }
 
     return rv;
   }
 
   template <typename T>
   static nsresult GetSharedPrefValue(const char* aName, T* aResult) {
     nsresult rv = NS_ERROR_UNEXPECTED;
 
     if (Maybe<PrefWrapper> pref = pref_SharedLookup(aName)) {
       rv = pref->GetValue(PrefValueKind::User, aResult);
 
 #ifdef MOZ_GECKO_PROFILER
       if (profiler_feature_active(ProfilerFeature::PreferenceReads)) {
-        profiler_add_marker(
-            "PreferenceRead", baseprofiler::category::OTHER_PreferenceRead, {},
-            PreferenceReadMarker{},
-            ProfilerString8View::WrapNullTerminatedString(aName),
-            Nothing() /* indicates Shared */, pref->Type(),
-            PrefValueToString(aResult));
+        PROFILER_ADD_MARKER_WITH_PAYLOAD(
+            "PreferenceRead", OTHER_PreferenceRead, PrefMarkerPayload,
+            (aName, Nothing() /* indicates Shared */, Some(pref->Type()),
+             PrefValueToString(aResult), TimeStamp::Now()));
       }
 #endif
     }
 
     return rv;
   }
 
   template <typename T>
--- a/mozglue/baseprofiler/core/ProfileBufferEntry.cpp
+++ b/mozglue/baseprofiler/core/ProfileBufferEntry.cpp
@@ -780,30 +780,23 @@ void ProfileBuffer::StreamMarkersToJSON(
                                         double aSinceTime,
                                         UniqueStacks& aUniqueStacks) const {
   mEntries.ReadEach([&](ProfileBufferEntryReader& aER) {
     auto type = static_cast<ProfileBufferEntry::Kind>(
         aER.ReadObject<ProfileBufferEntry::KindUnderlyingType>());
     MOZ_ASSERT(static_cast<ProfileBufferEntry::KindUnderlyingType>(type) <
                static_cast<ProfileBufferEntry::KindUnderlyingType>(
                    ProfileBufferEntry::Kind::MODERN_LIMIT));
-    bool entryWasFullyRead = false;
-
-    if (type == ProfileBufferEntry::Kind::Marker) {
-      entryWasFullyRead = ::mozilla::base_profiler_markers_detail::
-          DeserializeAfterKindAndStream(
-              aER, aWriter, aThreadId,
-              [&](ProfileChunkedBuffer& aChunkedBuffer) {
-                ProfilerBacktrace backtrace("", &aChunkedBuffer);
-                backtrace.StreamJSON(aWriter, TimeStamp::ProcessCreation(),
-                                     aUniqueStacks);
-              });
-    }
-
-    if (!entryWasFullyRead) {
+    if (type != ProfileBufferEntry::Kind::Marker ||
+        !::mozilla::base_profiler_markers_detail::DeserializeAfterKindAndStream(
+            aER, aWriter, aThreadId, [&](ProfileChunkedBuffer& aChunkedBuffer) {
+              ProfilerBacktrace backtrace("", &aChunkedBuffer);
+              backtrace.StreamJSON(aWriter, TimeStamp::ProcessCreation(),
+                                   aUniqueStacks);
+            })) {
       // Not a marker, or marker for another thread.
       // We probably didn't read the whole entry, so we need to skip to the end.
       aER.SetRemainingBytes(0);
     }
   });
 }
 
 void ProfileBuffer::StreamProfilerOverheadToJSON(
--- a/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h
+++ b/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h
@@ -24,17 +24,147 @@
 // existing PROFILER_ADD_MARKER calls. See meta bug 1661394.
 
 #include "mozilla/BaseProfilerMarkers.h"
 
 #ifdef MOZ_GECKO_PROFILER
 
 namespace mozilla::baseprofiler::markers {
 
-struct MediaSampleMarker {
+struct Tracing {
+  static constexpr Span<const char> MarkerTypeName() {
+    return MakeStringSpan("tracing");
+  }
+  static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter,
+                                   const ProfilerString8View& aCategory) {
+    if (aCategory.Length() != 0) {
+      aWriter.StringProperty("category", aCategory);
+    }
+  }
+  static MarkerSchema MarkerTypeDisplay() {
+    using MS = MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable,
+              MS::Location::timelineOverview};
+    schema.AddKeyLabelFormat("category", "Type", MS::Format::string);
+    return schema;
+  }
+};
+
+struct UserTimingMark {
+  static constexpr Span<const char> MarkerTypeName() {
+    return MakeStringSpan("UserTimingMark");
+  }
+  static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter,
+                                   const ProfilerString8View& aName) {
+    aWriter.StringProperty("name", aName);
+    aWriter.StringProperty("entryType", "mark");
+    aWriter.NullProperty("startMark");
+    aWriter.NullProperty("endMark");
+  }
+  static MarkerSchema MarkerTypeDisplay() {
+    using MS = MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable};
+    schema.SetAllLabels("{marker.data.name}");
+    schema.AddStaticLabelValue("Marker", "UserTiming");
+    schema.AddKeyLabelFormat("entryType", "Entry Type", MS::Format::string);
+    schema.AddKeyLabelFormat("name", "Name", MS::Format::string);
+    schema.AddStaticLabelValue(
+        "Description",
+        "UserTimingMark is created using the DOM API performance.mark().");
+    return schema;
+  }
+};
+
+struct UserTimingMeasure {
+  static constexpr Span<const char> MarkerTypeName() {
+    return MakeStringSpan("UserTimingMeasure");
+  }
+  static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter,
+                                   const ProfilerString8View& aName,
+                                   const Maybe<ProfilerString8View>& aStartMark,
+                                   const Maybe<ProfilerString8View>& aEndMark) {
+    aWriter.StringProperty("name", aName);
+    aWriter.StringProperty("entryType", "measure");
+
+    if (aStartMark.isSome()) {
+      aWriter.StringProperty("startMark", *aStartMark);
+    } else {
+      aWriter.NullProperty("startMark");
+    }
+    if (aEndMark.isSome()) {
+      aWriter.StringProperty("endMark", *aEndMark);
+    } else {
+      aWriter.NullProperty("endMark");
+    }
+  }
+  static MarkerSchema MarkerTypeDisplay() {
+    using MS = MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable};
+    schema.SetAllLabels("{marker.data.name}");
+    schema.AddStaticLabelValue("Marker", "UserTiming");
+    schema.AddKeyLabelFormat("entryType", "Entry Type", MS::Format::string);
+    schema.AddKeyLabelFormat("name", "Name", MS::Format::string);
+    schema.AddKeyLabelFormat("startMark", "Start Mark", MS::Format::string);
+    schema.AddKeyLabelFormat("endMark", "End Mark", MS::Format::string);
+    schema.AddStaticLabelValue("Description",
+                               "UserTimingMeasure is created using the DOM API "
+                               "performance.measure().");
+    return schema;
+  }
+};
+
+struct Hang {
+  static constexpr Span<const char> MarkerTypeName() {
+    return MakeStringSpan("BHR-detected hang");
+  }
+  static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter) {}
+  static MarkerSchema MarkerTypeDisplay() {
+    using MS = MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable,
+              MS::Location::timelineOverview};
+    return schema;
+  }
+};
+
+struct LongTask {
+  static constexpr Span<const char> MarkerTypeName() {
+    return MakeStringSpan("MainThreadLongTask");
+  }
+  static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter) {
+    aWriter.StringProperty("category", "LongTask");
+  }
+  static MarkerSchema MarkerTypeDisplay() {
+    using MS = MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable};
+    schema.AddKeyLabelFormat("category", "Type", MS::Format::string);
+    return schema;
+  }
+};
+
+struct Log {
+  static constexpr Span<const char> MarkerTypeName() {
+    return MakeStringSpan("Log");
+  }
+  static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter,
+                                   const ProfilerString8View& aModule,
+                                   const ProfilerString8View& aText) {
+    aWriter.StringProperty("module", aModule);
+    aWriter.StringProperty("name", aText);
+  }
+  static MarkerSchema MarkerTypeDisplay() {
+    using MS = MarkerSchema;
+    MS schema{MS::Location::markerTable};
+    schema.SetTableLabel("({marker.data.module}) {marker.data.name}");
+    schema.AddKeyLabelFormat("module", "Module", MS::Format::string);
+    schema.AddKeyLabelFormat("name", "Name", MS::Format::string);
+    return schema;
+  }
+};
+
+struct MediaSample {
   static constexpr Span<const char> MarkerTypeName() {
     return MakeStringSpan("MediaSample");
   }
   static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter,
                                    int64_t aSampleStartTimeUs,
                                    int64_t aSampleEndTimeUs) {
     aWriter.IntProperty("sampleStartTimeUs", aSampleStartTimeUs);
     aWriter.IntProperty("sampleEndTimeUs", aSampleEndTimeUs);
@@ -45,25 +175,13 @@ struct MediaSampleMarker {
     schema.AddKeyLabelFormat("sampleStartTimeUs", "Sample start time",
                              MS::Format::microseconds);
     schema.AddKeyLabelFormat("sampleEndTimeUs", "Sample end time",
                              MS::Format::microseconds);
     return schema;
   }
 };
 
-struct ContentBuildMarker {
-  static constexpr Span<const char> MarkerTypeName() {
-    return MakeStringSpan("CONTENT_FULL_PAINT_TIME");
-  }
-  static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter) {}
-  static MarkerSchema MarkerTypeDisplay() {
-    using MS = MarkerSchema;
-    MS schema{MS::Location::markerChart, MS::Location::markerTable};
-    return schema;
-  }
-};
-
 }  // namespace mozilla::baseprofiler::markers
 
 #endif  // MOZ_GECKO_PROFILER
 
 #endif  // BaseProfilerMarkerTypes_h
--- a/mozglue/baseprofiler/public/BaseProfilerMarkers.h
+++ b/mozglue/baseprofiler/public/BaseProfilerMarkers.h
@@ -155,35 +155,16 @@ struct Text {
     using MS = MarkerSchema;
     MS schema{MS::Location::markerChart, MS::Location::markerTable};
     schema.SetChartLabel("{marker.name} - {marker.data.name}");
     schema.SetTableLabel("{marker.name} - {marker.data.name}");
     schema.AddKeyLabelFormat("name", "Details", MarkerSchema::Format::string);
     return schema;
   }
 };
-
-struct Tracing {
-  static constexpr Span<const char> MarkerTypeName() {
-    return MakeStringSpan("tracing");
-  }
-  static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter,
-                                   const ProfilerString8View& aCategory) {
-    if (aCategory.Length() != 0) {
-      aWriter.StringProperty("category", aCategory);
-    }
-  }
-  static MarkerSchema MarkerTypeDisplay() {
-    using MS = MarkerSchema;
-    MS schema{MS::Location::markerChart, MS::Location::markerTable,
-              MS::Location::timelineOverview};
-    schema.AddKeyLabelFormat("category", "Type", MS::Format::string);
-    return schema;
-  }
-};
 }  // namespace mozilla::baseprofiler::markers
 
 // Add a text marker. This macro is safe to use even if MOZ_GECKO_PROFILER is
 // not #defined.
 #  define BASE_PROFILER_MARKER_TEXT(markerName, categoryName, options, text) \
     do {                                                                     \
       AUTO_PROFILER_STATS(BASE_PROFILER_MARKER_TEXT);                        \
       ::mozilla::baseprofiler::AddMarker(                                    \
--- a/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h
+++ b/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h
@@ -124,30 +124,29 @@ class MOZ_STACK_CLASS ProfilerStringView
   // Implicit constructor from an expiring string_view. We assume that the
   // pointed-at string will outlive this ProfilerStringView.
   constexpr MOZ_IMPLICIT ProfilerStringView(
       std::basic_string_view<CHAR>&& aStringView)
       : ProfilerStringView(aStringView.data(), aStringView.length(),
                            Ownership::Reference) {}
 
   // Implicit constructor from std::string.
-  constexpr MOZ_IMPLICIT ProfilerStringView(
-      const std::basic_string<CHAR>& aString)
+  constexpr MOZ_IMPLICIT ProfilerStringView(const std::string& aString)
       : ProfilerStringView(aString.data(), aString.length(),
                            Ownership::Reference) {}
 
   // Construction from a raw pointer to a null-terminated string.
   // This is a named class-static function to make it more obvious where work is
   // being done (to determine the string length), and encourage users to instead
   // provide a length, if already known.
   // TODO: Find callers and convert them to constructor instead if possible.
   static constexpr ProfilerStringView WrapNullTerminatedString(
       const CHAR* aString) {
     return ProfilerStringView(
-        aString, aString ? std::char_traits<CHAR>::length(aString) : 0,
+        aString, aString ? std::char_traits<char>::length(aString) : 0,
         Ownership::Reference);
   }
 
   // Implicit constructor for an object with member functions `Data()`
   // `Length()`, and `IsLiteral()`, common in xpcom strings.
   template <
       typename String,
       typename DataReturnType = decltype(std::declval<const String>().Data()),
@@ -178,18 +177,18 @@ class MOZ_STACK_CLASS ProfilerStringView
   [[nodiscard]] constexpr bool IsLiteral() const {
     return mOwnership == Ownership::Literal;
   }
   [[nodiscard]] constexpr bool IsReference() const {
     return mOwnership == Ownership::Reference;
   }
   // No `IsOwned...()` because it's a secret, only used internally!
 
-  [[nodiscard]] operator Span<const CHAR>() const {
-    return Span<const CHAR>(Data(), Length());
+  [[nodiscard]] operator Span<const char>() const {
+    return Span<const char>(Data(), Length());
   }
 
  private:
   enum class Ownership { Literal, Reference, OwnedThroughStringView };
 
   // Allow deserializer to store anything here.
   friend ProfileBufferEntryReader::Deserializer<ProfilerStringView>;
 
--- a/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h
+++ b/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h
@@ -65,18 +65,21 @@ enum class ProfileBufferEntryKind : Prof
 #undef KIND
 
   // Any value under `LEGACY_LIMIT` represents a `ProfileBufferEntry`.
   LEGACY_LIMIT,
 
   // Any value starting here does *not* represent a `ProfileBufferEntry` and
   // requires separate decoding and handling.
 
-  // Markers and their data.
-  Marker = LEGACY_LIMIT,
+  // Marker data, including payload.
+  MarkerData = LEGACY_LIMIT,
+
+  // Markers from 2.0 specs.
+  Marker,
 
   // Optional between TimeBeforeCompactStack and CompactStack.
   UnresponsiveDurationMs,
 
   // Collection of legacy stack entries, must follow a ThreadId and
   // TimeBeforeCompactStack (which are not included in the CompactStack;
   // TimeBeforeCompactStack is equivalent to Time, but indicates that a
   // CompactStack follows shortly afterwards).
--- a/mozglue/baseprofiler/public/ProfileBufferEntrySerialization.h
+++ b/mozglue/baseprofiler/public/ProfileBufferEntrySerialization.h
@@ -744,25 +744,24 @@ struct ProfileBufferEntryWriter::Seriali
     aEW.WriteBytes(&aWrapper.mRawPointer, sizeof(aWrapper.mRawPointer));
   }
 };
 
 // Usage: `aER.ReadObject<Foo*>;` or `Foo* p; aER.ReadIntoObject(p);`, no
 // wrapper necessary.
 template <typename T>
 struct ProfileBufferEntryReader::Deserializer<ProfileBufferRawPointer<T>> {
-  static void ReadInto(ProfileBufferEntryReader& aER,
-                       ProfileBufferRawPointer<T>& aPtr) {
-    aER.ReadBytes(&aPtr.mRawPointer, sizeof(aPtr));
+  static void ReadInto(ProfileBufferEntryReader& aER, T*& aPtr) {
+    aER.ReadBytes(&aPtr, sizeof(aPtr));
   }
 
-  static ProfileBufferRawPointer<T> Read(ProfileBufferEntryReader& aER) {
-    ProfileBufferRawPointer<T> rawPointer;
-    ReadInto(aER, rawPointer);
-    return rawPointer;
+  static T* Read(ProfileBufferEntryReader& aER) {
+    T* ptr;
+    ReadInto(aER, ptr);
+    return ptr;
   }
 };
 
 // ----------------------------------------------------------------------------
 // std::string contents
 
 // std::string contents are serialized as the number of characters (encoded as
 // ULEB128) and all the characters in the string. The terminal '\0' is omitted.
--- a/mozglue/tests/TestBaseProfiler.cpp
+++ b/mozglue/tests/TestBaseProfiler.cpp
@@ -3009,237 +3009,207 @@ void TestLiteralEmptyStringView() {
   static_assert(mozilla::LiteralEmptyStringView<char16_t>() ==
                 std::basic_string_view<char16_t>(u""));
   static_assert(!!mozilla::LiteralEmptyStringView<char16_t>().data());
   static_assert(mozilla::LiteralEmptyStringView<char16_t>().length() == 0);
 
   printf("TestLiteralEmptyStringView done\n");
 }
 
-template <typename CHAR>
 void TestProfilerStringView() {
-  if constexpr (std::is_same_v<CHAR, char>) {
-    printf("TestProfilerStringView<char>...\n");
-  } else if constexpr (std::is_same_v<CHAR, char16_t>) {
-    printf("TestProfilerStringView<char16_t>...\n");
-  } else {
-    MOZ_RELEASE_ASSERT(false,
-                       "TestProfilerStringView only handles char and char16_t");
-  }
+  printf("TestProfilerStringView...\n");
 
   // Used to verify implicit constructions, as this will normally be used in
   // function parameters.
-  auto BSV = [](mozilla::ProfilerStringView<CHAR>&& aBSV) {
-    return std::move(aBSV);
-  };
-
-  // These look like string literals, as expected by some string constructors.
-  const CHAR empty[0 + 1] = {CHAR('\0')};
-  const CHAR hi[2 + 1] = {
-      CHAR('h'),
-      CHAR('i'),
-      CHAR('\0'),
+  auto BS8V = [](mozilla::ProfilerString8View&& aBS8V) {
+    return std::move(aBS8V);
   };
 
   // Literal empty string.
-  MOZ_RELEASE_ASSERT(BSV(empty).Data());
-  MOZ_RELEASE_ASSERT(BSV(empty).Data()[0] == CHAR('\0'));
-  MOZ_RELEASE_ASSERT(BSV(empty).Length() == 0);
-  MOZ_RELEASE_ASSERT(BSV(empty).IsLiteral());
-  MOZ_RELEASE_ASSERT(!BSV(empty).IsReference());
+  MOZ_RELEASE_ASSERT(BS8V("").Data());
+  MOZ_RELEASE_ASSERT(BS8V("").Data()[0] == '\0');
+  MOZ_RELEASE_ASSERT(BS8V("").Length() == 0);
+  MOZ_RELEASE_ASSERT(BS8V("").IsLiteral());
+  MOZ_RELEASE_ASSERT(!BS8V("").IsReference());
 
   // Literal non-empty string.
-  MOZ_RELEASE_ASSERT(BSV(hi).Data());
-  MOZ_RELEASE_ASSERT(BSV(hi).Data()[0] == CHAR('h'));
-  MOZ_RELEASE_ASSERT(BSV(hi).Data()[1] == CHAR('i'));
-  MOZ_RELEASE_ASSERT(BSV(hi).Data()[2] == CHAR('\0'));
-  MOZ_RELEASE_ASSERT(BSV(hi).Length() == 2);
-  MOZ_RELEASE_ASSERT(BSV(hi).IsLiteral());
-  MOZ_RELEASE_ASSERT(!BSV(hi).IsReference());
+  MOZ_RELEASE_ASSERT(BS8V("hi").Data());
+  MOZ_RELEASE_ASSERT(BS8V("hi").Data()[0] == 'h');
+  MOZ_RELEASE_ASSERT(BS8V("hi").Data()[1] == 'i');
+  MOZ_RELEASE_ASSERT(BS8V("hi").Data()[2] == '\0');
+  MOZ_RELEASE_ASSERT(BS8V("hi").Length() == 2);
+  MOZ_RELEASE_ASSERT(BS8V("hi").IsLiteral());
+  MOZ_RELEASE_ASSERT(!BS8V("hi").IsReference());
 
   // std::string_view to a literal empty string.
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(empty)).Data());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(empty)).Data()[0] ==
-                     CHAR('\0'));
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(empty)).Length() == 0);
-  MOZ_RELEASE_ASSERT(!BSV(std::basic_string_view<CHAR>(empty)).IsLiteral());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(empty)).IsReference());
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view("")).Data());
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view("")).Data()[0] == '\0');
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view("")).Length() == 0);
+  MOZ_RELEASE_ASSERT(!BS8V(std::string_view("")).IsLiteral());
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view("")).IsReference());
 
   // std::string_view to a literal non-empty string.
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).Data());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).Data()[0] ==
-                     CHAR('h'));
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).Data()[1] ==
-                     CHAR('i'));
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).Data()[2] ==
-                     CHAR('\0'));
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).Length() == 2);
-  MOZ_RELEASE_ASSERT(!BSV(std::basic_string_view<CHAR>(hi)).IsLiteral());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).IsReference());
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Data());
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Data()[0] == 'h');
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Data()[1] == 'i');
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Data()[2] == '\0');
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Length() == 2);
+  MOZ_RELEASE_ASSERT(!BS8V(std::string_view("hi")).IsLiteral());
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).IsReference());
 
   // Default std::string_view points at nullptr, ProfilerStringView converts it
   // to the literal empty string.
-  MOZ_RELEASE_ASSERT(!std::basic_string_view<CHAR>().data());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>()).Data());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>()).Data()[0] ==
-                     CHAR('\0'));
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>()).Length() == 0);
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>()).IsLiteral());
-  MOZ_RELEASE_ASSERT(!BSV(std::basic_string_view<CHAR>()).IsReference());
+  MOZ_RELEASE_ASSERT(!std::string_view().data());
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view()).Data());
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view()).Data()[0] == '\0');
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view()).Length() == 0);
+  MOZ_RELEASE_ASSERT(BS8V(std::string_view()).IsLiteral());
+  MOZ_RELEASE_ASSERT(!BS8V(std::string_view()).IsReference());
 
   // std::string to a literal empty string.
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(empty)).Data());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(empty)).Data()[0] ==
-                     CHAR('\0'));
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(empty)).Length() == 0);
-  MOZ_RELEASE_ASSERT(!BSV(std::basic_string<CHAR>(empty)).IsLiteral());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(empty)).IsReference());
+  MOZ_RELEASE_ASSERT(BS8V(std::string("")).Data());
+  MOZ_RELEASE_ASSERT(BS8V(std::string("")).Data()[0] == '\0');
+  MOZ_RELEASE_ASSERT(BS8V(std::string("")).Length() == 0);
+  MOZ_RELEASE_ASSERT(!BS8V(std::string("")).IsLiteral());
+  MOZ_RELEASE_ASSERT(BS8V(std::string("")).IsReference());
 
   // std::string to a literal non-empty string.
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).Data());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).Data()[0] == CHAR('h'));
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).Data()[1] == CHAR('i'));
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).Data()[2] == CHAR('\0'));
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).Length() == 2);
-  MOZ_RELEASE_ASSERT(!BSV(std::basic_string<CHAR>(hi)).IsLiteral());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).IsReference());
-
-  // Default std::string contains an empty null-terminated string.
-  MOZ_RELEASE_ASSERT(std::basic_string<CHAR>().data());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>()).Data());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>()).Data()[0] == CHAR('\0'));
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>()).Length() == 0);
-  MOZ_RELEASE_ASSERT(!BSV(std::basic_string<CHAR>()).IsLiteral());
-  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>()).IsReference());
-
-  // Class that quacks like nsTString (with Data(), Length(), IsLiteral()), to
-  // check that ProfilerStringView can read from them.
-  class FakeNsTString {
+  MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data());
+  MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()[0] == 'h');
+  MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()[1] == 'i');
+  MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()[2] == '\0');
+  MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Length() == 2);
+  MOZ_RELEASE_ASSERT(!BS8V(std::string("hi")).IsLiteral());
+  MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).IsReference());
+
+  // Default std::string_view contains an empty null-terminated string.
+  MOZ_RELEASE_ASSERT(std::string().data());
+  MOZ_RELEASE_ASSERT(BS8V(std::string()).Data());
+  MOZ_RELEASE_ASSERT(BS8V(std::string()).Data()[0] == '\0');
+  MOZ_RELEASE_ASSERT(BS8V(std::string()).Length() == 0);
+  MOZ_RELEASE_ASSERT(!BS8V(std::string()).IsLiteral());
+  MOZ_RELEASE_ASSERT(BS8V(std::string()).IsReference());
+
+  class FakeNsCString {
    public:
-    FakeNsTString(const CHAR* aData, size_t aLength, bool aIsLiteral)
+    FakeNsCString(const char* aData, size_t aLength, bool aIsLiteral)
         : mData(aData), mLength(aLength), mIsLiteral(aIsLiteral) {}
 
-    const CHAR* Data() const { return mData; }
+    const char* Data() const { return mData; }
     size_t Length() const { return mLength; }
     bool IsLiteral() const { return mIsLiteral; }
 
    private:
-    const CHAR* mData;
+    const char* mData;
     size_t mLength;
     bool mIsLiteral;
   };
 
-  // FakeNsTString to nullptr.
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).Data());
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).Data()[0] ==
-                     CHAR('\0'));
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).Length() == 0);
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).IsLiteral());
-  MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(nullptr, 0, true)).IsReference());
-
-  // FakeNsTString to a literal empty string.
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).Data());
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).Data()[0] ==
-                     CHAR('\0'));
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).Length() == 0);
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).IsLiteral());
-  MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(empty, 0, true)).IsReference());
-
-  // FakeNsTString to a literal non-empty string.
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Data());
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Data()[0] == CHAR('h'));
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Data()[1] == CHAR('i'));
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Data()[2] == CHAR('\0'));
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Length() == 2);
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).IsLiteral());
-  MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(hi, 2, true)).IsReference());
-
-  // FakeNsTString to a non-literal non-empty string.
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Data());
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Data()[0] == CHAR('h'));
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Data()[1] == CHAR('i'));
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Data()[2] == CHAR('\0'));
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Length() == 2);
-  MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(hi, 2, false)).IsLiteral());
-  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).IsReference());
+  // FakeNsCString to nullptr.
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).Data());
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).Data()[0] == '\0');
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).Length() == 0);
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).IsLiteral());
+  MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString(nullptr, 0, true)).IsReference());
+
+  // FakeNsCString to a literal empty string.
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).Data());
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).Data()[0] == '\0');
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).Length() == 0);
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).IsLiteral());
+  MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString("", 0, true)).IsReference());
+
+  // FakeNsCString to a literal non-empty string.
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data());
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()[0] == 'h');
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()[1] == 'i');
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()[2] == '\0');
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Length() == 2);
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).IsLiteral());
+  MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString("hi", 2, true)).IsReference());
+
+  // FakeNsCString to a non-literal non-empty string.
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data());
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()[0] == 'h');
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()[1] == 'i');
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()[2] == '\0');
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Length() == 2);
+  MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString("hi", 2, false)).IsLiteral());
+  MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).IsReference());
 
   // Serialization and deserialization (with ownership).
   constexpr size_t bufferMaxSize = 1024;
   constexpr ProfileChunkedBuffer::Length chunkMinSize = 128;
   ProfileBufferChunkManagerWithLocalLimit cm(bufferMaxSize, chunkMinSize);
   ProfileChunkedBuffer cb(ProfileChunkedBuffer::ThreadSafety::WithMutex, cm);
 
   // Literal string, serialized as raw pointer.
-  MOZ_RELEASE_ASSERT(cb.PutObject(BSV(hi)));
+  MOZ_RELEASE_ASSERT(cb.PutObject(BS8V("hi")));
   {
     unsigned read = 0;
-    ProfilerStringView<CHAR> outerBSV;
+    ProfilerString8View outerBS8V;
     cb.ReadEach([&](ProfileBufferEntryReader& aER) {
       ++read;
-      auto bsv = aER.ReadObject<ProfilerStringView<CHAR>>();
-      MOZ_RELEASE_ASSERT(bsv.Data());
-      MOZ_RELEASE_ASSERT(bsv.Data()[0] == CHAR('h'));
-      MOZ_RELEASE_ASSERT(bsv.Data()[1] == CHAR('i'));
-      MOZ_RELEASE_ASSERT(bsv.Data()[2] == CHAR('\0'));
-      MOZ_RELEASE_ASSERT(bsv.Length() == 2);
-      MOZ_RELEASE_ASSERT(bsv.IsLiteral());
-      MOZ_RELEASE_ASSERT(!bsv.IsReference());
-      outerBSV = std::move(bsv);
+      auto bs8v = aER.ReadObject<ProfilerString8View>();
+      MOZ_RELEASE_ASSERT(bs8v.Data());
+      MOZ_RELEASE_ASSERT(bs8v.Data()[0] == 'h');
+      MOZ_RELEASE_ASSERT(bs8v.Data()[1] == 'i');
+      MOZ_RELEASE_ASSERT(bs8v.Data()[2] == '\0');
+      MOZ_RELEASE_ASSERT(bs8v.Length() == 2);
+      MOZ_RELEASE_ASSERT(bs8v.IsLiteral());
+      MOZ_RELEASE_ASSERT(!bs8v.IsReference());
+      outerBS8V = std::move(bs8v);
     });
     MOZ_RELEASE_ASSERT(read == 1);
-    MOZ_RELEASE_ASSERT(outerBSV.Data());
-    MOZ_RELEASE_ASSERT(outerBSV.Data()[0] == CHAR('h'));
-    MOZ_RELEASE_ASSERT(outerBSV.Data()[1] == CHAR('i'));
-    MOZ_RELEASE_ASSERT(outerBSV.Data()[2] == CHAR('\0'));
-    MOZ_RELEASE_ASSERT(outerBSV.Length() == 2);
-    MOZ_RELEASE_ASSERT(outerBSV.IsLiteral());
-    MOZ_RELEASE_ASSERT(!outerBSV.IsReference());
+    MOZ_RELEASE_ASSERT(outerBS8V.Data());
+    MOZ_RELEASE_ASSERT(outerBS8V.Data()[0] == 'h');
+    MOZ_RELEASE_ASSERT(outerBS8V.Data()[1] == 'i');
+    MOZ_RELEASE_ASSERT(outerBS8V.Data()[2] == '\0');
+    MOZ_RELEASE_ASSERT(outerBS8V.Length() == 2);
+    MOZ_RELEASE_ASSERT(outerBS8V.IsLiteral());
+    MOZ_RELEASE_ASSERT(!outerBS8V.IsReference());
   }
 
   cb.Clear();
 
   // Non-literal string, content is serialized.
-  std::basic_string<CHAR> hiString(hi);
-  MOZ_RELEASE_ASSERT(cb.PutObject(BSV(hiString)));
+  std::string hiString("hi");
+  MOZ_RELEASE_ASSERT(cb.PutObject(BS8V(hiString)));
   {
     unsigned read = 0;
-    ProfilerStringView<CHAR> outerBSV;
+    ProfilerString8View outerBS8V;
     cb.ReadEach([&](ProfileBufferEntryReader& aER) {
       ++read;
-      auto bsv = aER.ReadObject<ProfilerStringView<CHAR>>();
-      MOZ_RELEASE_ASSERT(bsv.Data());
-      MOZ_RELEASE_ASSERT(bsv.Data() != hiString.data());
-      MOZ_RELEASE_ASSERT(bsv.Data()[0] == CHAR('h'));
-      MOZ_RELEASE_ASSERT(bsv.Data()[1] == CHAR('i'));
-      MOZ_RELEASE_ASSERT(bsv.Data()[2] == CHAR('\0'));
-      MOZ_RELEASE_ASSERT(bsv.Length() == 2);
+      auto bs8v = aER.ReadObject<ProfilerString8View>();
+      MOZ_RELEASE_ASSERT(bs8v.Data());
+      MOZ_RELEASE_ASSERT(bs8v.Data() != hiString.data());
+      MOZ_RELEASE_ASSERT(bs8v.Data()[0] == 'h');
+      MOZ_RELEASE_ASSERT(bs8v.Data()[1] == 'i');
+      MOZ_RELEASE_ASSERT(bs8v.Data()[2] == '\0');
+      MOZ_RELEASE_ASSERT(bs8v.Length() == 2);
       // Special ownership case, neither a literal nor a reference!
-      MOZ_RELEASE_ASSERT(!bsv.IsLiteral());
-      MOZ_RELEASE_ASSERT(!bsv.IsReference());
+      MOZ_RELEASE_ASSERT(!bs8v.IsLiteral());
+      MOZ_RELEASE_ASSERT(!bs8v.IsReference());
       // Test move of ownership.
-      outerBSV = std::move(bsv);
+      outerBS8V = std::move(bs8v);
       // NOLINTNEXTLINE(bugprone-use-after-move, clang-analyzer-cplusplus.Move)
-      MOZ_RELEASE_ASSERT(bsv.Length() == 0);
+      MOZ_RELEASE_ASSERT(bs8v.Length() == 0);
     });
     MOZ_RELEASE_ASSERT(read == 1);
-    MOZ_RELEASE_ASSERT(outerBSV.Data());
-    MOZ_RELEASE_ASSERT(outerBSV.Data() != hiString.data());
-    MOZ_RELEASE_ASSERT(outerBSV.Data()[0] == CHAR('h'));
-    MOZ_RELEASE_ASSERT(outerBSV.Data()[1] == CHAR('i'));
-    MOZ_RELEASE_ASSERT(outerBSV.Data()[2] == CHAR('\0'));
-    MOZ_RELEASE_ASSERT(outerBSV.Length() == 2);
-    MOZ_RELEASE_ASSERT(!outerBSV.IsLiteral());
-    MOZ_RELEASE_ASSERT(!outerBSV.IsReference());
+    MOZ_RELEASE_ASSERT(outerBS8V.Data());
+    MOZ_RELEASE_ASSERT(outerBS8V.Data() != hiString.data());
+    MOZ_RELEASE_ASSERT(outerBS8V.Data()[0] == 'h');
+    MOZ_RELEASE_ASSERT(outerBS8V.Data()[1] == 'i');
+    MOZ_RELEASE_ASSERT(outerBS8V.Data()[2] == '\0');
+    MOZ_RELEASE_ASSERT(outerBS8V.Length() == 2);
+    MOZ_RELEASE_ASSERT(!outerBS8V.IsLiteral());
+    MOZ_RELEASE_ASSERT(!outerBS8V.IsReference());
   }
 
-  if constexpr (std::is_same_v<CHAR, char>) {
-    printf("TestProfilerStringView<char> done\n");
-  } else if constexpr (std::is_same_v<CHAR, char16_t>) {
-    printf("TestProfilerStringView<char16_t> done\n");
-  }
+  printf("TestProfilerStringView done\n");
 }
 
 void TestProfilerDependencies() {
   TestPowerOfTwoMask();
   TestPowerOfTwo();
   TestLEB128();
   TestChunk();
   TestChunkManagerSingle();
@@ -3249,18 +3219,17 @@ void TestProfilerDependencies() {
   TestChunkedBuffer();
   TestChunkedBufferSingle();
   TestModuloBuffer();
   TestBlocksRingBufferAPI();
   TestBlocksRingBufferUnderlyingBufferChanges();
   TestBlocksRingBufferThreading();
   TestBlocksRingBufferSerialization();
   TestLiteralEmptyStringView();
-  TestProfilerStringView<char>();
-  TestProfilerStringView<char16_t>();
+  TestProfilerStringView();
 }
 
 // Increase the depth, to a maximum (to avoid too-deep recursion).
 static constexpr size_t NextDepth(size_t aDepth) {
   constexpr size_t MAX_DEPTH = 128;
   return (aDepth < MAX_DEPTH) ? (aDepth + 1) : aDepth;
 }
 
@@ -3466,22 +3435,43 @@ void TestProfiler() {
         mozilla::baseprofiler::category::OTHER, MarkerInnerWindowId(123),
         ::mozilla::baseprofiler::markers::NoPayload{}));
 
     MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
         "tracing", mozilla::baseprofiler::category::OTHER, {},
         mozilla::baseprofiler::markers::Tracing{}, "category"));
 
     MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
+        "mark", mozilla::baseprofiler::category::OTHER, {},
+        mozilla::baseprofiler::markers::UserTimingMark{}, "mark name"));
+
+    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
+        "measure", mozilla::baseprofiler::category::OTHER, {},
+        mozilla::baseprofiler::markers::UserTimingMeasure{}, "measure name",
+        Some(ProfilerString8View("start")), Some(ProfilerString8View("end"))));
+
+    MOZ_RELEASE_ASSERT(
+        baseprofiler::AddMarker("hang", mozilla::baseprofiler::category::OTHER,
+                                {}, mozilla::baseprofiler::markers::Hang{}));
+
+    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
+        "longtask", mozilla::baseprofiler::category::OTHER, {},
+        mozilla::baseprofiler::markers::LongTask{}));
+
+    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
         "text", mozilla::baseprofiler::category::OTHER, {},
         mozilla::baseprofiler::markers::Text{}, "text text"));
 
     MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
+        "log", mozilla::baseprofiler::category::OTHER, {},
+        mozilla::baseprofiler::markers::Log{}, "module", "text"));
+
+    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
         "media sample", mozilla::baseprofiler::category::OTHER, {},
-        mozilla::baseprofiler::markers::MediaSampleMarker{}, 123, 456));
+        mozilla::baseprofiler::markers::MediaSample{}, 123, 456));
 
     printf("Sleep 1s...\n");
     {
       AUTO_BASE_PROFILER_THREAD_SLEEP;
       SleepMilli(1000);
     }
 
     printf("baseprofiler_pause()...\n");
@@ -3527,17 +3517,26 @@ void TestProfiler() {
     std::string_view profileSV = profile.get();
 
     constexpr const auto svnpos = std::string_view::npos;
     // TODO: Properly parse profile and check fields.
     // Check for some expected marker schema JSON output.
     MOZ_RELEASE_ASSERT(profileSV.find("\"markerSchema\": [") != svnpos);
     MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"Text\",") != svnpos);
     MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"tracing\",") != svnpos);
+    MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"UserTimingMark\",") !=
+                       svnpos);
+    MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"UserTimingMeasure\",") !=
+                       svnpos);
+    MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"BHR-detected hang\",") !=
+                       svnpos);
+    MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"Log\",") != svnpos);
     MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"MediaSample\",") != svnpos);
+    MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"MainThreadLongTask\",") !=
+                       svnpos);
     MOZ_RELEASE_ASSERT(profileSV.find("\"display\": [") != svnpos);
     MOZ_RELEASE_ASSERT(profileSV.find("\"marker-chart\"") != svnpos);
     MOZ_RELEASE_ASSERT(profileSV.find("\"marker-table\"") != svnpos);
     MOZ_RELEASE_ASSERT(profileSV.find("\"format\": \"string\"") != svnpos);
     // TODO: Add more checks for what's expected in the profile. Some of them
     // are done in gtest's.
 
     printf("baseprofiler_save_profile_to_file()...\n");
@@ -4031,22 +4030,46 @@ void TestPredefinedMarkers() {
       mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager);
 
   MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
       buffer, std::string_view("tracing"),
       mozilla::baseprofiler::category::OTHER, {},
       mozilla::baseprofiler::markers::Tracing{}, "category"));
 
   MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
+      buffer, std::string_view("mark"), mozilla::baseprofiler::category::OTHER,
+      {}, mozilla::baseprofiler::markers::UserTimingMark{}, "mark name"));
+
+  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
+      buffer, std::string_view("measure"),
+      mozilla::baseprofiler::category::OTHER, {},
+      mozilla::baseprofiler::markers::UserTimingMeasure{}, "measure name ",
+      mozilla::Some(mozilla::ProfilerString8View(" start ")),
+      mozilla::Some(mozilla::ProfilerString8View("end"))));
+
+  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
+      buffer, std::string_view("hang"), mozilla::baseprofiler::category::OTHER,
+      {}, mozilla::baseprofiler::markers::Hang{}));
+
+  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
+      buffer, std::string_view("long task"),
+      mozilla::baseprofiler::category::OTHER, {},
+      mozilla::baseprofiler::markers::LongTask{}));
+
+  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
       buffer, std::string_view("text"), mozilla::baseprofiler::category::OTHER,
       {}, mozilla::baseprofiler::markers::Text{}, "text text"));
 
   MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
+      buffer, std::string_view("log"), mozilla::baseprofiler::category::OTHER,
+      {}, mozilla::baseprofiler::markers::Log{}, "module", "text"));
+
+  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
       buffer, std::string_view("media"), mozilla::baseprofiler::category::OTHER,
-      {}, mozilla::baseprofiler::markers::MediaSampleMarker{}, 123, 456));
+      {}, mozilla::baseprofiler::markers::MediaSample{}, 123, 456));
 
 #  ifdef DEBUG
   buffer.Dump();
 #  endif
 
   PrintMarkers(buffer);
 
   printf("TestPredefinedMarkers done\n");
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -243,17 +243,17 @@ class HttpBaseChannel : public nsHashPro
   NS_IMETHOD GetTopLevelOuterContentWindowId(uint64_t* aWindowId) override;
   NS_IMETHOD SetTopLevelOuterContentWindowId(uint64_t aWindowId) override;
 
   NS_IMETHOD GetFlashPluginState(
       nsIHttpChannel::FlashPluginState* aState) override;
 
   using nsIClassifiedChannel::IsThirdPartyTrackingResource;
 
-  virtual void SetSource(UniquePtr<ProfileChunkedBuffer> aSource) override {
+  virtual void SetSource(UniqueProfilerBacktrace aSource) override {
     mSource = std::move(aSource);
   }
 
   // nsIHttpChannelInternal
   NS_IMETHOD GetDocumentURI(nsIURI** aDocumentURI) override;
   NS_IMETHOD SetDocumentURI(nsIURI* aDocumentURI) override;
   NS_IMETHOD GetRequestVersion(uint32_t* major, uint32_t* minor) override;
   NS_IMETHOD GetResponseVersion(uint32_t* major, uint32_t* minor) override;
@@ -751,17 +751,17 @@ class HttpBaseChannel : public nsHashPro
 
   // Use Release-Acquire ordering to ensure the OMT ODA is ignored while channel
   // is canceled on main thread.
   Atomic<bool, ReleaseAcquire> mCanceled;
   Atomic<uint32_t, ReleaseAcquire> mFirstPartyClassificationFlags;
   Atomic<uint32_t, ReleaseAcquire> mThirdPartyClassificationFlags;
   Atomic<uint32_t, ReleaseAcquire> mFlashPluginState;
 
-  UniquePtr<ProfileChunkedBuffer> mSource;
+  UniqueProfilerBacktrace mSource;
 
   uint32_t mLoadFlags;
   uint32_t mCaps;
   uint32_t mClassOfService;
 
   uint32_t mUpgradeToSecure : 1;
   uint32_t mApplyConversion : 1;
   // Set to true if DoApplyContentConversions has been applied to
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -62,16 +62,20 @@
 #include "nsApplicationCache.h"
 #include "ClassifierDummyChannel.h"
 #include "nsIOService.h"
 
 #ifdef MOZ_TASK_TRACER
 #  include "GeckoTaskTracer.h"
 #endif
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
+
 #include <functional>
 
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace net {
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -130,16 +130,20 @@
 #include "js/Conversions.h"
 #include "mozilla/dom/SecFetch.h"
 #include "mozilla/net/TRRService.h"
 
 #ifdef MOZ_TASK_TRACER
 #  include "GeckoTaskTracer.h"
 #endif
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
+
 namespace mozilla {
 
 using namespace dom;
 
 namespace net {
 
 namespace {
 
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -496,11 +496,11 @@ interface nsIHttpChannel : nsIIdentChann
     void logBlockedCORSRequest(in AString aMessage, in ACString aCategory);
 
     void logMimeTypeMismatch(in ACString aMessageName,
                              in boolean aWarning,
                              in AString aURL,
                              in AString aContentType);
 
 %{ C++
-  virtual void SetSource(mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource) {}
+  virtual void SetSource(UniqueProfilerBacktrace aSource) {}
 %}
 };
--- a/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
+++ b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
@@ -26,16 +26,20 @@
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "prinrval.h"
 #include "prthread.h"
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
+
 #include <algorithm>
 
 // Activate BHR only for one every BHR_BETA_MOD users.
 // We're doing experimentation with collecting a lot more data from BHR, and
 // don't want to enable it for beta users at the moment. We can scale this up in
 // the future.
 #define BHR_BETA_MOD INT32_MAX;
 
@@ -525,36 +529,22 @@ void BackgroundHangThread::ReportHang(Ti
     RefPtr<nsHangDetails> hd =
         new nsHangDetails(std::move(hangDetails), persistedToDisk);
     hd->Submit();
   }
 
   // If the profiler is enabled, add a marker.
 #ifdef MOZ_GECKO_PROFILER
   if (profiler_can_accept_markers()) {
-    struct HangMarker {
-      static constexpr Span<const char> MarkerTypeName() {
-        return MakeStringSpan("BHR-detected hang");
-      }
-      static void StreamJSONMarkerData(
-          baseprofiler::SpliceableJSONWriter& aWriter) {}
-      static MarkerSchema MarkerTypeDisplay() {
-        using MS = MarkerSchema;
-        MS schema{MS::Location::markerChart, MS::Location::markerTable,
-                  MS::Location::timelineOverview};
-        return schema;
-      }
-    };
-
-    const TimeStamp endTime = TimeStamp::NowUnfuzzed();
-    const TimeStamp startTime = endTime - aHangTime;
-    profiler_add_marker("BHR-detected hang", geckoprofiler::category::OTHER,
-                        {MarkerThreadId(mStackHelper.GetThreadId()),
-                         MarkerTiming::Interval(startTime, endTime)},
-                        HangMarker{});
+    TimeStamp endTime = TimeStamp::Now();
+    TimeStamp startTime = endTime - aHangTime;
+    AUTO_PROFILER_STATS(add_marker_with_HangMarkerPayload);
+    profiler_add_marker_for_thread(
+        mStackHelper.GetThreadId(), JS::ProfilingCategoryPair::OTHER,
+        "BHR-detected hang", HangMarkerPayload(startTime, endTime));
   }
 #endif
 }
 
 void BackgroundHangThread::ReportPermaHang() {
   // Permanently hanged; called on the monitor thread
   // mManager->mLock IS locked
 
--- a/tools/profiler/core/ProfileBufferEntry.cpp
+++ b/tools/profiler/core/ProfileBufferEntry.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ProfileBufferEntry.h"
 
 #include "mozilla/ProfilerMarkers.h"
 #include "platform.h"
 #include "ProfileBuffer.h"
 #include "ProfilerBacktrace.h"
+#include "ProfilerMarkerPayload.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/StackWalk.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
@@ -630,16 +631,17 @@ class EntryGetter {
 //         /* internally including:
 //           ( NativeLeafAddr
 //           | Label FrameFlags? DynamicStringFragment*
 //             LineNumber? CategoryPair?
 //           | JitReturnAddr
 //           )+
 //         */
 //   )
+//   | MarkerData
 //   | Marker
 //   | ( /* Counters */
 //       CounterId
 //       Time
 //       (
 //         CounterKey
 //         Count
 //         Number?
@@ -1147,33 +1149,88 @@ void ProfileBuffer::StreamMarkersToJSON(
                                         double aSinceTime,
                                         UniqueStacks& aUniqueStacks) const {
   mEntries.ReadEach([&](ProfileBufferEntryReader& aER) {
     auto type = static_cast<ProfileBufferEntry::Kind>(
         aER.ReadObject<ProfileBufferEntry::KindUnderlyingType>());
     MOZ_ASSERT(static_cast<ProfileBufferEntry::KindUnderlyingType>(type) <
                static_cast<ProfileBufferEntry::KindUnderlyingType>(
                    ProfileBufferEntry::Kind::MODERN_LIMIT));
-    bool entryWasFullyRead = false;
+    // Code should *return* from the switch if the entry was fully read.
+    // Code should *break* from the switch if the entry was not fully read (we
+    // then need to adjust the reader position to the end of the entry, as
+    // expected by the reader code.)
+    switch (type) {
+      case ProfileBufferEntry::Kind::MarkerData:
+        if (aER.ReadObject<int>() != aThreadId) {
+          break;  // Entry not fully read.
+        }
+        aWriter.StartArrayElement();
+        {
+          // Extract the information from the buffer:
+          // Each entry is made up of the following:
+          //
+          // [
+          //   ProfileBufferEntry::Kind::MarkerData, <- already read
+          //   threadId,                             <- already read
+          //   name,                                 <- next location in entries
+          //   startTime,
+          //   endTime,
+          //   phase,
+          //   categoryPair,
+          //   payload
+          // ]
+          auto name = aER.ReadObject<std::string>();
+          auto startTime = aER.ReadObject<double>();
+          auto endTime = aER.ReadObject<double>();
+          auto phase = aER.ReadObject<uint8_t>();
+          const JS::ProfilingCategoryPairInfo& info =
+              GetProfilingCategoryPairInfo(
+                  static_cast<JS::ProfilingCategoryPair>(
+                      aER.ReadObject<uint32_t>()));
+          auto payload = aER.ReadObject<UniquePtr<ProfilerMarkerPayload>>();
 
-    if (type == ProfileBufferEntry::Kind::Marker) {
-      entryWasFullyRead =
-          mozilla::base_profiler_markers_detail::DeserializeAfterKindAndStream(
-              aER, aWriter, aThreadId,
-              [&](ProfileChunkedBuffer& aChunkedBuffer) {
-                ProfilerBacktrace backtrace("", &aChunkedBuffer);
-                backtrace.StreamJSON(aWriter, aProcessStartTime, aUniqueStacks);
-              });
+          MOZ_ASSERT(aER.RemainingBytes() == 0);
+
+          // Now write this information to JSON with the following schema:
+          // [name, startTime, endTime, phase, category, data]
+          aUniqueStacks.mUniqueStrings->WriteElement(aWriter, name);
+          aWriter.DoubleElement(startTime);
+          aWriter.DoubleElement(endTime);
+          aWriter.IntElement(phase);
+          aWriter.IntElement(unsigned(info.mCategory));
+          if (payload) {
+            aWriter.StartObjectElement(SpliceableJSONWriter::SingleLineStyle);
+            {
+              payload->StreamPayload(aWriter, aProcessStartTime, aUniqueStacks);
+            }
+            aWriter.EndObject();
+          }
+        }
+        aWriter.EndArray();
+        return;  // Entry fully read.
+
+      case ProfileBufferEntry::Kind::Marker:
+        if (mozilla::base_profiler_markers_detail::
+                DeserializeAfterKindAndStream(
+                    aER, aWriter, aThreadId,
+                    [&](ProfileChunkedBuffer& aChunkedBuffer) {
+                      ProfilerBacktrace backtrace("", &aChunkedBuffer);
+                      backtrace.StreamJSON(aWriter, aProcessStartTime,
+                                           aUniqueStacks);
+                    })) {
+          return;  // Entry fully read.
+        }
+        break;  // Entry not fully read.
+
+      default:
+        break;  // Entry not fully read.
     }
 
-    if (!entryWasFullyRead) {
-      // The entry was not a marker, or it was a marker for another thread.
-      // We probably didn't read the whole entry, so we need to skip to the end.
-      aER.SetRemainingBytes(0);
-    }
+    aER.SetRemainingBytes(0);
   });
 }
 
 void ProfileBuffer::StreamProfilerOverheadToJSON(
     SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime,
     double aSinceTime) const {
   mEntries.Read([&](ProfileChunkedBuffer::Reader* aReader) {
     MOZ_ASSERT(aReader,
new file mode 100644
--- /dev/null
+++ b/tools/profiler/core/ProfilerMarkerPayload.cpp
@@ -0,0 +1,1126 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ProfilerMarkerPayload.h"
+
+#include "GeckoProfiler.h"
+#include "ProfileBufferEntry.h"
+#include "ProfilerBacktrace.h"
+
+#include "gfxASurface.h"
+#include "Layers.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/net/HttpBaseChannel.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ProfileBufferEntrySerializationGeckoExtensions.h"
+#include "mozilla/ProfileJSONWriter.h"
+#include "mozilla/Sprintf.h"
+
+#include <inttypes.h>
+
+using namespace mozilla;
+
+static UniquePtr<ProfilerMarkerPayload> DeserializeNothing(
+    ProfileBufferEntryReader&) {
+  return nullptr;
+}
+
+// Starting at 1 for the initial `DeserializeNothing`.
+// static
+Atomic<ProfilerMarkerPayload::DeserializerTagAtomic, ReleaseAcquire>
+    ProfilerMarkerPayload::sDeserializerCount{1};
+
+// Initialize `sDeserializers` with `DeserializeNothing` at index 0, all others
+// are nullptrs.
+// static
+ProfilerMarkerPayload::Deserializer
+    ProfilerMarkerPayload::sDeserializers[DeserializerMax] = {
+        DeserializeNothing};
+
+// static
+ProfilerMarkerPayload::DeserializerTag
+ProfilerMarkerPayload::TagForDeserializer(
+    ProfilerMarkerPayload::Deserializer aDeserializer) {
+  if (!aDeserializer) {
+    return 0;
+  }
+  // Start first search at index 0.
+  DeserializerTagAtomic start = 0;
+  for (;;) {
+    // Read the current count of deserializers.
+    const DeserializerTagAtomic tagCount = sDeserializerCount;
+    if (tagCount == 0) {
+      // Someone else is currently writing into the array, loop around until we
+      // get a valid count.
+      continue;
+    }
+    for (DeserializerTagAtomic i = start; i < tagCount; ++i) {
+      if (sDeserializers[i] == aDeserializer) {
+        // Deserializer already registered, return its tag.
+        return static_cast<ProfilerMarkerPayload::DeserializerTag>(i);
+      }
+    }
+    // Not found yet, let's register this new deserializer.
+    // Make sure we haven't reached the limit yet.
+    MOZ_RELEASE_ASSERT(tagCount < DeserializerMax);
+    // Reserve `tagCount` as an index, if not already claimed:
+    // If `sDeserializerCount` is still at our previously-read `tagCount`,
+    // replace it with a special 0 value to indicate a write.
+    if (sDeserializerCount.compareExchange(tagCount, 0)) {
+      // Here we own the `tagCount` index, write the deserializer there.
+      sDeserializers[tagCount] = aDeserializer;
+      // And publish by writing the real new count (1 past our index).
+      sDeserializerCount = tagCount + 1;
+      return static_cast<ProfilerMarkerPayload::DeserializerTag>(tagCount);
+    }
+    // Someone else beat us to grab an index, and it could be for the same
+    // deserializer! So let's just try searching starting from our recorded
+    // `tagCount` (and maybe attempting again to register). It should be rare
+    // enough and quick enough that it won't impact performances.
+    start = tagCount;
+  }
+}
+
+// static
+ProfilerMarkerPayload::Deserializer ProfilerMarkerPayload::DeserializerForTag(
+    ProfilerMarkerPayload::DeserializerTag aTag) {
+  MOZ_RELEASE_ASSERT(aTag < DeserializerMax);
+  MOZ_RELEASE_ASSERT(aTag < sDeserializerCount);
+  return sDeserializers[aTag];
+}
+
+static void MOZ_ALWAYS_INLINE WriteTime(SpliceableJSONWriter& aWriter,
+                                        const TimeStamp& aProcessStartTime,
+                                        const TimeStamp& aTime,
+                                        const char* aName) {
+  if (!aTime.IsNull()) {
+    aWriter.DoubleProperty(MakeStringSpan(aName),
+                           (aTime - aProcessStartTime).ToMilliseconds());
+  }
+}
+
+void ProfilerMarkerPayload::StreamType(const char* aMarkerType,
+                                       SpliceableJSONWriter& aWriter) const {
+  MOZ_ASSERT(aMarkerType);
+  aWriter.StringProperty("type", MakeStringSpan(aMarkerType));
+}
+
+ProfileBufferEntryWriter::Length
+ProfilerMarkerPayload::CommonPropsTagAndSerializationBytes() const {
+  return sizeof(DeserializerTag) +
+         ProfileBufferEntryWriter::SumBytes(
+             mCommonProps.mStartTime, mCommonProps.mEndTime,
+             mCommonProps.mStack, mCommonProps.mInnerWindowID);
+}
+
+void ProfilerMarkerPayload::SerializeTagAndCommonProps(
+    DeserializerTag aDeserializerTag,
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  aEntryWriter.WriteObject(aDeserializerTag);
+  aEntryWriter.WriteObject(mCommonProps.mStartTime);
+  aEntryWriter.WriteObject(mCommonProps.mEndTime);
+  aEntryWriter.WriteObject(mCommonProps.mStack);
+  aEntryWriter.WriteObject(mCommonProps.mInnerWindowID);
+}
+
+// static
+ProfilerMarkerPayload::CommonProps
+ProfilerMarkerPayload::DeserializeCommonProps(
+    ProfileBufferEntryReader& aEntryReader) {
+  CommonProps props;
+  aEntryReader.ReadIntoObject(props.mStartTime);
+  aEntryReader.ReadIntoObject(props.mEndTime);
+  aEntryReader.ReadIntoObject(props.mStack);
+  aEntryReader.ReadIntoObject(props.mInnerWindowID);
+  return props;
+}
+
+// Deprecated: This function is providing a way for a few payloads to use the
+// start time and end time in their payloads, which is currently deprecated.
+// The startTime and endTime were removed from most payloads, in favor of
+// the MarkerTiming::Phase idea. However, IPC and Network markers still have
+// them as it was harder to upgrade the front-end without them.
+void ProfilerMarkerPayload::StreamStartEndTime(
+    SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime) const {
+  WriteTime(aWriter, aProcessStartTime, mCommonProps.mStartTime, "startTime");
+  WriteTime(aWriter, aProcessStartTime, mCommonProps.mEndTime, "endTime");
+}
+
+void ProfilerMarkerPayload::StreamCommonProps(
+    const char* aMarkerType, SpliceableJSONWriter& aWriter,
+    const TimeStamp& aProcessStartTime, UniqueStacks& aUniqueStacks) const {
+  StreamType(aMarkerType, aWriter);
+  if (mCommonProps.mInnerWindowID) {
+    // Here, we are converting uint64_t to double. Both Browsing Context and
+    // Inner Window IDs are creating using
+    // `nsContentUtils::GenerateProcessSpecificId`, which is specifically
+    // designed to only use 53 of the 64 bits to be lossless when passed into
+    // and out of JS as a double.
+    aWriter.DoubleProperty("innerWindowID", mCommonProps.mInnerWindowID.ref());
+  }
+  if (mCommonProps.mStack) {
+    aWriter.StartObjectProperty("stack");
+    {
+      mCommonProps.mStack->StreamJSON(aWriter, aProcessStartTime,
+                                      aUniqueStacks);
+    }
+    aWriter.EndObject();
+  }
+}
+
+ProfileBufferEntryWriter::Length
+TracingMarkerPayload::TagAndSerializationBytes() const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(
+             WrapProfileBufferRawPointer(mCategory), mKind);
+}
+
+void TracingMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndPayload(tag, aEntryWriter);
+}
+
+void TracingMarkerPayload::SerializeTagAndPayload(
+    DeserializerTag aDeserializerTag,
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  SerializeTagAndCommonProps(aDeserializerTag, aEntryWriter);
+  aEntryWriter.WriteObject(WrapProfileBufferRawPointer(mCategory));
+  aEntryWriter.WriteObject(mKind);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> TracingMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  const char* category = aEntryReader.ReadObject<const char*>();
+  TracingKind kind = aEntryReader.ReadObject<TracingKind>();
+  return UniquePtr<ProfilerMarkerPayload>(
+      new TracingMarkerPayload(std::move(props), category, kind));
+}
+
+void TracingMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                         const TimeStamp& aProcessStartTime,
+                                         UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("tracing", aWriter, aProcessStartTime, aUniqueStacks);
+
+  if (mCategory) {
+    aWriter.StringProperty("category", MakeStringSpan(mCategory));
+  }
+
+  if (mKind == TRACING_INTERVAL_START) {
+    aWriter.StringProperty("interval", "start");
+  } else if (mKind == TRACING_INTERVAL_END) {
+    aWriter.StringProperty("interval", "end");
+  }
+}
+
+ProfileBufferEntryWriter::Length
+UserTimingMarkerPayload::TagAndSerializationBytes() const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(
+             WrapProfileBufferRawPointer(mEntryType), mName, mStartMark,
+             mEndMark);
+}
+
+void UserTimingMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(WrapProfileBufferRawPointer(mEntryType));
+  aEntryWriter.WriteObject(mName);
+  aEntryWriter.WriteObject(mStartMark);
+  aEntryWriter.WriteObject(mEndMark);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> UserTimingMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto entryType = aEntryReader.ReadObject<const char*>();
+  auto name = aEntryReader.ReadObject<nsString>();
+  auto startMark = aEntryReader.ReadObject<Maybe<nsString>>();
+  auto endMark = aEntryReader.ReadObject<Maybe<nsString>>();
+  return UniquePtr<ProfilerMarkerPayload>(
+      new UserTimingMarkerPayload(std::move(props), entryType, std::move(name),
+                                  std::move(startMark), std::move(endMark)));
+}
+
+void UserTimingMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                            const TimeStamp& aProcessStartTime,
+                                            UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("UserTiming", aWriter, aProcessStartTime, aUniqueStacks);
+  aWriter.StringProperty("name", NS_ConvertUTF16toUTF8(mName));
+  aWriter.StringProperty("entryType", MakeStringSpan(mEntryType));
+
+  if (mStartMark.isSome()) {
+    aWriter.StringProperty("startMark",
+                           NS_ConvertUTF16toUTF8(mStartMark.value()));
+  } else {
+    aWriter.NullProperty("startMark");
+  }
+  if (mEndMark.isSome()) {
+    aWriter.StringProperty("endMark", NS_ConvertUTF16toUTF8(mEndMark.value()));
+  } else {
+    aWriter.NullProperty("endMark");
+  }
+}
+
+ProfileBufferEntryWriter::Length TimingMarkerPayload::TagAndSerializationBytes()
+    const {
+  return CommonPropsTagAndSerializationBytes();
+}
+
+void TimingMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> TimingMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  return UniquePtr<ProfilerMarkerPayload>(
+      new TimingMarkerPayload(std::move(props)));
+}
+
+void TimingMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                        const TimeStamp& aProcessStartTime,
+                                        UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("Timing", aWriter, aProcessStartTime, aUniqueStacks);
+}
+
+ProfileBufferEntryWriter::Length TextMarkerPayload::TagAndSerializationBytes()
+    const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mText);
+}
+
+void TextMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mText);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> TextMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto text = aEntryReader.ReadObject<nsCString>();
+  return UniquePtr<ProfilerMarkerPayload>(
+      new TextMarkerPayload(std::move(props), std::move(text)));
+}
+
+void TextMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                      const TimeStamp& aProcessStartTime,
+                                      UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("Text", aWriter, aProcessStartTime, aUniqueStacks);
+  aWriter.StringProperty("name", mText);
+}
+
+ProfileBufferEntryWriter::Length LogMarkerPayload::TagAndSerializationBytes()
+    const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mModule, mText);
+}
+
+void LogMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mModule);
+  aEntryWriter.WriteObject(mText);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> LogMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto module = aEntryReader.ReadObject<nsAutoCStringN<32>>();
+  auto text = aEntryReader.ReadObject<nsCString>();
+  return UniquePtr<ProfilerMarkerPayload>(new LogMarkerPayload(
+      std::move(props), std::move(module), std::move(text)));
+}
+
+void LogMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                     const TimeStamp& aProcessStartTime,
+                                     UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("Log", aWriter, aProcessStartTime, aUniqueStacks);
+  aWriter.StringProperty("name", mText);
+  aWriter.StringProperty("module", mModule);
+}
+
+MediaSampleMarkerPayload::MediaSampleMarkerPayload(
+    const int64_t aSampleStartTimeUs, const int64_t aSampleEndTimeUs)
+    : mSampleStartTimeUs(aSampleStartTimeUs),
+      mSampleEndTimeUs(aSampleEndTimeUs) {}
+
+MediaSampleMarkerPayload::MediaSampleMarkerPayload(
+    CommonProps&& aCommonProps, const int64_t aSampleStartTimeUs,
+    const int64_t aSampleEndTimeUs)
+    : ProfilerMarkerPayload(std::move(aCommonProps)),
+      mSampleStartTimeUs(aSampleStartTimeUs),
+      mSampleEndTimeUs(aSampleEndTimeUs) {}
+
+ProfileBufferEntryWriter::Length
+MediaSampleMarkerPayload::TagAndSerializationBytes() const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mSampleStartTimeUs,
+                                            mSampleEndTimeUs);
+}
+
+void MediaSampleMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mSampleStartTimeUs);
+  aEntryWriter.WriteObject(mSampleEndTimeUs);
+}
+
+/* static */
+UniquePtr<ProfilerMarkerPayload> MediaSampleMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto sampleStartTimeUs = aEntryReader.ReadObject<int64_t>();
+  auto sampleEndTimeUs = aEntryReader.ReadObject<int64_t>();
+  return UniquePtr<ProfilerMarkerPayload>(new MediaSampleMarkerPayload(
+      std::move(props), sampleStartTimeUs, sampleEndTimeUs));
+}
+
+void MediaSampleMarkerPayload::StreamPayload(
+    SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime,
+    UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("MediaSample", aWriter, aProcessStartTime, aUniqueStacks);
+  aWriter.IntProperty("sampleStartTimeUs", mSampleStartTimeUs);
+  aWriter.IntProperty("sampleEndTimeUs", mSampleEndTimeUs);
+}
+
+ProfileBufferEntryWriter::Length PrefMarkerPayload::TagAndSerializationBytes()
+    const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mPrefAccessTime, mPrefName,
+                                            mPrefKind, mPrefType, mPrefValue);
+}
+
+void PrefMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mPrefAccessTime);
+  aEntryWriter.WriteObject(mPrefName);
+  aEntryWriter.WriteObject(mPrefKind);
+  aEntryWriter.WriteObject(mPrefType);
+  aEntryWriter.WriteObject(mPrefValue);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> PrefMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto prefAccessTime = aEntryReader.ReadObject<TimeStamp>();
+  auto prefName = aEntryReader.ReadObject<nsCString>();
+  auto prefKind = aEntryReader.ReadObject<Maybe<PrefValueKind>>();
+  auto prefType = aEntryReader.ReadObject<Maybe<PrefType>>();
+  auto prefValue = aEntryReader.ReadObject<nsCString>();
+  return UniquePtr<ProfilerMarkerPayload>(new PrefMarkerPayload(
+      std::move(props), prefAccessTime, std::move(prefName),
+      std::move(prefKind), std::move(prefType), std::move(prefValue)));
+}
+
+static Span<const char> PrefValueKindToString(
+    const Maybe<PrefValueKind>& aKind) {
+  if (aKind) {
+    return *aKind == PrefValueKind::Default ? MakeStringSpan("Default")
+                                            : MakeStringSpan("User");
+  }
+  return MakeStringSpan("Shared");
+}
+
+static Span<const char> PrefTypeToString(const Maybe<PrefType>& type) {
+  if (type) {
+    switch (*type) {
+      case PrefType::None:
+        return MakeStringSpan("None");
+      case PrefType::Int:
+        return MakeStringSpan("Int");
+      case PrefType::Bool:
+        return MakeStringSpan("Bool");
+      case PrefType::String:
+        return MakeStringSpan("String");
+      default:
+        MOZ_ASSERT_UNREACHABLE("Unknown preference type.");
+    }
+  }
+  return MakeStringSpan("Preference not found");
+}
+
+void PrefMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                      const TimeStamp& aProcessStartTime,
+                                      UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("PreferenceRead", aWriter, aProcessStartTime,
+                    aUniqueStacks);
+  WriteTime(aWriter, aProcessStartTime, mPrefAccessTime, "prefAccessTime");
+  aWriter.StringProperty("prefName", mPrefName);
+  aWriter.StringProperty("prefKind", PrefValueKindToString(mPrefKind));
+  aWriter.StringProperty("prefType", PrefTypeToString(mPrefType));
+  aWriter.StringProperty("prefValue", mPrefValue);
+}
+
+ProfileBufferEntryWriter::Length
+LayerTranslationMarkerPayload::TagAndSerializationBytes() const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(WrapProfileBufferRawPointer(mLayer),
+                                            mPoint);
+}
+
+void LayerTranslationMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(WrapProfileBufferRawPointer(mLayer));
+  aEntryWriter.WriteObject(mPoint);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> LayerTranslationMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto layer = aEntryReader.ReadObject<layers::Layer*>();
+  auto point = aEntryReader.ReadObject<gfx::Point>();
+  return UniquePtr<ProfilerMarkerPayload>(
+      new LayerTranslationMarkerPayload(std::move(props), layer, point));
+}
+
+void LayerTranslationMarkerPayload::StreamPayload(
+    SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime,
+    UniqueStacks& aUniqueStacks) const {
+  StreamType("LayerTranslation", aWriter);
+  const size_t bufferSize = 32;
+  char buffer[bufferSize];
+  const int written = SprintfLiteral(buffer, "%p", mLayer);
+  MOZ_RELEASE_ASSERT(written > 0);
+
+  aWriter.StringProperty("layer", Span<const char>(buffer, size_t(written)));
+  aWriter.IntProperty("x", mPoint.x);
+  aWriter.IntProperty("y", mPoint.y);
+}
+
+ProfileBufferEntryWriter::Length VsyncMarkerPayload::TagAndSerializationBytes()
+    const {
+  return CommonPropsTagAndSerializationBytes();
+}
+
+void VsyncMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> VsyncMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  return UniquePtr<ProfilerMarkerPayload>(
+      new VsyncMarkerPayload(std::move(props)));
+}
+
+void VsyncMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                       const TimeStamp& aProcessStartTime,
+                                       UniqueStacks& aUniqueStacks) const {
+  StreamType("VsyncTimestamp", aWriter);
+}
+
+ProfileBufferEntryWriter::Length
+NetworkMarkerPayload::TagAndSerializationBytes() const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(
+             mID, mURI, mRedirectURI, mRequestMethod, mType, mPri, mCount,
+             mTimings, mCacheDisposition, mContentType);
+}
+
+void NetworkMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mID);
+  aEntryWriter.WriteObject(mURI);
+  aEntryWriter.WriteObject(mRedirectURI);
+  aEntryWriter.WriteObject(mRequestMethod);
+  aEntryWriter.WriteObject(mType);
+  aEntryWriter.WriteObject(mPri);
+  aEntryWriter.WriteObject(mCount);
+  aEntryWriter.WriteObject(mTimings);
+  aEntryWriter.WriteObject(mCacheDisposition);
+  aEntryWriter.WriteObject(mContentType);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> NetworkMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto id = aEntryReader.ReadObject<int64_t>();
+  auto uri = aEntryReader.ReadObject<UniqueFreePtr<char>>();
+  auto redirectURI = aEntryReader.ReadObject<UniqueFreePtr<char>>();
+  auto requestMethod = aEntryReader.ReadObject<nsCString>();
+  auto type = aEntryReader.ReadObject<NetworkLoadType>();
+  auto pri = aEntryReader.ReadObject<int32_t>();
+  auto count = aEntryReader.ReadObject<int64_t>();
+  auto timings = aEntryReader.ReadObject<net::TimingStruct>();
+  auto cacheDisposition = aEntryReader.ReadObject<net::CacheDisposition>();
+  auto contentType = aEntryReader.ReadObject<Maybe<nsAutoCString>>();
+  return UniquePtr<ProfilerMarkerPayload>(new NetworkMarkerPayload(
+      std::move(props), id, std::move(uri), std::move(redirectURI),
+      std::move(requestMethod), type, pri, count, timings, cacheDisposition,
+      std::move(contentType)));
+}
+
+static Span<const char> GetNetworkState(NetworkLoadType aType) {
+  switch (aType) {
+    case NetworkLoadType::LOAD_START:
+      return MakeStringSpan("STATUS_START");
+    case NetworkLoadType::LOAD_STOP:
+      return MakeStringSpan("STATUS_STOP");
+    case NetworkLoadType::LOAD_REDIRECT:
+      return MakeStringSpan("STATUS_REDIRECT");
+  }
+  return MakeStringSpan("");
+}
+
+static Span<const char> GetCacheState(net::CacheDisposition aCacheDisposition) {
+  switch (aCacheDisposition) {
+    case net::kCacheUnresolved:
+      return MakeStringSpan("Unresolved");
+    case net::kCacheHit:
+      return MakeStringSpan("Hit");
+    case net::kCacheHitViaReval:
+      return MakeStringSpan("HitViaReval");
+    case net::kCacheMissedViaReval:
+      return MakeStringSpan("MissedViaReval");
+    case net::kCacheMissed:
+      return MakeStringSpan("Missed");
+    case net::kCacheUnknown:
+    default:
+      return MakeStringSpan("");
+  }
+}
+
+void NetworkMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                         const TimeStamp& aProcessStartTime,
+                                         UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("Network", aWriter, aProcessStartTime, aUniqueStacks);
+  // This payload still streams a startTime and endTime property because it made
+  // the migration to MarkerTiming on the front-end easier.
+  StreamStartEndTime(aWriter, aProcessStartTime);
+
+  aWriter.IntProperty("id", mID);
+  // want to use aUniqueStacks.mUniqueStrings->WriteElement(aWriter,
+  // typeString);
+  aWriter.StringProperty("status", GetNetworkState(mType));
+  if (Span<const char> cacheString = GetCacheState(mCacheDisposition);
+      !cacheString.empty()) {
+    aWriter.StringProperty("cache", cacheString);
+  }
+  aWriter.IntProperty("pri", mPri);
+  if (mCount > 0) {
+    aWriter.IntProperty("count", mCount);
+  }
+  if (mURI) {
+    aWriter.StringProperty("URI", MakeStringSpan(mURI.get()));
+  }
+  if (mRedirectURI) {
+    aWriter.StringProperty("RedirectURI", MakeStringSpan(mRedirectURI.get()));
+  }
+  aWriter.StringProperty("requestMethod", mRequestMethod);
+
+  if (mContentType.isSome()) {
+    aWriter.StringProperty("contentType", mContentType.value());
+  } else {
+    aWriter.NullProperty("contentType");
+  }
+
+  if (mType != NetworkLoadType::LOAD_START) {
+    WriteTime(aWriter, aProcessStartTime, mTimings.domainLookupStart,
+              "domainLookupStart");
+    WriteTime(aWriter, aProcessStartTime, mTimings.domainLookupEnd,
+              "domainLookupEnd");
+    WriteTime(aWriter, aProcessStartTime, mTimings.connectStart,
+              "connectStart");
+    WriteTime(aWriter, aProcessStartTime, mTimings.tcpConnectEnd,
+              "tcpConnectEnd");
+    WriteTime(aWriter, aProcessStartTime, mTimings.secureConnectionStart,
+              "secureConnectionStart");
+    WriteTime(aWriter, aProcessStartTime, mTimings.connectEnd, "connectEnd");
+    WriteTime(aWriter, aProcessStartTime, mTimings.requestStart,
+              "requestStart");
+    WriteTime(aWriter, aProcessStartTime, mTimings.responseStart,
+              "responseStart");
+    WriteTime(aWriter, aProcessStartTime, mTimings.responseEnd, "responseEnd");
+  }
+}
+
+ProfileBufferEntryWriter::Length ScreenshotPayload::TagAndSerializationBytes()
+    const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mScreenshotDataURL, mWindowSize,
+                                            mWindowIdentifier);
+}
+
+void ScreenshotPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mScreenshotDataURL);
+  aEntryWriter.WriteObject(mWindowSize);
+  aEntryWriter.WriteObject(mWindowIdentifier);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> ScreenshotPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto screenshotDataURL = aEntryReader.ReadObject<nsCString>();
+  auto windowSize = aEntryReader.ReadObject<gfx::IntSize>();
+  auto windowIdentifier = aEntryReader.ReadObject<uintptr_t>();
+  return UniquePtr<ProfilerMarkerPayload>(
+      new ScreenshotPayload(std::move(props), std::move(screenshotDataURL),
+                            windowSize, windowIdentifier));
+}
+
+void ScreenshotPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                      const TimeStamp& aProcessStartTime,
+                                      UniqueStacks& aUniqueStacks) const {
+  StreamType("CompositorScreenshot", aWriter);
+  aUniqueStacks.mUniqueStrings->WriteProperty(aWriter, MakeStringSpan("url"),
+                                              mScreenshotDataURL);
+
+  char hexWindowID[32];
+  const int written =
+      SprintfLiteral(hexWindowID, "0x%" PRIXPTR, mWindowIdentifier);
+  MOZ_RELEASE_ASSERT(written > 0);
+  aWriter.StringProperty("windowID",
+                         Span<const char>(hexWindowID, size_t(written)));
+  aWriter.DoubleProperty("windowWidth", mWindowSize.width);
+  aWriter.DoubleProperty("windowHeight", mWindowSize.height);
+}
+
+ProfileBufferEntryWriter::Length
+GCSliceMarkerPayload::TagAndSerializationBytes() const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mTimingJSON);
+}
+
+void GCSliceMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mTimingJSON);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> GCSliceMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto timingJSON = aEntryReader.ReadObject<JS::UniqueChars>();
+  return UniquePtr<ProfilerMarkerPayload>(
+      new GCSliceMarkerPayload(std::move(props), std::move(timingJSON)));
+}
+
+void GCSliceMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                         const TimeStamp& aProcessStartTime,
+                                         UniqueStacks& aUniqueStacks) const {
+  MOZ_ASSERT(mTimingJSON);
+  StreamCommonProps("GCSlice", aWriter, aProcessStartTime, aUniqueStacks);
+  if (mTimingJSON) {
+    aWriter.SplicedJSONProperty(MakeStringSpan("timings"),
+                                MakeStringSpan(mTimingJSON.get()));
+  } else {
+    aWriter.NullProperty("timings");
+  }
+}
+
+ProfileBufferEntryWriter::Length BudgetMarkerPayload::TagAndSerializationBytes()
+    const {
+  return CommonPropsTagAndSerializationBytes();
+}
+
+void BudgetMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> BudgetMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  return UniquePtr<ProfilerMarkerPayload>(
+      new BudgetMarkerPayload(std::move(props)));
+}
+
+void BudgetMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                        const TimeStamp& aProcessStartTime,
+                                        UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("Budget", aWriter, aProcessStartTime, aUniqueStacks);
+}
+
+ProfileBufferEntryWriter::Length
+GCMajorMarkerPayload::TagAndSerializationBytes() const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mTimingJSON);
+}
+
+void GCMajorMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mTimingJSON);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> GCMajorMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto timingJSON = aEntryReader.ReadObject<JS::UniqueChars>();
+  return UniquePtr<ProfilerMarkerPayload>(
+      new GCMajorMarkerPayload(std::move(props), std::move(timingJSON)));
+}
+
+void GCMajorMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                         const TimeStamp& aProcessStartTime,
+                                         UniqueStacks& aUniqueStacks) const {
+  MOZ_ASSERT(mTimingJSON);
+  StreamCommonProps("GCMajor", aWriter, aProcessStartTime, aUniqueStacks);
+  if (mTimingJSON) {
+    aWriter.SplicedJSONProperty(MakeStringSpan("timings"),
+                                MakeStringSpan(mTimingJSON.get()));
+  } else {
+    aWriter.NullProperty("timings");
+  }
+}
+
+ProfileBufferEntryWriter::Length
+GCMinorMarkerPayload::TagAndSerializationBytes() const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mTimingData);
+}
+
+void GCMinorMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mTimingData);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> GCMinorMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto timingData = aEntryReader.ReadObject<JS::UniqueChars>();
+  return UniquePtr<ProfilerMarkerPayload>(
+      new GCMinorMarkerPayload(std::move(props), std::move(timingData)));
+}
+
+void GCMinorMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                         const TimeStamp& aProcessStartTime,
+                                         UniqueStacks& aUniqueStacks) const {
+  MOZ_ASSERT(mTimingData);
+  StreamCommonProps("GCMinor", aWriter, aProcessStartTime, aUniqueStacks);
+  if (mTimingData) {
+    aWriter.SplicedJSONProperty(MakeStringSpan("nursery"),
+                                MakeStringSpan(mTimingData.get()));
+  } else {
+    aWriter.NullProperty("nursery");
+  }
+}
+
+ProfileBufferEntryWriter::Length HangMarkerPayload::TagAndSerializationBytes()
+    const {
+  return CommonPropsTagAndSerializationBytes();
+}
+
+void HangMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> HangMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  return UniquePtr<ProfilerMarkerPayload>(
+      new HangMarkerPayload(std::move(props)));
+}
+
+void HangMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                      const TimeStamp& aProcessStartTime,
+                                      UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("BHR-detected hang", aWriter, aProcessStartTime,
+                    aUniqueStacks);
+}
+
+ProfileBufferEntryWriter::Length StyleMarkerPayload::TagAndSerializationBytes()
+    const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mStats);
+}
+
+void StyleMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mStats);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> StyleMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto stats = aEntryReader.ReadObject<ServoTraversalStatistics>();
+  return UniquePtr<ProfilerMarkerPayload>(
+      new StyleMarkerPayload(std::move(props), stats));
+}
+
+void StyleMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                       const TimeStamp& aProcessStartTime,
+                                       UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("Styles", aWriter, aProcessStartTime, aUniqueStacks);
+  aWriter.StringProperty("category", "Paint");
+  aWriter.IntProperty("elementsTraversed", mStats.mElementsTraversed);
+  aWriter.IntProperty("elementsStyled", mStats.mElementsStyled);
+  aWriter.IntProperty("elementsMatched", mStats.mElementsMatched);
+  aWriter.IntProperty("stylesShared", mStats.mStylesShared);
+  aWriter.IntProperty("stylesReused", mStats.mStylesReused);
+}
+
+ProfileBufferEntryWriter::Length
+LongTaskMarkerPayload::TagAndSerializationBytes() const {
+  return CommonPropsTagAndSerializationBytes();
+}
+
+void LongTaskMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> LongTaskMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  return UniquePtr<ProfilerMarkerPayload>(
+      new LongTaskMarkerPayload(std::move(props)));
+}
+
+void LongTaskMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                          const TimeStamp& aProcessStartTime,
+                                          UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("MainThreadLongTask", aWriter, aProcessStartTime,
+                    aUniqueStacks);
+  aWriter.StringProperty("category", "LongTask");
+}
+
+ProfileBufferEntryWriter::Length
+JsAllocationMarkerPayload::TagAndSerializationBytes() const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mTypeName, mClassName,
+                                            mDescriptiveTypeName, mCoarseType,
+                                            mSize, mInNursery);
+}
+
+void JsAllocationMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mTypeName);
+  aEntryWriter.WriteObject(mClassName);
+  aEntryWriter.WriteObject(mDescriptiveTypeName);
+  aEntryWriter.WriteObject(WrapProfileBufferRawPointer(mCoarseType));
+  aEntryWriter.WriteObject(mSize);
+  aEntryWriter.WriteObject(mInNursery);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> JsAllocationMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto typeName = aEntryReader.ReadObject<UniqueFreePtr<const char16_t>>();
+  auto className = aEntryReader.ReadObject<UniqueFreePtr<const char>>();
+  auto descriptiveTypeName =
+      aEntryReader.ReadObject<UniqueFreePtr<const char16_t>>();
+  auto coarseType = aEntryReader.ReadObject<const char*>();
+  auto size = aEntryReader.ReadObject<uint64_t>();
+  auto inNursery = aEntryReader.ReadObject<bool>();
+  return UniquePtr<ProfilerMarkerPayload>(new JsAllocationMarkerPayload(
+      std::move(props), std::move(typeName), std::move(className),
+      std::move(descriptiveTypeName), coarseType, size, inNursery));
+}
+
+void JsAllocationMarkerPayload::StreamPayload(
+    SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime,
+    UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("JS allocation", aWriter, aProcessStartTime, aUniqueStacks);
+
+  if (mClassName) {
+    aWriter.StringProperty("className", MakeStringSpan(mClassName.get()));
+  }
+  if (mTypeName) {
+    aWriter.StringProperty("typeName", NS_ConvertUTF16toUTF8(mTypeName.get()));
+  }
+  if (mDescriptiveTypeName) {
+    aWriter.StringProperty("descriptiveTypeName",
+                           NS_ConvertUTF16toUTF8(mDescriptiveTypeName.get()));
+  }
+  aWriter.StringProperty("coarseType", MakeStringSpan(mCoarseType));
+  aWriter.IntProperty("size", mSize);
+  aWriter.BoolProperty("inNursery", mInNursery);
+}
+
+ProfileBufferEntryWriter::Length
+NativeAllocationMarkerPayload::TagAndSerializationBytes() const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mSize, mThreadId, mMemoryAddress);
+}
+
+void NativeAllocationMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mSize);
+  aEntryWriter.WriteObject(mMemoryAddress);
+  aEntryWriter.WriteObject(mThreadId);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> NativeAllocationMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto size = aEntryReader.ReadObject<int64_t>();
+  auto memoryAddress = aEntryReader.ReadObject<uintptr_t>();
+  auto threadId = aEntryReader.ReadObject<int>();
+  return UniquePtr<ProfilerMarkerPayload>(new NativeAllocationMarkerPayload(
+      std::move(props), size, memoryAddress, threadId));
+}
+
+void NativeAllocationMarkerPayload::StreamPayload(
+    SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime,
+    UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("Native allocation", aWriter, aProcessStartTime,
+                    aUniqueStacks);
+  aWriter.IntProperty("size", mSize);
+  aWriter.IntProperty("memoryAddress", static_cast<int64_t>(mMemoryAddress));
+  aWriter.IntProperty("threadId", mThreadId);
+}
+
+ProfileBufferEntryWriter::Length IPCMarkerPayload::TagAndSerializationBytes()
+    const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mOtherPid, mMessageSeqno,
+                                            mMessageType, mSide, mDirection,
+                                            mPhase, mSync);
+}
+
+void IPCMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mOtherPid);
+  aEntryWriter.WriteObject(mMessageSeqno);
+  aEntryWriter.WriteObject(mMessageType);
+  aEntryWriter.WriteObject(mSide);
+  aEntryWriter.WriteObject(mDirection);
+  aEntryWriter.WriteObject(mPhase);
+  aEntryWriter.WriteObject(mSync);
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> IPCMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto otherPid = aEntryReader.ReadObject<int32_t>();
+  auto messageSeqno = aEntryReader.ReadObject<int32_t>();
+  auto messageType = aEntryReader.ReadObject<IPC::Message::msgid_t>();
+  auto side = aEntryReader.ReadObject<ipc::Side>();
+  auto direction = aEntryReader.ReadObject<ipc::MessageDirection>();
+  auto phase = aEntryReader.ReadObject<ipc::MessagePhase>();
+  auto sync = aEntryReader.ReadObject<bool>();
+  return UniquePtr<ProfilerMarkerPayload>(
+      new IPCMarkerPayload(std::move(props), otherPid, messageSeqno,
+                           messageType, side, direction, phase, sync));
+}
+
+static Span<const char> IPCSideToString(ipc::Side aSide) {
+  switch (aSide) {
+    case ipc::ParentSide:
+      return MakeStringSpan("parent");
+    case ipc::ChildSide:
+      return MakeStringSpan("child");
+    case ipc::UnknownSide:
+      return MakeStringSpan("unknown");
+    default:
+      MOZ_ASSERT_UNREACHABLE("Invalid IPC side");
+      return MakeStringSpan("<invalid IPC side>");
+  }
+}
+
+static Span<const char> IPCPhaseToString(ipc::MessagePhase aPhase) {
+  switch (aPhase) {
+    case ipc::MessagePhase::Endpoint:
+      return MakeStringSpan("endpoint");
+    case ipc::MessagePhase::TransferStart:
+      return MakeStringSpan("transferStart");
+    case ipc::MessagePhase::TransferEnd:
+      return MakeStringSpan("transferEnd");
+    default:
+      MOZ_ASSERT_UNREACHABLE("Invalid IPC phase");
+      return MakeStringSpan("<invalid IPC phase>");
+  }
+}
+
+void IPCMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                     const TimeStamp& aProcessStartTime,
+                                     UniqueStacks& aUniqueStacks) const {
+  using namespace mozilla::ipc;
+  StreamCommonProps("IPC", aWriter, aProcessStartTime, aUniqueStacks);
+
+  // This payload still streams a startTime and endTime property because it made
+  // the migration to MarkerTiming on the front-end easier.
+  StreamStartEndTime(aWriter, aProcessStartTime);
+
+  aWriter.IntProperty("otherPid", mOtherPid);
+  aWriter.IntProperty("messageSeqno", mMessageSeqno);
+  aWriter.StringProperty(
+      "messageType",
+      MakeStringSpan(IPC::StringFromIPCMessageType(mMessageType)));
+  aWriter.StringProperty("side", IPCSideToString(mSide));
+  aWriter.StringProperty("direction", mDirection == MessageDirection::eSending
+                                          ? MakeStringSpan("sending")
+                                          : MakeStringSpan("receiving"));
+  aWriter.StringProperty("phase", IPCPhaseToString(mPhase));
+  aWriter.BoolProperty("sync", mSync);
+}
--- a/tools/profiler/core/RegisteredThread.cpp
+++ b/tools/profiler/core/RegisteredThread.cpp
@@ -32,16 +32,17 @@ RegisteredThread::~RegisteredThread() { 
 
 size_t RegisteredThread::SizeOfIncludingThis(
     mozilla::MallocSizeOf aMallocSizeOf) const {
   size_t n = aMallocSizeOf(this);
 
   // Measurement of the following members may be added later if DMD finds it
   // is worthwhile:
   // - mPlatformData
+  // - mRacyRegisteredThread.mPendingMarkers
   //
   // The following members are not measured:
   // - mThreadInfo: because it is non-owning
 
   return n;
 }
 
 void RegisteredThread::GetRunningEventDelay(const mozilla::TimeStamp& aNow,
--- a/tools/profiler/core/memory_hooks.cpp
+++ b/tools/profiler/core/memory_hooks.cpp
@@ -260,16 +260,20 @@ class ThreadIntercept {
   // needed because collecting stacks creates new allocations. When blocked,
   // these allocations are then ignored by the memory hook.
   static PROFILER_THREAD_LOCAL(bool) tlsIsBlocked;
 
   // This is a quick flag to check and see if the allocations feature is enabled
   // or disabled.
   static mozilla::Atomic<bool, mozilla::Relaxed> sAllocationsFeatureEnabled;
 
+  // The markers will be stored on the main thread. Retain the id to the main
+  // thread of this process here.
+  static mozilla::Atomic<int, mozilla::Relaxed> sMainThreadId;
+
   ThreadIntercept() = default;
 
   // Only allow consumers to access this information if they run
   // ThreadIntercept::MaybeGet and ask through the non-static version.
   static bool IsBlocked_() {
     // When the native allocations feature is turned on, memory hooks run on
     // every single allocation. For a subset of these allocations, the stack
     // gets sampled by running profiler_get_backtrace(), which locks the
@@ -306,26 +310,33 @@ class ThreadIntercept {
 
   void Unblock() {
     MOZ_ASSERT(tlsIsBlocked.get());
     tlsIsBlocked.set(false);
   }
 
   bool IsBlocked() const { return ThreadIntercept::IsBlocked_(); }
 
-  static void EnableAllocationFeature() { sAllocationsFeatureEnabled = true; }
+  static void EnableAllocationFeature(int aMainThreadId) {
+    sAllocationsFeatureEnabled = true;
+    sMainThreadId = aMainThreadId;
+  }
 
   static void DisableAllocationFeature() { sAllocationsFeatureEnabled = false; }
+
+  static int MainThreadId() { return sMainThreadId; }
 };
 
 PROFILER_THREAD_LOCAL(bool) ThreadIntercept::tlsIsBlocked;
 
 mozilla::Atomic<bool, mozilla::Relaxed>
     ThreadIntercept::sAllocationsFeatureEnabled(false);
 
+mozilla::Atomic<int, mozilla::Relaxed> ThreadIntercept::sMainThreadId(0);
+
 // An object of this class must be created (on the stack) before running any
 // code that might allocate.
 class AutoBlockIntercepts {
   ThreadIntercept& mThreadIntercept;
 
  public:
   // Disallow copy and assign.
   AutoBlockIntercepts(const AutoBlockIntercepts&) = delete;
@@ -373,17 +384,17 @@ static void AllocCallback(void* aPtr, si
   // larger allocations are weighted heavier than smaller allocations.
   MOZ_ASSERT(gBernoulli,
              "gBernoulli must be properly installed for the memory hooks.");
   if (
       // First perform the Bernoulli trial.
       gBernoulli->trial(actualSize) &&
       // Second, attempt to add a marker if the Bernoulli trial passed.
       profiler_add_native_allocation_marker(
-          static_cast<int64_t>(actualSize),
+          ThreadIntercept::MainThreadId(), static_cast<int64_t>(actualSize),
           reinterpret_cast<uintptr_t>(aPtr))) {
     MOZ_ASSERT(gAllocationTracker,
                "gAllocationTracker must be properly installed for the memory "
                "hooks.");
     // Only track the memory if the allocation marker was actually added to the
     // profiler.
     gAllocationTracker->AddMemoryAddress(aPtr);
   }
@@ -416,17 +427,18 @@ static void FreeCallback(void* aPtr) {
   // Perform a bernoulli trial, which will return true or false based on its
   // configured probability. It takes into account the byte size so that
   // larger allocations are weighted heavier than smaller allocations.
   MOZ_ASSERT(
       gAllocationTracker,
       "gAllocationTracker must be properly installed for the memory hooks.");
   if (gAllocationTracker->RemoveMemoryAddressIfFound(aPtr)) {
     // This size here is negative, indicating a deallocation.
-    profiler_add_native_allocation_marker(signedSize,
+    profiler_add_native_allocation_marker(ThreadIntercept::MainThreadId(),
+                                          signedSize,
                                           reinterpret_cast<uintptr_t>(aPtr));
   }
 }
 
 }  // namespace mozilla::profiler
 
 //---------------------------------------------------------------------------
 // malloc/free interception
@@ -550,17 +562,17 @@ void install_memory_hooks() {
 }
 
 // Remove the hooks, but leave the sCounter machinery. Deleting the counter
 // would race with any existing memory hooks that are currently running. Rather
 // than adding overhead here of mutexes it's cheaper for the performance to just
 // leak these values.
 void remove_memory_hooks() { jemalloc_replace_dynamic(nullptr); }
 
-void enable_native_allocations() {
+void enable_native_allocations(int aMainThreadId) {
   // The bloat log tracks allocations and deallocations. This can conflict
   // with the memory hook machinery, as the bloat log creates its own
   // allocations. This means we can re-enter inside the bloat log machinery. At
   // this time, the bloat log does not know about cannot handle the native
   // allocation feature.
   //
   // At the time of this writing, we hit this assertion:
   // IsIdle(oldState) || IsRead(oldState) in Checker::StartReadOp()
@@ -579,17 +591,17 @@ void enable_native_allocations() {
   //    #12: profiler_get_backtrace()
   //    ...
   MOZ_ASSERT(!PR_GetEnv("XPCOM_MEM_BLOAT_LOG"),
              "The bloat log feature is not compatible with the native "
              "allocations instrumentation.");
 
   EnsureBernoulliIsInstalled();
   EnsureAllocationTrackerIsInstalled();
-  ThreadIntercept::EnableAllocationFeature();
+  ThreadIntercept::EnableAllocationFeature(aMainThreadId);
 }
 
 // This is safe to call even if native allocations hasn't been enabled.
 void disable_native_allocations() {
   ThreadIntercept::DisableAllocationFeature();
   if (gAllocationTracker) {
     gAllocationTracker->Reset();
   }
--- a/tools/profiler/core/memory_hooks.h
+++ b/tools/profiler/core/memory_hooks.h
@@ -8,16 +8,16 @@
 #define memory_hooks_h
 
 #if defined(MOZ_REPLACE_MALLOC) && defined(MOZ_PROFILER_MEMORY)
 namespace mozilla {
 namespace profiler {
 
 void install_memory_hooks();
 void remove_memory_hooks();
-void enable_native_allocations();
+void enable_native_allocations(int aMainThreadId);
 void disable_native_allocations();
 
 }  // namespace profiler
 }  // namespace mozilla
 #endif
 
 #endif
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -32,31 +32,31 @@
 #include "GeckoProfilerReporter.h"
 #include "PageInformation.h"
 #include "ProfileBuffer.h"
 #include "ProfiledThreadData.h"
 #include "ProfilerBacktrace.h"
 #include "ProfilerChild.h"
 #include "ProfilerCodeAddressService.h"
 #include "ProfilerIOInterposeObserver.h"
+#include "ProfilerMarkerPayload.h"
 #include "ProfilerParent.h"
 #include "RegisteredThread.h"
 #include "shared-libraries.h"
 #include "ThreadInfo.h"
 #include "VTuneProfiler.h"
 
 #include "js/TraceLoggerAPI.h"
 #include "js/ProfilingFrameIterator.h"
 #include "memory_hooks.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/AutoProfilerLabel.h"
 #include "mozilla/ExtensionPolicyService.h"
 #include "mozilla/extensions/WebExtensionPolicy.h"
-#include "mozilla/net/HttpBaseChannel.h"  // for net::TimingStruct
 #include "mozilla/Printf.h"
 #include "mozilla/ProfileBufferChunkManagerSingle.h"
 #include "mozilla/ProfileBufferChunkManagerWithLocalLimit.h"
 #include "mozilla/ProfileChunkedBuffer.h"
 #include "mozilla/SchedulerGroup.h"
 #include "mozilla/Services.h"
 #include "mozilla/StackWalk.h"
 #include "mozilla/StaticPtr.h"
@@ -1626,16 +1626,54 @@ void ProfilingStackOwner::DumpStackAndCr
   }
 
   MOZ_CRASH("Non-empty stack!");
 }
 
 // The name of the main thread.
 static const char* const kMainThreadName = "GeckoMain";
 
+// TODO - It is better to have the marker timing created by the original callers
+// of the profiler_add_marker API, rather than deduce it from the payload. This
+// is a bigger code diff for adding MarkerTiming, so do that work in a
+// follow-up.
+MarkerTiming get_marker_timing_from_payload(
+    const ProfilerMarkerPayload& aPayload) {
+  const TimeStamp& start = aPayload.GetStartTime();
+  const TimeStamp& end = aPayload.GetEndTime();
+  if (start.IsNull()) {
+    if (end.IsNull()) {
+      // The payload contains no time information, use the current time.
+      return MarkerTiming::InstantAt(TimeStamp::NowUnfuzzed());
+    }
+    return MarkerTiming::IntervalEnd(end);
+  }
+  if (end.IsNull()) {
+    return MarkerTiming::IntervalStart(start);
+  }
+  if (start == end) {
+    return MarkerTiming::InstantAt(start);
+  }
+  return MarkerTiming::Interval(start, end);
+}
+
+// Add the marker to the given buffer with the given information.
+// This is a unified insertion point for all the markers.
+static void StoreMarker(ProfileChunkedBuffer& aChunkedBuffer, int aThreadId,
+                        const char* aMarkerName,
+                        const MarkerTiming& aMarkerTiming,
+                        JS::ProfilingCategoryPair aCategoryPair,
+                        const ProfilerMarkerPayload* aPayload) {
+  aChunkedBuffer.PutObjects(
+      ProfileBufferEntry::Kind::MarkerData, aThreadId,
+      WrapProfileBufferUnownedCString(aMarkerName),
+      aMarkerTiming.GetStartTime(), aMarkerTiming.GetEndTime(),
+      aMarkerTiming.GetPhase(), static_cast<uint32_t>(aCategoryPair), aPayload);
+}
+
 ////////////////////////////////////////////////////////////////////////
 // BEGIN sampling/unwinding code
 
 // The registers used for stack unwinding and a few other sampling purposes.
 // The ctor does nothing; users are responsible for filling in the fields.
 class Registers {
  public:
   Registers() : mPC{nullptr}, mSP{nullptr}, mFP{nullptr}, mLR{nullptr} {}
@@ -2493,17 +2531,17 @@ static PreRecordedMetaInformation PreRec
   return info;
 }
 
 static void StreamMetaJSCustomObject(
     PSLockRef aLock, SpliceableJSONWriter& aWriter, bool aIsShuttingDown,
     const PreRecordedMetaInformation& aPreRecordedMetaInformation) {
   MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock));
 
-  aWriter.IntProperty("version", 22);
+  aWriter.IntProperty("version", 21);
 
   // The "startTime" field holds the number of milliseconds since midnight
   // January 1, 1970 GMT. This grotty code computes (Now - (Now -
   // ProcessStartTime)) to convert CorePS::ProcessStartTime() into that form.
   TimeDuration delta = TimeStamp::NowUnfuzzed() - CorePS::ProcessStartTime();
   aWriter.DoubleProperty(
       "startTime",
       static_cast<double>(PR_Now() / 1000.0 - delta.ToMilliseconds()));
@@ -2739,25 +2777,29 @@ static void CollectJavaThreadProfileData
                             : CorePS::ProcessStartTime() +
                                   TimeDuration::FromMilliseconds(endTimeMs);
     MarkerTiming timing = endTimeMs == 0
                               ? MarkerTiming::InstantAt(startTime)
                               : MarkerTiming::Interval(startTime, endTime);
 
     if (!text) {
       // This marker doesn't have a text.
-      AddMarkerToBuffer(aProfileBuffer.UnderlyingChunkedBuffer(), markerName,
-                        geckoprofiler::category::JAVA_ANDROID,
-                        {MarkerThreadId(threadId), std::move(timing)});
+      StoreMarker(aProfileBuffer.UnderlyingChunkedBuffer(), threadId,
+                  markerName.get(), timing,
+                  JS::ProfilingCategoryPair::JAVA_ANDROID, nullptr);
     } else {
       // This marker has a text.
-      AddMarkerToBuffer(aProfileBuffer.UnderlyingChunkedBuffer(), markerName,
-                        geckoprofiler::category::JAVA_ANDROID,
-                        {MarkerThreadId(threadId), std::move(timing)},
-                        geckoprofiler::markers::Text{}, text->ToCString());
+      nsCString textString = text->ToCString();
+      const TextMarkerPayload payload(textString, startTime, endTime, Nothing(),
+                                      nullptr);
+
+      // Put the marker inside the buffer.
+      StoreMarker(aProfileBuffer.UnderlyingChunkedBuffer(), threadId,
+                  markerName.get(), timing,
+                  JS::ProfilingCategoryPair::JAVA_ANDROID, &payload);
     }
   }
 }
 #endif
 
 UniquePtr<ProfilerCodeAddressService>
 profiler_code_address_service_for_presymbolication() {
   static const bool preSymbolicate = []() {
@@ -4547,17 +4589,17 @@ static void locked_profiler_start(PSLock
   if (HasMinimumLength(baseprofile.get(), 2)) {
     // The BaseProfiler startup profile will be stored as a separate "process"
     // in the Gecko Profiler profile, and shown as a new track under the
     // corresponding Gecko Profiler thread.
     ActivePS::AddBaseProfileThreads(aLock, std::move(baseprofile));
   }
 
   // Set up profiling for each registered thread, if appropriate.
-  bool isMainThreadBeingProfiled = false;
+  Maybe<int> mainThreadId;
   int tid = profiler_current_thread_id();
   const Vector<UniquePtr<RegisteredThread>>& registeredThreads =
       CorePS::RegisteredThreads(aLock);
   for (auto& registeredThread : registeredThreads) {
     RefPtr<ThreadInfo> info = registeredThread->Info();
 
     if (ActivePS::ShouldProfileThread(aLock, info)) {
       registeredThread->RacyRegisteredThread().SetIsBeingProfiled(true);
@@ -4574,17 +4616,17 @@ static void locked_profiler_start(PSLock
         } else if (info->IsMainThread()) {
           // Dispatch a runnable to the main thread to call PollJSSampling(),
           // so that we don't have wait for the next JS interrupt callback in
           // order to start profiling JS.
           TriggerPollJSSamplingOnMainThread();
         }
       }
       if (info->IsMainThread()) {
-        isMainThreadBeingProfiled = true;
+        mainThreadId = Some(info->ThreadId());
       }
       registeredThread->RacyRegisteredThread().ReinitializeOnResume();
       if (registeredThread->GetJSContext()) {
         profiledThreadData->NotifyReceivedJSContext(0);
       }
     }
   }
 
@@ -4610,18 +4652,18 @@ static void locked_profiler_start(PSLock
     java::GeckoJavaSampler::Start(
         javaInterval, std::round((double)(capacity.Value()) * interval /
                                  (double)(javaInterval)));
   }
 #endif
 
 #if defined(MOZ_REPLACE_MALLOC) && defined(MOZ_PROFILER_MEMORY)
   if (ActivePS::FeatureNativeAllocations(aLock)) {
-    if (isMainThreadBeingProfiled) {
-      mozilla::profiler::enable_native_allocations();
+    if (mainThreadId.isSome()) {
+      mozilla::profiler::enable_native_allocations(mainThreadId.value());
     } else {
       NS_WARNING(
           "The nativeallocations feature is turned on, but the main thread is "
           "not being profiled. The allocations are only stored on the main "
           "thread.");
     }
   }
 #endif
@@ -5020,16 +5062,21 @@ void profiler_add_sampled_counter(BasePr
 void profiler_remove_sampled_counter(BaseProfilerCount* aCounter) {
   DEBUG_LOG("profiler_remove_sampled_counter(%s)", aCounter->mLabel);
   PSAutoLock lock(gPSMutex);
   // Note: we don't enforce a final sample, though we could do so if the
   // profiler was active
   CorePS::RemoveCounter(lock, aCounter);
 }
 
+static void maybelocked_profiler_add_marker_for_thread(
+    int aThreadId, JS::ProfilingCategoryPair aCategoryPair,
+    const char* aMarkerName, const ProfilerMarkerPayload& aPayload,
+    const PSAutoLock* aLockOrNull);
+
 ProfilingStack* profiler_register_thread(const char* aName,
                                          void* aGuessStackTop) {
   DEBUG_LOG("profiler_register_thread(%s)", aName);
 
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
   // Make sure we have a nsThread wrapper for the current thread, and that NSPR
   // knows its name.
@@ -5056,18 +5103,20 @@ ProfilingStack* profiler_register_thread
     // In the meantime, we record a marker that could be used in the frontend.
     nsCString text("Thread ");
     text.AppendInt(profiler_current_thread_id());
     text.AppendLiteral(" \"");
     text.AppendASCII(thread->Info()->Name());
     text.AppendLiteral("\" attempted to re-register as \"");
     text.AppendASCII(aName);
     text.AppendLiteral("\"");
-    PROFILER_MARKER_TEXT("profiler_register_thread again", OTHER_Profiling,
-                         MarkerThreadId::MainThread(), text);
+    maybelocked_profiler_add_marker_for_thread(
+        profiler_main_thread_id(), JS::ProfilingCategoryPair::OTHER_Profiling,
+        "profiler_register_thread again",
+        TextMarkerPayload(text, TimeStamp::NowUnfuzzed()), &lock);
 
     return &thread->RacyRegisteredThread().ProfilingStack();
   }
 
   void* stackTop = GetStackTop(aGuessStackTop);
   return locked_register_thread(lock, aName, stackTop);
 }
 
@@ -5137,18 +5186,20 @@ void profiler_unregister_thread() {
     // We cannot record a marker on this thread because it was already
     // unregistered. Send it to the main thread (unless this *is* already the
     // main thread, which has been unregistered); this may be useful to catch
     // mismatched register/unregister pairs in Firefox.
     if (int tid = profiler_current_thread_id();
         tid != profiler_main_thread_id()) {
       nsCString threadIdString;
       threadIdString.AppendInt(tid);
-      PROFILER_MARKER_TEXT("profiler_unregister_thread again", OTHER_Profiling,
-                           MarkerThreadId::MainThread(), threadIdString);
+      maybelocked_profiler_add_marker_for_thread(
+          profiler_main_thread_id(), JS::ProfilingCategoryPair::OTHER_Profiling,
+          "profiler_unregister_thread again",
+          TextMarkerPayload(threadIdString, TimeStamp::NowUnfuzzed()), &lock);
     }
   }
 }
 
 void profiler_register_page(uint64_t aBrowsingContextID,
                             uint64_t aInnerWindowID, const nsCString& aUrl,
                             uint64_t aEmbedderInnerWindowID) {
   DEBUG_LOG("profiler_register_page(%" PRIu64 ", %" PRIu64 ", %s, %" PRIu64 ")",
@@ -5360,270 +5411,254 @@ UniqueProfilerBacktrace profiler_get_bac
   return UniqueProfilerBacktrace(
       new ProfilerBacktrace("SyncProfile", std::move(buffer)));
 }
 
 void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) {
   delete aBacktrace;
 }
 
+static void racy_profiler_add_marker(const char* aMarkerName,
+                                     JS::ProfilingCategoryPair aCategoryPair,
+                                     const ProfilerMarkerPayload* aPayload) {
+  MOZ_RELEASE_ASSERT(CorePS::Exists());
+
+  // This function is hot enough that we use RacyFeatures, not ActivePS.
+  if (!profiler_can_accept_markers()) {
+    return;
+  }
+
+  // Note that it's possible that the above test would change again before we
+  // actually record the marker. Because of this imprecision it's possible to
+  // miss a marker or record one we shouldn't. Either way is not a big deal.
+
+  RacyRegisteredThread* racyRegisteredThread =
+      TLSRegisteredThread::RacyRegisteredThread();
+  if (!racyRegisteredThread || !racyRegisteredThread->IsBeingProfiled()) {
+    return;
+  }
+
+  const MarkerTiming markerTiming =
+      aPayload ? get_marker_timing_from_payload(*aPayload)
+               : MarkerTiming::InstantNow();
+
+  StoreMarker(CorePS::CoreBuffer(), racyRegisteredThread->ThreadId(),
+              aMarkerName, markerTiming, aCategoryPair, aPayload);
+}
+
+void profiler_add_marker(const char* aMarkerName,
+                         JS::ProfilingCategoryPair aCategoryPair,
+                         const ProfilerMarkerPayload& aPayload) {
+  racy_profiler_add_marker(aMarkerName, aCategoryPair, &aPayload);
+}
+
 // 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, const char* aMarkerText) {
   PROFILER_MARKER_TEXT(
       ProfilerString8View::WrapNullTerminatedString(aMarkerName), JS, {},
       ProfilerString8View::WrapNullTerminatedString(aMarkerText));
 }
 
 void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info) {
   if (!profiler_can_accept_markers()) {
     return;
   }
-
-  struct JsAllocationMarker {
-    static constexpr mozilla::Span<const char> MarkerTypeName() {
-      return mozilla::MakeStringSpan("JS allocation");
-    }
-    static void StreamJSONMarkerData(
-        mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
-        const mozilla::ProfilerString16View& aTypeName,
-        const mozilla::ProfilerString8View& aClassName,
-        const mozilla::ProfilerString16View& aDescriptiveTypeName,
-        const mozilla::ProfilerString8View& aCoarseType, uint64_t aSize,
-        bool aInNursery) {
-      if (aClassName.Length() != 0) {
-        aWriter.StringProperty("className", aClassName);
-      }
-      if (aTypeName.Length() != 0) {
-        aWriter.StringProperty(
-            "typeName",
-            NS_ConvertUTF16toUTF8(aTypeName.Data(), aTypeName.Length()));
-      }
-      if (aDescriptiveTypeName.Length() != 0) {
-        aWriter.StringProperty(
-            "descriptiveTypeName",
-            NS_ConvertUTF16toUTF8(aDescriptiveTypeName.Data(),
-                                  aDescriptiveTypeName.Length()));
-      }
-      aWriter.StringProperty("coarseType", aCoarseType);
-      aWriter.IntProperty("size", aSize);
-      aWriter.BoolProperty("inNursery", aInNursery);
-    }
-    static mozilla::MarkerSchema MarkerTypeDisplay() {
-      return mozilla::MarkerSchema::SpecialFrontendLocation{};
-    }
-  };
-
+  AUTO_PROFILER_STATS(add_marker_with_JsAllocationMarkerPayload);
   profiler_add_marker(
-      "JS allocation", geckoprofiler::category::JS, MarkerStack::Capture(),
-      JsAllocationMarker{},
-      ProfilerString16View::WrapNullTerminatedString(info.typeName),
-      ProfilerString8View::WrapNullTerminatedString(info.className),
-      ProfilerString16View::WrapNullTerminatedString(info.descriptiveTypeName),
-      ProfilerString8View::WrapNullTerminatedString(info.coarseType), info.size,
-      info.inNursery);
+      "JS allocation", JS::ProfilingCategoryPair::JS,
+      JsAllocationMarkerPayload(TimeStamp::Now(), std::move(info),
+                                profiler_get_backtrace()));
 }
 
 bool profiler_is_locked_on_current_thread() {
   // This function is used to help users avoid calling `profiler_...` functions
   // when the profiler may already have a lock in place, which would prevent a
   // 2nd recursive lock (resulting in a crash or a never-ending wait).
   // So we must return `true` for any of:
   // - The main profiler mutex, used by most functions, and/or
   // - The buffer mutex, used directly in some functions without locking the
   //   main mutex, e.g., marker-related functions.
   return gPSMutex.IsLockedOnCurrentThread() ||
          CorePS::CoreBuffer().IsThreadSafeAndLockedOnCurrentThread();
 }
 
-static constexpr net::TimingStruct scEmptyNetTimingStruct;
-
 void profiler_add_network_marker(
     nsIURI* aURI, const nsACString& aRequestMethod, int32_t aPriority,
     uint64_t aChannelId, NetworkLoadType aType, mozilla::TimeStamp aStart,
     mozilla::TimeStamp aEnd, int64_t aCount,
     mozilla::net::CacheDisposition aCacheDisposition, uint64_t aInnerWindowID,
     const mozilla::net::TimingStruct* aTimings, nsIURI* aRedirectURI,
-    UniquePtr<ProfileChunkedBuffer> aSource,
+    UniqueProfilerBacktrace aSource,
     const Maybe<nsDependentCString>& aContentType) {
   if (!profiler_can_accept_markers()) {
     return;
   }
-
-  nsAutoCStringN<2048> name;
-  name.AppendASCII("Load ");
-  // top 32 bits are process id of the load
-  name.AppendInt(aChannelId & 0xFFFFFFFFu);
-
-  // These can do allocations/frees/etc; avoid if not active
-  nsAutoCStringN<2048> spec;
+  // These do allocations/frees/etc; avoid if not active
+  nsAutoCString spec;
+  nsAutoCString redirect_spec;
   if (aURI) {
     aURI->GetAsciiSpec(spec);
-    name.AppendASCII(": ");
-    name.Append(spec);
-  }
-
-  nsAutoCString redirect_spec;
+  }
   if (aRedirectURI) {
     aRedirectURI->GetAsciiSpec(redirect_spec);
   }
-
-  struct NetworkMarker {
-    static constexpr Span<const char> MarkerTypeName() {
-      return MakeStringSpan("Network");
+  // 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());
+  AUTO_PROFILER_STATS(add_marker_with_NetworkMarkerPayload);
+  profiler_add_marker(
+      name, JS::ProfilingCategoryPair::NETWORK,
+      NetworkMarkerPayload(static_cast<int64_t>(aChannelId),
+                           PromiseFlatCString(spec).get(), aRequestMethod,
+                           aType, aStart, aEnd, aPriority, aCount,
+                           aCacheDisposition, aInnerWindowID, aTimings,
+                           PromiseFlatCString(redirect_spec).get(),
+                           std::move(aSource), aContentType));
+}
+
+static void maybelocked_profiler_add_marker_for_thread(
+    int aThreadId, JS::ProfilingCategoryPair aCategoryPair,
+    const char* aMarkerName, const ProfilerMarkerPayload& aPayload,
+    const PSAutoLock* aLockOrNull) {
+  MOZ_RELEASE_ASSERT(CorePS::Exists());
+
+  if (!profiler_can_accept_markers()) {
+    return;
+  }
+
+#ifdef DEBUG
+  auto checkThreadId = [](int aThreadId, const PSAutoLock& aLock) {
+    if (!ActivePS::Exists(aLock)) {
+      return;
     }
-    static void StreamJSONMarkerData(
-        baseprofiler::SpliceableJSONWriter& aWriter, mozilla::TimeStamp aStart,
-        mozilla::TimeStamp aEnd, int64_t aID, const ProfilerString8View& aURI,
-        const ProfilerString8View& aRequestMethod, NetworkLoadType aType,
-        int32_t aPri, int64_t aCount, net::CacheDisposition aCacheDisposition,
-        const net::TimingStruct& aTimings,
-        const ProfilerString8View& aRedirectURI,
-        const ProfilerString8View& aContentType) {
-      // This payload still streams a startTime and endTime property because it
-      // made the migration to MarkerTiming on the front-end easier.
-      baseprofiler::WritePropertyTime(aWriter, "startTime", aStart);
-      baseprofiler::WritePropertyTime(aWriter, "endTime", aEnd);
-
-      aWriter.IntProperty("id", aID);
-      aWriter.StringProperty("status", GetNetworkState(aType));
-      if (Span<const char> cacheString = GetCacheState(aCacheDisposition);
-          !cacheString.IsEmpty()) {
-        aWriter.StringProperty("cache", cacheString);
-      }
-      aWriter.IntProperty("pri", aPri);
-      if (aCount > 0) {
-        aWriter.IntProperty("count", aCount);
-      }
-      if (aURI.Length() != 0) {
-        aWriter.StringProperty("URI", aURI);
-      }
-      if (aRedirectURI.Length() != 0) {
-        aWriter.StringProperty("RedirectURI", aRedirectURI);
-      }
-      aWriter.StringProperty("requestMethod", aRequestMethod);
-
-      if (aContentType.Length() != 0) {
-        aWriter.StringProperty("contentType", aContentType);
-      } else {
-        aWriter.NullProperty("contentType");
-      }
-
-      if (aType != NetworkLoadType::LOAD_START) {
-        baseprofiler::WritePropertyTime(aWriter, "domainLookupStart",
-                                        aTimings.domainLookupStart);
-        baseprofiler::WritePropertyTime(aWriter, "domainLookupEnd",
-                                        aTimings.domainLookupEnd);
-        baseprofiler::WritePropertyTime(aWriter, "connectStart",
-                                        aTimings.connectStart);
-        baseprofiler::WritePropertyTime(aWriter, "tcpConnectEnd",
-                                        aTimings.tcpConnectEnd);
-        baseprofiler::WritePropertyTime(aWriter, "secureConnectionStart",
-                                        aTimings.secureConnectionStart);
-        baseprofiler::WritePropertyTime(aWriter, "connectEnd",
-                                        aTimings.connectEnd);
-        baseprofiler::WritePropertyTime(aWriter, "requestStart",
-                                        aTimings.requestStart);
-        baseprofiler::WritePropertyTime(aWriter, "responseStart",
-                                        aTimings.responseStart);
-        baseprofiler::WritePropertyTime(aWriter, "responseEnd",
-                                        aTimings.responseEnd);
+
+    // Assert that our thread ID makes sense
+    bool realThread = false;
+    const Vector<UniquePtr<RegisteredThread>>& registeredThreads =
+        CorePS::RegisteredThreads(aLock);
+    for (auto& thread : registeredThreads) {
+      RefPtr<ThreadInfo> info = thread->Info();
+      if (info->ThreadId() == aThreadId) {
+        realThread = true;
+        break;
       }
     }
-    static MarkerSchema MarkerTypeDisplay() {
-      return MarkerSchema::SpecialFrontendLocation{};
-    }
-
-   private:
-    static Span<const char> GetNetworkState(NetworkLoadType aType) {
-      switch (aType) {
-        case NetworkLoadType::LOAD_START:
-          return MakeStringSpan("STATUS_START");
-        case NetworkLoadType::LOAD_STOP:
-          return MakeStringSpan("STATUS_STOP");
-        case NetworkLoadType::LOAD_REDIRECT:
-          return MakeStringSpan("STATUS_REDIRECT");
-        default:
-          return MakeStringSpan("");
-      }
-    }
-
-    static Span<const char> GetCacheState(
-        net::CacheDisposition aCacheDisposition) {
-      switch (aCacheDisposition) {
-        case net::kCacheUnresolved:
-          return MakeStringSpan("Unresolved");
-        case net::kCacheHit:
-          return MakeStringSpan("Hit");
-        case net::kCacheHitViaReval:
-          return MakeStringSpan("HitViaReval");
-        case net::kCacheMissedViaReval:
-          return MakeStringSpan("MissedViaReval");
-        case net::kCacheMissed:
-          return MakeStringSpan("Missed");
-        case net::kCacheUnknown:
-        default:
-          return MakeStringSpan("");
-      }
-    }
+    MOZ_ASSERT(realThread, "Invalid thread id");
   };
 
-  profiler_add_marker(
-      name, geckoprofiler::category::NETWORK,
-      {MarkerTiming::Interval(aStart, aEnd),
-       MarkerStack::TakeBacktrace(std::move(aSource)),
-       MarkerInnerWindowId(aInnerWindowID)},
-      NetworkMarker{}, aStart, aEnd, static_cast<int64_t>(aChannelId), spec,
-      aRequestMethod, aType, aPriority, aCount, aCacheDisposition,
-      aTimings ? *aTimings : scEmptyNetTimingStruct, redirect_spec,
-      aContentType ? ProfilerString8View(*aContentType)
-                   : ProfilerString8View());
+  if (aLockOrNull) {
+    checkThreadId(aThreadId, *aLockOrNull);
+  } else {
+    PSAutoLock lock(gPSMutex);
+    checkThreadId(aThreadId, lock);
+  }
+#endif
+
+  StoreMarker(CorePS::CoreBuffer(), aThreadId, aMarkerName,
+              get_marker_timing_from_payload(aPayload), aCategoryPair,
+              &aPayload);
 }
 
-bool profiler_add_native_allocation_marker(int64_t aSize,
+void profiler_add_marker_for_thread(int aThreadId,
+                                    JS::ProfilingCategoryPair aCategoryPair,
+                                    const char* aMarkerName,
+                                    const ProfilerMarkerPayload& aPayload) {
+  return maybelocked_profiler_add_marker_for_thread(
+      aThreadId, aCategoryPair, aMarkerName, aPayload, nullptr);
+}
+
+void profiler_add_marker_for_mainthread(JS::ProfilingCategoryPair aCategoryPair,
+                                        const char* aMarkerName,
+                                        const ProfilerMarkerPayload& aPayload) {
+  profiler_add_marker_for_thread(profiler_main_thread_id(), aCategoryPair,
+                                 aMarkerName, aPayload);
+}
+
+bool profiler_add_native_allocation_marker(int aMainThreadId, int64_t aSize,
                                            uintptr_t aMemoryAddress) {
   if (!profiler_can_accept_markers()) {
     return false;
   }
 
   // Because native allocations may be intercepted anywhere, blocking while
   // locking the profiler mutex here could end up causing a deadlock if another
   // mutex is taken, which the profiler may indirectly need elsewhere.
   // See bug 1642726 for such a scenario.
   // So instead we bail out if the mutex is already locked. Native allocations
   // are statistically sampled anyway, so missing a few because of this is
   // acceptable.
   if (gPSMutex.IsLockedOnCurrentThread()) {
     return false;
   }
 
-  struct NativeAllocationMarker {
-    static constexpr mozilla::Span<const char> MarkerTypeName() {
-      return mozilla::MakeStringSpan("Native allocation");
-    }
-    static void StreamJSONMarkerData(
-        mozilla::baseprofiler::SpliceableJSONWriter& aWriter, int64_t aSize,
-        uintptr_t aMemoryAddress, int aThreadId) {
-      aWriter.IntProperty("size", aSize);
-      aWriter.IntProperty("memoryAddress",
-                          static_cast<int64_t>(aMemoryAddress));
-      aWriter.IntProperty("threadId", aThreadId);
-    }
-    static mozilla::MarkerSchema MarkerTypeDisplay() {
-      return mozilla::MarkerSchema::SpecialFrontendLocation{};
-    }
-  };
-
-  profiler_add_marker("Native allocation", geckoprofiler::category::OTHER,
-                      {MarkerThreadId::MainThread(), MarkerStack::Capture()},
-                      NativeAllocationMarker{}, aSize, aMemoryAddress,
-                      profiler_current_thread_id());
+  AUTO_PROFILER_STATS(add_marker_with_NativeAllocationMarkerPayload);
+  maybelocked_profiler_add_marker_for_thread(
+      aMainThreadId, JS::ProfilingCategoryPair::OTHER, "Native allocation",
+      NativeAllocationMarkerPayload(TimeStamp::Now(), aSize, aMemoryAddress,
+                                    profiler_current_thread_id(),
+                                    profiler_get_backtrace()),
+      nullptr);
   return true;
 }
 
+void profiler_tracing_marker(const char* aCategoryString,
+                             const char* aMarkerName,
+                             JS::ProfilingCategoryPair aCategoryPair,
+                             TracingKind aKind,
+                             const Maybe<uint64_t>& aInnerWindowID) {
+  MOZ_RELEASE_ASSERT(CorePS::Exists());
+
+  VTUNE_TRACING(aMarkerName, aKind);
+
+  // This function is hot enough that we use RacyFeatures, notActivePS.
+  if (!profiler_can_accept_markers()) {
+    return;
+  }
+
+  AUTO_PROFILER_STATS(add_marker_with_TracingMarkerPayload);
+  profiler_add_marker(
+      aMarkerName, aCategoryPair,
+      TracingMarkerPayload(aCategoryString, aKind, TimeStamp::NowUnfuzzed(),
+                           aInnerWindowID));
+}
+
+void profiler_tracing_marker(const char* aCategoryString,
+                             const char* aMarkerName,
+                             JS::ProfilingCategoryPair aCategoryPair,
+                             TracingKind aKind, UniqueProfilerBacktrace aCause,
+                             const Maybe<uint64_t>& aInnerWindowID) {
+  MOZ_RELEASE_ASSERT(CorePS::Exists());
+
+  VTUNE_TRACING(aMarkerName, aKind);
+
+  // This function is hot enough that we use RacyFeatures, notActivePS.
+  if (!profiler_can_accept_markers()) {
+    return;
+  }
+
+  profiler_add_marker(
+      aMarkerName, aCategoryPair,
+      TracingMarkerPayload(aCategoryString, aKind, TimeStamp::NowUnfuzzed(),
+                           aInnerWindowID, std::move(aCause)));
+}
+
+void profiler_add_text_marker(const char* aMarkerName, const nsACString& aText,
+                              JS::ProfilingCategoryPair aCategoryPair,
+                              const mozilla::TimeStamp& aStartTime,
+                              const mozilla::TimeStamp& aEndTime,
+                              const mozilla::Maybe<uint64_t>& aInnerWindowID,
+                              UniqueProfilerBacktrace aCause) {
+  AUTO_PROFILER_STATS(add_marker_with_TextMarkerPayload);
+  profiler_add_marker(aMarkerName, aCategoryPair,
+                      TextMarkerPayload(aText, aStartTime, aEndTime,
+                                        aInnerWindowID, std::move(aCause)));
+}
+
 void profiler_set_js_context(JSContext* aCx) {
   MOZ_ASSERT(aCx);
 
   PSAutoLock lock(gPSMutex);
 
   RegisteredThread* registeredThread =
       TLSRegisteredThread::RegisteredThread(lock);
   if (!registeredThread) {
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -10,27 +10,29 @@ if CONFIG["MOZ_GECKO_PROFILER"]:
     XPIDL_SOURCES += [
         "gecko/nsIProfiler.idl",
     ]
     EXPORTS += [
         "public/ChildProfilerController.h",
         "public/GeckoProfilerReporter.h",
         "public/ProfilerChild.h",
         "public/ProfilerCodeAddressService.h",
+        "public/ProfilerMarkerPayload.h",
         "public/ProfilerParent.h",
         "public/shared-libraries.h",
     ]
     UNIFIED_SOURCES += [
         "core/PageInformation.cpp",
         "core/platform.cpp",
         "core/ProfileBuffer.cpp",
         "core/ProfileBufferEntry.cpp",
         "core/ProfiledThreadData.cpp",
         "core/ProfilerBacktrace.cpp",
         "core/ProfilerCodeAddressService.cpp",
+        "core/ProfilerMarkerPayload.cpp",
         "core/RegisteredThread.cpp",
         "gecko/ChildProfilerController.cpp",
         "gecko/nsProfilerStartParams.cpp",
         "gecko/ProfilerChild.cpp",
         "gecko/ProfilerIOInterposeObserver.cpp",
         "gecko/ProfilerParent.cpp",
     ]
     if CONFIG["MOZ_REPLACE_MALLOC"] and CONFIG["MOZ_PROFILER_MEMORY"]:
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -57,16 +57,27 @@
 #  define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(label, categoryPair, nsCStr)
 #  define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE( \
       label, categoryPair, nsCStr)
 #  define AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(label, categoryPair, nsStr)
 #  define AUTO_PROFILER_LABEL_FAST(label, categoryPair, ctx)
 #  define AUTO_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString, categoryPair, \
                                            ctx, flags)
 
+#  define PROFILER_ADD_MARKER_WITH_PAYLOAD(markerName, categoryPair, \
+                                           PayloadType, payloadArgs)
+
+#  define PROFILER_TRACING_MARKER(categoryString, markerName, categoryPair, \
+                                  kind)
+#  define PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \
+                                           categoryPair, kind, docshell)
+#  define AUTO_PROFILER_TRACING_MARKER(categoryString, markerName, categoryPair)
+#  define AUTO_PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \
+                                                categoryPair, docShell)
+
 // Function stubs for when MOZ_GECKO_PROFILER is not defined.
 
 // This won't be used, it's just there to allow the empty definition of
 // `profiler_get_backtrace`.
 struct ProfilerBacktrace {};
 using UniqueProfilerBacktrace = mozilla::UniquePtr<int>;
 
 // Get/Capture-backtrace functions can return nullptr or false, the result
@@ -104,16 +115,17 @@ profiler_capture_backtrace() {
 #  include "nscore.h"
 #  include "nsString.h"
 
 #  include <functional>
 #  include <stdint.h>
 
 class ProfilerBacktrace;
 class ProfilerCodeAddressService;
+class ProfilerMarkerPayload;
 namespace mozilla {
 class ProfileBufferControlledChunkManager;
 class ProfileChunkedBuffer;
 namespace baseprofiler {
 class SpliceableJSONWriter;
 }  // namespace baseprofiler
 namespace net {
 struct TimingStruct;
@@ -873,39 +885,68 @@ mozilla::Maybe<ProfilerBufferInfo> profi
 // additional set of flags. The flags parameter should carry values from the
 // js::ProfilingStackFrame::Flags enum.
 #  define AUTO_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString, categoryPair, \
                                            ctx, flags)                         \
     mozilla::AutoProfilerLabel PROFILER_RAII(                                  \
         ctx, label, dynamicString, JS::ProfilingCategoryPair::categoryPair,    \
         flags)
 
+// `PayloadType` is a sub-class of MarkerPayload, `parenthesizedPayloadArgs` is
+// the argument list used to construct that `PayloadType`. E.g.:
+// `PROFILER_ADD_MARKER_WITH_PAYLOAD("Load", DOM, TextMarkerPayload,
+//                                   ("text", start, end, ds, dsh))`
+#  define PROFILER_ADD_MARKER_WITH_PAYLOAD(                            \
+      markerName, categoryPair, PayloadType, parenthesizedPayloadArgs) \
+    do {                                                               \
+      AUTO_PROFILER_STATS(add_marker_with_##PayloadType);              \
+      ::profiler_add_marker(markerName,                                \
+                            ::JS::ProfilingCategoryPair::categoryPair, \
+                            PayloadType parenthesizedPayloadArgs);     \
+    } while (false)
+
+void profiler_add_marker(const char* aMarkerName,
+                         JS::ProfilingCategoryPair aCategoryPair,
+                         const ProfilerMarkerPayload& aPayload);
+
 void profiler_add_js_marker(const char* aMarkerName, const char* aMarkerText);
 void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info);
 
 // Returns true or or false depending on whether the marker was actually added
 // or not.
-bool profiler_add_native_allocation_marker(int64_t aSize,
-                                           uintptr_t aMemoryAddress);
+bool profiler_add_native_allocation_marker(int aMainThreadId, int64_t aSize,
+                                           uintptr_t aMemorySize);
 
 // Returns true if the profiler lock is currently held *on the current thread*.
 // This may be used by re-entrant code that may call profiler functions while
 // the profiler already has the lock (which would deadlock).
 bool profiler_is_locked_on_current_thread();
 
+// Insert a marker in the profile timeline for a specified thread.
+void profiler_add_marker_for_thread(int aThreadId,
+                                    JS::ProfilingCategoryPair aCategoryPair,
+                                    const char* aMarkerName,
+                                    const ProfilerMarkerPayload& aPayload);
+
+// Insert a marker in the profile timeline for the main thread.
+// This may be used to gather some markers from any thread, that should be
+// displayed in the main thread track.
+void profiler_add_marker_for_mainthread(JS::ProfilingCategoryPair aCategoryPair,
+                                        const char* aMarkerName,
+                                        const ProfilerMarkerPayload& aPayload);
+
 enum class NetworkLoadType { LOAD_START, LOAD_STOP, LOAD_REDIRECT };
 
 void profiler_add_network_marker(
     nsIURI* aURI, const nsACString& aRequestMethod, int32_t aPriority,
     uint64_t aChannelId, NetworkLoadType aType, mozilla::TimeStamp aStart,
     mozilla::TimeStamp aEnd, int64_t aCount,
     mozilla::net::CacheDisposition aCacheDisposition, uint64_t aInnerWindowID,
     const mozilla::net::TimingStruct* aTimings = nullptr,
-    nsIURI* aRedirectURI = nullptr,
-    mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource = nullptr,
+    nsIURI* aRedirectURI = nullptr, UniqueProfilerBacktrace aSource = nullptr,
     const mozilla::Maybe<nsDependentCString>& aContentType =
         mozilla::Nothing());
 
 enum TracingKind {
   TRACING_EVENT,
   TRACING_INTERVAL_START,
   TRACING_INTERVAL_END,
 };
@@ -923,16 +964,50 @@ inline mozilla::MarkerInnerWindowId Mark
   mozilla::Maybe<uint64_t> id =
       profiler_get_inner_window_id_from_docshell(aDocshell);
   if (!id) {
     return mozilla::MarkerInnerWindowId::NoId();
   }
   return mozilla::MarkerInnerWindowId(*id);
 }
 
+// Adds a tracing marker to the profile. A no-op if the profiler is inactive.
+
+#  define PROFILER_TRACING_MARKER(categoryString, markerName, categoryPair, \
+                                  kind)                                     \
+    profiler_tracing_marker(categoryString, markerName,                     \
+                            JS::ProfilingCategoryPair::categoryPair, kind)
+#  define PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName,       \
+                                           categoryPair, kind, docShell)     \
+    profiler_tracing_marker(                                                 \
+        categoryString, markerName, JS::ProfilingCategoryPair::categoryPair, \
+        kind, profiler_get_inner_window_id_from_docshell(docShell))
+
+void profiler_tracing_marker(
+    const char* aCategoryString, const char* aMarkerName,
+    JS::ProfilingCategoryPair aCategoryPair, TracingKind aKind,
+    const mozilla::Maybe<uint64_t>& aInnerWindowID = mozilla::Nothing());
+void profiler_tracing_marker(
+    const char* aCategoryString, const char* aMarkerName,
+    JS::ProfilingCategoryPair aCategoryPair, TracingKind aKind,
+    UniqueProfilerBacktrace aCause,
+    const mozilla::Maybe<uint64_t>& aInnerWindowID = mozilla::Nothing());
+
+// Adds a START/END pair of tracing markers.
+#  define AUTO_PROFILER_TRACING_MARKER(categoryString, markerName,           \
+                                       categoryPair)                         \
+    mozilla::AutoProfilerTracing PROFILER_RAII(                              \
+        categoryString, markerName, JS::ProfilingCategoryPair::categoryPair, \
+        mozilla::Nothing())
+#  define AUTO_PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName,  \
+                                                categoryPair, docShell)      \
+    mozilla::AutoProfilerTracing PROFILER_RAII(                              \
+        categoryString, markerName, JS::ProfilingCategoryPair::categoryPair, \
+        profiler_get_inner_window_id_from_docshell(docShell))
+
 //---------------------------------------------------------------------------
 // Output profiles
 //---------------------------------------------------------------------------
 
 // Set a user-friendly process name, used in JSON stream.  Allows an optional
 // detailed name which may include private info (eTLD+1 in fission)
 void profiler_set_process_name(const nsACString& aProcessName,
                                const nsACString* aETLDplus1 = nullptr);
@@ -1145,16 +1220,54 @@ class MOZ_RAII AutoProfilerLabel {
 
     enum class State { Uninitialized = 0, Initialized, Unavailable };
     static State sState;
 
     static MOZ_THREAD_LOCAL(ProfilingStackOwner*) sProfilingStackOwnerTLS;
   };
 };
 
+class MOZ_RAII AutoProfilerTracing {
+ public:
+  AutoProfilerTracing(const char* aCategoryString, const char* aMarkerName,
+                      JS::ProfilingCategoryPair aCategoryPair,
+                      const mozilla::Maybe<uint64_t>& aInnerWindowID)
+      : mCategoryString(aCategoryString),
+        mMarkerName(aMarkerName),
+        mCategoryPair(aCategoryPair),
+        mInnerWindowID(aInnerWindowID) {
+    profiler_tracing_marker(mCategoryString, mMarkerName, aCategoryPair,
+                            TRACING_INTERVAL_START, mInnerWindowID);
+  }
+
+  AutoProfilerTracing(const char* aCategoryString, const char* aMarkerName,
+                      JS::ProfilingCategoryPair aCategoryPair,
+                      UniqueProfilerBacktrace aBacktrace,
+                      const mozilla::Maybe<uint64_t>& aInnerWindowID)
+      : mCategoryString(aCategoryString),
+        mMarkerName(aMarkerName),
+        mCategoryPair(aCategoryPair),
+        mInnerWindowID(aInnerWindowID) {
+    profiler_tracing_marker(mCategoryString, mMarkerName, aCategoryPair,
+                            TRACING_INTERVAL_START, std::move(aBacktrace),
+                            mInnerWindowID);
+  }
+
+  ~AutoProfilerTracing() {
+    profiler_tracing_marker(mCategoryString, mMarkerName, mCategoryPair,
+                            TRACING_INTERVAL_END, mInnerWindowID);
+  }
+
+ protected:
+  const char* mCategoryString;
+  const char* mMarkerName;
+  const JS::ProfilingCategoryPair mCategoryPair;
+  const mozilla::Maybe<uint64_t> mInnerWindowID;
+};
+
 // 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
 // each variable to be set.
 void GetProfilerEnvVarsForChildProcess(
     std::function<void(const char* key, const char* value)>&& aSetEnv);
 
new file mode 100644
--- /dev/null
+++ b/tools/profiler/public/ProfilerMarkerPayload.h
@@ -0,0 +1,839 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ProfilerMarkerPayload_h
+#define ProfilerMarkerPayload_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/net/TimingStruct.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ProfileBufferEntrySerialization.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+
+#include "nsString.h"
+#include "nsCRTGlue.h"
+#include "GeckoProfiler.h"
+
+#include "js/Utility.h"
+#include "js/AllocationRecording.h"
+#include "js/ProfilingFrameIterator.h"
+#include "gfxASurface.h"
+#include "mozilla/ServoTraversalStatistics.h"
+
+namespace mozilla {
+namespace baseprofiler {
+class SpliceableJSONWriter;
+}  // namespace baseprofiler
+namespace layers {
+class Layer;
+}  // namespace layers
+}  // namespace mozilla
+
+class UniqueStacks;
+
+// This is an abstract class that can be implemented to supply data to be
+// attached with a profiler marker.
+//
+// When subclassing this, note that the destructor can be called on any thread,
+// i.e. not necessarily on the thread that created the object.
+class ProfilerMarkerPayload {
+ public:
+  explicit ProfilerMarkerPayload(
+      const mozilla::Maybe<uint64_t>& aInnerWindowID = mozilla::Nothing(),
+      UniqueProfilerBacktrace aStack = nullptr)
+      : mCommonProps{mozilla::TimeStamp{}, mozilla::TimeStamp{},
+                     std::move(aStack), aInnerWindowID} {}
+
+  ProfilerMarkerPayload(
+      const mozilla::TimeStamp& aStartTime, const mozilla::TimeStamp& aEndTime,
+      const mozilla::Maybe<uint64_t>& aInnerWindowID = mozilla::Nothing(),
+      UniqueProfilerBacktrace aStack = nullptr)
+      : mCommonProps{aStartTime, aEndTime, std::move(aStack), aInnerWindowID} {}
+
+  virtual ~ProfilerMarkerPayload() = default;
+
+  // Compute the number of bytes needed to serialize the `DeserializerTag` and
+  // payload, including in the no-payload (nullptr) case.
+  static mozilla::ProfileBufferEntryWriter::Length TagAndSerializationBytes(
+      const ProfilerMarkerPayload* aPayload) {
+    if (!aPayload) {
+      return sizeof(DeserializerTag);
+    }
+    return aPayload->TagAndSerializationBytes();
+  }
+
+  // Serialize the payload into an EntryWriter, including in the no-payload
+  // (nullptr) case. Must be of the exact size given by
+  // `TagAndSerializationBytes(aPayload)`.
+  static void TagAndSerialize(const ProfilerMarkerPayload* aPayload,
+                              mozilla::ProfileBufferEntryWriter& aEntryWriter) {
+    if (!aPayload) {
+      aEntryWriter.WriteObject(DeserializerTag(0));
+      return;
+    }
+    aPayload->SerializeTagAndPayload(aEntryWriter);
+  }
+
+  // Deserialize a payload from an EntryReader, including in the no-payload
+  // (nullptr) case.
+  static mozilla::UniquePtr<ProfilerMarkerPayload> DeserializeTagAndPayload(
+      mozilla::ProfileBufferEntryReader& aER) {
+    const auto tag = aER.ReadObject<DeserializerTag>();
+    Deserializer deserializer = DeserializerForTag(tag);
+    return deserializer(aER);
+  }
+
+  virtual void StreamPayload(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+      const mozilla::TimeStamp& aProcessStartTime,
+      UniqueStacks& aUniqueStacks) const = 0;
+
+  const mozilla::TimeStamp& GetStartTime() const {
+    return mCommonProps.mStartTime;
+  }
+  const mozilla::TimeStamp& GetEndTime() const { return mCommonProps.mEndTime; }
+
+ protected:
+  // A `Deserializer` is a free function that can read a serialized payload from
+  // an `EntryReader` and return a reconstructed `ProfilerMarkerPayload`
+  // sub-object (may be null if there was no payload).
+  typedef mozilla::UniquePtr<ProfilerMarkerPayload> (*Deserializer)(
+      mozilla::ProfileBufferEntryReader&);
+
+  // A `DeserializerTag` will be added before the payload, to help select the
+  // correct deserializer when reading back the payload.
+  using DeserializerTag = unsigned char;
+
+  // This needs to be big enough to handle all possible sub-types of
+  // ProfilerMarkerPayload.
+  static constexpr DeserializerTag DeserializerMax = 32;
+
+  // We need an atomic type that can hold a `DeserializerTag`. (Atomic doesn't
+  // work with too-small types.)
+  using DeserializerTagAtomic = int;
+
+  // Number of currently-registered deserializers.
+  static mozilla::Atomic<DeserializerTagAtomic, mozilla::ReleaseAcquire>
+      sDeserializerCount;
+
+  // List of currently-registered deserializers.
+  // sDeserializers[0] is a no-payload deserializer.
+  static Deserializer sDeserializers[DeserializerMax];
+
+  // Get the `DeserializerTag` for a `Deserializer` (which gets registered on
+  // the first call.) Tag 0 means no payload; a null `aDeserializer` gives that
+  // 0 tag.
+  static DeserializerTag TagForDeserializer(Deserializer aDeserializer);
+
+  // Get the `Deserializer` for a given `DeserializerTag`.
+  // Tag 0 is reserved as no-payload deserializer (which returns nullptr).
+  static Deserializer DeserializerForTag(DeserializerTag aTag);
+
+  struct CommonProps {
+    mozilla::TimeStamp mStartTime;
+    mozilla::TimeStamp mEndTime;
+    UniqueProfilerBacktrace mStack;
+    mozilla::Maybe<uint64_t> mInnerWindowID;
+  };
+
+  // Deserializers can use this base constructor.
+  explicit ProfilerMarkerPayload(CommonProps&& aCommonProps)
+      : mCommonProps(std::move(aCommonProps)) {}
+
+  // Serialization/deserialization of common props in ProfilerMarkerPayload.
+  mozilla::ProfileBufferEntryWriter::Length
+  CommonPropsTagAndSerializationBytes() const;
+  void SerializeTagAndCommonProps(
+      DeserializerTag aDeserializerTag,
+      mozilla::ProfileBufferEntryWriter& aEntryWriter) const;
+  static CommonProps DeserializeCommonProps(
+      mozilla::ProfileBufferEntryReader& aEntryReader);
+
+  void StreamType(const char* aMarkerType,
+                  mozilla::baseprofiler::SpliceableJSONWriter& aWriter) const;
+
+  void StreamCommonProps(const char* aMarkerType,
+                         mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+                         const mozilla::TimeStamp& aProcessStartTime,
+                         UniqueStacks& aUniqueStacks) const;
+  void StreamStartEndTime(mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+                          const mozilla::TimeStamp& aProcessStartTime) const;
+
+ private:
+  // Compute the number of bytes needed to serialize payload in
+  // `SerializeTagAndPayload` below.
+  virtual mozilla::ProfileBufferEntryWriter::Length TagAndSerializationBytes()
+      const = 0;
+
+  // Serialize the payload into an EntryWriter.
+  // Must be of the exact size given by `TagAndSerializationBytes()`.
+  virtual void SerializeTagAndPayload(
+      mozilla::ProfileBufferEntryWriter& aEntryWriter) const = 0;
+
+  CommonProps mCommonProps;
+};
+
+#define DECL_STREAM_PAYLOAD                                                    \
+  void StreamPayload(mozilla::baseprofiler::SpliceableJSONWriter& aWriter,     \
+                     const mozilla::TimeStamp& aProcessStartTime,              \
+                     UniqueStacks& aUniqueStacks) const override;              \
+  static mozilla::UniquePtr<ProfilerMarkerPayload> Deserialize(                \
+      mozilla::ProfileBufferEntryReader& aEntryReader);                        \
+  mozilla::ProfileBufferEntryWriter::Length TagAndSerializationBytes()         \
+      const override;                                                          \
+  void SerializeTagAndPayload(mozilla::ProfileBufferEntryWriter& aEntryWriter) \
+      const override;
+
+// TODO: Increase the coverage of tracing markers that include InnerWindowID
+// information
+class TracingMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  TracingMarkerPayload(
+      const char* aCategory, TracingKind aKind, const mozilla::TimeStamp& aTime,
+      const mozilla::Maybe<uint64_t>& aInnerWindowID = mozilla::Nothing(),
+      UniqueProfilerBacktrace aCause = nullptr)
+      : ProfilerMarkerPayload((aKind != TracingKind::TRACING_INTERVAL_END)
+                                  ? aTime
+                                  : mozilla::TimeStamp{},
+                              (aKind != TracingKind::TRACING_INTERVAL_START)
+                                  ? aTime
+                                  : mozilla::TimeStamp{},
+                              aInnerWindowID, std::move(aCause)),
+        mCategory(aCategory),
+        mKind(aKind) {}
+
+  TracingMarkerPayload(const char* aCategory, const mozilla::TimeStamp& aStart,
+                       const mozilla::TimeStamp& aEnd)
+      : ProfilerMarkerPayload(aStart, aEnd, mozilla::Nothing(), nullptr),
+        mCategory(aCategory),
+        mKind(TRACING_EVENT) {}
+
+  DECL_STREAM_PAYLOAD
+
+ protected:
+  TracingMarkerPayload(CommonProps&& aCommonProps, const char* aCategory,
+                       TracingKind aKind)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mCategory(aCategory),
+        mKind(aKind) {}
+
+  // May be used by derived classes.
+  void SerializeTagAndPayload(
+      DeserializerTag aDeserializerTag,
+      mozilla::ProfileBufferEntryWriter& aEntryWriter) const;
+
+ private:
+  const char* mCategory;
+  TracingKind mKind;
+};
+
+class BudgetMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  BudgetMarkerPayload(const mozilla::TimeStamp& aStartTime,
+                      const mozilla::TimeStamp& aEndTime)
+      : ProfilerMarkerPayload(aStartTime, aEndTime) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  explicit BudgetMarkerPayload(CommonProps&& aCommonProps)
+      : ProfilerMarkerPayload(std::move(aCommonProps)) {}
+};
+
+class PrefMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  PrefMarkerPayload(const char* aPrefName,
+                    const mozilla::Maybe<mozilla::PrefValueKind>& aPrefKind,
+                    const mozilla::Maybe<mozilla::PrefType>& aPrefType,
+                    const nsCString& aPrefValue,
+                    const mozilla::TimeStamp& aPrefAccessTime)
+      : ProfilerMarkerPayload(aPrefAccessTime, aPrefAccessTime),
+        mPrefAccessTime(aPrefAccessTime),
+        mPrefName(aPrefName),
+        mPrefKind(aPrefKind),
+        mPrefType(aPrefType),
+        mPrefValue(aPrefValue) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  PrefMarkerPayload(CommonProps&& aCommonProps,
+                    mozilla::TimeStamp aPrefAccessTime, nsCString&& aPrefName,
+                    mozilla::Maybe<mozilla::PrefValueKind>&& aPrefKind,
+                    mozilla::Maybe<mozilla::PrefType>&& aPrefType,
+                    nsCString&& aPrefValue)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mPrefAccessTime(aPrefAccessTime),
+        mPrefName(aPrefName),
+        mPrefKind(aPrefKind),
+        mPrefType(aPrefType),
+        mPrefValue(aPrefValue) {}
+
+  mozilla::TimeStamp mPrefAccessTime;
+  nsCString mPrefName;
+  // Nothing means this is a shared preference. Something, on the other hand,
+  // holds an actual PrefValueKind indicating either a Default or User
+  // preference.
+  mozilla::Maybe<mozilla::PrefValueKind> mPrefKind;
+  // Nothing means that the mPrefName preference was not found. Something
+  // contains the type of the preference.
+  mozilla::Maybe<mozilla::PrefType> mPrefType;
+  nsCString mPrefValue;
+};
+
+class UserTimingMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  UserTimingMarkerPayload(const nsAString& aName,
+                          const mozilla::TimeStamp& aStartTime,
+                          const mozilla::Maybe<uint64_t>& aInnerWindowID)
+      : ProfilerMarkerPayload(aStartTime, aStartTime, aInnerWindowID),
+        mEntryType("mark"),
+        mName(aName) {}
+
+  UserTimingMarkerPayload(const nsAString& aName,
+                          const mozilla::Maybe<nsString>& aStartMark,
+                          const mozilla::Maybe<nsString>& aEndMark,
+                          const mozilla::TimeStamp& aStartTime,
+                          const mozilla::TimeStamp& aEndTime,
+                          const mozilla::Maybe<uint64_t>& aInnerWindowID)
+      : ProfilerMarkerPayload(aStartTime, aEndTime, aInnerWindowID),
+        mEntryType("measure"),
+        mName(aName),
+        mStartMark(aStartMark),
+        mEndMark(aEndMark) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  UserTimingMarkerPayload(CommonProps&& aCommonProps, const char* aEntryType,
+                          nsString&& aName,
+                          mozilla::Maybe<nsString>&& aStartMark,
+                          mozilla::Maybe<nsString>&& aEndMark)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mEntryType(aEntryType),
+        mName(std::move(aName)),
+        mStartMark(std::move(aStartMark)),
+        mEndMark(std::move(aEndMark)) {}
+
+  // Either "mark" or "measure".
+  const char* mEntryType;
+  nsString mName;
+  mozilla::Maybe<nsString> mStartMark;
+  mozilla::Maybe<nsString> mEndMark;
+};
+
+// Contains the translation applied to a 2d layer so we can track the layer
+// position at each frame.
+class LayerTranslationMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  LayerTranslationMarkerPayload(mozilla::layers::Layer* aLayer,
+                                mozilla::gfx::Point aPoint,
+                                mozilla::TimeStamp aStartTime)
+      : ProfilerMarkerPayload(aStartTime, aStartTime),
+        mLayer(aLayer),
+        mPoint(aPoint) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  LayerTranslationMarkerPayload(CommonProps&& aCommonProps,
+                                mozilla::layers::Layer* aLayer,
+                                mozilla::gfx::Point aPoint)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mLayer(aLayer),
+        mPoint(aPoint) {}
+
+  mozilla::layers::Layer* mLayer;
+  mozilla::gfx::Point mPoint;
+};
+
+#include "Units.h"  // For ScreenIntPoint
+
+// Tracks when a vsync occurs according to the HardwareComposer.
+class VsyncMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  explicit VsyncMarkerPayload(mozilla::TimeStamp aVsyncTimestamp)
+      : ProfilerMarkerPayload(aVsyncTimestamp, aVsyncTimestamp) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  explicit VsyncMarkerPayload(CommonProps&& aCommonProps)
+      : ProfilerMarkerPayload(std::move(aCommonProps)) {}
+};
+
+class NetworkMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  NetworkMarkerPayload(int64_t aID, const char* aURI,
+                       const nsACString& aRequestMethod, NetworkLoadType aType,
+                       const mozilla::TimeStamp& aStartTime,
+                       const mozilla::TimeStamp& aEndTime, int32_t aPri,
+                       int64_t aCount,
+                       mozilla::net::CacheDisposition aCacheDisposition,
+                       uint64_t aInnerWindowID,
+                       const mozilla::net::TimingStruct* aTimings = nullptr,
+                       const char* aRedirectURI = nullptr,
+                       UniqueProfilerBacktrace aSource = nullptr,
+                       const mozilla::Maybe<nsDependentCString>& aContentType =
+                           mozilla::Nothing())
+      : ProfilerMarkerPayload(aStartTime, aEndTime,
+                              mozilla::Some(aInnerWindowID),
+                              std::move(aSource)),
+        mID(aID),
+        mURI(aURI ? strdup(aURI) : nullptr),
+        mRedirectURI(aRedirectURI && (strlen(aRedirectURI) > 0)
+                         ? strdup(aRedirectURI)
+                         : nullptr),
+        mRequestMethod(aRequestMethod),
+        mType(aType),
+        mPri(aPri),
+        mCount(aCount),
+        mCacheDisposition(aCacheDisposition),
+        mContentType(aContentType) {
+    if (aTimings) {
+      mTimings = *aTimings;
+    }
+  }
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  NetworkMarkerPayload(CommonProps&& aCommonProps, int64_t aID,
+                       mozilla::UniqueFreePtr<char>&& aURI,
+                       mozilla::UniqueFreePtr<char>&& aRedirectURI,
+                       nsCString&& aRequestMethod, NetworkLoadType aType,
+                       int32_t aPri, int64_t aCount,
+                       mozilla::net::TimingStruct aTimings,
+                       mozilla::net::CacheDisposition aCacheDisposition,
+                       mozilla::Maybe<nsAutoCString>&& aContentType)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mID(aID),
+        mURI(std::move(aURI)),
+        mRedirectURI(std::move(aRedirectURI)),
+        mRequestMethod(std::move(aRequestMethod)),
+        mType(aType),
+        mPri(aPri),
+        mCount(aCount),
+        mTimings(aTimings),
+        mCacheDisposition(aCacheDisposition),
+        mContentType(std::move(aContentType)) {}
+
+  int64_t mID;
+  mozilla::UniqueFreePtr<char> mURI;
+  mozilla::UniqueFreePtr<char> mRedirectURI;
+  // Request method and content type further down are usually short,
+  // e.g., "GET" and "text/html", so we use nsAutoCString to reduce
+  // heap usage; the bigger object size is acceptable here because
+  // markers are short-lived on-stack objects.
+  nsAutoCString mRequestMethod;
+  NetworkLoadType mType;
+  int32_t mPri;
+  int64_t mCount;
+  mozilla::net::TimingStruct mTimings;
+  mozilla::net::CacheDisposition mCacheDisposition;
+  mozilla::Maybe<nsAutoCString> mContentType;
+};
+
+class ScreenshotPayload : public ProfilerMarkerPayload {
+ public:
+  explicit ScreenshotPayload(mozilla::TimeStamp aTimeStamp,
+                             nsCString&& aScreenshotDataURL,
+                             const mozilla::gfx::IntSize& aWindowSize,
+                             uintptr_t aWindowIdentifier)
+      : ProfilerMarkerPayload(aTimeStamp, mozilla::TimeStamp()),
+        mScreenshotDataURL(std::move(aScreenshotDataURL)),
+        mWindowSize(aWindowSize),
+        mWindowIdentifier(aWindowIdentifier) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  ScreenshotPayload(CommonProps&& aCommonProps, nsCString&& aScreenshotDataURL,
+                    mozilla::gfx::IntSize aWindowSize,
+                    uintptr_t aWindowIdentifier)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mScreenshotDataURL(std::move(aScreenshotDataURL)),
+        mWindowSize(aWindowSize),
+        mWindowIdentifier(aWindowIdentifier) {}
+
+  nsCString mScreenshotDataURL;
+  mozilla::gfx::IntSize mWindowSize;
+  uintptr_t mWindowIdentifier;
+};
+
+class GCSliceMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  GCSliceMarkerPayload(const mozilla::TimeStamp& aStartTime,
+                       const mozilla::TimeStamp& aEndTime,
+                       JS::UniqueChars&& aTimingJSON)
+      : ProfilerMarkerPayload(aStartTime, aEndTime),
+        mTimingJSON(std::move(aTimingJSON)) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  GCSliceMarkerPayload(CommonProps&& aCommonProps,
+                       JS::UniqueChars&& aTimingJSON)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mTimingJSON(std::move(aTimingJSON)) {}
+
+  JS::UniqueChars mTimingJSON;
+};
+
+class GCMajorMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  GCMajorMarkerPayload(const mozilla::TimeStamp& aStartTime,
+                       const mozilla::TimeStamp& aEndTime,
+                       JS::UniqueChars&& aTimingJSON)
+      : ProfilerMarkerPayload(aStartTime, aEndTime),
+        mTimingJSON(std::move(aTimingJSON)) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  GCMajorMarkerPayload(CommonProps&& aCommonProps,
+                       JS::UniqueChars&& aTimingJSON)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mTimingJSON(std::move(aTimingJSON)) {}
+
+  JS::UniqueChars mTimingJSON;
+};
+
+class GCMinorMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  GCMinorMarkerPayload(const mozilla::TimeStamp& aStartTime,
+                       const mozilla::TimeStamp& aEndTime,
+                       JS::UniqueChars&& aTimingData)
+      : ProfilerMarkerPayload(aStartTime, aEndTime),
+        mTimingData(std::move(aTimingData)) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  GCMinorMarkerPayload(CommonProps&& aCommonProps,
+                       JS::UniqueChars&& aTimingData)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mTimingData(std::move(aTimingData)) {}
+
+  JS::UniqueChars mTimingData;
+};
+
+class HangMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  HangMarkerPayload(const mozilla::TimeStamp& aStartTime,
+                    const mozilla::TimeStamp& aEndTime)
+      : ProfilerMarkerPayload(aStartTime, aEndTime) {}
+
+  DECL_STREAM_PAYLOAD
+ private:
+  explicit HangMarkerPayload(CommonProps&& aCommonProps)
+      : ProfilerMarkerPayload(std::move(aCommonProps)) {}
+};
+
+class StyleMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  StyleMarkerPayload(const mozilla::TimeStamp& aStartTime,
+                     const mozilla::TimeStamp& aEndTime,
+                     UniqueProfilerBacktrace aCause,
+                     const mozilla::ServoTraversalStatistics& aStats,
+                     const mozilla::Maybe<uint64_t>& aInnerWindowID)
+      : ProfilerMarkerPayload(aStartTime, aEndTime, aInnerWindowID,
+                              std::move(aCause)),
+        mStats(aStats) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  StyleMarkerPayload(CommonProps&& aCommonProps,
+                     mozilla::ServoTraversalStatistics aStats)
+      : ProfilerMarkerPayload(std::move(aCommonProps)), mStats(aStats) {}
+
+  mozilla::ServoTraversalStatistics mStats;
+};
+
+class LongTaskMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  LongTaskMarkerPayload(const mozilla::TimeStamp& aStartTime,
+                        const mozilla::TimeStamp& aEndTime)
+      : ProfilerMarkerPayload(aStartTime, aEndTime) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  explicit LongTaskMarkerPayload(CommonProps&& aCommonProps)
+      : ProfilerMarkerPayload(std::move(aCommonProps)) {}
+};
+
+class TimingMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  TimingMarkerPayload(const mozilla::TimeStamp& aStartTime,
+                      const mozilla::TimeStamp& aEndTime)
+      : ProfilerMarkerPayload(aStartTime, aEndTime) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  explicit TimingMarkerPayload(CommonProps&& aCommonProps)
+      : ProfilerMarkerPayload(std::move(aCommonProps)) {}
+};
+
+class TextMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  TextMarkerPayload(const nsACString& aText,
+                    const mozilla::TimeStamp& aStartTime)
+      : ProfilerMarkerPayload(aStartTime, aStartTime), mText(aText) {}
+
+  TextMarkerPayload(const nsACString& aText,
+                    const mozilla::TimeStamp& aStartTime,
+                    const mozilla::TimeStamp& aEndTime)
+      : ProfilerMarkerPayload(aStartTime, aEndTime), mText(aText) {}
+
+  TextMarkerPayload(const nsACString& aText,
+                    const mozilla::TimeStamp& aStartTime,
+                    const mozilla::Maybe<uint64_t>& aInnerWindowID)
+      : ProfilerMarkerPayload(aStartTime, aStartTime, aInnerWindowID),
+        mText(aText) {}
+
+  TextMarkerPayload(const nsACString& aText,
+                    const mozilla::TimeStamp& aStartTime,
+                    const mozilla::TimeStamp& aEndTime,
+                    const mozilla::Maybe<uint64_t>& aInnerWindowID,
+                    UniqueProfilerBacktrace aCause = nullptr)
+      : ProfilerMarkerPayload(aStartTime, aEndTime, aInnerWindowID,
+                              std::move(aCause)),
+        mText(aText) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  TextMarkerPayload(CommonProps&& aCommonProps, nsCString&& aText)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mText(std::move(aText)) {}
+
+  nsCString mText;
+};
+
+class LogMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  LogMarkerPayload(const char* aModule, const char* aText,
+                   const mozilla::TimeStamp& aStartTime)
+      : ProfilerMarkerPayload(aStartTime, aStartTime),
+        mModule(aModule),
+        mText(aText) {}
+
+  LogMarkerPayload(const char* aModule, const char* aText,
+                   const mozilla::TimeStamp& aStartTime,
+                   const mozilla::TimeStamp& aEndTime)
+      : ProfilerMarkerPayload(aStartTime, aEndTime),
+        mModule(aModule),
+        mText(aText) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  LogMarkerPayload(CommonProps&& aCommonProps, nsAutoCStringN<32>&& aModule,
+                   nsCString&& aText)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mModule(std::move(aModule)),
+        mText(std::move(aText)) {}
+
+  nsAutoCStringN<32> mModule;  // longest known LazyLogModule name is ~24
+  nsCString mText;
+};
+
+class MediaSampleMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  MediaSampleMarkerPayload(const int64_t aSampleStartTimeUs,
+                           const int64_t aSampleEndTimeUs);
+  DECL_STREAM_PAYLOAD
+
+ private:
+  MediaSampleMarkerPayload(CommonProps&& aCommonProps,
+                           const int64_t aSampleStartTimeUs,
+                           const int64_t aSampleEndTimeUs);
+
+  int64_t mSampleStartTimeUs;
+  int64_t mSampleEndTimeUs;
+};
+
+class JsAllocationMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  JsAllocationMarkerPayload(const mozilla::TimeStamp& aStartTime,
+                            JS::RecordAllocationInfo&& aInfo,
+                            UniqueProfilerBacktrace aStack)
+      : ProfilerMarkerPayload(aStartTime, aStartTime, mozilla::Nothing(),
+                              std::move(aStack)),
+        // Copy the strings, and take ownership of them.
+        mTypeName(aInfo.typeName ? NS_xstrdup(aInfo.typeName) : nullptr),
+        mClassName(aInfo.className ? strdup(aInfo.className) : nullptr),
+        mDescriptiveTypeName(aInfo.descriptiveTypeName
+                                 ? NS_xstrdup(aInfo.descriptiveTypeName)
+                                 : nullptr),
+        // The coarseType points to a string literal, so does not need to be
+        // duplicated.
+        mCoarseType(aInfo.coarseType),
+        mSize(aInfo.size),
+        mInNursery(aInfo.inNursery) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  JsAllocationMarkerPayload(
+      CommonProps&& aCommonProps,
+      mozilla::UniqueFreePtr<const char16_t>&& aTypeName,
+      mozilla::UniqueFreePtr<const char>&& aClassName,
+      mozilla::UniqueFreePtr<const char16_t>&& aDescriptiveTypeName,
+      const char* aCoarseType, uint64_t aSize, bool aInNursery)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mTypeName(std::move(aTypeName)),
+        mClassName(std::move(aClassName)),
+        mDescriptiveTypeName(std::move(aDescriptiveTypeName)),
+        mCoarseType(aCoarseType),
+        mSize(aSize),
+        mInNursery(aInNursery) {}
+
+  mozilla::UniqueFreePtr<const char16_t> mTypeName;
+  mozilla::UniqueFreePtr<const char> mClassName;
+  mozilla::UniqueFreePtr<const char16_t> mDescriptiveTypeName;
+  // Points to a string literal, so does not need to be freed.
+  const char* mCoarseType;
+
+  // The size in bytes of the allocation.
+  uint64_t mSize;
+
+  // Whether or not the allocation is in the nursery or not.
+  bool mInNursery;
+};
+
+// This payload is for collecting information about native allocations. There is
+// a memory hook into malloc and other memory functions that can sample a subset
+// of the allocations. This information is then stored in this payload.
+class NativeAllocationMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  NativeAllocationMarkerPayload(const mozilla::TimeStamp& aStartTime,
+                                int64_t aSize, uintptr_t aMemoryAddress,
+                                int aThreadId, UniqueProfilerBacktrace aStack)
+      : ProfilerMarkerPayload(aStartTime, aStartTime, mozilla::Nothing(),
+                              std::move(aStack)),
+        mSize(aSize),
+        mMemoryAddress(aMemoryAddress),
+        mThreadId(aThreadId) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  NativeAllocationMarkerPayload(CommonProps&& aCommonProps, int64_t aSize,
+                                uintptr_t aMemoryAddress, int aThreadId)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mSize(aSize),
+        mMemoryAddress(aMemoryAddress),
+        mThreadId(aThreadId) {}
+
+  // The size in bytes of the allocation. If the number is negative then it
+  // represents a de-allocation.
+  int64_t mSize;
+  // The memory address of the allocation or de-allocation.
+  uintptr_t mMemoryAddress;
+
+  int mThreadId;
+};
+
+class IPCMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  IPCMarkerPayload(int32_t aOtherPid, int32_t aMessageSeqno,
+                   IPC::Message::msgid_t aMessageType, mozilla::ipc::Side aSide,
+                   mozilla::ipc::MessageDirection aDirection,
+                   mozilla::ipc::MessagePhase aPhase, bool aSync,
+                   const mozilla::TimeStamp& aStartTime)
+      : ProfilerMarkerPayload(aStartTime, aStartTime),
+        mOtherPid(aOtherPid),
+        mMessageSeqno(aMessageSeqno),
+        mMessageType(aMessageType),
+        mSide(aSide),
+        mDirection(aDirection),
+        mPhase(aPhase),
+        mSync(aSync) {}
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  IPCMarkerPayload(CommonProps&& aCommonProps, int32_t aOtherPid,
+                   int32_t aMessageSeqno, IPC::Message::msgid_t aMessageType,
+                   mozilla::ipc::Side aSide,
+                   mozilla::ipc::MessageDirection aDirection,
+                   mozilla::ipc::MessagePhase aPhase, bool aSync)
+      : ProfilerMarkerPayload(std::move(aCommonProps)),
+        mOtherPid(aOtherPid),
+        mMessageSeqno(aMessageSeqno),
+        mMessageType(aMessageType),
+        mSide(aSide),
+        mDirection(aDirection),
+        mPhase(aPhase),
+        mSync(aSync) {}
+
+  int32_t mOtherPid;
+  int32_t mMessageSeqno;
+  IPC::Message::msgid_t mMessageType;
+  mozilla::ipc::Side mSide;
+  mozilla::ipc::MessageDirection mDirection;
+  mozilla::ipc::MessagePhase mPhase;
+  bool mSync;
+};
+
+namespace mozilla {
+
+// Serialize a pointed-at ProfilerMarkerPayload, may be null when there are no
+// payloads.
+template <>
+struct ProfileBufferEntryWriter::Serializer<const ProfilerMarkerPayload*> {
+  static Length Bytes(const ProfilerMarkerPayload* aPayload) {
+    return ProfilerMarkerPayload::TagAndSerializationBytes(aPayload);
+  }
+
+  static void Write(ProfileBufferEntryWriter& aEW,
+                    const ProfilerMarkerPayload* aPayload) {
+    ProfilerMarkerPayload::TagAndSerialize(aPayload, aEW);
+  }
+};
+
+// Serialize a pointed-at ProfilerMarkerPayload, may be null when there are no
+// payloads.
+template <>
+struct ProfileBufferEntryWriter::Serializer<UniquePtr<ProfilerMarkerPayload>> {
+  static Length Bytes(const UniquePtr<ProfilerMarkerPayload>& aPayload) {
+    return ProfilerMarkerPayload::TagAndSerializationBytes(aPayload.get());
+  }
+
+  static void Write(ProfileBufferEntryWriter& aEW,
+                    const UniquePtr<ProfilerMarkerPayload>& aPayload) {
+    ProfilerMarkerPayload::TagAndSerialize(aPayload.get(), aEW);
+  }
+};
+
+// Deserialize a ProfilerMarkerPayload into a UniquePtr, may be null if there
+// are no payloads.
+template <>
+struct ProfileBufferEntryReader::Deserializer<
+    UniquePtr<ProfilerMarkerPayload>> {
+  static void ReadInto(ProfileBufferEntryReader& aER,
+                       UniquePtr<ProfilerMarkerPayload>& aPayload) {
+    aPayload = Read(aER);
+  }
+
+  static UniquePtr<ProfilerMarkerPayload> Read(ProfileBufferEntryReader& aER) {
+    return ProfilerMarkerPayload::DeserializeTagAndPayload(aER);
+  }
+};
+
+}  // namespace mozilla
+
+#endif  // ProfilerMarkerPayload_h
--- a/tools/profiler/public/ProfilerMarkerTypes.h
+++ b/tools/profiler/public/ProfilerMarkerTypes.h
@@ -18,24 +18,496 @@
 // Further work is needed to complete these definitions, and use them to convert
 // existing PROFILER_ADD_MARKER calls. See meta bug 1661394.
 
 #include "mozilla/BaseProfilerMarkerTypes.h"
 #include "mozilla/ProfilerMarkers.h"
 
 #ifdef MOZ_GECKO_PROFILER
 
+#  include "gfxASurface.h"
+#  include "js/AllocationRecording.h"
 #  include "js/ProfilingFrameIterator.h"
 #  include "js/Utility.h"
+#  include "Layers.h"
+#  include "mozilla/ipc/ProtocolUtils.h"
+#  include "mozilla/net/HttpBaseChannel.h"
 #  include "mozilla/Preferences.h"
 #  include "mozilla/ServoTraversalStatistics.h"
 
 namespace geckoprofiler::markers {
 
 // Import some common markers from mozilla::baseprofiler::markers.
-using MediaSampleMarker = mozilla::baseprofiler::markers::MediaSampleMarker;
-using ContentBuildMarker = mozilla::baseprofiler::markers::ContentBuildMarker;
+using Tracing = mozilla::baseprofiler::markers::Tracing;
+using UserTimingMark = mozilla::baseprofiler::markers::UserTimingMark;
+using UserTimingMeasure = mozilla::baseprofiler::markers::UserTimingMeasure;
+using Hang = mozilla::baseprofiler::markers::Hang;
+using LongTask = mozilla::baseprofiler::markers::LongTask;
+using Log = mozilla::baseprofiler::markers::Log;
+using MediaSample = mozilla::baseprofiler::markers::MediaSample;
+
+struct Budget {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("Budget");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter) {}
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    using MS = mozilla::MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable};
+    // Nothing outside the defaults.
+    return schema;
+  }
+};
+
+struct Pref {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("PreferenceRead");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+      const mozilla::ProfilerString8View& aPrefName,
+      const mozilla::Maybe<mozilla::PrefValueKind>& aPrefKind,
+      const mozilla::Maybe<mozilla::PrefType>& aPrefType,
+      const mozilla::ProfilerString8View& aPrefValue,
+      const mozilla::TimeStamp& aPrefAccessTime) {
+    // TODO: This looks like it's always `Now()`, so it could probably be
+    // removed; but the frontend may need updating first.
+    mozilla::baseprofiler::WritePropertyTime(aWriter, "prefAccessTime",
+                                             aPrefAccessTime);
+    aWriter.StringProperty("prefName", aPrefName);
+    aWriter.StringProperty("prefKind", PrefValueKindToString(aPrefKind));
+    aWriter.StringProperty("prefType", PrefTypeToString(aPrefType));
+    aWriter.StringProperty("prefValue", aPrefValue);
+  }
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    using MS = mozilla::MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable};
+    schema.AddKeyLabelFormat("prefName", "Name", MS::Format::string);
+    schema.AddKeyLabelFormat("prefKind", "Kind", MS::Format::string);
+    schema.AddKeyLabelFormat("prefType", "Type", MS::Format::string);
+    schema.AddKeyLabelFormat("prefValue", "Value", MS::Format::string);
+    return schema;
+  }
+
+ private:
+  static mozilla::Span<const char> PrefValueKindToString(
+      const mozilla::Maybe<mozilla::PrefValueKind>& aKind) {
+    if (aKind) {
+      return *aKind == mozilla::PrefValueKind::Default
+                 ? mozilla::MakeStringSpan("Default")
+                 : mozilla::MakeStringSpan("User");
+    }
+    return "Shared";
+  }
+
+  static mozilla::Span<const char> PrefTypeToString(
+      const mozilla::Maybe<mozilla::PrefType>& type) {
+    if (type) {
+      switch (*type) {
+        case mozilla::PrefType::None:
+          return "None";
+        case mozilla::PrefType::Int:
+          return "Int";
+        case mozilla::PrefType::Bool:
+          return "Bool";
+        case mozilla::PrefType::String:
+          return "String";
+        default:
+          MOZ_ASSERT_UNREACHABLE("Unknown preference type.");
+      }
+    }
+    return "Preference not found";
+  }
+};
+
+// Contains the translation applied to a 2d layer so we can track the layer
+// position at each frame.
+struct LayerTranslation {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("LayerTranslation");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+      mozilla::layers::Layer* aLayer, mozilla::gfx::Point aPoint) {
+    const size_t bufferSize = 32;
+    char buffer[bufferSize];
+    SprintfLiteral(buffer, "%p", aLayer);
+
+    aWriter.StringProperty("layer", buffer);
+    aWriter.IntProperty("x", aPoint.x);
+    aWriter.IntProperty("y", aPoint.y);
+  }
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    using MS = mozilla::MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable};
+    schema.AddKeyLabelFormat("layer", "Layer", MS::Format::string);
+    schema.AddKeyLabelFormat("x", "X", MS::Format::integer);
+    schema.AddKeyLabelFormat("y", "Y", MS::Format::integer);
+    return schema;
+  }
+};
+
+// Tracks when a vsync occurs according to the HardwareComposer.
+struct Vsync {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("VsyncTimestamp");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter) {}
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    using MS = mozilla::MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable};
+    // Nothing outside the defaults.
+    return schema;
+  }
+};
+
+struct Network {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("Network");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter, int64_t aID,
+      const mozilla::ProfilerString8View& aURI, NetworkLoadType aType,
+      const mozilla::TimeStamp& aStartTime, const mozilla::TimeStamp& aEndTime,
+      int32_t aPri, int64_t aCount,
+      mozilla::net::CacheDisposition aCacheDisposition, uint64_t aInnerWindowID,
+      const mozilla::net::TimingStruct& aTimings,
+      const mozilla::ProfilerString8View& aRedirectURI,
+      UniqueProfilerBacktrace aSource,
+      const mozilla::ProfilerString8View& aContentType) {
+    // TODO: Remove these Legacy start&end times when frontend is updated.
+    mozilla::baseprofiler::WritePropertyTime(aWriter, "startTime", aStartTime);
+    mozilla::baseprofiler::WritePropertyTime(aWriter, "endTime", aEndTime);
+
+    aWriter.IntProperty("id", aID);
+    mozilla::Span<const char> typeString = GetNetworkState(aType);
+    mozilla::Span<const char> cacheString = GetCacheState(aCacheDisposition);
+    // want to use aUniqueStacks.mUniqueStrings->WriteElement(aWriter,
+    // typeString);
+    aWriter.StringProperty("status", typeString);
+    if (!cacheString.IsEmpty()) {
+      aWriter.StringProperty("cache", cacheString);
+    }
+    aWriter.IntProperty("pri", aPri);
+    if (aCount > 0) {
+      aWriter.IntProperty("count", aCount);
+    }
+    if (aURI.Length() != 0) {
+      aWriter.StringProperty("URI", aURI);
+    }
+    if (aRedirectURI.Length() != 0) {
+      aWriter.StringProperty("RedirectURI", aRedirectURI);
+    }
+
+    if (aContentType.Length() != 0) {
+      aWriter.StringProperty("contentType", aContentType);
+    } else {
+      aWriter.NullProperty("contentType");
+    }
+
+    if (aType != NetworkLoadType::LOAD_START) {
+      mozilla::baseprofiler::WritePropertyTime(aWriter, "domainLookupStart",
+                                               aTimings.domainLookupStart);
+      mozilla::baseprofiler::WritePropertyTime(aWriter, "domainLookupEnd",
+                                               aTimings.domainLookupEnd);
+      mozilla::baseprofiler::WritePropertyTime(aWriter, "connectStart",
+                                               aTimings.connectStart);
+      mozilla::baseprofiler::WritePropertyTime(aWriter, "tcpConnectEnd",
+                                               aTimings.tcpConnectEnd);
+      mozilla::baseprofiler::WritePropertyTime(aWriter, "secureConnectionStart",
+                                               aTimings.secureConnectionStart);
+      mozilla::baseprofiler::WritePropertyTime(aWriter, "connectEnd",
+                                               aTimings.connectEnd);
+      mozilla::baseprofiler::WritePropertyTime(aWriter, "requestStart",
+                                               aTimings.requestStart);
+      mozilla::baseprofiler::WritePropertyTime(aWriter, "responseStart",
+                                               aTimings.responseStart);
+      mozilla::baseprofiler::WritePropertyTime(aWriter, "responseEnd",
+                                               aTimings.responseEnd);
+    }
+  }
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    return mozilla::MarkerSchema::SpecialFrontendLocation{};
+  }
+
+ private:
+  static mozilla::Span<const char> GetNetworkState(NetworkLoadType aType) {
+    switch (aType) {
+      case NetworkLoadType::LOAD_START:
+        return mozilla::MakeStringSpan("STATUS_START");
+      case NetworkLoadType::LOAD_STOP:
+        return mozilla::MakeStringSpan("STATUS_STOP");
+      case NetworkLoadType::LOAD_REDIRECT:
+        return mozilla::MakeStringSpan("STATUS_REDIRECT");
+    }
+    return mozilla::MakeStringSpan("");
+  }
+
+  static mozilla::Span<const char> GetCacheState(
+      mozilla::net::CacheDisposition aCacheDisposition) {
+    switch (aCacheDisposition) {
+      case mozilla::net::kCacheUnresolved:
+        return mozilla::MakeStringSpan("Unresolved");
+      case mozilla::net::kCacheHit:
+        return mozilla::MakeStringSpan("Hit");
+      case mozilla::net::kCacheHitViaReval:
+        return mozilla::MakeStringSpan("HitViaReval");
+      case mozilla::net::kCacheMissedViaReval:
+        return mozilla::MakeStringSpan("MissedViaReval");
+      case mozilla::net::kCacheMissed:
+        return mozilla::MakeStringSpan("Missed");
+      case mozilla::net::kCacheUnknown:
+      default:
+        return mozilla::MakeStringSpan("");
+    }
+  }
+};
+
+struct ScreenshotPayload {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("CompositorScreenshot");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+      const mozilla::ProfilerString8View& aScreenshotDataURL,
+      const mozilla::gfx::IntSize& aWindowSize, uintptr_t aWindowIdentifier) {
+    // TODO: Use UniqueStacks&Strings
+    // aUniqueStacks.mUniqueStrings->WriteProperty(aWriter, "url",
+    //                                             mScreenshotDataURL.get());
+    aWriter.StringProperty("url", aScreenshotDataURL);
+
+    char hexWindowID[32];
+    SprintfLiteral(hexWindowID, "0x%" PRIXPTR, aWindowIdentifier);
+    aWriter.StringProperty("windowID", hexWindowID);
+    aWriter.DoubleProperty("windowWidth", aWindowSize.width);
+    aWriter.DoubleProperty("windowHeight", aWindowSize.height);
+  }
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    return mozilla::MarkerSchema::SpecialFrontendLocation{};
+  }
+};
+
+struct GCSlice {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("GCSlice");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+      const mozilla::ProfilerString8View& aTimingJSON) {
+    if (aTimingJSON.Length() != 0) {
+      // TODO: Is SplicedJSONProperty necessary here? (Guessing yes!)
+      // aWriter.SplicedJSONProperty("timings", aTimingJSON);
+      aWriter.StringProperty("timings", aTimingJSON);
+    } else {
+      aWriter.NullProperty("timings");
+    }
+  }
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    using MS = mozilla::MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable,
+              MS::Location::timelineMemory};
+    // No display instructions here, there is special handling in the front-end.
+    return schema;
+  }
+};
+
+struct GCMajor {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("GCMajor");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+      const mozilla::ProfilerString8View& aTimingJSON) {
+    if (aTimingJSON.Length() != 0) {
+      // TODO: Is SplicedJSONProperty necessary here? (Guessing yes!)
+      // aWriter.SplicedJSONProperty("timings", aTimingJSON);
+      aWriter.StringProperty("timings", aTimingJSON);
+    } else {
+      aWriter.NullProperty("timings");
+    }
+  }
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    using MS = mozilla::MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable,
+              MS::Location::timelineMemory};
+    // No display instructions here, there is special handling in the front-end.
+    return schema;
+  }
+};
+
+struct GCMinor {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("GCMinor");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+      const mozilla::ProfilerString8View& aTimingJSON) {
+    if (aTimingJSON.Length() != 0) {
+      // TODO: Is SplicedJSONProperty necessary here? (Guessing yes!)
+      // aWriter.SplicedJSONProperty("nursery", aTimingJSON);
+      aWriter.StringProperty("nursery", aTimingJSON);
+    } else {
+      aWriter.NullProperty("nursery");
+    }
+  }
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    using MS = mozilla::MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable,
+              MS::Location::timelineMemory};
+    // No display instructions here, there is special handling in the front-end.
+    return schema;
+  }
+};
+
+struct StyleMarkerPayload {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("Styles");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+      const mozilla::ServoTraversalStatistics& aStats) {
+    aWriter.IntProperty("elementsTraversed", aStats.mElementsTraversed);
+    aWriter.IntProperty("elementsStyled", aStats.mElementsStyled);
+    aWriter.IntProperty("elementsMatched", aStats.mElementsMatched);
+    aWriter.IntProperty("stylesShared", aStats.mStylesShared);
+    aWriter.IntProperty("stylesReused", aStats.mStylesReused);
+  }
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    using MS = mozilla::MarkerSchema;
+    MS schema{MS::Location::markerChart, MS::Location::markerTable,
+              MS::Location::timelineOverview};
+    schema.AddKeyLabelFormat("elementsTraversed", "Elements traversed",
+                             MS::Format::integer);
+    schema.AddKeyLabelFormat("elementsStyled", "Elements styled",
+                             MS::Format::integer);
+    schema.AddKeyLabelFormat("elementsMatched", "Elements matched",
+                             MS::Format::integer);
+    schema.AddKeyLabelFormat("stylesShared", "Styles shared",
+                             MS::Format::integer);
+    schema.AddKeyLabelFormat("stylesReused", "Styles reused",
+                             MS::Format::integer);
+    return schema;
+  }
+};
+
+class JsAllocationMarkerPayload {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("JS allocation");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
+      const mozilla::ProfilerString16View& aTypeName,
+      const mozilla::ProfilerString8View& aClassName,
+      const mozilla::ProfilerString16View& aDescriptiveTypeName,
+      const mozilla::ProfilerString8View& aCoarseType, uint64_t aSize,
+      bool aInNursery) {
+    if (aClassName.Length() != 0) {
+      aWriter.StringProperty("className", aClassName);
+    }
+    if (aTypeName.Length() != 0) {
+      aWriter.StringProperty(
+          "typeName",
+          NS_ConvertUTF16toUTF8(aTypeName.Data(), aTypeName.Length()));
+    }
+    if (aDescriptiveTypeName.Length() != 0) {
+      aWriter.StringProperty(
+          "descriptiveTypeName",
+          NS_ConvertUTF16toUTF8(aDescriptiveTypeName.Data(),
+                                aDescriptiveTypeName.Length()));
+    }
+    aWriter.StringProperty("coarseType", aCoarseType);
+    aWriter.IntProperty("size", aSize);
+    aWriter.BoolProperty("inNursery", aInNursery);
+  }
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    return mozilla::MarkerSchema::SpecialFrontendLocation{};
+  }
+};
+
+// This payload is for collecting information about native allocations. There is
+// a memory hook into malloc and other memory functions that can sample a subset
+// of the allocations. This information is then stored in this payload.
+struct NativeAllocationMarkerPayload {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("Native allocation");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter, int64_t aSize,
+      uintptr_t aMemoryAddress, int aThreadId) {
+    aWriter.IntProperty("size", aSize);
+    aWriter.IntProperty("memoryAddress", static_cast<int64_t>(aMemoryAddress));
+    aWriter.IntProperty("threadId", aThreadId);
+  }
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    return mozilla::MarkerSchema::SpecialFrontendLocation{};
+  }
+};
+
+struct IPCMarkerPayload {
+  static constexpr mozilla::Span<const char> MarkerTypeName() {
+    return mozilla::MakeStringSpan("IPC");
+  }
+  static void StreamJSONMarkerData(
+      mozilla::baseprofiler::SpliceableJSONWriter& aWriter, int32_t aOtherPid,
+      int32_t aMessageSeqno, IPC::Message::msgid_t aMessageType,
+      mozilla::ipc::Side aSide, mozilla::ipc::MessageDirection aDirection,
+      mozilla::ipc::MessagePhase aPhase, bool aSync,
+      const mozilla::TimeStamp& aTime) {
+    // TODO: Remove these Legacy times when frontend is updated.
+    mozilla::baseprofiler::WritePropertyTime(aWriter, "startTime", aTime);
+    mozilla::baseprofiler::WritePropertyTime(aWriter, "endTime", aTime);
+
+    using namespace mozilla::ipc;
+    aWriter.IntProperty("otherPid", aOtherPid);
+    aWriter.IntProperty("messageSeqno", aMessageSeqno);
+    aWriter.StringProperty(
+        "messageType",
+        mozilla::MakeStringSpan(IPC::StringFromIPCMessageType(aMessageType)));
+    aWriter.StringProperty("side", IPCSideToString(aSide));
+    aWriter.StringProperty("direction",
+                           aDirection == MessageDirection::eSending
+                               ? mozilla::MakeStringSpan("sending")
+                               : mozilla::MakeStringSpan("receiving"));
+    aWriter.StringProperty("phase", IPCPhaseToString(aPhase));
+    aWriter.BoolProperty("sync", aSync);
+  }
+  static mozilla::MarkerSchema MarkerTypeDisplay() {
+    return mozilla::MarkerSchema::SpecialFrontendLocation{};
+  }
+
+ private:
+  static mozilla::Span<const char> IPCSideToString(mozilla::ipc::Side aSide) {
+    switch (aSide) {
+      case mozilla::ipc::ParentSide:
+        return mozilla::MakeStringSpan("parent");
+      case mozilla::ipc::ChildSide:
+        return mozilla::MakeStringSpan("child");
+      case mozilla::ipc::UnknownSide:
+        return mozilla::MakeStringSpan("unknown");
+      default:
+        MOZ_ASSERT_UNREACHABLE("Invalid IPC side");
+        return mozilla::MakeStringSpan("<invalid IPC side>");
+    }
+  }
+
+  static mozilla::Span<const char> IPCPhaseToString(
+      mozilla::ipc::MessagePhase aPhase) {
+    switch (aPhase) {
+      case mozilla::ipc::MessagePhase::Endpoint:
+        return mozilla::MakeStringSpan("endpoint");
+      case mozilla::ipc::MessagePhase::TransferStart:
+        return mozilla::MakeStringSpan("transferStart");
+      case mozilla::ipc::MessagePhase::TransferEnd:
+        return mozilla::MakeStringSpan("transferEnd");
+      default:
+        MOZ_ASSERT_UNREACHABLE("Invalid IPC phase");
+        return mozilla::MakeStringSpan("<invalid IPC phase>");
+    }
+  }
+};
 
 }  // namespace geckoprofiler::markers
 
 #endif  // MOZ_GECKO_PROFILER
 
 #endif  // ProfilerMarkerTypes_h
--- a/tools/profiler/public/ProfilerMarkers.h
+++ b/tools/profiler/public/ProfilerMarkers.h
@@ -39,19 +39,16 @@
 #include "GeckoProfiler.h"
 
 #ifndef MOZ_GECKO_PROFILER
 
 #  define PROFILER_MARKER_UNTYPED(markerName, categoryName, ...)
 #  define PROFILER_MARKER(markerName, categoryName, options, MarkerType, ...)
 #  define PROFILER_MARKER_TEXT(markerName, categoryName, options, text)
 #  define AUTO_PROFILER_MARKER_TEXT(markerName, categoryName, options, text)
-#  define AUTO_PROFILER_TRACING_MARKER(categoryString, markerName, categoryPair)
-#  define AUTO_PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \
-                                                categoryPair, docShell)
 
 #else  // ndef MOZ_GECKO_PROFILER
 
 // Bring category names from Base Profiler into the geckoprofiler::category
 // namespace, for consistency with other Gecko Profiler identifiers.
 namespace geckoprofiler::category {
 using namespace ::mozilla::baseprofiler::category;
 }
@@ -127,19 +124,18 @@ inline mozilla::ProfileBufferBlockIndex 
     do {                                                                      \
       AUTO_PROFILER_STATS(PROFILER_MARKER_with_##MarkerType);                 \
       ::profiler_add_marker(                                                  \
           markerName, ::geckoprofiler::category::categoryName, options,       \
           ::geckoprofiler::markers::MarkerType{}, ##__VA_ARGS__);             \
     } while (false)
 
 namespace geckoprofiler::markers {
-// Most common marker types. Others are in ProfilerMarkerTypes.h.
+// Most common marker type. Others are in ProfilerMarkerTypes.h.
 using Text = ::mozilla::baseprofiler::markers::Text;
-using Tracing = mozilla::baseprofiler::markers::Tracing;
 }  // namespace geckoprofiler::markers
 
 // Add a text marker. This macro is safe to use even if MOZ_GECKO_PROFILER is
 // not #defined.
 #  define PROFILER_MARKER_TEXT(markerName, categoryName, options, text)       \
     do {                                                                      \
       AUTO_PROFILER_STATS(PROFILER_MARKER_TEXT);                              \
       ::profiler_add_marker(markerName,                                       \
@@ -184,80 +180,11 @@ class MOZ_RAII AutoProfilerTextMarker {
 
 // Creates an AutoProfilerTextMarker RAII object.  This macro is safe to use
 // even if MOZ_GECKO_PROFILER is not #defined.
 #  define AUTO_PROFILER_MARKER_TEXT(markerName, categoryName, options, text)  \
     AutoProfilerTextMarker PROFILER_RAII(                                     \
         markerName, ::mozilla::baseprofiler::category::categoryName, options, \
         text)
 
-class MOZ_RAII AutoProfilerTracing {
- public:
-  AutoProfilerTracing(const char* aCategoryString, const char* aMarkerName,
-                      mozilla::MarkerCategory aCategoryPair,
-                      const mozilla::Maybe<uint64_t>& aInnerWindowID)
-      : mCategoryString(aCategoryString),
-        mMarkerName(aMarkerName),
-        mCategoryPair(aCategoryPair),
-        mInnerWindowID(aInnerWindowID) {
-    profiler_add_marker(
-        mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName),
-        mCategoryPair,
-        {mozilla::MarkerTiming::IntervalStart(),
-         mozilla::MarkerInnerWindowId(mInnerWindowID)},
-        geckoprofiler::markers::Tracing{},
-        mozilla::ProfilerString8View::WrapNullTerminatedString(
-            mCategoryString));
-  }
-
-  AutoProfilerTracing(
-      const char* aCategoryString, const char* aMarkerName,
-      mozilla::MarkerCategory aCategoryPair,
-      mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aBacktrace,
-      const mozilla::Maybe<uint64_t>& aInnerWindowID)
-      : mCategoryString(aCategoryString),
-        mMarkerName(aMarkerName),
-        mCategoryPair(aCategoryPair),
-        mInnerWindowID(aInnerWindowID) {
-    profiler_add_marker(
-        mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName),
-        mCategoryPair,
-        {mozilla::MarkerTiming::IntervalStart(),
-         mozilla::MarkerInnerWindowId(mInnerWindowID),
-         mozilla::MarkerStack::TakeBacktrace(std::move(aBacktrace))},
-        geckoprofiler::markers::Tracing{},
-        mozilla::ProfilerString8View::WrapNullTerminatedString(
-            mCategoryString));
-  }
-
-  ~AutoProfilerTracing() {
-    profiler_add_marker(
-        mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName),
-        mCategoryPair,
-        {mozilla::MarkerTiming::IntervalEnd(),
-         mozilla::MarkerInnerWindowId(mInnerWindowID)},
-        geckoprofiler::markers::Tracing{},
-        mozilla::ProfilerString8View::WrapNullTerminatedString(
-            mCategoryString));
-  }
-
- protected:
-  const char* mCategoryString;
-  const char* mMarkerName;
-  const mozilla::MarkerCategory mCategoryPair;
-  const mozilla::Maybe<uint64_t> mInnerWindowID;
-};
-
-// Adds a START/END pair of tracing markers.
-#  define AUTO_PROFILER_TRACING_MARKER(categoryString, markerName,           \
-                                       categoryPair)                         \
-    AutoProfilerTracing PROFILER_RAII(categoryString, markerName,            \
-                                      geckoprofiler::category::categoryPair, \
-                                      mozilla::Nothing())
-#  define AUTO_PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \
-                                                categoryPair, docShell)     \
-    AutoProfilerTracing PROFILER_RAII(                                      \
-        categoryString, markerName, geckoprofiler::category::categoryPair,  \
-        profiler_get_inner_window_id_from_docshell(docShell))
-
 #endif  // nfed MOZ_GECKO_PROFILER else
 
 #endif  // ProfilerMarkers_h
--- a/tools/profiler/tests/gtest/GeckoProfiler.cpp
+++ b/tools/profiler/tests/gtest/GeckoProfiler.cpp
@@ -9,16 +9,17 @@
 // happens when calling these functions. They don't do much inspection of
 // profiler internals.
 
 #include "GeckoProfiler.h"
 #include "mozilla/ProfilerMarkerTypes.h"
 #include "mozilla/ProfilerMarkers.h"
 #include "platform.h"
 #include "ProfileBuffer.h"
+#include "ProfilerMarkerPayload.h"
 
 #include "js/Initialization.h"
 #include "js/Printf.h"
 #include "jsapi.h"
 #include "json/json.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/BlocksRingBuffer.h"
 #include "mozilla/ProfileBufferEntrySerializationGeckoExtensions.h"
@@ -573,38 +574,126 @@ TEST(GeckoProfiler, Pause)
   ASSERT_TRUE(profiler_can_accept_markers());
 
   profiler_stop();
 
   ASSERT_TRUE(!profiler_is_paused());
   ASSERT_TRUE(!profiler_can_accept_markers());
 }
 
+// A class that keeps track of how many instances have been created, streamed,
+// and destroyed.
+class GTestMarkerPayload : public ProfilerMarkerPayload {
+ public:
+  explicit GTestMarkerPayload(int aN) : mN(aN) { ++sNumCreated; }
+
+  virtual ~GTestMarkerPayload() { ++sNumDestroyed; }
+
+  DECL_STREAM_PAYLOAD
+
+ private:
+  GTestMarkerPayload(CommonProps&& aCommonProps, int aN)
+      : ProfilerMarkerPayload(std::move(aCommonProps)), mN(aN) {
+    ++sNumDeserialized;
+  }
+
+  int mN;
+
+ public:
+  // The number of GTestMarkerPayload instances that have been created,
+  // streamed, and destroyed.
+  static int sNumCreated;
+  static int sNumSerialized;
+  static int sNumDeserialized;
+  static int sNumStreamed;
+  static int sNumDestroyed;
+};
+
+int GTestMarkerPayload::sNumCreated = 0;
+int GTestMarkerPayload::sNumSerialized = 0;
+int GTestMarkerPayload::sNumDeserialized = 0;
+int GTestMarkerPayload::sNumStreamed = 0;
+int GTestMarkerPayload::sNumDestroyed = 0;
+
+ProfileBufferEntryWriter::Length GTestMarkerPayload::TagAndSerializationBytes()
+    const {
+  return CommonPropsTagAndSerializationBytes() +
+         ProfileBufferEntryWriter::SumBytes(mN);
+}
+
+void GTestMarkerPayload::SerializeTagAndPayload(
+    ProfileBufferEntryWriter& aEntryWriter) const {
+  static const DeserializerTag tag = TagForDeserializer(Deserialize);
+  SerializeTagAndCommonProps(tag, aEntryWriter);
+  aEntryWriter.WriteObject(mN);
+  ++sNumSerialized;
+}
+
+// static
+UniquePtr<ProfilerMarkerPayload> GTestMarkerPayload::Deserialize(
+    ProfileBufferEntryReader& aEntryReader) {
+  ProfilerMarkerPayload::CommonProps props =
+      DeserializeCommonProps(aEntryReader);
+  auto n = aEntryReader.ReadObject<int>();
+  return UniquePtr<ProfilerMarkerPayload>(
+      new GTestMarkerPayload(std::move(props), n));
+}
+
+void GTestMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                       const mozilla::TimeStamp& aStartTime,
+                                       UniqueStacks& aUniqueStacks) const {
+  StreamCommonProps("gtest", aWriter, aStartTime, aUniqueStacks);
+  char buf[64];
+  int written = SprintfLiteral(buf, "gtest-%d", mN);
+  ASSERT_GT(written, 0);
+  aWriter.IntProperty(mozilla::Span<const char>(buf, size_t(written)), mN);
+  ++sNumStreamed;
+}
+
 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), 0);
 
-  PROFILER_MARKER("tracing event", OTHER, {}, Tracing, "A");
-  PROFILER_MARKER("tracing start", OTHER, MarkerTiming::IntervalStart(),
-                  Tracing, "A");
-  PROFILER_MARKER("tracing end", OTHER, MarkerTiming::IntervalEnd(), Tracing,
-                  "A");
+  // Used in markers below.
+  TimeStamp ts0 = TimeStamp::NowUnfuzzed();
 
-  auto bt = profiler_capture_backtrace();
-  PROFILER_MARKER("tracing event with stack", OTHER,
-                  MarkerStack::TakeBacktrace(std::move(bt)), Tracing, "B");
+  profiler_tracing_marker("A", "tracing event",
+                          JS::ProfilingCategoryPair::OTHER, TRACING_EVENT);
+  PROFILER_TRACING_MARKER("A", "tracing start", OTHER, TRACING_INTERVAL_START);
+  PROFILER_TRACING_MARKER("A", "tracing end", OTHER, TRACING_INTERVAL_END);
+
+  UniqueProfilerBacktrace bt = profiler_get_backtrace();
+  profiler_tracing_marker("B", "tracing event with stack",
+                          JS::ProfilingCategoryPair::OTHER, TRACING_EVENT,
+                          std::move(bt));
 
   { AUTO_PROFILER_TRACING_MARKER("C", "auto tracing", OTHER); }
 
   PROFILER_MARKER_UNTYPED("M1", OTHER, {});
+  PROFILER_ADD_MARKER_WITH_PAYLOAD("M2", OTHER, TracingMarkerPayload,
+                                   ("C", TRACING_EVENT, ts0));
   PROFILER_MARKER_UNTYPED("M3", OTHER, {});
+  PROFILER_ADD_MARKER_WITH_PAYLOAD(
+      "M4", OTHER, TracingMarkerPayload,
+      ("C", TRACING_EVENT, ts0, mozilla::Nothing(), profiler_get_backtrace()));
+
+  for (int i = 0; i < 10; i++) {
+    PROFILER_ADD_MARKER_WITH_PAYLOAD("M5", OTHER, GTestMarkerPayload, (i));
+  }
+  // The GTestMarkerPayloads should have been created, serialized, and
+  // destroyed.
+  EXPECT_EQ(GTestMarkerPayload::sNumCreated, 10);
+  EXPECT_EQ(GTestMarkerPayload::sNumSerialized, 10);
+  EXPECT_EQ(GTestMarkerPayload::sNumDeserialized, 0);
+  EXPECT_EQ(GTestMarkerPayload::sNumStreamed, 0);
+  EXPECT_EQ(GTestMarkerPayload::sNumDestroyed, 10);
 
   // Create three strings: two that are 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);
   UniquePtr<char[]> longstrCut = MakeUnique<char[]>(kMax + 1);
@@ -669,86 +758,121 @@ TEST(GeckoProfiler, Markers)
   // to serialized numbers in other markers.)
   MOZ_RELEASE_ASSERT(
       profiler_add_marker("FirstMarker", geckoprofiler::category::OTHER,
                           MarkerTiming::Interval(ts1, ts2),
                           geckoprofiler::markers::Text{}, "FirstMarker"));
 
   // Other markers in alphabetical order of payload class names.
 
-  nsCOMPtr<nsIURI> uri;
-  ASSERT_TRUE(
-      NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), "http://mozilla.org/"_ns)));
-  // The marker name will be "Load <aChannelId>: <aURI>".
-  profiler_add_network_marker(
-      /* nsIURI* aURI */ uri,
-      /* const nsACString& aRequestMethod */ "GET"_ns,
-      /* int32_t aPriority */ 34,
-      /* uint64_t aChannelId */ 1,
-      /* NetworkLoadType aType */ NetworkLoadType::LOAD_START,
-      /* mozilla::TimeStamp aStart */ ts1,
-      /* mozilla::TimeStamp aEnd */ ts2,
-      /* int64_t aCount */ 56,
-      /* mozilla::net::CacheDisposition aCacheDisposition */
-      net::kCacheHit,
-      /* uint64_t aInnerWindowID */ 78
-      /* const mozilla::net::TimingStruct* aTimings = nullptr */
-      /* nsIURI* aRedirectURI = nullptr */
-      /* mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource =
-         nullptr */
-      /* const mozilla::Maybe<nsDependentCString>& aContentType =
-         mozilla::Nothing() */);
+  {
+    const char gcMajorJSON[] = "42";
+    const auto len = strlen(gcMajorJSON);
+    char* buffer =
+        static_cast<char*>(js::SystemAllocPolicy{}.pod_malloc<char>(len + 1));
+    strncpy(buffer, gcMajorJSON, len);
+    buffer[len] = '\0';
+    PROFILER_ADD_MARKER_WITH_PAYLOAD("GCMajorMarkerPayload marker", OTHER,
+                                     GCMajorMarkerPayload,
+                                     (ts1, ts2, JS::UniqueChars(buffer)));
+  }
+
+  {
+    const char gcMinorJSON[] = "43";
+    const auto len = strlen(gcMinorJSON);
+    char* buffer =
+        static_cast<char*>(js::SystemAllocPolicy{}.pod_malloc<char>(len + 1));
+    strncpy(buffer, gcMinorJSON, len);
+    buffer[len] = '\0';
+    PROFILER_ADD_MARKER_WITH_PAYLOAD("GCMinorMarkerPayload marker", OTHER,
+                                     GCMinorMarkerPayload,
+                                     (ts1, ts2, JS::UniqueChars(buffer)));
+  }
+
+  {
+    const char gcSliceJSON[] = "44";
+    const auto len = strlen(gcSliceJSON);
+    char* buffer =
+        static_cast<char*>(js::SystemAllocPolicy{}.pod_malloc<char>(len + 1));
+    strncpy(buffer, gcSliceJSON, len);
+    buffer[len] = '\0';
+    PROFILER_ADD_MARKER_WITH_PAYLOAD("GCSliceMarkerPayload marker", OTHER,
+                                     GCSliceMarkerPayload,
+                                     (ts1, ts2, JS::UniqueChars(buffer)));
+  }
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD("HangMarkerPayload marker", OTHER,
+                                   HangMarkerPayload, (ts1, ts2));
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD("LogMarkerPayload marker", OTHER,
+                                   LogMarkerPayload, ("module", "text", ts1));
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD("LongTaskMarkerPayload marker", OTHER,
+                                   LongTaskMarkerPayload, (ts1, ts2));
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD("NativeAllocationMarkerPayload marker",
+                                   OTHER, NativeAllocationMarkerPayload,
+                                   (ts1, 9876543210, 1234, 5678, nullptr));
 
-  profiler_add_network_marker(
-      /* nsIURI* aURI */ uri,
-      /* const nsACString& aRequestMethod */ "GET"_ns,
-      /* int32_t aPriority */ 34,
-      /* uint64_t aChannelId */ 12,
-      /* NetworkLoadType aType */ NetworkLoadType::LOAD_STOP,
-      /* mozilla::TimeStamp aStart */ ts1,
-      /* mozilla::TimeStamp aEnd */ ts2,
-      /* int64_t aCount */ 56,
-      /* mozilla::net::CacheDisposition aCacheDisposition */
-      net::kCacheUnresolved,
-      /* uint64_t aInnerWindowID */ 78,
-      /* const mozilla::net::TimingStruct* aTimings = nullptr */ nullptr,
-      /* nsIURI* aRedirectURI = nullptr */ nullptr,
-      /* mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource =
-         nullptr */
-      nullptr,
-      /* const mozilla::Maybe<nsDependentCString>& aContentType =
-         mozilla::Nothing() */
-      Some(nsDependentCString("text/html")));
+  nsCString requestMethod = "GET"_ns;
+  PROFILER_ADD_MARKER_WITH_PAYLOAD(
+      "NetworkMarkerPayload start marker", OTHER, NetworkMarkerPayload,
+      (1, "http://mozilla.org/", requestMethod, NetworkLoadType::LOAD_START,
+       ts1, ts2, 34, 56, net::kCacheHit, 78));
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD(
+      "NetworkMarkerPayload stop marker", OTHER, NetworkMarkerPayload,
+      (12, "http://mozilla.org/", requestMethod, NetworkLoadType::LOAD_STOP,
+       ts1, ts2, 34, 56, net::kCacheUnresolved, 78, nullptr, nullptr, nullptr,
+       Some(nsDependentCString("text/html"))));
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD(
+      "NetworkMarkerPayload redirect marker", OTHER, NetworkMarkerPayload,
+      (123, "http://mozilla.org/", requestMethod,
+       NetworkLoadType::LOAD_REDIRECT, ts1, ts2, 34, 56, net::kCacheUnresolved,
+       78, nullptr, "http://example.com/"));
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD(
+      "PrefMarkerPayload marker", OTHER, PrefMarkerPayload,
+      ("preference name", mozilla::Nothing(), mozilla::Nothing(),
+       "preference value"_ns, ts1));
 
-  nsCOMPtr<nsIURI> redirectURI;
-  ASSERT_TRUE(NS_SUCCEEDED(
-      NS_NewURI(getter_AddRefs(redirectURI), "http://example.com/"_ns)));
-  profiler_add_network_marker(
-      /* nsIURI* aURI */ uri,
-      /* const nsACString& aRequestMethod */ "GET"_ns,
-      /* int32_t aPriority */ 34,
-      /* uint64_t aChannelId */ 123,
-      /* NetworkLoadType aType */ NetworkLoadType::LOAD_REDIRECT,
-      /* mozilla::TimeStamp aStart */ ts1,
-      /* mozilla::TimeStamp aEnd */ ts2,
-      /* int64_t aCount */ 56,
-      /* mozilla::net::CacheDisposition aCacheDisposition */
-      net::kCacheUnresolved,
-      /* uint64_t aInnerWindowID */ 78,
-      /* const mozilla::net::TimingStruct* aTimings = nullptr */ nullptr,
-      /* nsIURI* aRedirectURI = nullptr */ redirectURI
-      /* mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource =
-         nullptr */
-      /* const mozilla::Maybe<nsDependentCString>& aContentType =
-         mozilla::Nothing() */);
+  nsCString screenshotURL = "url"_ns;
+  PROFILER_ADD_MARKER_WITH_PAYLOAD(
+      "ScreenshotPayload marker", OTHER, ScreenshotPayload,
+      (ts1, std::move(screenshotURL), mozilla::gfx::IntSize(12, 34),
+       uintptr_t(0x45678u)));
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD("TextMarkerPayload marker 1", OTHER,
+                                   TextMarkerPayload, ("text"_ns, ts1));
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD("TextMarkerPayload marker 2", OTHER,
+                                   TextMarkerPayload, ("text"_ns, ts1, ts2));
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD("UserTimingMarkerPayload marker mark", OTHER,
+                                   UserTimingMarkerPayload,
+                                   (u"mark name"_ns, ts1, mozilla::Nothing()));
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD(
+      "UserTimingMarkerPayload marker measure", OTHER, UserTimingMarkerPayload,
+      (u"measure name"_ns, Some(u"start mark"_ns), Some(u"end mark"_ns), ts1,
+       ts2, mozilla::Nothing()));
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD("VsyncMarkerPayload marker", OTHER,
+                                   VsyncMarkerPayload, (ts1));
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD(
+      "IPCMarkerPayload marker", IPC, IPCMarkerPayload,
+      (1111, 1, 3 /* PAPZ::Msg_LayerTransforms */, mozilla::ipc::ParentSide,
+       mozilla::ipc::MessageDirection::eSending,
+       mozilla::ipc::MessagePhase::Endpoint, false, ts1));
 
   MOZ_RELEASE_ASSERT(profiler_add_marker(
       "Text in main thread with stack", geckoprofiler::category::OTHER,
-      {MarkerStack::Capture(), MarkerTiming::Interval(ts1, ts2)},
-      geckoprofiler::markers::Text{}, ""));
+      MarkerStack::Capture(), geckoprofiler::markers::Text{}, ""));
   MOZ_RELEASE_ASSERT(profiler_add_marker(
       "Text from main thread with stack", geckoprofiler::category::OTHER,
       MarkerOptions(MarkerThreadId::MainThread(), MarkerStack::Capture()),
       geckoprofiler::markers::Text{}, ""));
 
   std::thread registeredThread([]() {
     AUTO_PROFILER_REGISTER_THREAD("Marker test sub-thread");
     // Marker in non-profiled thread won't be stored.
@@ -779,50 +903,110 @@ TEST(GeckoProfiler, Markers)
         geckoprofiler::markers::Text{}, ""));
   });
   unregisteredThread.join();
 
   MOZ_RELEASE_ASSERT(
       profiler_add_marker("Tracing", geckoprofiler::category::OTHER, {},
                           geckoprofiler::markers::Tracing{}, "category"));
 
+  MOZ_RELEASE_ASSERT(profiler_add_marker(
+      "UserTimingMark", geckoprofiler::category::OTHER, {},
+      geckoprofiler::markers::UserTimingMark{}, "mark name"));
+
+  MOZ_RELEASE_ASSERT(profiler_add_marker(
+      "UserTimingMeasure", geckoprofiler::category::OTHER, {},
+      geckoprofiler::markers::UserTimingMeasure{}, "measure name",
+      Some(mozilla::ProfilerString8View("start")),
+      Some(mozilla::ProfilerString8View("end"))));
+
+  MOZ_RELEASE_ASSERT(profiler_add_marker("Hang", geckoprofiler::category::OTHER,
+                                         {}, geckoprofiler::markers::Hang{}));
+
+  MOZ_RELEASE_ASSERT(profiler_add_marker("LongTask",
+                                         geckoprofiler::category::OTHER, {},
+                                         geckoprofiler::markers::LongTask{}));
+
   MOZ_RELEASE_ASSERT(profiler_add_marker("Text", geckoprofiler::category::OTHER,
                                          {}, geckoprofiler::markers::Text{},
                                          "Text text"));
 
-  MOZ_RELEASE_ASSERT(profiler_add_marker(
-      "MediaSample", geckoprofiler::category::OTHER, {},
-      geckoprofiler::markers::MediaSampleMarker{}, 123, 456));
+  MOZ_RELEASE_ASSERT(profiler_add_marker("Log", geckoprofiler::category::OTHER,
+                                         {}, geckoprofiler::markers::Log{},
+                                         "module", "log text"));
+
+  MOZ_RELEASE_ASSERT(
+      profiler_add_marker("MediaSample", geckoprofiler::category::OTHER, {},
+                          geckoprofiler::markers::MediaSample{}, 123, 456));
+
+  MOZ_RELEASE_ASSERT(profiler_add_marker("Budget",
+                                         geckoprofiler::category::OTHER, {},
+                                         geckoprofiler::markers::Budget{}));
 
   SpliceableChunkedJSONWriter w;
   w.Start();
   EXPECT_TRUE(::profiler_stream_json_for_this_process(w));
   w.End();
 
+  // The GTestMarkerPayloads should have been deserialized, streamed, and
+  // destroyed.
+  EXPECT_EQ(GTestMarkerPayload::sNumCreated, 10 + 0);
+  EXPECT_EQ(GTestMarkerPayload::sNumSerialized, 10 + 0);
+  EXPECT_EQ(GTestMarkerPayload::sNumDeserialized, 0 + 10);
+  EXPECT_EQ(GTestMarkerPayload::sNumStreamed, 0 + 10);
+  EXPECT_EQ(GTestMarkerPayload::sNumDestroyed, 10 + 10);
+
   UniquePtr<char[]> profile = w.ChunkedWriteFunc().CopyData();
   ASSERT_TRUE(!!profile.get());
 
   // Expected markers, in order.
   enum State {
     S_tracing_event,
     S_tracing_start,
     S_tracing_end,
     S_tracing_event_with_stack,
     S_tracing_auto_tracing_start,
     S_tracing_auto_tracing_end,
     S_M1,
+    S_tracing_M2_C,
     S_M3,
+    S_tracing_M4_C_stack,
+    S_M5_gtest0,
+    S_M5_gtest1,
+    S_M5_gtest2,
+    S_M5_gtest3,
+    S_M5_gtest4,
+    S_M5_gtest5,
+    S_M5_gtest6,
+    S_M5_gtest7,
+    S_M5_gtest8,
+    S_M5_gtest9,
     S_Markers2DefaultEmptyOptions,
     S_Markers2DefaultWithOptions,
     S_Markers2ExplicitDefaultEmptyOptions,
     S_Markers2ExplicitDefaultWithOptions,
     S_FirstMarker,
+    S_GCMajorMarkerPayload,
+    S_GCMinorMarkerPayload,
+    S_GCSliceMarkerPayload,
+    S_HangMarkerPayload,
+    S_LogMarkerPayload,
+    S_LongTaskMarkerPayload,
+    S_NativeAllocationMarkerPayload,
     S_NetworkMarkerPayload_start,
     S_NetworkMarkerPayload_stop,
     S_NetworkMarkerPayload_redirect,
+    S_PrefMarkerPayload,
+    S_ScreenshotPayload,
+    S_TextMarkerPayload1,
+    S_TextMarkerPayload2,
+    S_UserTimingMarkerPayload_mark,
+    S_UserTimingMarkerPayload_measure,
+    S_VsyncMarkerPayload,
+    S_IPCMarkerPayload,
     S_TextWithStack,
     S_TextToMTWithStack,
     S_RegThread_TextToMTWithStack,
     S_UnregThread_TextToMTWithStack,
 
     S_LAST,
   } state = State(0);
 
@@ -1045,132 +1229,314 @@ TEST(GeckoProfiler, Markers)
               std::string typeString = type.asString();
 
               if (nameString == "tracing event") {
                 EXPECT_EQ(state, S_tracing_event);
                 state = State(S_tracing_event + 1);
                 EXPECT_EQ(typeString, "tracing");
                 EXPECT_TIMING_INSTANT;
                 EXPECT_EQ_JSON(payload["category"], String, "A");
+                EXPECT_TRUE(payload["interval"].isNull());
                 EXPECT_TRUE(payload["stack"].isNull());
 
               } else if (nameString == "tracing start") {
                 EXPECT_EQ(state, S_tracing_start);
                 state = State(S_tracing_start + 1);
                 EXPECT_EQ(typeString, "tracing");
                 EXPECT_TIMING_START;
                 EXPECT_EQ_JSON(payload["category"], String, "A");
+                EXPECT_EQ_JSON(payload["interval"], String, "start");
                 EXPECT_TRUE(payload["stack"].isNull());
 
               } else if (nameString == "tracing end") {
                 EXPECT_EQ(state, S_tracing_end);
                 state = State(S_tracing_end + 1);
                 EXPECT_EQ(typeString, "tracing");
                 EXPECT_TIMING_END;
                 EXPECT_EQ_JSON(payload["category"], String, "A");
+                EXPECT_EQ_JSON(payload["interval"], String, "end");
                 EXPECT_TRUE(payload["stack"].isNull());
 
               } else if (nameString == "tracing event with stack") {
                 EXPECT_EQ(state, S_tracing_event_with_stack);
                 state = State(S_tracing_event_with_stack + 1);
                 EXPECT_EQ(typeString, "tracing");
                 EXPECT_TIMING_INSTANT;
                 EXPECT_EQ_JSON(payload["category"], String, "B");
+                EXPECT_TRUE(payload["interval"].isNull());
                 EXPECT_TRUE(payload["stack"].isObject());
 
               } else if (nameString == "auto tracing") {
                 switch (state) {
                   case S_tracing_auto_tracing_start:
                     state = State(S_tracing_auto_tracing_start + 1);
                     EXPECT_EQ(typeString, "tracing");
                     EXPECT_TIMING_START;
                     EXPECT_EQ_JSON(payload["category"], String, "C");
+                    EXPECT_EQ_JSON(payload["interval"], String, "start");
                     EXPECT_TRUE(payload["stack"].isNull());
                     break;
                   case S_tracing_auto_tracing_end:
                     state = State(S_tracing_auto_tracing_end + 1);
                     EXPECT_EQ(typeString, "tracing");
                     EXPECT_TIMING_END;
                     EXPECT_EQ_JSON(payload["category"], String, "C");
+                    EXPECT_EQ_JSON(payload["interval"], String, "end");
                     ASSERT_TRUE(payload["stack"].isNull());
                     break;
                   default:
                     EXPECT_TRUE(state == S_tracing_auto_tracing_start ||
                                 state == S_tracing_auto_tracing_end);
                     break;
                 }
 
+              } else if (nameString == "M2") {
+                EXPECT_EQ(state, S_tracing_M2_C);
+                state = State(S_tracing_M2_C + 1);
+                EXPECT_EQ(typeString, "tracing");
+                EXPECT_TIMING_INSTANT;
+                EXPECT_EQ_JSON(payload["category"], String, "C");
+                EXPECT_TRUE(payload["interval"].isNull());
+                EXPECT_TRUE(payload["stack"].isNull());
+
+              } else if (nameString == "M4") {
+                EXPECT_EQ(state, S_tracing_M4_C_stack);
+                state = State(S_tracing_M4_C_stack + 1);
+                EXPECT_EQ(typeString, "tracing");
+                EXPECT_TIMING_INSTANT;
+                EXPECT_EQ_JSON(payload["category"], String, "C");
+                EXPECT_TRUE(payload["interval"].isNull());
+                EXPECT_TRUE(payload["stack"].isObject());
+
+              } else if (nameString == "M5") {
+                EXPECT_EQ(typeString, "gtest");
+                // It should only have one more element (apart from "type").
+                ASSERT_EQ(payload.size(), 2u);
+                const auto itEnd = payload.end();
+                for (auto it = payload.begin(); it != itEnd; ++it) {
+                  std::string key = it.name();
+                  if (key != "type") {
+                    const Json::Value& value = *it;
+                    ASSERT_TRUE(value.isInt());
+                    int valueInt = value.asInt();
+                    // We expect `"gtest-<i>" : <i>`.
+                    EXPECT_EQ(state, State(S_M5_gtest0 + valueInt));
+                    state = State(state + 1);
+                    EXPECT_EQ(key,
+                              std::string("gtest-") + std::to_string(valueInt));
+                  }
+                }
+
               } else if (nameString ==
                          "default-templated markers 2.0 with option") {
                 // TODO: Remove this when bug 1646714 lands.
                 EXPECT_EQ(state, S_Markers2DefaultWithOptions);
                 state = State(S_Markers2DefaultWithOptions + 1);
                 EXPECT_EQ(typeString, "NoPayloadUserData");
                 EXPECT_FALSE(payload["stack"].isNull());
 
               } else if (nameString == "FirstMarker") {
                 // Record start and end times, to compare with timestamps in
                 // following markers.
                 EXPECT_EQ(state, S_FirstMarker);
                 ts1Double = marker[START_TIME].asDouble();
                 ts2Double = marker[END_TIME].asDouble();
                 state = State(S_FirstMarker + 1);
+              } else if (nameString == "GCMajorMarkerPayload marker") {
+                EXPECT_EQ(state, S_GCMajorMarkerPayload);
+                state = State(S_GCMajorMarkerPayload + 1);
+                EXPECT_EQ(typeString, "GCMajor");
+                EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+                EXPECT_EQ_JSON(payload["timings"], Int, 42);
 
-              } else if (nameString == "Load 1: http://mozilla.org/") {
+              } else if (nameString == "GCMinorMarkerPayload marker") {
+                EXPECT_EQ(state, S_GCMinorMarkerPayload);
+                state = State(S_GCMinorMarkerPayload + 1);
+                EXPECT_EQ(typeString, "GCMinor");
+                EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+                EXPECT_EQ_JSON(payload["nursery"], Int, 43);
+
+              } else if (nameString == "GCSliceMarkerPayload marker") {
+                EXPECT_EQ(state, S_GCSliceMarkerPayload);
+                state = State(S_GCSliceMarkerPayload + 1);
+                EXPECT_EQ(typeString, "GCSlice");
+                EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+                EXPECT_EQ_JSON(payload["timings"], Int, 44);
+
+              } else if (nameString == "HangMarkerPayload marker") {
+                EXPECT_EQ(state, S_HangMarkerPayload);
+                state = State(S_HangMarkerPayload + 1);
+                EXPECT_EQ(typeString, "BHR-detected hang");
+                EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+
+              } else if (nameString == "LogMarkerPayload marker") {
+                EXPECT_EQ(state, S_LogMarkerPayload);
+                state = State(S_LogMarkerPayload + 1);
+                EXPECT_EQ(typeString, "Log");
+                EXPECT_TIMING_INSTANT_AT(ts1Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+                EXPECT_EQ_JSON(payload["name"], String, "text");
+                EXPECT_EQ_JSON(payload["module"], String, "module");
+
+              } else if (nameString == "LongTaskMarkerPayload marker") {
+                EXPECT_EQ(state, S_LongTaskMarkerPayload);
+                state = State(S_LongTaskMarkerPayload + 1);
+                EXPECT_EQ(typeString, "MainThreadLongTask");
+                EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+                EXPECT_EQ_JSON(payload["category"], String, "LongTask");
+
+              } else if (nameString == "NativeAllocationMarkerPayload marker") {
+                EXPECT_EQ(state, S_NativeAllocationMarkerPayload);
+                state = State(S_NativeAllocationMarkerPayload + 1);
+                EXPECT_EQ(typeString, "Native allocation");
+                EXPECT_TIMING_INSTANT_AT(ts1Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+                EXPECT_EQ_JSON(payload["size"], Int64, 9876543210);
+                EXPECT_EQ_JSON(payload["memoryAddress"], Int64, 1234);
+                EXPECT_EQ_JSON(payload["threadId"], Int64, 5678);
+
+              } else if (nameString == "NetworkMarkerPayload start marker") {
                 EXPECT_EQ(state, S_NetworkMarkerPayload_start);
                 state = State(S_NetworkMarkerPayload_start + 1);
                 EXPECT_EQ(typeString, "Network");
-                EXPECT_EQ_JSON(payload["startTime"], Double, ts1Double);
-                EXPECT_EQ_JSON(payload["endTime"], Double, ts2Double);
                 EXPECT_EQ_JSON(payload["id"], Int64, 1);
                 EXPECT_EQ_JSON(payload["URI"], String, "http://mozilla.org/");
                 EXPECT_EQ_JSON(payload["requestMethod"], String, "GET");
                 EXPECT_EQ_JSON(payload["pri"], Int64, 34);
                 EXPECT_EQ_JSON(payload["count"], Int64, 56);
                 EXPECT_EQ_JSON(payload["cache"], String, "Hit");
-                EXPECT_TRUE(payload["RedirectURI"].isNull());
+                EXPECT_EQ_JSON(payload["RedirectURI"], String, "");
                 EXPECT_TRUE(payload["contentType"].isNull());
 
-              } else if (nameString == "Load 12: http://mozilla.org/") {
+              } else if (nameString == "NetworkMarkerPayload stop marker") {
                 EXPECT_EQ(state, S_NetworkMarkerPayload_stop);
                 state = State(S_NetworkMarkerPayload_stop + 1);
                 EXPECT_EQ(typeString, "Network");
-                EXPECT_EQ_JSON(payload["startTime"], Double, ts1Double);
-                EXPECT_EQ_JSON(payload["endTime"], Double, ts2Double);
                 EXPECT_EQ_JSON(payload["id"], Int64, 12);
                 EXPECT_EQ_JSON(payload["URI"], String, "http://mozilla.org/");
                 EXPECT_EQ_JSON(payload["requestMethod"], String, "GET");
                 EXPECT_EQ_JSON(payload["pri"], Int64, 34);
                 EXPECT_EQ_JSON(payload["count"], Int64, 56);
                 EXPECT_EQ_JSON(payload["cache"], String, "Unresolved");
-                EXPECT_TRUE(payload["RedirectURI"].isNull());
+                EXPECT_EQ_JSON(payload["RedirectURI"], String, "");
                 EXPECT_EQ_JSON(payload["contentType"], String, "text/html");
 
-              } else if (nameString == "Load 123: http://mozilla.org/") {
+              } else if (nameString == "NetworkMarkerPayload redirect marker") {
                 EXPECT_EQ(state, S_NetworkMarkerPayload_redirect);
                 state = State(S_NetworkMarkerPayload_redirect + 1);
                 EXPECT_EQ(typeString, "Network");
-                EXPECT_EQ_JSON(payload["startTime"], Double, ts1Double);
-                EXPECT_EQ_JSON(payload["endTime"], Double, ts2Double);
                 EXPECT_EQ_JSON(payload["id"], Int64, 123);
                 EXPECT_EQ_JSON(payload["URI"], String, "http://mozilla.org/");
                 EXPECT_EQ_JSON(payload["requestMethod"], String, "GET");
                 EXPECT_EQ_JSON(payload["pri"], Int64, 34);
                 EXPECT_EQ_JSON(payload["count"], Int64, 56);
                 EXPECT_EQ_JSON(payload["cache"], String, "Unresolved");
                 EXPECT_EQ_JSON(payload["RedirectURI"], String,
                                "http://example.com/");
                 EXPECT_TRUE(payload["contentType"].isNull());
 
+              } else if (nameString == "PrefMarkerPayload marker") {
+                EXPECT_EQ(state, S_PrefMarkerPayload);
+                state = State(S_PrefMarkerPayload + 1);
+                EXPECT_EQ(typeString, "PreferenceRead");
+                EXPECT_TIMING_INSTANT_AT(ts1Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+                EXPECT_EQ_JSON(payload["prefAccessTime"], Double, ts1Double);
+                EXPECT_EQ_JSON(payload["prefName"], String, "preference name");
+                EXPECT_EQ_JSON(payload["prefKind"], String, "Shared");
+                EXPECT_EQ_JSON(payload["prefType"], String,
+                               "Preference not found");
+                EXPECT_EQ_JSON(payload["prefValue"], String,
+                               "preference value");
+
+              } else if (nameString == "ScreenshotPayload marker") {
+                EXPECT_EQ(state, S_ScreenshotPayload);
+                state = State(S_ScreenshotPayload + 1);
+                EXPECT_EQ(typeString, "CompositorScreenshot");
+                EXPECT_EQ_STRINGTABLE(payload["url"], "url");
+                EXPECT_EQ_JSON(payload["windowID"], String, "0x45678");
+                EXPECT_EQ_JSON(payload["windowWidth"], Int, 12);
+                EXPECT_EQ_JSON(payload["windowHeight"], Int, 34);
+
+              } else if (nameString == "TextMarkerPayload marker 1") {
+                EXPECT_EQ(state, S_TextMarkerPayload1);
+                state = State(S_TextMarkerPayload1 + 1);
+                EXPECT_EQ(typeString, "Text");
+                EXPECT_TIMING_INSTANT_AT(ts1Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+                EXPECT_EQ_JSON(payload["name"], String, "text");
+
+              } else if (nameString == "TextMarkerPayload marker 2") {
+                EXPECT_EQ(state, S_TextMarkerPayload2);
+                state = State(S_TextMarkerPayload2 + 1);
+                EXPECT_EQ(typeString, "Text");
+                EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+                EXPECT_EQ_JSON(payload["name"], String, "text");
+
+              } else if (nameString == "UserTimingMarkerPayload marker mark") {
+                EXPECT_EQ(state, S_UserTimingMarkerPayload_mark);
+                state = State(S_UserTimingMarkerPayload_mark + 1);
+                EXPECT_EQ(typeString, "UserTiming");
+                EXPECT_TIMING_INSTANT_AT(ts1Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+                EXPECT_EQ_JSON(payload["name"], String, "mark name");
+                EXPECT_EQ_JSON(payload["entryType"], String, "mark");
+
+              } else if (nameString ==
+                         "UserTimingMarkerPayload marker measure") {
+                EXPECT_EQ(state, S_UserTimingMarkerPayload_measure);
+                state = State(S_UserTimingMarkerPayload_measure + 1);
+                EXPECT_EQ(typeString, "UserTiming");
+                EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+                EXPECT_EQ_JSON(payload["name"], String, "measure name");
+                EXPECT_EQ_JSON(payload["entryType"], String, "measure");
+                EXPECT_EQ_JSON(payload["startMark"], String, "start mark");
+                EXPECT_EQ_JSON(payload["endMark"], String, "end mark");
+
+              } else if (nameString == "VsyncMarkerPayload marker") {
+                EXPECT_EQ(state, S_VsyncMarkerPayload);
+                state = State(S_VsyncMarkerPayload + 1);
+                EXPECT_EQ(typeString, "VsyncTimestamp");
+                // Timestamp is stored in marker outside of payload.
+                EXPECT_TIMING_INSTANT_AT(ts1Double);
+                EXPECT_TRUE(payload["stack"].isNull());
+
+              } else if (nameString == "IPCMarkerPayload marker") {
+                EXPECT_EQ(state, S_IPCMarkerPayload);
+                state = State(S_IPCMarkerPayload + 1);
+                EXPECT_EQ(typeString, "IPC");
+                EXPECT_TIMING_INSTANT_AT(ts1Double);
+
+                // The startTime and endTime are currently duplicated in the
+                // payload.
+                EXPECT_EQ_JSON(payload["startTime"], Double, ts1Double);
+                EXPECT_EQ_JSON(payload["endTime"], Double, ts1Double);
+
+                EXPECT_TRUE(payload["stack"].isNull());
+                EXPECT_EQ_JSON(payload["otherPid"], Int, 1111);
+                EXPECT_EQ_JSON(payload["messageSeqno"], Int, 1);
+                EXPECT_EQ_JSON(payload["messageType"], String,
+                               "PAPZ::Msg_LayerTransforms");
+                EXPECT_EQ_JSON(payload["side"], String, "parent");
+                EXPECT_EQ_JSON(payload["direction"], String, "sending");
+                EXPECT_EQ_JSON(payload["phase"], String, "endpoint");
+                EXPECT_EQ_JSON(payload["sync"], Bool, false);
+
               } else if (nameString == "Text in main thread with stack") {
                 EXPECT_EQ(state, S_TextWithStack);
                 state = State(S_TextWithStack + 1);
                 EXPECT_EQ(typeString, "Text");
                 EXPECT_FALSE(payload["stack"].isNull());
-                EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double);
                 EXPECT_EQ_JSON(payload["name"], String, "");
 
               } else if (nameString == "Text from main thread with stack") {
                 EXPECT_EQ(state, S_TextToMTWithStack);
                 state = State(S_TextToMTWithStack + 1);
                 EXPECT_EQ(typeString, "Text");
                 EXPECT_FALSE(payload["stack"].isNull());
                 EXPECT_EQ_JSON(payload["name"], String, "");
@@ -1273,16 +1639,80 @@ TEST(GeckoProfiler, Markers)
 
           ASSERT_EQ(data.size(), 1u);
 
           ASSERT_TRUE(data[0u].isObject());
           EXPECT_EQ_JSON(data[0u]["key"], String, "category");
           EXPECT_EQ_JSON(data[0u]["label"], String, "Type");
           EXPECT_EQ_JSON(data[0u]["format"], String, "string");
 
+        } else if (nameString == "UserTimingMark") {
+          EXPECT_EQ(display.size(), 2u);
+          EXPECT_EQ(display[0u].asString(), "marker-chart");
+          EXPECT_EQ(display[1u].asString(), "marker-table");
+
+          ASSERT_EQ(data.size(), 4u);
+
+          ASSERT_TRUE(data[0u].isObject());
+          EXPECT_EQ_JSON(data[0u]["label"], String, "Marker");
+          EXPECT_EQ_JSON(data[0u]["value"], String, "UserTiming");
+
+          ASSERT_TRUE(data[1u].isObject());
+          EXPECT_EQ_JSON(data[1u]["key"], String, "entryType");
+          EXPECT_EQ_JSON(data[1u]["label"], String, "Entry Type");
+          EXPECT_EQ_JSON(data[1u]["format"], String, "string");
+
+          ASSERT_TRUE(data[2u].isObject());
+          EXPECT_EQ_JSON(data[2u]["key"], String, "name");
+          EXPECT_EQ_JSON(data[2u]["label"], String, "Name");
+          EXPECT_EQ_JSON(data[2u]["format"], String, "string");
+
+          ASSERT_TRUE(data[3u].isObject());
+          EXPECT_EQ_JSON(data[3u]["label"], String, "Description");
+          EXPECT_EQ_JSON(data[3u]["value"], String,
+                         "UserTimingMark is created using the DOM API "
+                         "performance.mark().");
+
+        } else if (nameString == "UserTimingMeasure") {
+          EXPECT_EQ(display.size(), 2u);
+          EXPECT_EQ(display[0u].asString(), "marker-chart");
+          EXPECT_EQ(display[1u].asString(), "marker-table");
+
+          ASSERT_EQ(data.size(), 6u);
+
+          ASSERT_TRUE(data[0u].isObject());
+          EXPECT_EQ_JSON(data[0u]["label"], String, "Marker");
+          EXPECT_EQ_JSON(data[0u]["value"], String, "UserTiming");
+
+          ASSERT_TRUE(data[1u].isObject());
+          EXPECT_EQ_JSON(data[1u]["key"], String, "entryType");
+          EXPECT_EQ_JSON(data[1u]["label"], String, "Entry Type");
+          EXPECT_EQ_JSON(data[1u]["format"], String, "string");
+
+          ASSERT_TRUE(data[2u].isObject());
+          EXPECT_EQ_JSON(data[2u]["key"], String, "name");
+          EXPECT_EQ_JSON(data[2u]["label"], String, "Name");
+          EXPECT_EQ_JSON(data[2u]["format"], String, "string");
+
+          ASSERT_TRUE(data[3u].isObject());
+          EXPECT_EQ_JSON(data[3u]["key"], String, "startMark");
+          EXPECT_EQ_JSON(data[3u]["label"], String, "Start Mark");
+          EXPECT_EQ_JSON(data[3u]["format"], String, "string");
+
+          ASSERT_TRUE(data[4u].isObject());
+          EXPECT_EQ_JSON(data[4u]["key"], String, "endMark");
+          EXPECT_EQ_JSON(data[4u]["label"], String, "End Mark");
+          EXPECT_EQ_JSON(data[4u]["format"], String, "string");
+
+          ASSERT_TRUE(data[5u].isObject());
+          EXPECT_EQ_JSON(data[5u]["label"], String, "Description");
+          EXPECT_EQ_JSON(data[5u]["value"], String,
+                         "UserTimingMeasure is created using the DOM API "
+                         "performance.measure().");
+
         } else if (nameString == "BHR-detected hang") {
           EXPECT_EQ(display.size(), 3u);
           EXPECT_EQ(display[0u].asString(), "marker-chart");
           EXPECT_EQ(display[1u].asString(), "marker-table");
           EXPECT_EQ(display[2u].asString(), "timeline-overview");
 
           ASSERT_EQ(data.size(), 0u);
 
@@ -1342,18 +1772,28 @@ TEST(GeckoProfiler, Markers)
           ADD_FAILURE() << "Unknown marker schema '" << nameString.c_str()
                         << "'";
         }
       }
 
       // Check that we've got all expected schema.
       EXPECT_TRUE(testedSchemaNames.find("Text") != testedSchemaNames.end());
       EXPECT_TRUE(testedSchemaNames.find("tracing") != testedSchemaNames.end());
+      EXPECT_TRUE(testedSchemaNames.find("UserTimingMark") !=
+                  testedSchemaNames.end());
+      EXPECT_TRUE(testedSchemaNames.find("UserTimingMeasure") !=
+                  testedSchemaNames.end());
+      EXPECT_TRUE(testedSchemaNames.find("BHR-detected hang") !=
+                  testedSchemaNames.end());
+      EXPECT_TRUE(testedSchemaNames.find("MainThreadLongTask") !=
+                  testedSchemaNames.end());
+      EXPECT_TRUE(testedSchemaNames.find("Log") != testedSchemaNames.end());
       EXPECT_TRUE(testedSchemaNames.find("MediaSample") !=
                   testedSchemaNames.end());
+      EXPECT_TRUE(testedSchemaNames.find("Budget") != testedSchemaNames.end());
     }  // markerSchema
   }    // meta
 
   Maybe<ProfilerBufferInfo> info = profiler_get_buffer_info();
   MOZ_RELEASE_ASSERT(info.isSome());
   printf("Profiler buffer range: %llu .. %llu (%llu bytes)\n",
          static_cast<unsigned long long>(info->mRangeStart),
          static_cast<unsigned long long>(info->mRangeEnd),
@@ -1378,37 +1818,80 @@ TEST(GeckoProfiler, Markers)
          info->mCountersNs.min, info->mCountersNs.sum / info->mCountersNs.n,
          info->mCountersNs.max, info->mCountersNs.n);
   printf("  - Threads:   %7.1f .. %7.1f  .. %7.1f  [%u]\n",
          info->mThreadsNs.min, info->mThreadsNs.sum / info->mThreadsNs.n,
          info->mThreadsNs.max, info->mThreadsNs.n);
 
   profiler_stop();
 
+  // Nothing more should have happened to the GTestMarkerPayloads.
+  EXPECT_EQ(GTestMarkerPayload::sNumCreated, 10 + 0 + 0);
+  EXPECT_EQ(GTestMarkerPayload::sNumSerialized, 10 + 0 + 0);
+  EXPECT_EQ(GTestMarkerPayload::sNumDeserialized, 0 + 10 + 0);
+  EXPECT_EQ(GTestMarkerPayload::sNumStreamed, 0 + 10 + 0);
+  EXPECT_EQ(GTestMarkerPayload::sNumDestroyed, 10 + 10 + 0);
+
   // Try to add markers while the profiler is stopped.
-  PROFILER_MARKER_UNTYPED("marker after profiler_stop", OTHER);
+  for (int i = 0; i < 10; i++) {
+    PROFILER_ADD_MARKER_WITH_PAYLOAD("M5", OTHER, GTestMarkerPayload, (i));
+  }
 
   // Warning: this could be racy
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL, features,
                  filters, MOZ_ARRAY_LENGTH(filters), 0);
 
-  // This last marker shouldn't get streamed.
-  SpliceableChunkedJSONWriter w2;
-  w2.Start();
-  EXPECT_TRUE(::profiler_stream_json_for_this_process(w2));
-  w2.End();
-  UniquePtr<char[]> profile2 = w.ChunkedWriteFunc().CopyData();
-  ASSERT_TRUE(!!profile2.get());
-  EXPECT_TRUE(
-      std::string_view(profile2.get()).find("marker after profiler_stop") ==
-      std::string_view::npos);
+  EXPECT_TRUE(::profiler_stream_json_for_this_process(w));
 
   profiler_stop();
+
+  // The second set of GTestMarkerPayloads should not have been serialized or
+  // streamed.
+  EXPECT_EQ(GTestMarkerPayload::sNumCreated, 10 + 0 + 0 + 10);
+  EXPECT_EQ(GTestMarkerPayload::sNumSerialized, 10 + 0 + 0 + 0);
+  EXPECT_EQ(GTestMarkerPayload::sNumDeserialized, 0 + 10 + 0 + 0);
+  EXPECT_EQ(GTestMarkerPayload::sNumStreamed, 0 + 10 + 0 + 0);
+  EXPECT_EQ(GTestMarkerPayload::sNumDestroyed, 10 + 10 + 0 + 10);
 }
 
+// The duration limit will be removed from Firefox, see bug 1632365.
+#if 0
+TEST(GeckoProfiler, DurationLimit)
+{
+  uint32_t features = ProfilerFeature::StackWalk;
+  const char* filters[] = {"GeckoMain"};
+
+  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL, features,
+                 filters, MOZ_ARRAY_LENGTH(filters), 0, Some(1.5));
+
+  // Clear up the counters after the last test.
+  GTestMarkerPayload::sNumCreated = 0;
+  GTestMarkerPayload::sNumSerialized = 0;
+  GTestMarkerPayload::sNumDeserialized = 0;
+  GTestMarkerPayload::sNumStreamed = 0;
+  GTestMarkerPayload::sNumDestroyed = 0;
+
+  PROFILER_ADD_MARKER_WITH_PAYLOAD("M1", OTHER, GTestMarkerPayload, (1));
+  PR_Sleep(PR_MillisecondsToInterval(1100));
+  PROFILER_ADD_MARKER_WITH_PAYLOAD("M2", OTHER, GTestMarkerPayload, (2));
+  PR_Sleep(PR_MillisecondsToInterval(500));
+
+  SpliceableChunkedJSONWriter w;
+  ASSERT_TRUE(profiler_stream_json_for_this_process(w));
+
+  // Both markers created, serialized, destroyed; Only the first marker should
+  // have been deserialized, streamed, and destroyed again.
+  EXPECT_EQ(GTestMarkerPayload::sNumCreated, 2);
+  EXPECT_EQ(GTestMarkerPayload::sNumSerialized, 2);
+  EXPECT_EQ(GTestMarkerPayload::sNumDeserialized, 1);
+  EXPECT_EQ(GTestMarkerPayload::sNumStreamed, 1);
+  EXPECT_EQ(GTestMarkerPayload::sNumDestroyed, 3);
+}
+#endif
+
 #define COUNTER_NAME "TestCounter"
 #define COUNTER_DESCRIPTION "Test of counters in profiles"
 #define COUNTER_NAME2 "Counter2"
 #define COUNTER_DESCRIPTION2 "Second Test of counters in profiles"
 
 PROFILER_DEFINE_COUNT_TOTAL(TestCounter, COUNTER_NAME, COUNTER_DESCRIPTION);
 PROFILER_DEFINE_COUNT_TOTAL(TestCounter2, COUNTER_NAME2, COUNTER_DESCRIPTION2);
 
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -88,16 +88,20 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsCycleCollector.h"
 #include "nsDOMJSUtils.h"
 #include "nsExceptionHandler.h"
 #include "nsJSUtils.h"
 #include "nsStringBuffer.h"
 #include "nsWrapperCache.h"
 
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
+
 #if defined(XP_MACOSX)
 #  include "nsMacUtilsImpl.h"
 #endif
 
 #include "nsThread.h"
 #include "nsThreadUtils.h"
 #include "xpcpublic.h"
 
@@ -983,75 +987,25 @@ void CycleCollectedJSRuntime::GCSliceCal
                                               JS::GCProgress aProgress,
                                               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) {
-      struct GCMajorMarker {
-        static constexpr mozilla::Span<const char> MarkerTypeName() {
-          return mozilla::MakeStringSpan("GCMajor");
-        }
-        static void StreamJSONMarkerData(
-            mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
-            const mozilla::ProfilerString8View& aTimingJSON) {
-          if (aTimingJSON.Length() != 0) {
-            aWriter.SplicedJSONProperty("timings", aTimingJSON);
-          } else {
-            aWriter.NullProperty("timings");
-          }
-        }
-        static mozilla::MarkerSchema MarkerTypeDisplay() {
-          using MS = mozilla::MarkerSchema;
-          MS schema{MS::Location::markerChart, MS::Location::markerTable,
-                    MS::Location::timelineMemory};
-          // No display instructions here, there is special handling in the
-          // front-end.
-          return schema;
-        }
-      };
-
-      profiler_add_marker("GCMajor", baseprofiler::category::GCCC,
-                          MarkerTiming::Interval(aDesc.startTime(aContext),
-                                                 aDesc.endTime(aContext)),
-                          GCMajorMarker{},
-                          ProfilerString8View::WrapNullTerminatedString(
-                              aDesc.formatJSONProfiler(aContext).get()));
+      PROFILER_ADD_MARKER_WITH_PAYLOAD(
+          "GCMajor", GCCC, GCMajorMarkerPayload,
+          (aDesc.startTime(aContext), aDesc.endTime(aContext),
+           aDesc.formatJSONProfiler(aContext)));
     } else if (aProgress == JS::GC_SLICE_END) {
-      struct GCSliceMarker {
-        static constexpr mozilla::Span<const char> MarkerTypeName() {
-          return mozilla::MakeStringSpan("GCSlice");
-        }
-        static void StreamJSONMarkerData(
-            mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
-            const mozilla::ProfilerString8View& aTimingJSON) {
-          if (aTimingJSON.Length() != 0) {
-            aWriter.SplicedJSONProperty("timings", aTimingJSON);
-          } else {
-            aWriter.NullProperty("timings");
-          }
-        }
-        static mozilla::MarkerSchema MarkerTypeDisplay() {
-          using MS = mozilla::MarkerSchema;
-          MS schema{MS::Location::markerChart, MS::Location::markerTable,
-                    MS::Location::timelineMemory};
-          // No display instructions here, there is special handling in the
-          // front-end.
-          return schema;
-        }
-      };
-
-      profiler_add_marker("GCSlice", baseprofiler::category::GCCC,
-                          MarkerTiming::Interval(aDesc.lastSliceStart(aContext),
-                                                 aDesc.lastSliceEnd(aContext)),
-                          GCSliceMarker{},
-                          ProfilerString8View::WrapNullTerminatedString(
-                              aDesc.sliceToJSONProfiler(aContext).get()));
+      PROFILER_ADD_MARKER_WITH_PAYLOAD(
+          "GCSlice", GCCC, GCSliceMarkerPayload,
+          (aDesc.lastSliceStart(aContext), aDesc.lastSliceEnd(aContext),
+           aDesc.sliceToJSONProfiler(aContext)));
     }
   }
 #endif
 
   if (aProgress == JS::GC_CYCLE_END &&
       JS::dbg::FireOnGarbageCollectionHookRequired(aContext)) {
     JS::GCReason reason = aDesc.reason_;
     Unused << NS_WARN_IF(
@@ -1120,45 +1074,20 @@ void CycleCollectedJSRuntime::GCNurseryC
   }
 
   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()) {
-    struct GCMinorMarker {
-      static constexpr mozilla::Span<const char> MarkerTypeName() {
-        return mozilla::MakeStringSpan("GCMinor");
-      }
-      static void StreamJSONMarkerData(
-          mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
-          const mozilla::ProfilerString8View& aTimingJSON) {
-        if (aTimingJSON.Length() != 0) {
-          aWriter.SplicedJSONProperty("nursery", aTimingJSON);
-        } else {
-          aWriter.NullProperty("nursery");
-        }
-      }
-      static mozilla::MarkerSchema MarkerTypeDisplay() {
-        using MS = mozilla::MarkerSchema;
-        MS schema{MS::Location::markerChart, MS::Location::markerTable,
-                  MS::Location::timelineMemory};
-        // No display instructions here, there is special handling in the
-        // front-end.
-        return schema;
-      }
-    };
-
-    profiler_add_marker(
-        "GCMinor", baseprofiler::category::GCCC,
-        MarkerTiming::IntervalUntilNowFrom(self->mLatestNurseryCollectionStart),
-        GCMinorMarker{},
-        ProfilerString8View::WrapNullTerminatedString(
-            JS::MinorGcToJSON(aContext).get()));
+    PROFILER_ADD_MARKER_WITH_PAYLOAD(
+        "GCMinor", GCCC, 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
@@ -18,16 +18,19 @@
 #include "mozilla/Sprintf.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "MainThreadUtils.h"
 #include "nsClassHashtable.h"
 #include "nsDebug.h"
 #include "nsDebugImpl.h"
 #include "NSPRLogModulesParser.h"
 #include "LogCommandLineHandler.h"
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
 
 #include "prenv.h"
 #ifdef XP_WIN
 #  include <process.h>
 #else
 #  include <sys/types.h>
 #  include <unistd.h>
 #endif
@@ -411,43 +414,25 @@ class LogModuleManager {
       // We may have maxed out, allocate a buffer instead.
       allocatedBuff = mozilla::Vsmprintf(aFmt, aArgs);
       buffToWrite = allocatedBuff.get();
       charsWritten = strlen(buffToWrite);
     }
 
 #ifdef MOZ_GECKO_PROFILER
     if (mAddProfilerMarker && profiler_can_accept_markers()) {
-      struct LogMarker {
-        static constexpr Span<const char> MarkerTypeName() {
-          return MakeStringSpan("Log");
-        }
-        static void StreamJSONMarkerData(
-            baseprofiler::SpliceableJSONWriter& aWriter,
-            const ProfilerString8View& aModule,
-            const ProfilerString8View& aText) {
-          aWriter.StringProperty("module", aModule);
-          aWriter.StringProperty("name", aText);
-        }
-        static MarkerSchema MarkerTypeDisplay() {
-          using MS = MarkerSchema;
-          MS schema{MS::Location::markerTable};
-          schema.SetTableLabel("({marker.data.module}) {marker.data.name}");
-          schema.AddKeyLabelFormat("module", "Module", MS::Format::string);
-          schema.AddKeyLabelFormat("name", "Name", MS::Format::string);
-          return schema;
-        }
-      };
-
-      profiler_add_marker(
-          "LogMessages", geckoprofiler::category::OTHER,
-          aStart ? MarkerTiming::IntervalUntilNowFrom(*aStart)
-                 : MarkerTiming::InstantNow(),
-          LogMarker{}, ProfilerString8View::WrapNullTerminatedString(aName),
-          ProfilerString8View::WrapNullTerminatedString(buffToWrite));
+      if (aStart) {
+        PROFILER_ADD_MARKER_WITH_PAYLOAD(
+            "LogMessages", OTHER, LogMarkerPayload,
+            (aName, buffToWrite, *aStart, TimeStamp::Now()));
+      } else {
+        PROFILER_ADD_MARKER_WITH_PAYLOAD(
+            "LogMessages", OTHER, 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/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -37,16 +37,19 @@
 #include "mozilla/ChaosMode.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsThreadSyncDispatch.h"
 #include "nsServiceManagerUtils.h"
 #include "GeckoProfiler.h"
+#ifdef MOZ_GECKO_PROFILER
+#  include "ProfilerMarkerPayload.h"
+#endif
 #include "InputEventStatistics.h"
 #include "ThreadEventQueue.h"
 #include "ThreadEventTarget.h"
 #include "ThreadDelay.h"
 
 #include <limits>
 
 #ifdef XP_LINUX
@@ -1516,36 +1519,17 @@ void PerformanceCounterState::MaybeRepor
     // Idle events (gc...) don't *really* count here
     if (!mCurrentRunnableIsIdleRunnable) {
       mLastLongNonIdleTaskEnd = aNow;
     }
     mLastLongTaskEnd = aNow;
 
 #ifdef MOZ_GECKO_PROFILER
     if (profiler_thread_is_being_profiled()) {
-      struct LongTaskMarker {
-        static constexpr Span<const char> MarkerTypeName() {
-          return MakeStringSpan("MainThreadLongTask");
-        }
-        static void StreamJSONMarkerData(
-            baseprofiler::SpliceableJSONWriter& aWriter) {
-          aWriter.StringProperty("category", "LongTask");
-        }
-        static MarkerSchema MarkerTypeDisplay() {
-          using MS = MarkerSchema;
-          MS schema{MS::Location::markerChart, MS::Location::markerTable};
-          schema.AddKeyLabelFormat("category", "Type", MS::Format::string);
-          return schema;
-        }
-      };
-
-      profiler_add_marker(mCurrentRunnableIsIdleRunnable
-                              ? ProfilerString8View("LongIdleTask")
-                              : ProfilerString8View("LongTask"),
-                          geckoprofiler::category::OTHER,
-                          MarkerTiming::Interval(mCurrentTimeSliceStart, aNow),
-                          LongTaskMarker{});
+      PROFILER_ADD_MARKER_WITH_PAYLOAD(
+          mCurrentRunnableIsIdleRunnable ? "LongIdleTask" : "LongTask", OTHER,
+          LongTaskMarkerPayload, (mCurrentTimeSliceStart, aNow));
     }
 #endif
   }
 }
 
 }  // namespace mozilla