Backed out changeset 0a133d5fd155 (bug 1864534) for causing screenshot related failur...
[gecko.git] / mozglue / baseprofiler / public / BaseProfilerMarkersDetail.h
blob110249910027ba1a0157d2c4fab2bc245ed9c952
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/. */
7 #ifndef BaseProfilerMarkersDetail_h
8 #define BaseProfilerMarkersDetail_h
10 #ifndef BaseProfilerMarkers_h
11 # error "This header should only be #included by BaseProfilerMarkers.h"
12 #endif
14 #include "mozilla/BaseProfilerMarkersPrerequisites.h"
16 // ~~ HERE BE DRAGONS ~~
18 // Everything below is internal implementation detail, you shouldn't need to
19 // look at it unless working on the profiler code.
21 #include "mozilla/BaseProfileJSONWriter.h"
22 #include "mozilla/ProfileBufferEntryKinds.h"
24 #include <limits>
25 #include <tuple>
26 #include <type_traits>
28 namespace mozilla::baseprofiler {
29 // Implemented in platform.cpp
30 MFBT_API ProfileChunkedBuffer& profiler_get_core_buffer();
31 } // namespace mozilla::baseprofiler
33 namespace mozilla::base_profiler_markers_detail {
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&);
41 // A `MarkerTypeNameFunction` is a free function that returns the name of the
42 // marker type.
43 using MarkerTypeNameFunction = Span<const char> (*)();
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 (*)();
50 struct MarkerTypeFunctions {
51 MarkerDataDeserializer mMarkerDataDeserializer = nullptr;
52 MarkerTypeNameFunction mMarkerTypeNameFunction = nullptr;
53 MarkerSchemaFunction mMarkerSchemaFunction = nullptr;
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;
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);
71 // Get the `MarkerDataDeserializer` for a given `DeserializerTag`.
72 MFBT_API static MarkerDataDeserializer DeserializerForTag(
73 DeserializerTag aTag);
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();
82 LockedMarkerTypeFunctionsList(const LockedMarkerTypeFunctionsList&) =
83 delete;
84 LockedMarkerTypeFunctionsList& operator=(
85 const LockedMarkerTypeFunctionsList&) = delete;
87 auto begin() const { return mMarkerTypeFunctionsSpan.begin(); }
88 auto end() const { return mMarkerTypeFunctionsSpan.end(); }
90 private:
91 Span<const MarkerTypeFunctions> mMarkerTypeFunctionsSpan;
95 // This helper will examine a marker type's `StreamJSONMarkerData` function, see
96 // specialization below.
97 template <typename T>
98 struct StreamFunctionTypeHelper;
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>>...>;
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...);
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>;
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...);
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...);
200 public:
201 static void Deserialize(ProfileBufferEntryReader& aEntryReader,
202 baseprofiler::SpliceableJSONWriter& aWriter) {
203 aWriter.StringProperty("type", MarkerType::MarkerTypeName());
204 DeserializeArguments(aEntryReader, aWriter);
208 template <>
209 struct MarkerTypeSerialization<::mozilla::baseprofiler::markers::NoPayload> {
210 // Nothing! NoPayload has special handling avoiding payload work.
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");
234 static void StreamJSONMarkerData(
235 baseprofiler::SpliceableJSONWriter& aWriter) {
236 // No user payload.
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;
245 return MarkerTypeSerialization<NoPayloadUserData>::Serialize(
246 aBuffer, aName, aCategory, std::move(aOptions));
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...);
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);
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();
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());
287 if (aOptions.IsTimingUnspecified()) {
288 // If yet unspecified, set timing to this instant of adding the marker.
289 aOptions.Set(MarkerTiming::InstantNow());
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...);
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);
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);
327 return AddMarkerWithOptionalStackToBuffer<MarkerType>(
328 aBuffer, aName, aCategory, std::move(aOptions), aTs...);
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>();
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;
364 // Write the information to JSON with the following schema:
365 // [name, startTime, endTime, phase, category, data]
366 writer->StartArrayElement();
368 writer->UniqueStringElement(aEntryReader.ReadObject<ProfilerString8View>());
370 const double startTime = options.Timing().GetStartTime();
371 writer->TimeDoubleMsElement(startTime);
373 const double endTime = options.Timing().GetEndTime();
374 writer->TimeDoubleMsElement(endTime);
376 writer->IntElement(static_cast<int64_t>(options.Timing().MarkerPhase()));
378 MarkerCategory category = aEntryReader.ReadObject<MarkerCategory>();
379 writer->IntElement(static_cast<int64_t>(category.GetCategory()));
381 if (const auto tag =
382 aEntryReader.ReadObject<mozilla::base_profiler_markers_detail::
383 Streaming::DeserializerTag>();
384 tag != 0) {
385 writer->StartObjectElement();
387 // Stream "common props".
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()));
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 { std::forward<StackCallback>(aStackCallback)(*chunkedBuffer); }
407 writer->EndObject();
410 auto payloadType = static_cast<mozilla::MarkerPayloadType>(
411 aEntryReader.ReadObject<
412 std::underlying_type_t<mozilla::MarkerPayloadType>>());
414 // Stream the payload, including the type.
415 switch (payloadType) {
416 case mozilla::MarkerPayloadType::Cpp: {
417 mozilla::base_profiler_markers_detail::Streaming::
418 MarkerDataDeserializer deserializer =
419 mozilla::base_profiler_markers_detail::Streaming::
420 DeserializerForTag(tag);
421 MOZ_RELEASE_ASSERT(deserializer);
422 deserializer(aEntryReader, *writer);
423 MOZ_ASSERT(aEntryReader.RemainingBytes() == 0u);
424 break;
426 case mozilla::MarkerPayloadType::Rust:
427 std::forward<RustMarkerCallback>(aRustMarkerCallback)(tag);
428 MOZ_ASSERT(aEntryReader.RemainingBytes() == 0u);
429 break;
430 default:
431 MOZ_ASSERT_UNREACHABLE("Unknown payload type.");
432 break;
435 writer->EndObject();
438 writer->EndArray();
439 MOZ_ASSERT(aEntryReader.RemainingBytes() == 0u);
442 } // namespace mozilla::base_profiler_markers_detail
444 namespace mozilla {
446 // ----------------------------------------------------------------------------
447 // Serializer, Deserializer: ProfilerStringView<CHAR>
449 // The serialization starts with a ULEB128 number that encodes both whether the
450 // ProfilerStringView is literal (Least Significant Bit = 0) or not (LSB = 1),
451 // plus the string length (excluding null terminator) in bytes, shifted left by
452 // 1 bit. Following that number:
453 // - If literal, the string pointer value.
454 // - If non-literal, the contents as bytes (excluding null terminator if any).
455 template <typename CHAR>
456 struct ProfileBufferEntryWriter::Serializer<ProfilerStringView<CHAR>> {
457 static Length Bytes(const ProfilerStringView<CHAR>& aString) {
458 MOZ_RELEASE_ASSERT(
459 aString.Length() < std::numeric_limits<Length>::max() / 2,
460 "Double the string length doesn't fit in Length type");
461 const Length stringLength = static_cast<Length>(aString.Length());
462 if (aString.IsLiteral()) {
463 // Literal -> Length shifted left and LSB=0, then pointer.
464 return ULEB128Size(stringLength << 1 | 0u) +
465 static_cast<ProfileChunkedBuffer::Length>(sizeof(const CHAR*));
467 // Non-literal -> Length shifted left and LSB=1, then string size in bytes.
468 return ULEB128Size((stringLength << 1) | 1u) + stringLength * sizeof(CHAR);
471 static void Write(ProfileBufferEntryWriter& aEW,
472 const ProfilerStringView<CHAR>& aString) {
473 MOZ_RELEASE_ASSERT(
474 aString.Length() < std::numeric_limits<Length>::max() / 2,
475 "Double the string length doesn't fit in Length type");
476 const Span<const CHAR> span = aString;
477 if (aString.IsLiteral()) {
478 // Literal -> Length shifted left and LSB=0, then pointer.
479 aEW.WriteULEB128(span.Length() << 1 | 0u);
480 aEW.WriteObject(WrapProfileBufferRawPointer(span.Elements()));
481 return;
483 // Non-literal -> Length shifted left and LSB=1, then string size in bytes.
484 aEW.WriteULEB128(span.Length() << 1 | 1u);
485 aEW.WriteBytes(span.Elements(), span.LengthBytes());
489 template <typename CHAR>
490 struct ProfileBufferEntryReader::Deserializer<ProfilerStringView<CHAR>> {
491 static void ReadInto(ProfileBufferEntryReader& aER,
492 ProfilerStringView<CHAR>& aString) {
493 aString = Read(aER);
496 static ProfilerStringView<CHAR> Read(ProfileBufferEntryReader& aER) {
497 const Length lengthAndIsLiteral = aER.ReadULEB128<Length>();
498 const Length stringLength = lengthAndIsLiteral >> 1;
499 if ((lengthAndIsLiteral & 1u) == 0u) {
500 // LSB==0 -> Literal string, read the string pointer.
501 return ProfilerStringView<CHAR>(
502 aER.ReadObject<const CHAR*>(), stringLength,
503 ProfilerStringView<CHAR>::Ownership::Literal);
505 // LSB==1 -> Not a literal string.
506 ProfileBufferEntryReader::DoubleSpanOfConstBytes spans =
507 aER.ReadSpans(stringLength * sizeof(CHAR));
508 if (MOZ_LIKELY(spans.IsSingleSpan()) &&
509 reinterpret_cast<uintptr_t>(spans.mFirstOrOnly.Elements()) %
510 alignof(CHAR) ==
511 0u) {
512 // Only a single span, correctly aligned for the CHAR type, we can just
513 // refer to it directly, assuming that this ProfilerStringView will not
514 // outlive the chunk.
515 return ProfilerStringView<CHAR>(
516 reinterpret_cast<const CHAR*>(spans.mFirstOrOnly.Elements()),
517 stringLength, ProfilerStringView<CHAR>::Ownership::Reference);
518 } else {
519 // Two spans, we need to concatenate them; or one span, but misaligned.
520 // Allocate a buffer to store the string (plus terminal, for safety), and
521 // give it to the ProfilerStringView; Note that this is a secret use of
522 // ProfilerStringView, which is intended to only be used between
523 // deserialization and JSON streaming.
524 CHAR* buffer = new CHAR[stringLength + 1];
525 spans.CopyBytesTo(buffer);
526 buffer[stringLength] = CHAR(0);
527 return ProfilerStringView<CHAR>(
528 buffer, stringLength,
529 ProfilerStringView<CHAR>::Ownership::OwnedThroughStringView);
534 // Serializer, Deserializer: MarkerCategory
536 // The serialization contains both category numbers encoded as ULEB128.
537 template <>
538 struct ProfileBufferEntryWriter::Serializer<MarkerCategory> {
539 static Length Bytes(const MarkerCategory& aCategory) {
540 return ULEB128Size(static_cast<uint32_t>(aCategory.CategoryPair()));
543 static void Write(ProfileBufferEntryWriter& aEW,
544 const MarkerCategory& aCategory) {
545 aEW.WriteULEB128(static_cast<uint32_t>(aCategory.CategoryPair()));
549 template <>
550 struct ProfileBufferEntryReader::Deserializer<MarkerCategory> {
551 static void ReadInto(ProfileBufferEntryReader& aER,
552 MarkerCategory& aCategory) {
553 aCategory = Read(aER);
556 static MarkerCategory Read(ProfileBufferEntryReader& aER) {
557 return MarkerCategory(static_cast<baseprofiler::ProfilingCategoryPair>(
558 aER.ReadULEB128<uint32_t>()));
562 // ----------------------------------------------------------------------------
563 // Serializer, Deserializer: MarkerTiming
565 // The serialization starts with the marker phase, followed by one or two
566 // timestamps as needed.
567 template <>
568 struct ProfileBufferEntryWriter::Serializer<MarkerTiming> {
569 static Length Bytes(const MarkerTiming& aTiming) {
570 MOZ_ASSERT(!aTiming.IsUnspecified());
571 const auto phase = aTiming.MarkerPhase();
572 switch (phase) {
573 case MarkerTiming::Phase::Instant:
574 return SumBytes(phase, aTiming.StartTime());
575 case MarkerTiming::Phase::Interval:
576 return SumBytes(phase, aTiming.StartTime(), aTiming.EndTime());
577 case MarkerTiming::Phase::IntervalStart:
578 return SumBytes(phase, aTiming.StartTime());
579 case MarkerTiming::Phase::IntervalEnd:
580 return SumBytes(phase, aTiming.EndTime());
581 default:
582 MOZ_RELEASE_ASSERT(phase == MarkerTiming::Phase::Instant ||
583 phase == MarkerTiming::Phase::Interval ||
584 phase == MarkerTiming::Phase::IntervalStart ||
585 phase == MarkerTiming::Phase::IntervalEnd);
586 return 0; // Only to avoid build errors.
590 static void Write(ProfileBufferEntryWriter& aEW,
591 const MarkerTiming& aTiming) {
592 MOZ_ASSERT(!aTiming.IsUnspecified());
593 const auto phase = aTiming.MarkerPhase();
594 switch (phase) {
595 case MarkerTiming::Phase::Instant:
596 aEW.WriteObjects(phase, aTiming.StartTime());
597 return;
598 case MarkerTiming::Phase::Interval:
599 aEW.WriteObjects(phase, aTiming.StartTime(), aTiming.EndTime());
600 return;
601 case MarkerTiming::Phase::IntervalStart:
602 aEW.WriteObjects(phase, aTiming.StartTime());
603 return;
604 case MarkerTiming::Phase::IntervalEnd:
605 aEW.WriteObjects(phase, aTiming.EndTime());
606 return;
607 default:
608 MOZ_RELEASE_ASSERT(phase == MarkerTiming::Phase::Instant ||
609 phase == MarkerTiming::Phase::Interval ||
610 phase == MarkerTiming::Phase::IntervalStart ||
611 phase == MarkerTiming::Phase::IntervalEnd);
612 return;
617 template <>
618 struct ProfileBufferEntryReader::Deserializer<MarkerTiming> {
619 static void ReadInto(ProfileBufferEntryReader& aER, MarkerTiming& aTiming) {
620 aTiming.mPhase = aER.ReadObject<MarkerTiming::Phase>();
621 switch (aTiming.mPhase) {
622 case MarkerTiming::Phase::Instant:
623 aTiming.mStartTime = aER.ReadObject<TimeStamp>();
624 aTiming.mEndTime = TimeStamp{};
625 break;
626 case MarkerTiming::Phase::Interval:
627 aTiming.mStartTime = aER.ReadObject<TimeStamp>();
628 aTiming.mEndTime = aER.ReadObject<TimeStamp>();
629 break;
630 case MarkerTiming::Phase::IntervalStart:
631 aTiming.mStartTime = aER.ReadObject<TimeStamp>();
632 aTiming.mEndTime = TimeStamp{};
633 break;
634 case MarkerTiming::Phase::IntervalEnd:
635 aTiming.mStartTime = TimeStamp{};
636 aTiming.mEndTime = aER.ReadObject<TimeStamp>();
637 break;
638 default:
639 MOZ_RELEASE_ASSERT(aTiming.mPhase == MarkerTiming::Phase::Instant ||
640 aTiming.mPhase == MarkerTiming::Phase::Interval ||
641 aTiming.mPhase ==
642 MarkerTiming::Phase::IntervalStart ||
643 aTiming.mPhase == MarkerTiming::Phase::IntervalEnd);
644 break;
648 static MarkerTiming Read(ProfileBufferEntryReader& aER) {
649 TimeStamp start;
650 TimeStamp end;
651 auto phase = aER.ReadObject<MarkerTiming::Phase>();
652 switch (phase) {
653 case MarkerTiming::Phase::Instant:
654 start = aER.ReadObject<TimeStamp>();
655 break;
656 case MarkerTiming::Phase::Interval:
657 start = aER.ReadObject<TimeStamp>();
658 end = aER.ReadObject<TimeStamp>();
659 break;
660 case MarkerTiming::Phase::IntervalStart:
661 start = aER.ReadObject<TimeStamp>();
662 break;
663 case MarkerTiming::Phase::IntervalEnd:
664 end = aER.ReadObject<TimeStamp>();
665 break;
666 default:
667 MOZ_RELEASE_ASSERT(phase == MarkerTiming::Phase::Instant ||
668 phase == MarkerTiming::Phase::Interval ||
669 phase == MarkerTiming::Phase::IntervalStart ||
670 phase == MarkerTiming::Phase::IntervalEnd);
671 break;
673 return MarkerTiming(start, end, phase);
677 // ----------------------------------------------------------------------------
678 // Serializer, Deserializer: MarkerStack
680 // The serialization only contains the `ProfileChunkedBuffer` from the
681 // backtrace; if there is no backtrace or if it's empty, this will implicitly
682 // store a nullptr (see
683 // `ProfileBufferEntryWriter::Serializer<ProfilerChunkedBuffer*>`).
684 template <>
685 struct ProfileBufferEntryWriter::Serializer<MarkerStack> {
686 static Length Bytes(const MarkerStack& aStack) {
687 return SumBytes(aStack.GetChunkedBuffer());
690 static void Write(ProfileBufferEntryWriter& aEW, const MarkerStack& aStack) {
691 aEW.WriteObject(aStack.GetChunkedBuffer());
695 template <>
696 struct ProfileBufferEntryReader::Deserializer<MarkerStack> {
697 static void ReadInto(ProfileBufferEntryReader& aER, MarkerStack& aStack) {
698 aStack = Read(aER);
701 static MarkerStack Read(ProfileBufferEntryReader& aER) {
702 return MarkerStack(aER.ReadObject<UniquePtr<ProfileChunkedBuffer>>());
706 // ----------------------------------------------------------------------------
707 // Serializer, Deserializer: MarkerOptions
709 // The serialization contains all members (either trivially-copyable, or they
710 // provide their specialization above).
711 template <>
712 struct ProfileBufferEntryWriter::Serializer<MarkerOptions> {
713 static Length Bytes(const MarkerOptions& aOptions) {
714 return SumBytes(aOptions.ThreadId(), aOptions.Timing(), aOptions.Stack(),
715 aOptions.InnerWindowId());
718 static void Write(ProfileBufferEntryWriter& aEW,
719 const MarkerOptions& aOptions) {
720 aEW.WriteObjects(aOptions.ThreadId(), aOptions.Timing(), aOptions.Stack(),
721 aOptions.InnerWindowId());
725 template <>
726 struct ProfileBufferEntryReader::Deserializer<MarkerOptions> {
727 static void ReadInto(ProfileBufferEntryReader& aER, MarkerOptions& aOptions) {
728 aER.ReadIntoObjects(aOptions.mThreadId, aOptions.mTiming, aOptions.mStack,
729 aOptions.mInnerWindowId);
732 static MarkerOptions Read(ProfileBufferEntryReader& aER) {
733 MarkerOptions options;
734 ReadInto(aER, options);
735 return options;
739 } // namespace mozilla
741 #endif // BaseProfilerMarkersDetail_h