tor-browser

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

BaseProfilerMarkersDetail.h (31883B)


      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 #ifndef BaseProfilerMarkersDetail_h
      8 #define BaseProfilerMarkersDetail_h
      9 
     10 #ifndef BaseProfilerMarkers_h
     11 #  error "This header should only be #included by BaseProfilerMarkers.h"
     12 #endif
     13 
     14 #include "mozilla/BaseProfilerMarkersPrerequisites.h"
     15 
     16 //                        ~~ HERE BE DRAGONS ~~
     17 //
     18 // Everything below is internal implementation detail, you shouldn't need to
     19 // look at it unless working on the profiler code.
     20 
     21 #include "mozilla/BaseProfileJSONWriter.h"
     22 #include "mozilla/ProfileBufferEntryKinds.h"
     23 
     24 #include <limits>
     25 #include <tuple>
     26 #include <type_traits>
     27 
     28 namespace mozilla::baseprofiler {
     29 // Implemented in platform.cpp
     30 MFBT_API ProfileChunkedBuffer& profiler_get_core_buffer();
     31 }  // namespace mozilla::baseprofiler
     32 
     33 namespace mozilla::base_profiler_markers_detail {
     34 
     35 struct Streaming {
     36  // A `MarkerDataDeserializer` is a free function that can read a serialized
     37  // payload from an `EntryReader` and streams it as JSON object properties.
     38  using MarkerDataDeserializer = void (*)(ProfileBufferEntryReader&,
     39                                          baseprofiler::SpliceableJSONWriter&);
     40 
     41  // A `MarkerTypeNameFunction` is a free function that returns the name of the
     42  // marker type.
     43  using MarkerTypeNameFunction = Span<const char> (*)();
     44 
     45  // A `MarkerSchemaFunction` is a free function that returns a
     46  // `MarkerSchema`, which contains all the information needed to stream
     47  // the display schema associated with a marker type.
     48  using MarkerSchemaFunction = MarkerSchema (*)();
     49 
     50  struct MarkerTypeFunctions {
     51    MarkerDataDeserializer mMarkerDataDeserializer = nullptr;
     52    MarkerTypeNameFunction mMarkerTypeNameFunction = nullptr;
     53    MarkerSchemaFunction mMarkerSchemaFunction = nullptr;
     54  };
     55 
     56  // A `DeserializerTag` will be added before the payload, to help select the
     57  // correct deserializer when reading back the payload.
     58  using DeserializerTag = uint8_t;
     59 
     60  // Store a deserializer (and other marker-type-specific functions) and get its
     61  // `DeserializerTag`.
     62  // This is intended to be only used once per deserializer when a new marker
     63  // type is used for the first time, so it should be called to initialize a
     64  // `static const` tag that will be re-used by all markers of the corresponding
     65  // payload type -- see use below.
     66  MFBT_API static DeserializerTag TagForMarkerTypeFunctions(
     67      MarkerDataDeserializer aDeserializer,
     68      MarkerTypeNameFunction aMarkerTypeNameFunction,
     69      MarkerSchemaFunction aMarkerSchemaFunction);
     70 
     71  // Get the `MarkerDataDeserializer` for a given `DeserializerTag`.
     72  MFBT_API static MarkerDataDeserializer DeserializerForTag(
     73      DeserializerTag aTag);
     74 
     75  // Retrieve all MarkerTypeFunctions's.
     76  // While this object lives, no other operations can happen on this list.
     77  class LockedMarkerTypeFunctionsList {
     78   public:
     79    MFBT_API LockedMarkerTypeFunctionsList();
     80    MFBT_API ~LockedMarkerTypeFunctionsList();
     81 
     82    LockedMarkerTypeFunctionsList(const LockedMarkerTypeFunctionsList&) =
     83        delete;
     84    LockedMarkerTypeFunctionsList& operator=(
     85        const LockedMarkerTypeFunctionsList&) = delete;
     86 
     87    auto begin() const { return mMarkerTypeFunctionsSpan.begin(); }
     88    auto end() const { return mMarkerTypeFunctionsSpan.end(); }
     89 
     90   private:
     91    Span<const MarkerTypeFunctions> mMarkerTypeFunctionsSpan;
     92  };
     93 };
     94 
     95 // This helper will examine a marker type's `StreamJSONMarkerData` function, see
     96 // specialization below.
     97 template <typename T>
     98 struct StreamFunctionTypeHelper;
     99 
    100 // Helper specialization that takes the expected
    101 // `StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter&, ...)` function and
    102 // provide information about the `...` parameters.
    103 template <typename R, typename... As>
    104 struct StreamFunctionTypeHelper<R(baseprofiler::SpliceableJSONWriter&, As...)> {
    105  constexpr static size_t scArity = sizeof...(As);
    106  using TupleType =
    107      std::tuple<std::remove_cv_t<std::remove_reference_t<As>>...>;
    108 
    109  // Serialization function that takes the exact same parameter types
    110  // (const-ref'd) as `StreamJSONMarkerData`. This has to be inside the helper
    111  // because only here can we access the raw parameter pack `As...`.
    112  // And because we're using the same argument types through
    113  // references-to-const, permitted implicit conversions can happen.
    114  static ProfileBufferBlockIndex Serialize(
    115      ProfileChunkedBuffer& aBuffer, const ProfilerString8View& aName,
    116      const MarkerCategory& aCategory, MarkerOptions&& aOptions,
    117      Streaming::DeserializerTag aDeserializerTag, const As&... aAs) {
    118    // Note that options are first after the entry kind, because they contain
    119    // the thread id, which is handled first to filter markers by threads.
    120    return aBuffer.PutObjects(ProfileBufferEntryKind::Marker, aOptions, aName,
    121                              aCategory, aDeserializerTag,
    122                              MarkerPayloadType::Cpp, aAs...);
    123  }
    124 };
    125 
    126 // Helper for a marker type.
    127 // A marker type is defined in a `struct` with some expected static member
    128 // functions. See example in BaseProfilerMarkers.h.
    129 template <typename MarkerType>
    130 struct MarkerTypeSerialization {
    131  // Definitions to access the expected
    132  // `StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter&, ...)` function
    133  // and its parameters.
    134  using StreamFunctionType =
    135      StreamFunctionTypeHelper<decltype(MarkerType::StreamJSONMarkerData)>;
    136  constexpr static size_t scStreamFunctionParameterCount =
    137      StreamFunctionType::scArity;
    138  using StreamFunctionUserParametersTuple =
    139      typename StreamFunctionType::TupleType;
    140  template <size_t i>
    141  using StreamFunctionParameter =
    142      std::tuple_element_t<i, StreamFunctionUserParametersTuple>;
    143 
    144  template <typename... Ts>
    145  static ProfileBufferBlockIndex Serialize(ProfileChunkedBuffer& aBuffer,
    146                                           const ProfilerString8View& aName,
    147                                           const MarkerCategory& aCategory,
    148                                           MarkerOptions&& aOptions,
    149                                           const Ts&... aTs) {
    150    static_assert(!std::is_same_v<MarkerType,
    151                                  ::mozilla::baseprofiler::markers::NoPayload>,
    152                  "NoPayload should have been handled in the caller.");
    153    // Register marker type functions, and get the tag for this deserializer.
    154    // Note that the tag is stored in a function-static object, and this
    155    // function is static in a templated struct, so there should only be one tag
    156    // per MarkerType.
    157    // Making the tag class-static may have been more efficient (to avoid a
    158    // thread-safe init check at every call), but random global static
    159    // initialization order would make it more complex to coordinate with
    160    // `Streaming::TagForMarkerTypeFunctions()`, and also would add a (small)
    161    // cost for everybody, even the majority of users not using the profiler.
    162    static const Streaming::DeserializerTag tag =
    163        Streaming::TagForMarkerTypeFunctions(Deserialize,
    164                                             MarkerType::MarkerTypeName,
    165                                             MarkerType::MarkerTypeDisplay);
    166    return StreamFunctionType::Serialize(aBuffer, aName, aCategory,
    167                                         std::move(aOptions), tag, aTs...);
    168  }
    169 
    170 private:
    171  // This templated function will recursively deserialize each argument expected
    172  // by `MarkerType::StreamJSONMarkerData()` on the stack, and call it at the
    173  // end. E.g., for `StreamJSONMarkerData(int, char)`:
    174  // - DeserializeArguments<0>(aER, aWriter) reads an int and calls:
    175  // - DeserializeArguments<1>(aER, aWriter, const int&) reads a char and calls:
    176  // - MarkerType::StreamJSONMarkerData(aWriter, const int&, const char&).
    177  // Prototyping on godbolt showed that clang and gcc can flatten these
    178  // recursive calls into one function with successive reads followed by the one
    179  // stream call; tested up to 40 arguments: https://godbolt.org/z/5KeeM4
    180  template <size_t i = 0, typename... Args>
    181  static void DeserializeArguments(ProfileBufferEntryReader& aEntryReader,
    182                                   baseprofiler::SpliceableJSONWriter& aWriter,
    183                                   const Args&... aArgs) {
    184    static_assert(sizeof...(Args) == i,
    185                  "We should have collected `i` arguments so far");
    186    if constexpr (i < scStreamFunctionParameterCount) {
    187      // Deserialize the i-th argument on this stack.
    188      auto argument = aEntryReader.ReadObject<StreamFunctionParameter<i>>();
    189      // Add our local argument to the next recursive call.
    190      DeserializeArguments<i + 1>(aEntryReader, aWriter, aArgs..., argument);
    191    } else {
    192      // We've read all the arguments, finally call the `StreamJSONMarkerData`
    193      // function, which should write the appropriate JSON elements for this
    194      // marker type. Note that the MarkerType-specific "type" element is
    195      // already written.
    196      MarkerType::StreamJSONMarkerData(aWriter, aArgs...);
    197    }
    198  }
    199 
    200 public:
    201  static void Deserialize(ProfileBufferEntryReader& aEntryReader,
    202                          baseprofiler::SpliceableJSONWriter& aWriter) {
    203    aWriter.StringProperty("type", MarkerType::MarkerTypeName());
    204    DeserializeArguments(aEntryReader, aWriter);
    205  }
    206 };
    207 
    208 template <>
    209 struct MarkerTypeSerialization<::mozilla::baseprofiler::markers::NoPayload> {
    210  // Nothing! NoPayload has special handling avoiding payload work.
    211 };
    212 
    213 template <typename MarkerType, typename... Ts>
    214 static ProfileBufferBlockIndex AddMarkerWithOptionalStackToBuffer(
    215    ProfileChunkedBuffer& aBuffer, const ProfilerString8View& aName,
    216    const MarkerCategory& aCategory, MarkerOptions&& aOptions,
    217    const Ts&... aTs) {
    218  if constexpr (std::is_same_v<MarkerType,
    219                               ::mozilla::baseprofiler::markers::NoPayload>) {
    220    static_assert(sizeof...(Ts) == 0,
    221                  "NoPayload does not accept any payload arguments.");
    222    // Special case for NoPayload where there is a stack or inner window id:
    223    // Because these options would be stored in the payload 'data' object, but
    224    // there is no such object for NoPayload, we convert the marker to another
    225    // type (without user fields in the 'data' object), so that the stack and/or
    226    // inner window id are not lost.
    227    // TODO: Remove this when bug 1646714 lands.
    228    if (aOptions.Stack().GetChunkedBuffer() ||
    229        !aOptions.InnerWindowId().IsUnspecified()) {
    230      struct NoPayloadUserData {
    231        static constexpr Span<const char> MarkerTypeName() {
    232          return MakeStringSpan("NoPayloadUserData");
    233        }
    234        static void StreamJSONMarkerData(
    235            baseprofiler::SpliceableJSONWriter& aWriter) {
    236          // No user payload.
    237        }
    238        static mozilla::MarkerSchema MarkerTypeDisplay() {
    239          using MS = mozilla::MarkerSchema;
    240          MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
    241          // No user data to display.
    242          return schema;
    243        }
    244      };
    245      return MarkerTypeSerialization<NoPayloadUserData>::Serialize(
    246          aBuffer, aName, aCategory, std::move(aOptions));
    247    }
    248 
    249    // Note that options are first after the entry kind, because they contain
    250    // the thread id, which is handled first to filter markers by threads.
    251    return aBuffer.PutObjects(
    252        ProfileBufferEntryKind::Marker, std::move(aOptions), aName, aCategory,
    253        base_profiler_markers_detail::Streaming::DeserializerTag(0));
    254  } else {
    255    return MarkerTypeSerialization<MarkerType>::Serialize(
    256        aBuffer, aName, aCategory, std::move(aOptions), aTs...);
    257  }
    258 }
    259 
    260 // Pointer to a function that can capture a backtrace into the provided
    261 // `ProfileChunkedBuffer`, and returns true when successful.
    262 using OptionalBacktraceCaptureFunction = bool (*)(ProfileChunkedBuffer&,
    263                                                  StackCaptureOptions);
    264 
    265 // Use a pre-allocated and cleared chunked buffer in the main thread's
    266 // `AddMarkerToBuffer()`.
    267 // Null if not the main thread, or if profilers are not active.
    268 MFBT_API ProfileChunkedBuffer* GetClearedBufferForMainThreadAddMarker();
    269 // Called by the profiler(s) when starting/stopping. Safe to nest.
    270 MFBT_API void EnsureBufferForMainThreadAddMarker();
    271 MFBT_API void ReleaseBufferForMainThreadAddMarker();
    272 
    273 // Add a marker with the given name, options, and arguments to the given buffer.
    274 // Because this may be called from either Base or Gecko Profiler functions, the
    275 // appropriate backtrace-capturing function must also be provided.
    276 template <typename MarkerType, typename... Ts>
    277 ProfileBufferBlockIndex AddMarkerToBuffer(
    278    ProfileChunkedBuffer& aBuffer, const ProfilerString8View& aName,
    279    const MarkerCategory& aCategory, MarkerOptions&& aOptions,
    280    OptionalBacktraceCaptureFunction aOptionalBacktraceCaptureFunction,
    281    const Ts&... aTs) {
    282  if (aOptions.ThreadId().IsUnspecified()) {
    283    // If yet unspecified, set thread to this thread where the marker is added.
    284    aOptions.Set(MarkerThreadId::CurrentThread());
    285  }
    286 
    287  if (aOptions.IsTimingUnspecified()) {
    288    // If yet unspecified, set timing to this instant of adding the marker.
    289    aOptions.Set(MarkerTiming::InstantNow());
    290  }
    291 
    292  StackCaptureOptions captureOptions = aOptions.Stack().CaptureOptions();
    293  if (captureOptions != StackCaptureOptions::NoStack &&
    294      // Backtrace capture function will be nullptr if the profiler
    295      // NoMarkerStacks feature is set.
    296      aOptionalBacktraceCaptureFunction != nullptr) {
    297    // A capture was requested, let's attempt to do it here&now. This avoids a
    298    // lot of allocations that would be necessary if capturing a backtrace
    299    // separately.
    300    // TODO reduce internal profiler stack levels, see bug 1659872.
    301    auto CaptureStackAndAddMarker = [&](ProfileChunkedBuffer& aChunkedBuffer) {
    302      aOptions.StackRef().UseRequestedBacktrace(
    303          aOptionalBacktraceCaptureFunction(aChunkedBuffer, captureOptions)
    304              ? &aChunkedBuffer
    305              : nullptr);
    306      // This call must be made from here, while chunkedBuffer is in scope.
    307      return AddMarkerWithOptionalStackToBuffer<MarkerType>(
    308          aBuffer, aName, aCategory, std::move(aOptions), aTs...);
    309    };
    310 
    311    if (ProfileChunkedBuffer* buffer = GetClearedBufferForMainThreadAddMarker();
    312        buffer) {
    313      // Use a pre-allocated buffer for the main thread (because it's the most
    314      // used thread, and most sensitive to overhead), so it's only allocated
    315      // once. It could be null if this is not the main thread, or no profilers
    316      // are currently active.
    317      return CaptureStackAndAddMarker(*buffer);
    318    }
    319    // TODO use a local on-stack byte buffer to remove last allocation.
    320    ProfileBufferChunkManagerSingle chunkManager(
    321        ProfileBufferChunkManager::scExpectedMaximumStackSize);
    322    ProfileChunkedBuffer chunkedBuffer(
    323        ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager);
    324    return CaptureStackAndAddMarker(chunkedBuffer);
    325  }
    326 
    327  return AddMarkerWithOptionalStackToBuffer<MarkerType>(
    328      aBuffer, aName, aCategory, std::move(aOptions), aTs...);
    329 }
    330 
    331 // Assuming aEntryReader points right after the entry type (being Marker), this
    332 // reads the remainder of the marker and outputs it.
    333 // - GetWriterForThreadCallback, called first, after the thread id is read:
    334 //     (ThreadId) -> SpliceableJSONWriter* or null
    335 //   If null, nothing will be output, but aEntryReader will still be read fully.
    336 // - StackCallback, only called if GetWriterForThreadCallback didn't return
    337 //   null, and if the marker contains a stack:
    338 //     (ProfileChunkedBuffer&) -> void
    339 // - RustMarkerCallback, only called if GetWriterForThreadCallback didn't return
    340 //   null, and if the marker contains a Rust payload:
    341 //     (DeserializerTag) -> void
    342 template <typename GetWriterForThreadCallback, typename StackCallback,
    343          typename RustMarkerCallback>
    344 void DeserializeAfterKindAndStream(
    345    ProfileBufferEntryReader& aEntryReader,
    346    GetWriterForThreadCallback&& aGetWriterForThreadCallback,
    347    StackCallback&& aStackCallback, RustMarkerCallback&& aRustMarkerCallback) {
    348  // Each entry is made up of the following:
    349  //   ProfileBufferEntry::Kind::Marker, <- already read by caller
    350  //   options,                          <- next location in entries
    351  //   name,
    352  //   payload
    353  const MarkerOptions options = aEntryReader.ReadObject<MarkerOptions>();
    354 
    355  baseprofiler::SpliceableJSONWriter* writer =
    356      std::forward<GetWriterForThreadCallback>(aGetWriterForThreadCallback)(
    357          options.ThreadId().ThreadId());
    358  if (!writer) {
    359    // No writer associated with this thread id, drop it.
    360    aEntryReader.SetRemainingBytes(0);
    361    return;
    362  }
    363 
    364  // Write the information to JSON with the following schema:
    365  // [name, startTime, endTime, phase, category, data]
    366  writer->StartArrayElement();
    367  {
    368    writer->UniqueStringElement(aEntryReader.ReadObject<ProfilerString8View>());
    369 
    370    const double startTime = options.Timing().GetStartTime();
    371    writer->TimeDoubleMsElement(startTime);
    372 
    373    const double endTime = options.Timing().GetEndTime();
    374    writer->TimeDoubleMsElement(endTime);
    375 
    376    writer->IntElement(static_cast<int64_t>(options.Timing().MarkerPhase()));
    377 
    378    MarkerCategory category = aEntryReader.ReadObject<MarkerCategory>();
    379    writer->IntElement(static_cast<int64_t>(category.GetCategory()));
    380 
    381    if (const auto tag =
    382            aEntryReader.ReadObject<mozilla::base_profiler_markers_detail::
    383                                        Streaming::DeserializerTag>();
    384        tag != 0) {
    385      writer->StartObjectElement();
    386      {
    387        // Stream "common props".
    388 
    389        // TODO: Move this to top-level tuple, when frontend supports it.
    390        if (!options.InnerWindowId().IsUnspecified()) {
    391          // Here, we are converting uint64_t to double. Both Browsing Context
    392          // and Inner Window IDs are created using
    393          // `nsContentUtils::GenerateProcessSpecificId`, which is specifically
    394          // designed to only use 53 of the 64 bits to be lossless when passed
    395          // into and out of JS as a double.
    396          writer->DoubleProperty(
    397              "innerWindowID",
    398              static_cast<double>(options.InnerWindowId().Id()));
    399        }
    400 
    401        // TODO: Move this to top-level tuple, when frontend supports it.
    402        if (ProfileChunkedBuffer* chunkedBuffer =
    403                options.Stack().GetChunkedBuffer();
    404            chunkedBuffer) {
    405          writer->StartObjectProperty("stack");
    406          {
    407            std::forward<StackCallback>(aStackCallback)(*chunkedBuffer);
    408          }
    409          writer->EndObject();
    410        }
    411 
    412        auto payloadType = static_cast<mozilla::MarkerPayloadType>(
    413            aEntryReader.ReadObject<
    414                std::underlying_type_t<mozilla::MarkerPayloadType>>());
    415 
    416        // Stream the payload, including the type.
    417        switch (payloadType) {
    418          case mozilla::MarkerPayloadType::Cpp: {
    419            mozilla::base_profiler_markers_detail::Streaming::
    420                MarkerDataDeserializer deserializer =
    421                    mozilla::base_profiler_markers_detail::Streaming::
    422                        DeserializerForTag(tag);
    423            MOZ_RELEASE_ASSERT(deserializer);
    424            deserializer(aEntryReader, *writer);
    425            MOZ_ASSERT(aEntryReader.RemainingBytes() == 0u);
    426            break;
    427          }
    428          case mozilla::MarkerPayloadType::Rust:
    429            std::forward<RustMarkerCallback>(aRustMarkerCallback)(tag);
    430            MOZ_ASSERT(aEntryReader.RemainingBytes() == 0u);
    431            break;
    432          default:
    433            MOZ_ASSERT_UNREACHABLE("Unknown payload type.");
    434            break;
    435        }
    436      }
    437      writer->EndObject();
    438    }
    439  }
    440  writer->EndArray();
    441  MOZ_ASSERT(aEntryReader.RemainingBytes() == 0u);
    442 }
    443 
    444 }  // namespace mozilla::base_profiler_markers_detail
    445 
    446 namespace mozilla {
    447 
    448 // ----------------------------------------------------------------------------
    449 // Serializer, Deserializer: ProfilerStringView<CHAR>
    450 
    451 // The serialization starts with a ULEB128 number that encodes both whether the
    452 // ProfilerStringView is literal (Least Significant Bit = 0) or not (LSB = 1),
    453 // plus the string length (excluding null terminator) in bytes, shifted left by
    454 // 1 bit. Following that number:
    455 // - If literal, the string pointer value.
    456 // - If non-literal, the contents as bytes (excluding null terminator if any).
    457 template <typename CHAR>
    458 struct ProfileBufferEntryWriter::Serializer<ProfilerStringView<CHAR>> {
    459  static Length Bytes(const ProfilerStringView<CHAR>& aString) {
    460    MOZ_RELEASE_ASSERT(
    461        aString.Length() < std::numeric_limits<Length>::max() / 2,
    462        "Double the string length doesn't fit in Length type");
    463    const Length stringLength = static_cast<Length>(aString.Length());
    464    if (aString.IsLiteral()) {
    465      // Literal -> Length shifted left and LSB=0, then pointer.
    466      return ULEB128Size(stringLength << 1 | 0u) +
    467             static_cast<ProfileChunkedBuffer::Length>(sizeof(const CHAR*));
    468    }
    469    // Non-literal -> Length shifted left and LSB=1, then string size in bytes.
    470    return ULEB128Size((stringLength << 1) | 1u) + stringLength * sizeof(CHAR);
    471  }
    472 
    473  static void Write(ProfileBufferEntryWriter& aEW,
    474                    const ProfilerStringView<CHAR>& aString) {
    475    MOZ_RELEASE_ASSERT(
    476        aString.Length() < std::numeric_limits<Length>::max() / 2,
    477        "Double the string length doesn't fit in Length type");
    478    const Span<const CHAR> span = aString;
    479    if (aString.IsLiteral()) {
    480      // Literal -> Length shifted left and LSB=0, then pointer.
    481      aEW.WriteULEB128(span.Length() << 1 | 0u);
    482      aEW.WriteObject(WrapProfileBufferRawPointer(span.Elements()));
    483      return;
    484    }
    485    // Non-literal -> Length shifted left and LSB=1, then string size in bytes.
    486    aEW.WriteULEB128(span.Length() << 1 | 1u);
    487    aEW.WriteBytes(span.Elements(), span.LengthBytes());
    488  }
    489 };
    490 
    491 template <typename CHAR>
    492 struct ProfileBufferEntryReader::Deserializer<ProfilerStringView<CHAR>> {
    493  static void ReadInto(ProfileBufferEntryReader& aER,
    494                       ProfilerStringView<CHAR>& aString) {
    495    aString = Read(aER);
    496  }
    497 
    498  static ProfilerStringView<CHAR> Read(ProfileBufferEntryReader& aER) {
    499    const Length lengthAndIsLiteral = aER.ReadULEB128<Length>();
    500    const Length stringLength = lengthAndIsLiteral >> 1;
    501    if ((lengthAndIsLiteral & 1u) == 0u) {
    502      // LSB==0 -> Literal string, read the string pointer.
    503      return ProfilerStringView<CHAR>(
    504          aER.ReadObject<const CHAR*>(), stringLength,
    505          ProfilerStringView<CHAR>::Ownership::Literal);
    506    }
    507    // LSB==1 -> Not a literal string.
    508    ProfileBufferEntryReader::DoubleSpanOfConstBytes spans =
    509        aER.ReadSpans(stringLength * sizeof(CHAR));
    510    if (MOZ_LIKELY(spans.IsSingleSpan()) &&
    511        reinterpret_cast<uintptr_t>(spans.mFirstOrOnly.Elements()) %
    512                alignof(CHAR) ==
    513            0u) {
    514      // Only a single span, correctly aligned for the CHAR type, we can just
    515      // refer to it directly, assuming that this ProfilerStringView will not
    516      // outlive the chunk.
    517      return ProfilerStringView<CHAR>(
    518          reinterpret_cast<const CHAR*>(spans.mFirstOrOnly.Elements()),
    519          stringLength, ProfilerStringView<CHAR>::Ownership::Reference);
    520    } else {
    521      // Two spans, we need to concatenate them; or one span, but misaligned.
    522      // Allocate a buffer to store the string (plus terminal, for safety), and
    523      // give it to the ProfilerStringView; Note that this is a secret use of
    524      // ProfilerStringView, which is intended to only be used between
    525      // deserialization and JSON streaming.
    526      CHAR* buffer = new CHAR[stringLength + 1];
    527      spans.CopyBytesTo(buffer);
    528      buffer[stringLength] = CHAR(0);
    529      return ProfilerStringView<CHAR>(
    530          buffer, stringLength,
    531          ProfilerStringView<CHAR>::Ownership::OwnedThroughStringView);
    532    }
    533  }
    534 };
    535 
    536 // Serializer, Deserializer: MarkerCategory
    537 
    538 // The serialization contains both category numbers encoded as ULEB128.
    539 template <>
    540 struct ProfileBufferEntryWriter::Serializer<MarkerCategory> {
    541  static Length Bytes(const MarkerCategory& aCategory) {
    542    return ULEB128Size(static_cast<uint32_t>(aCategory.CategoryPair()));
    543  }
    544 
    545  static void Write(ProfileBufferEntryWriter& aEW,
    546                    const MarkerCategory& aCategory) {
    547    aEW.WriteULEB128(static_cast<uint32_t>(aCategory.CategoryPair()));
    548  }
    549 };
    550 
    551 template <>
    552 struct ProfileBufferEntryReader::Deserializer<MarkerCategory> {
    553  static void ReadInto(ProfileBufferEntryReader& aER,
    554                       MarkerCategory& aCategory) {
    555    aCategory = Read(aER);
    556  }
    557 
    558  static MarkerCategory Read(ProfileBufferEntryReader& aER) {
    559    return MarkerCategory(static_cast<baseprofiler::ProfilingCategoryPair>(
    560        aER.ReadULEB128<uint32_t>()));
    561  }
    562 };
    563 
    564 // ----------------------------------------------------------------------------
    565 // Serializer, Deserializer: MarkerTiming
    566 
    567 // The serialization starts with the marker phase, followed by one or two
    568 // timestamps as needed.
    569 template <>
    570 struct ProfileBufferEntryWriter::Serializer<MarkerTiming> {
    571  static Length Bytes(const MarkerTiming& aTiming) {
    572    MOZ_ASSERT(!aTiming.IsUnspecified());
    573    const auto phase = aTiming.MarkerPhase();
    574    switch (phase) {
    575      case MarkerTiming::Phase::Instant:
    576        return SumBytes(phase, aTiming.StartTime());
    577      case MarkerTiming::Phase::Interval:
    578        return SumBytes(phase, aTiming.StartTime(), aTiming.EndTime());
    579      case MarkerTiming::Phase::IntervalStart:
    580        return SumBytes(phase, aTiming.StartTime());
    581      case MarkerTiming::Phase::IntervalEnd:
    582        return SumBytes(phase, aTiming.EndTime());
    583      default:
    584        MOZ_RELEASE_ASSERT(phase == MarkerTiming::Phase::Instant ||
    585                           phase == MarkerTiming::Phase::Interval ||
    586                           phase == MarkerTiming::Phase::IntervalStart ||
    587                           phase == MarkerTiming::Phase::IntervalEnd);
    588        return 0;  // Only to avoid build errors.
    589    }
    590  }
    591 
    592  static void Write(ProfileBufferEntryWriter& aEW,
    593                    const MarkerTiming& aTiming) {
    594    MOZ_ASSERT(!aTiming.IsUnspecified());
    595    const auto phase = aTiming.MarkerPhase();
    596    switch (phase) {
    597      case MarkerTiming::Phase::Instant:
    598        aEW.WriteObjects(phase, aTiming.StartTime());
    599        return;
    600      case MarkerTiming::Phase::Interval:
    601        aEW.WriteObjects(phase, aTiming.StartTime(), aTiming.EndTime());
    602        return;
    603      case MarkerTiming::Phase::IntervalStart:
    604        aEW.WriteObjects(phase, aTiming.StartTime());
    605        return;
    606      case MarkerTiming::Phase::IntervalEnd:
    607        aEW.WriteObjects(phase, aTiming.EndTime());
    608        return;
    609      default:
    610        MOZ_RELEASE_ASSERT(phase == MarkerTiming::Phase::Instant ||
    611                           phase == MarkerTiming::Phase::Interval ||
    612                           phase == MarkerTiming::Phase::IntervalStart ||
    613                           phase == MarkerTiming::Phase::IntervalEnd);
    614        return;
    615    }
    616  }
    617 };
    618 
    619 template <>
    620 struct ProfileBufferEntryReader::Deserializer<MarkerTiming> {
    621  static void ReadInto(ProfileBufferEntryReader& aER, MarkerTiming& aTiming) {
    622    aTiming.mPhase = aER.ReadObject<MarkerTiming::Phase>();
    623    switch (aTiming.mPhase) {
    624      case MarkerTiming::Phase::Instant:
    625        aTiming.mStartTime = aER.ReadObject<TimeStamp>();
    626        aTiming.mEndTime = TimeStamp{};
    627        break;
    628      case MarkerTiming::Phase::Interval:
    629        aTiming.mStartTime = aER.ReadObject<TimeStamp>();
    630        aTiming.mEndTime = aER.ReadObject<TimeStamp>();
    631        break;
    632      case MarkerTiming::Phase::IntervalStart:
    633        aTiming.mStartTime = aER.ReadObject<TimeStamp>();
    634        aTiming.mEndTime = TimeStamp{};
    635        break;
    636      case MarkerTiming::Phase::IntervalEnd:
    637        aTiming.mStartTime = TimeStamp{};
    638        aTiming.mEndTime = aER.ReadObject<TimeStamp>();
    639        break;
    640      default:
    641        MOZ_RELEASE_ASSERT(aTiming.mPhase == MarkerTiming::Phase::Instant ||
    642                           aTiming.mPhase == MarkerTiming::Phase::Interval ||
    643                           aTiming.mPhase ==
    644                               MarkerTiming::Phase::IntervalStart ||
    645                           aTiming.mPhase == MarkerTiming::Phase::IntervalEnd);
    646        break;
    647    }
    648  }
    649 
    650  static MarkerTiming Read(ProfileBufferEntryReader& aER) {
    651    TimeStamp start;
    652    TimeStamp end;
    653    auto phase = aER.ReadObject<MarkerTiming::Phase>();
    654    switch (phase) {
    655      case MarkerTiming::Phase::Instant:
    656        start = aER.ReadObject<TimeStamp>();
    657        break;
    658      case MarkerTiming::Phase::Interval:
    659        start = aER.ReadObject<TimeStamp>();
    660        end = aER.ReadObject<TimeStamp>();
    661        break;
    662      case MarkerTiming::Phase::IntervalStart:
    663        start = aER.ReadObject<TimeStamp>();
    664        break;
    665      case MarkerTiming::Phase::IntervalEnd:
    666        end = aER.ReadObject<TimeStamp>();
    667        break;
    668      default:
    669        MOZ_RELEASE_ASSERT(phase == MarkerTiming::Phase::Instant ||
    670                           phase == MarkerTiming::Phase::Interval ||
    671                           phase == MarkerTiming::Phase::IntervalStart ||
    672                           phase == MarkerTiming::Phase::IntervalEnd);
    673        break;
    674    }
    675    return MarkerTiming(start, end, phase);
    676  }
    677 };
    678 
    679 // ----------------------------------------------------------------------------
    680 // Serializer, Deserializer: MarkerStack
    681 
    682 // The serialization only contains the `ProfileChunkedBuffer` from the
    683 // backtrace; if there is no backtrace or if it's empty, this will implicitly
    684 // store a nullptr (see
    685 // `ProfileBufferEntryWriter::Serializer<ProfilerChunkedBuffer*>`).
    686 template <>
    687 struct ProfileBufferEntryWriter::Serializer<MarkerStack> {
    688  static Length Bytes(const MarkerStack& aStack) {
    689    return SumBytes(aStack.GetChunkedBuffer());
    690  }
    691 
    692  static void Write(ProfileBufferEntryWriter& aEW, const MarkerStack& aStack) {
    693    aEW.WriteObject(aStack.GetChunkedBuffer());
    694  }
    695 };
    696 
    697 template <>
    698 struct ProfileBufferEntryReader::Deserializer<MarkerStack> {
    699  static void ReadInto(ProfileBufferEntryReader& aER, MarkerStack& aStack) {
    700    aStack = Read(aER);
    701  }
    702 
    703  static MarkerStack Read(ProfileBufferEntryReader& aER) {
    704    return MarkerStack(aER.ReadObject<UniquePtr<ProfileChunkedBuffer>>());
    705  }
    706 };
    707 
    708 // ----------------------------------------------------------------------------
    709 // Serializer, Deserializer: MarkerOptions
    710 
    711 // The serialization contains all members (either trivially-copyable, or they
    712 // provide their specialization above).
    713 template <>
    714 struct ProfileBufferEntryWriter::Serializer<MarkerOptions> {
    715  static Length Bytes(const MarkerOptions& aOptions) {
    716    return SumBytes(aOptions.ThreadId(), aOptions.Timing(), aOptions.Stack(),
    717                    aOptions.InnerWindowId());
    718  }
    719 
    720  static void Write(ProfileBufferEntryWriter& aEW,
    721                    const MarkerOptions& aOptions) {
    722    aEW.WriteObjects(aOptions.ThreadId(), aOptions.Timing(), aOptions.Stack(),
    723                     aOptions.InnerWindowId());
    724  }
    725 };
    726 
    727 template <>
    728 struct ProfileBufferEntryReader::Deserializer<MarkerOptions> {
    729  static void ReadInto(ProfileBufferEntryReader& aER, MarkerOptions& aOptions) {
    730    aER.ReadIntoObjects(aOptions.mThreadId, aOptions.mTiming, aOptions.mStack,
    731                        aOptions.mInnerWindowId);
    732  }
    733 
    734  static MarkerOptions Read(ProfileBufferEntryReader& aER) {
    735    MarkerOptions options;
    736    ReadInto(aER, options);
    737    return options;
    738  }
    739 };
    740 
    741 }  // namespace mozilla
    742 
    743 #endif  // BaseProfilerMarkersDetail_h