tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

BaseProfilerMarkersPrerequisites.h (43120B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 // This header contains basic definitions required to create marker types, and
      8 // to add markers to the profiler buffers.
      9 //
     10 // In most cases, #include "mozilla/BaseProfilerMarkers.h" instead, or
     11 // #include "mozilla/BaseProfilerMarkerTypes.h" for common marker types.
     12 
     13 #ifndef BaseProfilerMarkersPrerequisites_h
     14 #define BaseProfilerMarkersPrerequisites_h
     15 
     16 namespace mozilla {
     17 
     18 enum class StackCaptureOptions {
     19  NoStack,    // No stack captured.
     20  Full,       // Capture a full stack, including label frames, JS frames and
     21              // native frames.
     22  NonNative,  // Capture a stack without native frames for reduced overhead.
     23 };
     24 
     25 }
     26 
     27 #include "mozilla/BaseProfileJSONWriter.h"
     28 #include "mozilla/BaseProfilingCategory.h"
     29 #include "mozilla/Maybe.h"
     30 #include "mozilla/ProfileChunkedBuffer.h"
     31 #include "mozilla/BaseProfilerState.h"
     32 #include "mozilla/TimeStamp.h"
     33 #include "mozilla/UniquePtr.h"
     34 #include "mozilla/Variant.h"
     35 
     36 #include <initializer_list>
     37 #include <string_view>
     38 #include <string>
     39 #include <type_traits>
     40 #include <utility>
     41 #include <vector>
     42 
     43 // The header <X11/X.h> defines "None" as a macro that expands to "0L".
     44 // This is terrible because we have an enum variant named "None" too in this
     45 // file. To work around this, we undefine the macro "None".
     46 #ifdef None
     47 #  undef None
     48 #endif
     49 
     50 namespace mozilla {
     51 
     52 // Return a NotNull<const CHAR*> pointing at the literal empty string `""`.
     53 template <typename CHAR>
     54 constexpr const CHAR* LiteralEmptyStringPointer() {
     55  static_assert(std::is_same_v<CHAR, char> || std::is_same_v<CHAR, char16_t>,
     56                "Only char and char16_t are supported in Firefox");
     57  if constexpr (std::is_same_v<CHAR, char>) {
     58    return "";
     59  }
     60  if constexpr (std::is_same_v<CHAR, char16_t>) {
     61    return u"";
     62  }
     63 }
     64 
     65 // Return a string_view<CHAR> pointing at the literal empty string.
     66 template <typename CHAR>
     67 constexpr std::basic_string_view<CHAR> LiteralEmptyStringView() {
     68  static_assert(std::is_same_v<CHAR, char> || std::is_same_v<CHAR, char16_t>,
     69                "Only char and char16_t are supported in Firefox");
     70  // Use `operator""sv()` from <string_view>.
     71  using namespace std::literals::string_view_literals;
     72  if constexpr (std::is_same_v<CHAR, char>) {
     73    return ""sv;
     74  }
     75  if constexpr (std::is_same_v<CHAR, char16_t>) {
     76    return u""sv;
     77  }
     78 }
     79 
     80 // General string view, optimized for short on-stack life before serialization,
     81 // and between deserialization and JSON-streaming.
     82 template <typename CHAR>
     83 class MOZ_STACK_CLASS ProfilerStringView {
     84 public:
     85  // Default constructor points at "" (literal empty string).
     86  constexpr ProfilerStringView() = default;
     87 
     88  // Don't allow copy.
     89  ProfilerStringView(const ProfilerStringView&) = delete;
     90  ProfilerStringView& operator=(const ProfilerStringView&) = delete;
     91 
     92  // Allow move. For consistency the moved-from string is always reset to "".
     93  constexpr ProfilerStringView(ProfilerStringView&& aOther)
     94      : mStringView(std::move(aOther.mStringView)),
     95        mOwnership(aOther.mOwnership) {
     96    if (mOwnership == Ownership::OwnedThroughStringView) {
     97      // We now own the buffer, make the other point at the literal "".
     98      aOther.mStringView = LiteralEmptyStringView<CHAR>();
     99      aOther.mOwnership = Ownership::Literal;
    100    }
    101  }
    102  constexpr ProfilerStringView& operator=(ProfilerStringView&& aOther) {
    103    mStringView = std::move(aOther.mStringView);
    104    mOwnership = aOther.mOwnership;
    105    if (mOwnership == Ownership::OwnedThroughStringView) {
    106      // We now own the buffer, make the other point at the literal "".
    107      aOther.mStringView = LiteralEmptyStringView<CHAR>();
    108      aOther.mOwnership = Ownership::Literal;
    109    }
    110    return *this;
    111  }
    112 
    113  ~ProfilerStringView() {
    114    if (MOZ_UNLIKELY(mOwnership == Ownership::OwnedThroughStringView)) {
    115      // We own the buffer pointed at by mStringView, destroy it.
    116      // This is only used between deserialization and streaming.
    117      delete mStringView.data();
    118    }
    119  }
    120 
    121  // Implicit construction from nullptr, points at "" (literal empty string).
    122  constexpr MOZ_IMPLICIT ProfilerStringView(decltype(nullptr)) {}
    123 
    124  // Implicit constructor from a literal string.
    125  template <size_t Np1>
    126  constexpr MOZ_IMPLICIT ProfilerStringView(const CHAR (&aLiteralString)[Np1])
    127      : ProfilerStringView(aLiteralString, Np1 - 1, Ownership::Literal) {}
    128 
    129  // Constructor from a non-literal string.
    130  constexpr ProfilerStringView(const CHAR* aString, size_t aLength)
    131      : ProfilerStringView(aString, aLength, Ownership::Reference) {}
    132 
    133  // Implicit constructor from a string_view.
    134  constexpr MOZ_IMPLICIT ProfilerStringView(
    135      const std::basic_string_view<CHAR>& aStringView)
    136      : ProfilerStringView(aStringView.data(), aStringView.length(),
    137                           Ownership::Reference) {}
    138 
    139  // Implicit constructor from an expiring string_view. We assume that the
    140  // pointed-at string will outlive this ProfilerStringView.
    141  constexpr MOZ_IMPLICIT ProfilerStringView(
    142      std::basic_string_view<CHAR>&& aStringView)
    143      : ProfilerStringView(aStringView.data(), aStringView.length(),
    144                           Ownership::Reference) {}
    145 
    146  // Implicit constructor from std::string.
    147  constexpr MOZ_IMPLICIT ProfilerStringView(
    148      const std::basic_string<CHAR>& aString)
    149      : ProfilerStringView(aString.data(), aString.length(),
    150                           Ownership::Reference) {}
    151 
    152  // Construction from a raw pointer to a null-terminated string.
    153  // This is a named class-static function to make it more obvious where work is
    154  // being done (to determine the string length), and encourage users to instead
    155  // provide a length, if already known.
    156  // TODO: Find callers and convert them to constructor instead if possible.
    157  static constexpr ProfilerStringView WrapNullTerminatedString(
    158      const CHAR* aString) {
    159    return ProfilerStringView(
    160        aString, aString ? std::char_traits<CHAR>::length(aString) : 0,
    161        Ownership::Reference);
    162  }
    163 
    164  // Implicit constructor for an object with member functions `Data()`
    165  // `Length()`, and `IsLiteral()`, common in xpcom strings.
    166  template <
    167      typename String,
    168      typename DataReturnType = decltype(std::declval<const String>().Data()),
    169      typename LengthReturnType =
    170          decltype(std::declval<const String>().Length()),
    171      typename IsLiteralReturnType =
    172          decltype(std::declval<const String>().IsLiteral()),
    173      typename =
    174          std::enable_if_t<std::is_convertible_v<DataReturnType, const CHAR*> &&
    175                           std::is_integral_v<LengthReturnType> &&
    176                           std::is_same_v<IsLiteralReturnType, bool>>>
    177  constexpr MOZ_IMPLICIT ProfilerStringView(const String& aString)
    178      : ProfilerStringView(
    179            static_cast<const CHAR*>(aString.Data()), aString.Length(),
    180            aString.IsLiteral() ? Ownership::Literal : Ownership::Reference) {}
    181 
    182  [[nodiscard]] constexpr const std::basic_string_view<CHAR>& StringView()
    183      const {
    184    return mStringView;
    185  }
    186 
    187  [[nodiscard]] constexpr size_t Length() const { return mStringView.length(); }
    188 
    189  [[nodiscard]] constexpr bool IsLiteral() const {
    190    return mOwnership == Ownership::Literal;
    191  }
    192  [[nodiscard]] constexpr bool IsReference() const {
    193    return mOwnership == Ownership::Reference;
    194  }
    195  // No `IsOwned...()` because it's a secret, only used internally!
    196 
    197  [[nodiscard]] Span<const CHAR> AsSpan() const {
    198    return Span<const CHAR>(mStringView.data(), mStringView.length());
    199  }
    200  [[nodiscard]] operator Span<const CHAR>() const { return AsSpan(); }
    201 
    202 private:
    203  enum class Ownership { Literal, Reference, OwnedThroughStringView };
    204 
    205  // Allow deserializer to store anything here.
    206  friend ProfileBufferEntryReader::Deserializer<ProfilerStringView>;
    207 
    208  constexpr ProfilerStringView(const CHAR* aString, size_t aLength,
    209                               Ownership aOwnership)
    210      : mStringView(aString ? std::basic_string_view<CHAR>(aString, aLength)
    211                            : LiteralEmptyStringView<CHAR>()),
    212        mOwnership(aString ? aOwnership : Ownership::Literal) {}
    213 
    214  // String view to an outside string (literal or reference).
    215  // We may actually own the pointed-at buffer, but it is only used internally
    216  // between deserialization and JSON streaming.
    217  std::basic_string_view<CHAR> mStringView = LiteralEmptyStringView<CHAR>();
    218 
    219  Ownership mOwnership = Ownership::Literal;
    220 };
    221 
    222 using ProfilerString8View = ProfilerStringView<char>;
    223 using ProfilerString16View = ProfilerStringView<char16_t>;
    224 
    225 // This compulsory marker parameter contains the required category information.
    226 class MarkerCategory {
    227 public:
    228  // Constructor from category pair (includes both super- and sub-categories).
    229  constexpr explicit MarkerCategory(
    230      baseprofiler::ProfilingCategoryPair aCategoryPair)
    231      : mCategoryPair(aCategoryPair) {}
    232 
    233  // Returns the stored category pair.
    234  constexpr baseprofiler::ProfilingCategoryPair CategoryPair() const {
    235    return mCategoryPair;
    236  }
    237 
    238  // Returns the super-category from the stored category pair.
    239  baseprofiler::ProfilingCategory GetCategory() const {
    240    return GetProfilingCategoryPairInfo(mCategoryPair).mCategory;
    241  }
    242 
    243 private:
    244  baseprofiler::ProfilingCategoryPair mCategoryPair =
    245      baseprofiler::ProfilingCategoryPair::OTHER;
    246 };
    247 
    248 namespace baseprofiler::category {
    249 
    250 // Each category pair name constructs a MarkerCategory.
    251 // E.g.: mozilla::baseprofiler::category::OTHER_Profiling
    252 // Profiler macros will take the category name alone without namespace.
    253 // E.g.: `PROFILER_MARKER_UNTYPED("name", OTHER_Profiling)`
    254 #define CATEGORY_ENUM_BEGIN_CATEGORY(name, labelAsString, color)
    255 #define CATEGORY_ENUM_SUBCATEGORY(supercategory, name, labelAsString) \
    256  static constexpr MarkerCategory name{ProfilingCategoryPair::name};
    257 #define CATEGORY_ENUM_END_CATEGORY
    258 MOZ_PROFILING_CATEGORY_LIST(CATEGORY_ENUM_BEGIN_CATEGORY,
    259                            CATEGORY_ENUM_SUBCATEGORY,
    260                            CATEGORY_ENUM_END_CATEGORY)
    261 #undef CATEGORY_ENUM_BEGIN_CATEGORY
    262 #undef CATEGORY_ENUM_SUBCATEGORY
    263 #undef CATEGORY_ENUM_END_CATEGORY
    264 
    265 // Import `MarkerCategory` into this namespace. This will allow using this type
    266 // dynamically in macros that prepend `::mozilla::baseprofiler::category::` to
    267 // the given category, e.g.:
    268 // `PROFILER_MARKER_UNTYPED("name", MarkerCategory(...))`
    269 using MarkerCategory = ::mozilla::MarkerCategory;
    270 
    271 }  // namespace baseprofiler::category
    272 
    273 // The classes below are all embedded in a `MarkerOptions` object.
    274 class MarkerOptions;
    275 
    276 // This marker option captures a given thread id.
    277 // If left unspecified (by default construction) during the add-marker call, the
    278 // current thread id will be used then.
    279 class MarkerThreadId {
    280 public:
    281  // Default constructor, keeps the thread id unspecified.
    282  constexpr MarkerThreadId() = default;
    283 
    284  // Constructor from a given thread id.
    285  constexpr explicit MarkerThreadId(
    286      baseprofiler::BaseProfilerThreadId aThreadId)
    287      : mThreadId(aThreadId) {}
    288 
    289  // Use the current thread's id.
    290  static MarkerThreadId CurrentThread() {
    291    return MarkerThreadId(baseprofiler::profiler_current_thread_id());
    292  }
    293 
    294  // Use the main thread's id. This can be useful to record a marker from a
    295  // possibly-unregistered thread, and display it in the main thread track.
    296  static MarkerThreadId MainThread() {
    297    return MarkerThreadId(baseprofiler::profiler_main_thread_id());
    298  }
    299 
    300  [[nodiscard]] constexpr baseprofiler::BaseProfilerThreadId ThreadId() const {
    301    return mThreadId;
    302  }
    303 
    304  [[nodiscard]] constexpr bool IsUnspecified() const {
    305    return !mThreadId.IsSpecified();
    306  }
    307 
    308 private:
    309  baseprofiler::BaseProfilerThreadId mThreadId;
    310 };
    311 
    312 // This marker option contains marker timing information.
    313 // This class encapsulates the logic for correctly storing a marker based on its
    314 // Use the static methods to create the MarkerTiming. This is a transient object
    315 // that is being used to enforce the constraints of the combinations of the
    316 // data.
    317 class MarkerTiming {
    318 public:
    319  // The following static methods are used to create the MarkerTiming based on
    320  // the type that it is.
    321 
    322  static MarkerTiming InstantAt(const TimeStamp& aTime) {
    323    MOZ_ASSERT(!aTime.IsNull(), "Time is null for an instant marker.");
    324    return MarkerTiming{aTime, TimeStamp{}, MarkerTiming::Phase::Instant};
    325  }
    326 
    327  static MarkerTiming InstantNow() { return InstantAt(TimeStamp::Now()); }
    328 
    329  static MarkerTiming Interval(const TimeStamp& aStartTime,
    330                               const TimeStamp& aEndTime) {
    331    MOZ_ASSERT(!aStartTime.IsNull(),
    332               "Start time is null for an interval marker.");
    333    MOZ_ASSERT(!aEndTime.IsNull(), "End time is null for an interval marker.");
    334    return MarkerTiming{aStartTime, aEndTime, MarkerTiming::Phase::Interval};
    335  }
    336 
    337  static MarkerTiming IntervalUntilNowFrom(const TimeStamp& aStartTime) {
    338    return Interval(aStartTime, TimeStamp::Now());
    339  }
    340 
    341  static MarkerTiming IntervalStart(const TimeStamp& aTime = TimeStamp::Now()) {
    342    MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval start marker.");
    343    return MarkerTiming{aTime, TimeStamp{}, MarkerTiming::Phase::IntervalStart};
    344  }
    345 
    346  static MarkerTiming IntervalEnd(const TimeStamp& aTime = TimeStamp::Now()) {
    347    MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval end marker.");
    348    return MarkerTiming{TimeStamp{}, aTime, MarkerTiming::Phase::IntervalEnd};
    349  }
    350 
    351  // Set the interval end in this timing.
    352  // If there was already a start time, this makes it a full interval.
    353  void SetIntervalEnd(const TimeStamp& aTime = TimeStamp::Now()) {
    354    MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval end marker.");
    355    mEndTime = aTime;
    356    mPhase = mStartTime.IsNull() ? Phase::IntervalEnd : Phase::Interval;
    357  }
    358 
    359  [[nodiscard]] const TimeStamp& StartTime() const { return mStartTime; }
    360  [[nodiscard]] const TimeStamp& EndTime() const { return mEndTime; }
    361 
    362  // The phase differentiates Instant markers from Interval markers.
    363  // Interval markers can either carry both timestamps on a single marker,
    364  // or they can be split into individual Start and End markers, which are
    365  // associated with each other via the marker name.
    366  //
    367  // The numeric representation of this enum value is also exposed in the
    368  // ETW trace event's Phase field.
    369  enum class Phase : uint8_t {
    370    Instant = 0,
    371    Interval = 1,
    372    IntervalStart = 2,
    373    IntervalEnd = 3,
    374  };
    375 
    376  [[nodiscard]] Phase MarkerPhase() const {
    377    MOZ_ASSERT(!IsUnspecified());
    378    return mPhase;
    379  }
    380 
    381  // The following getter methods are used to put the value into the buffer for
    382  // storage.
    383  [[nodiscard]] double GetStartTime() const {
    384    MOZ_ASSERT(!IsUnspecified());
    385    // If mStartTime is null (e.g., for IntervalEnd), this will output 0.0 as
    386    // expected.
    387    return MarkerTiming::timeStampToDouble(mStartTime);
    388  }
    389 
    390  [[nodiscard]] double GetEndTime() const {
    391    MOZ_ASSERT(!IsUnspecified());
    392    // If mEndTime is null (e.g., for Instant or IntervalStart), this will
    393    // output 0.0 as expected.
    394    return MarkerTiming::timeStampToDouble(mEndTime);
    395  }
    396 
    397  [[nodiscard]] uint8_t GetPhase() const {
    398    MOZ_ASSERT(!IsUnspecified());
    399    return static_cast<uint8_t>(mPhase);
    400  }
    401 
    402  // This is a constructor for Rust FFI bindings. It must not be used outside of
    403  // this! Please see the other static constructors above.
    404  static void UnsafeConstruct(MarkerTiming* aMarkerTiming,
    405                              const TimeStamp& aStartTime,
    406                              const TimeStamp& aEndTime, Phase aPhase) {
    407    new (aMarkerTiming) MarkerTiming{aStartTime, aEndTime, aPhase};
    408  }
    409 
    410 private:
    411  friend ProfileBufferEntryWriter::Serializer<MarkerTiming>;
    412  friend ProfileBufferEntryReader::Deserializer<MarkerTiming>;
    413  friend MarkerOptions;
    414 
    415  // Default timing leaves it internally "unspecified", serialization getters
    416  // and add-marker functions will default to `InstantNow()`.
    417  constexpr MarkerTiming() = default;
    418 
    419  // This should only be used by internal profiler code.
    420  [[nodiscard]] bool IsUnspecified() const {
    421    return mStartTime.IsNull() && mEndTime.IsNull();
    422  }
    423 
    424  // Full constructor, used by static factory functions.
    425  constexpr MarkerTiming(const TimeStamp& aStartTime, const TimeStamp& aEndTime,
    426                         Phase aPhase)
    427      : mStartTime(aStartTime), mEndTime(aEndTime), mPhase(aPhase) {}
    428 
    429  static double timeStampToDouble(const TimeStamp& time) {
    430    if (time.IsNull()) {
    431      // The Phase lets us know not to use this value.
    432      return 0;
    433    }
    434    return (time - TimeStamp::ProcessCreation()).ToMilliseconds();
    435  }
    436 
    437  TimeStamp mStartTime;
    438  TimeStamp mEndTime;
    439  Phase mPhase = Phase::Instant;
    440 };
    441 
    442 // This marker option allows three cases:
    443 // - By default, no stacks are captured.
    444 // - The caller can request a stack capture, and the add-marker code will take
    445 //   care of it in the most efficient way.
    446 // - The caller can still provide an existing backtrace, for cases where a
    447 //   marker reports something that happened elsewhere.
    448 class MarkerStack {
    449 public:
    450  // Default constructor, no capture.
    451  constexpr MarkerStack() = default;
    452 
    453  // Disallow copy.
    454  MarkerStack(const MarkerStack&) = delete;
    455  MarkerStack& operator=(const MarkerStack&) = delete;
    456 
    457  // Allow move.
    458  MarkerStack(MarkerStack&& aOther)
    459      : mCaptureOptions(aOther.mCaptureOptions),
    460        mOptionalChunkedBufferStorage(
    461            std::move(aOther.mOptionalChunkedBufferStorage)),
    462        mChunkedBuffer(aOther.mChunkedBuffer) {
    463    AssertInvariants();
    464    aOther.Clear();
    465  }
    466  MarkerStack& operator=(MarkerStack&& aOther) {
    467    mCaptureOptions = aOther.mCaptureOptions;
    468    mOptionalChunkedBufferStorage =
    469        std::move(aOther.mOptionalChunkedBufferStorage);
    470    mChunkedBuffer = aOther.mChunkedBuffer;
    471    AssertInvariants();
    472    aOther.Clear();
    473    return *this;
    474  }
    475 
    476  // Take ownership of a backtrace. If null or empty, equivalent to NoStack().
    477  explicit MarkerStack(UniquePtr<ProfileChunkedBuffer>&& aExternalChunkedBuffer)
    478      : mOptionalChunkedBufferStorage(
    479            (!aExternalChunkedBuffer || aExternalChunkedBuffer->IsEmpty())
    480                ? nullptr
    481                : std::move(aExternalChunkedBuffer)),
    482        mChunkedBuffer(mOptionalChunkedBufferStorage.get()) {
    483    AssertInvariants();
    484  }
    485 
    486  // Use an existing backtrace stored elsewhere, which the user must guarantee
    487  // is alive during the add-marker call. If empty, equivalent to NoStack().
    488  explicit MarkerStack(ProfileChunkedBuffer& aExternalChunkedBuffer)
    489      : mChunkedBuffer(aExternalChunkedBuffer.IsEmpty()
    490                           ? nullptr
    491                           : &aExternalChunkedBuffer) {
    492    AssertInvariants();
    493  }
    494 
    495  // Don't capture a stack in this marker.
    496  static MarkerStack NoStack() {
    497    return MarkerStack(StackCaptureOptions::NoStack);
    498  }
    499 
    500  // Capture a stack when adding this marker.
    501  static MarkerStack Capture(
    502      StackCaptureOptions aCaptureOptions = StackCaptureOptions::Full) {
    503    // Actual capture will be handled inside profiler_add_marker.
    504    return MarkerStack(aCaptureOptions);
    505  }
    506 
    507  // Optionally capture a stack, useful for avoiding long-winded ternaries.
    508  static MarkerStack MaybeCapture(bool aDoCapture) {
    509    return aDoCapture ? Capture() : NoStack();
    510  }
    511 
    512  // Use an existing backtrace stored elsewhere, which the user must guarantee
    513  // is alive during the add-marker call. If empty, equivalent to NoStack().
    514  static MarkerStack UseBacktrace(
    515      ProfileChunkedBuffer& aExternalChunkedBuffer) {
    516    return MarkerStack(aExternalChunkedBuffer);
    517  }
    518 
    519  // Take ownership of a backtrace previously captured with
    520  // `profiler_capture_backtrace()`. If null, equivalent to NoStack().
    521  static MarkerStack TakeBacktrace(
    522      UniquePtr<ProfileChunkedBuffer>&& aExternalChunkedBuffer) {
    523    return MarkerStack(std::move(aExternalChunkedBuffer));
    524  }
    525 
    526  // Construct with the given capture options.
    527  static MarkerStack WithCaptureOptions(StackCaptureOptions aCaptureOptions) {
    528    return MarkerStack(aCaptureOptions);
    529  }
    530 
    531  [[nodiscard]] StackCaptureOptions CaptureOptions() const {
    532    return mCaptureOptions;
    533  }
    534 
    535  ProfileChunkedBuffer* GetChunkedBuffer() const { return mChunkedBuffer; }
    536 
    537  // Use backtrace after a request. If null, equivalent to NoStack().
    538  void UseRequestedBacktrace(ProfileChunkedBuffer* aExternalChunkedBuffer) {
    539    MOZ_RELEASE_ASSERT(mCaptureOptions != StackCaptureOptions::NoStack);
    540    mCaptureOptions = StackCaptureOptions::NoStack;
    541    if (aExternalChunkedBuffer && !aExternalChunkedBuffer->IsEmpty()) {
    542      // We only need to use the provided buffer if it is not empty.
    543      mChunkedBuffer = aExternalChunkedBuffer;
    544    }
    545    AssertInvariants();
    546  }
    547 
    548  void Clear() {
    549    mCaptureOptions = StackCaptureOptions::NoStack;
    550    mOptionalChunkedBufferStorage.reset();
    551    mChunkedBuffer = nullptr;
    552    AssertInvariants();
    553  }
    554 
    555 private:
    556  explicit MarkerStack(StackCaptureOptions aCaptureOptions)
    557      : mCaptureOptions(aCaptureOptions) {
    558    AssertInvariants();
    559  }
    560 
    561  // This should be called after every constructor and non-const function.
    562  void AssertInvariants() const {
    563 #ifdef DEBUG
    564    if (mCaptureOptions != StackCaptureOptions::NoStack) {
    565      MOZ_ASSERT(!mOptionalChunkedBufferStorage,
    566                 "We should not hold a buffer when capture is requested");
    567      MOZ_ASSERT(!mChunkedBuffer,
    568                 "We should not point at a buffer when capture is requested");
    569    } else {
    570      if (mOptionalChunkedBufferStorage) {
    571        MOZ_ASSERT(mChunkedBuffer == mOptionalChunkedBufferStorage.get(),
    572                   "Non-null mOptionalChunkedBufferStorage must be pointed-at "
    573                   "by mChunkedBuffer");
    574      }
    575      if (mChunkedBuffer) {
    576        MOZ_ASSERT(!mChunkedBuffer->IsEmpty(),
    577                   "Non-null mChunkedBuffer must not be empty");
    578      }
    579    }
    580 #endif  // DEBUG
    581  }
    582 
    583  StackCaptureOptions mCaptureOptions = StackCaptureOptions::NoStack;
    584 
    585  // Optional storage for the backtrace, in case it was captured before the
    586  // add-marker call.
    587  UniquePtr<ProfileChunkedBuffer> mOptionalChunkedBufferStorage;
    588 
    589  // If not null, this points to the backtrace. It may point to a backtrace
    590  // temporarily stored on the stack, or to mOptionalChunkedBufferStorage.
    591  ProfileChunkedBuffer* mChunkedBuffer = nullptr;
    592 };
    593 
    594 // This marker option captures a given inner window id.
    595 class MarkerInnerWindowId {
    596 public:
    597  // Default constructor, it leaves the id unspecified.
    598  constexpr MarkerInnerWindowId() = default;
    599 
    600  // Constructor with a specified inner window id.
    601  constexpr explicit MarkerInnerWindowId(uint64_t i) : mInnerWindowId(i) {}
    602 
    603  // Constructor with either specified inner window id or Nothing.
    604  constexpr explicit MarkerInnerWindowId(const Maybe<uint64_t>& i)
    605      : mInnerWindowId(i.valueOr(scNoId)) {}
    606 
    607  // Explicit option with unspecified id.
    608  constexpr static MarkerInnerWindowId NoId() { return MarkerInnerWindowId{}; }
    609 
    610  [[nodiscard]] bool IsUnspecified() const { return mInnerWindowId == scNoId; }
    611 
    612  [[nodiscard]] constexpr uint64_t Id() const { return mInnerWindowId; }
    613 
    614 private:
    615  static constexpr uint64_t scNoId = 0;
    616  uint64_t mInnerWindowId = scNoId;
    617 };
    618 
    619 // This class combines each of the possible marker options above.
    620 class MarkerOptions {
    621 public:
    622  // Constructor from individual options (including none).
    623  // Implicit to allow `{}` and one option type as-is.
    624  // Options that are not provided here are defaulted. In particular, timing
    625  // defaults to `MarkerTiming::InstantNow()` when the marker is recorded.
    626  template <typename... Options>
    627  MOZ_IMPLICIT MarkerOptions(Options&&... aOptions) {
    628    (Set(std::forward<Options>(aOptions)), ...);
    629  }
    630 
    631  // Disallow copy.
    632  MarkerOptions(const MarkerOptions&) = delete;
    633  MarkerOptions& operator=(const MarkerOptions&) = delete;
    634 
    635  // Allow move.
    636  MarkerOptions(MarkerOptions&&) = default;
    637  MarkerOptions& operator=(MarkerOptions&&) = default;
    638 
    639  // The embedded `MarkerTiming` hasn't been specified yet.
    640  [[nodiscard]] bool IsTimingUnspecified() const {
    641    return mTiming.IsUnspecified();
    642  }
    643 
    644  // Each option may be added in a chain by e.g.:
    645  // `options.Set(MarkerThreadId(123)).Set(MarkerTiming::IntervalEnd())`.
    646  // When passed to an add-marker function, it must be an rvalue, either created
    647  // on the spot, or `std::move`d from storage, e.g.:
    648  // `PROFILER_MARKER_UNTYPED("...", std::move(options).Set(...))`;
    649  //
    650  // Options can be read by their name (without "Marker"), e.g.: `o.ThreadId()`.
    651  // Add "Ref" for a non-const reference, e.g.: `o.ThreadIdRef() = ...;`
    652 #define FUNCTIONS_ON_MEMBER(NAME)                      \
    653  MarkerOptions& Set(Marker##NAME&& a##NAME) & {       \
    654    m##NAME = std::move(a##NAME);                      \
    655    return *this;                                      \
    656  }                                                    \
    657                                                       \
    658  MarkerOptions&& Set(Marker##NAME&& a##NAME) && {     \
    659    m##NAME = std::move(a##NAME);                      \
    660    return std::move(*this);                           \
    661  }                                                    \
    662                                                       \
    663  const Marker##NAME& NAME() const { return m##NAME; } \
    664                                                       \
    665  Marker##NAME& NAME##Ref() { return m##NAME; }
    666 
    667  FUNCTIONS_ON_MEMBER(ThreadId);
    668  FUNCTIONS_ON_MEMBER(Timing);
    669  FUNCTIONS_ON_MEMBER(Stack);
    670  FUNCTIONS_ON_MEMBER(InnerWindowId);
    671 #undef FUNCTIONS_ON_MEMBER
    672 
    673 private:
    674  friend ProfileBufferEntryReader::Deserializer<MarkerOptions>;
    675 
    676  MarkerThreadId mThreadId;
    677  MarkerTiming mTiming;
    678  MarkerStack mStack;
    679  MarkerInnerWindowId mInnerWindowId;
    680 };
    681 
    682 }  // namespace mozilla
    683 
    684 namespace mozilla::baseprofiler::markers {
    685 
    686 // Default marker payload types, with no extra information, not even a marker
    687 // type and payload. This is intended for label-only markers.
    688 struct NoPayload final {};
    689 
    690 }  // namespace mozilla::baseprofiler::markers
    691 
    692 namespace mozilla {
    693 
    694 class JSONWriter;
    695 
    696 // This class collects all the information necessary to stream the JSON schema
    697 // that informs the front-end how to display a type of markers.
    698 // It will be created and populated in `MarkerTypeDisplay()` functions in each
    699 // marker type definition, see Add/Set functions.
    700 class MarkerSchema {
    701 public:
    702  // This is used to describe a C++ type that is expected to be specified to
    703  // the marker and used in PayloadField. This type is the expected input type
    704  // to the marker data.
    705  enum class InputType {
    706    Uint64,
    707    Uint32,
    708    Uint8,
    709    Int64,
    710    Int32,
    711    Int8,
    712    Double,
    713    Boolean,
    714    CString,
    715    String,
    716    TimeStamp,
    717    TimeDuration
    718  };
    719 
    720  template <typename T>
    721  static constexpr InputType getDefaultInputTypeForType() {
    722    using CleanT = std::remove_cv_t<std::remove_pointer_t<T>>;
    723 
    724    if constexpr (std::is_same_v<CleanT, bool>) {
    725      return InputType::Boolean;
    726    } else if constexpr (std::is_same_v<CleanT, double>) {
    727      return InputType::Double;
    728    } else if constexpr (std::is_unsigned_v<CleanT> && sizeof(CleanT) == 4) {
    729      return InputType::Uint32;
    730    } else if constexpr (std::is_unsigned_v<CleanT> && sizeof(CleanT) == 8) {
    731      return InputType::Uint64;
    732    } else if constexpr (std::is_unsigned_v<CleanT> && sizeof(CleanT) == 1) {
    733      return InputType::Uint8;
    734    } else if constexpr (std::is_signed_v<CleanT> &&
    735                         std::is_integral_v<CleanT> && sizeof(CleanT) == 4) {
    736      return InputType::Int32;
    737    } else if constexpr (std::is_signed_v<CleanT> &&
    738                         std::is_integral_v<CleanT> && sizeof(CleanT) == 8) {
    739      return InputType::Int64;
    740    } else if constexpr (std::is_signed_v<CleanT> &&
    741                         std::is_integral_v<CleanT> && sizeof(CleanT) == 1) {
    742      return InputType::Int8;
    743    } else if constexpr (std::is_same_v<CleanT, TimeStamp>) {
    744      return InputType::TimeStamp;
    745    } else if constexpr (std::is_same_v<CleanT, TimeDuration>) {
    746      return InputType::TimeDuration;
    747    } else if constexpr (std::is_same_v<CleanT, ProfilerString8View>) {
    748      return InputType::CString;
    749    } else {
    750      static_assert(sizeof(T) == 0, "Unsupported type");
    751    }
    752  }
    753 
    754  enum class Location : unsigned {
    755    MarkerChart,
    756    MarkerTable,
    757    // This adds markers to the main marker timeline in the header.
    758    TimelineOverview,
    759    // In the timeline, this is a section that breaks out markers that are
    760    // related to memory. When memory counters are enabled, this is its own
    761    // track, otherwise it is displayed with the main thread.
    762    TimelineMemory,
    763    // This adds markers to the IPC timeline area in the header.
    764    TimelineIPC,
    765    // This adds markers to the FileIO timeline area in the header.
    766    TimelineFileIO,
    767    // TODO - This is not supported yet.
    768    StackChart
    769  };
    770 
    771  // Used as constructor parameter, to explicitly specify that the location (and
    772  // other display options) are handled as a special case in the front-end.
    773  // In this case, *no* schema will be output for this type.
    774  struct SpecialFrontendLocation {};
    775 
    776  enum class Format {
    777    // ----------------------------------------------------
    778    // String types.
    779 
    780    // Show the URL, and handle PII sanitization
    781    Url,
    782    // Show the file path, and handle PII sanitization.
    783    FilePath,
    784    // Show arbitrary string and handle PII sanitization
    785    SanitizedString,
    786    // Important, do not put URL or file path information here, as it will not
    787    // be sanitized. Please be careful with including other types of PII here as
    788    // well.
    789    // e.g. "Label: Some String"
    790    String,
    791 
    792    // Show a string from a UniqueStringArray given an index in the profile.
    793    // e.g. 1, given string table ["hello", "world"] will show "world"
    794    UniqueString,
    795 
    796    // ----------------------------------------------------
    797    // Numeric types
    798 
    799    // For time data that represents a duration of time.
    800    // e.g. "Label: 5s, 5ms, 5μs"
    801    Duration,
    802    // Data that happened at a specific time, relative to the start of the
    803    // profile. e.g. "Label: 15.5s, 20.5ms, 30.5μs"
    804    Time,
    805    // The following are alternatives to display a time only in a specific unit
    806    // of time.
    807    Seconds,       // "Label: 5s"
    808    Milliseconds,  // "Label: 5ms"
    809    Microseconds,  // "Label: 5μs"
    810    Nanoseconds,   // "Label: 5ns"
    811    // e.g. "Label: 5.55mb, 5 bytes, 312.5kb"
    812    Bytes,
    813    // This should be a value between 0 and 1.
    814    // "Label: 50%"
    815    Percentage,
    816    // The integer should be used for generic representations of numbers.
    817    // Do not use it for time information.
    818    // "Label: 52, 5,323, 1,234,567"
    819    Integer,
    820    // The decimal should be used for generic representations of numbers.
    821    // Do not use it for time information.
    822    // "Label: 52.23, 0.0054, 123,456.78"
    823    Decimal,
    824 
    825    // A flow is a u64 identifier that's unique across processes. All of
    826    // the markers with same flow id before a terminating flow id will be
    827    // considered part of the same "flow" and linked together.
    828    Flow,
    829    // A terminating flow ends a flow of a particular id and allows that id
    830    // to be reused again. It often makes sense for destructors to create
    831    // a marker with a field of this type.
    832    TerminatingFlow
    833  };
    834 
    835  template <typename T>
    836  static constexpr Format getDefaultFormatForType() {
    837    using CleanT = std::remove_cv_t<T>;
    838 
    839    if constexpr (std::is_integral_v<CleanT> || std::is_same_v<CleanT, bool>) {
    840      return Format::Integer;
    841    } else if constexpr (std::is_same_v<CleanT, double>) {
    842      return Format::Decimal;
    843    } else if constexpr (std::is_same_v<CleanT, TimeStamp>) {
    844      return Format::Time;
    845    } else if constexpr (std::is_same_v<CleanT, TimeDuration>) {
    846      return Format::Duration;
    847    } else if constexpr (std::is_same_v<CleanT, ProfilerString8View>) {
    848      return Format::SanitizedString;
    849    } else {
    850      static_assert(sizeof(T) == 0, "Unsupported type");
    851    }
    852  }
    853 
    854  // This represents groups of markers which MarkerTypes can expose to indicate
    855  // what group they belong to (multiple groups are allowed combined in bitwise
    856  // or). This is currently only used for ETW filtering. In the long run this
    857  // should be generalized to gecko markers.
    858  enum class ETWMarkerGroup : uint64_t {
    859    Generic = 1,
    860    UserMarkers = 1 << 1,
    861    Memory = 1 << 2,
    862    Scheduling = 1 << 3,
    863    Text = 1 << 4,
    864    Tracing = 1 << 5
    865  };
    866 
    867  // Flags which describe additional information for a PayloadField.
    868  enum class PayloadFlags : uint32_t {
    869    None = 0,
    870    Searchable = 1 << 0,
    871    Hidden = 1 << 1,
    872  };
    873 
    874  // This is one field of payload to be used for additional marker data.
    875  struct PayloadField {
    876    // Key identifying the marker.
    877    const char* Key;
    878    // Input type, this represents the data type specified.
    879    InputType InputTy;
    880    // Label, additional description.
    881    const char* Label = nullptr;
    882    // Format as written to the JSON.
    883    Format Fmt = Format::String;
    884    // Optional PayloadFlags.
    885    PayloadFlags Flags = PayloadFlags::None;
    886  };
    887 
    888  enum class GraphType { Line, Bar, FilledLine };
    889  enum class GraphColor {
    890    Blue,
    891    Green,
    892    Grey,
    893    Ink,
    894    Magenta,
    895    Orange,
    896    Purple,
    897    Red,
    898    Teal,
    899    Yellow
    900  };
    901 
    902  // Marker schema, with a non-empty list of locations where markers should be
    903  // shown.
    904  // Tech note: Even though `aLocations` are templated arguments, they are
    905  // assigned to an `enum class` object, so they can only be of that enum type.
    906  template <typename... Locations>
    907  explicit MarkerSchema(Location aLocation, Locations... aLocations)
    908      : mLocations{aLocation, aLocations...} {}
    909 
    910  // Alternative constructor for MarkerSchema.
    911  explicit MarkerSchema(const mozilla::MarkerSchema::Location* aLocations,
    912                        size_t aLength)
    913      : mLocations(aLocations, aLocations + aLength) {}
    914 
    915  // Marker schema for types that have special frontend handling.
    916  // Nothing else should be set in this case.
    917  // Implicit to allow quick return from MarkerTypeDisplay functions.
    918  MOZ_IMPLICIT MarkerSchema(SpecialFrontendLocation) {}
    919 
    920  // Caller must specify location(s) or SpecialFrontendLocation above.
    921  MarkerSchema() = delete;
    922 
    923  // Optional labels in the marker chart, the chart tooltip, and the marker
    924  // table. If not provided, the marker "name" will be used. The given string
    925  // can contain element keys in braces to include data elements streamed by
    926  // `StreamJSONMarkerData()`. E.g.: "This is {text}"
    927 
    928 #define LABEL_SETTER(name)                       \
    929  MarkerSchema& Set##name(std::string a##name) { \
    930    m##name = std::move(a##name);                \
    931    return *this;                                \
    932  }
    933 
    934  LABEL_SETTER(ChartLabel)
    935  LABEL_SETTER(TooltipLabel)
    936  LABEL_SETTER(TableLabel)
    937 
    938 #undef LABEL_SETTER
    939 
    940  MarkerSchema& SetAllLabels(std::string aText) {
    941    // Here we set the same text in each label.
    942    // TODO: Move to a single "label" field once the front-end allows it.
    943    SetChartLabel(aText);
    944    SetTooltipLabel(aText);
    945    SetTableLabel(std::move(aText));
    946    return *this;
    947  }
    948 
    949  MarkerSchema& SetIsStackBased() {
    950    mIsStackBased = true;
    951    return *this;
    952  }
    953 
    954  // Each data element that is streamed by `StreamJSONMarkerData()` can be
    955  // displayed as indicated by using one of the `Add...` function below.
    956  // Each `Add...` will add a line in the full marker description. Parameters:
    957  // - `aKey`: Element property name as streamed by `StreamJSONMarkerData()`.
    958  // - `aLabel`: Optional prefix. Defaults to the key name.
    959  // - `aFormat`: How to format the data element value, see `Format` above.
    960  // - `aPayloadFlags`: Optional, indicates additinal flags to serialize inside
    961  // the marker schema object. Defaults to `PayloadFlags::None`.
    962 
    963  MarkerSchema& AddKeyFormat(std::string aKey, Format aFormat,
    964                             PayloadFlags aPayloadFlags = PayloadFlags::None) {
    965    mData.emplace_back(mozilla::VariantType<DynamicData>{},
    966                       DynamicData{std::move(aKey), mozilla::Nothing{}, aFormat,
    967                                   aPayloadFlags});
    968    return *this;
    969  }
    970 
    971  MarkerSchema& AddKeyLabelFormat(
    972      std::string aKey, std::string aLabel, Format aFormat,
    973      PayloadFlags aPayloadFlags = PayloadFlags::None) {
    974    mData.emplace_back(
    975        mozilla::VariantType<DynamicData>{},
    976        DynamicData{std::move(aKey), mozilla::Some(std::move(aLabel)), aFormat,
    977                    aPayloadFlags});
    978    return *this;
    979  }
    980 
    981  // The display may also include static rows.
    982 
    983  MarkerSchema& AddStaticLabelValue(std::string aLabel, std::string aValue) {
    984    mData.emplace_back(mozilla::VariantType<StaticData>{},
    985                       StaticData{std::move(aLabel), std::move(aValue)});
    986    return *this;
    987  }
    988 
    989  // Markers can be shown as timeline tracks.
    990 
    991  MarkerSchema& AddChart(std::string aKey, GraphType aType) {
    992    mGraphs.emplace_back(GraphData{std::move(aKey), aType, mozilla::Nothing{}});
    993    return *this;
    994  }
    995 
    996  MarkerSchema& AddChartColor(std::string aKey, GraphType aType,
    997                              GraphColor aColor) {
    998    mGraphs.emplace_back(
    999        GraphData{std::move(aKey), aType, mozilla::Some(aColor)});
   1000    return *this;
   1001  }
   1002 
   1003  // Internal streaming function.
   1004  MFBT_API void Stream(JSONWriter& aWriter, const Span<const char>& aName) &&;
   1005 
   1006 private:
   1007  MFBT_API static Span<const char> LocationToStringSpan(Location aLocation);
   1008  MFBT_API static Span<const char> FormatToStringSpan(Format aFormat);
   1009  MFBT_API static Span<const char> GraphTypeToStringSpan(GraphType aType);
   1010  MFBT_API static Span<const char> GraphColorToStringSpan(GraphColor aColor);
   1011 
   1012  // List of marker display locations. Empty for SpecialFrontendLocation.
   1013  std::vector<Location> mLocations;
   1014  // Labels for different places.
   1015  std::string mChartLabel;
   1016  std::string mTooltipLabel;
   1017  std::string mTableLabel;
   1018  bool mIsStackBased = false;
   1019  // Main display, made of zero or more rows of key+label+format or label+value.
   1020 private:
   1021  struct DynamicData {
   1022    std::string mKey;
   1023    mozilla::Maybe<std::string> mLabel;
   1024    Format mFormat;
   1025    PayloadFlags mPayloadFlags;
   1026  };
   1027  struct StaticData {
   1028    std::string mLabel;
   1029    std::string mValue;
   1030  };
   1031  using DataRow = mozilla::Variant<DynamicData, StaticData>;
   1032  using DataRowVector = std::vector<DataRow>;
   1033 
   1034  DataRowVector mData;
   1035 
   1036  struct GraphData {
   1037    std::string mKey;
   1038    GraphType mType;
   1039    mozilla::Maybe<GraphColor> mColor;
   1040  };
   1041  std::vector<GraphData> mGraphs;
   1042 };
   1043 
   1044 namespace detail {
   1045 // GCC doesn't allow this to live inside the class.
   1046 template <typename PayloadType>
   1047 static void StreamPayload(baseprofiler::SpliceableJSONWriter& aWriter,
   1048                          const Span<const char> aKey,
   1049                          const PayloadType& aPayload) {
   1050  using CleanT = std::remove_cv_t<PayloadType>;
   1051  if constexpr (std::is_integral_v<CleanT>) {
   1052    aWriter.IntProperty(aKey, aPayload);
   1053  } else if constexpr (std::is_same_v<CleanT, double>) {
   1054    aWriter.DoubleProperty(aKey, aPayload);
   1055  } else {
   1056    aWriter.StringProperty(aKey, aPayload);
   1057  }
   1058 }
   1059 
   1060 template <typename PayloadType>
   1061 inline void StreamPayload(baseprofiler::SpliceableJSONWriter& aWriter,
   1062                          const Span<const char> aKey,
   1063                          const Maybe<PayloadType>& aPayload) {
   1064  if (aPayload.isSome()) {
   1065    StreamPayload(aWriter, aKey, *aPayload);
   1066  } else {
   1067    aWriter.NullProperty(aKey);
   1068  }
   1069 }
   1070 
   1071 template <>
   1072 inline void StreamPayload<bool>(baseprofiler::SpliceableJSONWriter& aWriter,
   1073                                const Span<const char> aKey,
   1074                                const bool& aPayload) {
   1075  aWriter.BoolProperty(aKey, aPayload);
   1076 }
   1077 
   1078 template <>
   1079 inline void StreamPayload<Flow>(baseprofiler::SpliceableJSONWriter& aWriter,
   1080                                const Span<const char> aKey,
   1081                                const Flow& aPayload) {
   1082  aWriter.FlowProperty(aKey, aPayload);
   1083 }
   1084 
   1085 }  // namespace detail
   1086 
   1087 // This helper class is used by MarkerTypes that want to support the general
   1088 // MarkerType object schema. When using this the markers will also transmit
   1089 // their payload to the ETW tracer as well as requiring less inline code.
   1090 // This is a curiously recurring template, the template argument is the child
   1091 // class itself.
   1092 template <typename T>
   1093 struct BaseMarkerType {
   1094  static constexpr const char* Description = nullptr;
   1095 
   1096  static constexpr const char* AllLabels = nullptr;
   1097  static constexpr const char* ChartLabel = nullptr;
   1098  static constexpr const char* TableLabel = nullptr;
   1099  static constexpr const char* TooltipLabel = nullptr;
   1100 
   1101  // Setting this property to true is a promise that the the marker will nest
   1102  // properly.  i.e. it can't have a partially overlapping time range with any
   1103  // other stack based markers on the same thread.
   1104  static constexpr bool IsStackBased = false;
   1105 
   1106  // This indicates whether this marker type wants the names passed to the
   1107  // individual marker calls stores along with the marker.
   1108  static constexpr bool StoreName = false;
   1109 
   1110  static constexpr MarkerSchema::ETWMarkerGroup Group =
   1111      MarkerSchema::ETWMarkerGroup::Generic;
   1112 
   1113  static MarkerSchema MarkerTypeDisplay() {
   1114    using MS = MarkerSchema;
   1115    MS schema{T::Locations, std::size(T::Locations)};
   1116    if (T::AllLabels) {
   1117      schema.SetAllLabels(T::AllLabels);
   1118    }
   1119    if (T::ChartLabel) {
   1120      schema.SetChartLabel(T::ChartLabel);
   1121    }
   1122    if (T::TableLabel) {
   1123      schema.SetTableLabel(T::TableLabel);
   1124    }
   1125    if (T::TooltipLabel) {
   1126      schema.SetTooltipLabel(T::TooltipLabel);
   1127    }
   1128    if (T::IsStackBased) {
   1129      schema.SetIsStackBased();
   1130    }
   1131    for (const MS::PayloadField field : T::PayloadFields) {
   1132      if (field.Label) {
   1133        schema.AddKeyLabelFormat(field.Key, field.Label, field.Fmt,
   1134                                 field.Flags);
   1135      } else {
   1136        schema.AddKeyFormat(field.Key, field.Fmt, field.Flags);
   1137      }
   1138    }
   1139    if (T::Description) {
   1140      schema.AddStaticLabelValue("Description", T::Description);
   1141    }
   1142    return schema;
   1143  }
   1144 
   1145  static constexpr Span<const char> MarkerTypeName() {
   1146    return MakeStringSpan(T::Name);
   1147  }
   1148 
   1149  // This is called by the child class since the child class version of this
   1150  // function is used to infer the argument types by the profile buffer and
   1151  // allows the child to do any special data conversion it needs to do.
   1152  // Optionally the child can opt not to use this at all and write the data
   1153  // out itself.
   1154  template <typename... PayloadArguments>
   1155  static void StreamJSONMarkerDataImpl(
   1156      baseprofiler::SpliceableJSONWriter& aWriter,
   1157      const PayloadArguments&... aPayloadArguments) {
   1158    size_t i = 0;
   1159    (detail::StreamPayload(aWriter, MakeStringSpan(T::PayloadFields[i++].Key),
   1160                           aPayloadArguments),
   1161     ...);
   1162  }
   1163 };
   1164 }  // namespace mozilla
   1165 
   1166 #endif  // BaseProfilerMarkersPrerequisites_h