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"
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"
27 namespace mozilla::baseprofiler
{
28 // Implemented in platform.cpp
29 MFBT_API ProfileChunkedBuffer
& profiler_get_core_buffer();
30 } // namespace mozilla::baseprofiler
32 namespace mozilla::base_profiler_markers_detail
{
34 // Get the core buffer from the profiler, and cache it in a
35 // non-templated-function static reference.
36 inline ProfileChunkedBuffer
& CachedBaseCoreBuffer() {
37 static ProfileChunkedBuffer
& coreBuffer
=
38 baseprofiler::profiler_get_core_buffer();
43 // A `MarkerDataDeserializer` is a free function that can read a serialized
44 // payload from an `EntryReader` and streams it as JSON object properties.
45 using MarkerDataDeserializer
= void (*)(ProfileBufferEntryReader
&,
46 baseprofiler::SpliceableJSONWriter
&);
48 // A `MarkerTypeNameFunction` is a free function that returns the name of the
50 using MarkerTypeNameFunction
= Span
<const char> (*)();
52 // A `MarkerSchemaFunction` is a free function that returns a
53 // `MarkerSchema`, which contains all the information needed to stream
54 // the display schema associated with a marker type.
55 using MarkerSchemaFunction
= MarkerSchema (*)();
57 struct MarkerTypeFunctions
{
58 MarkerDataDeserializer mMarkerDataDeserializer
= nullptr;
59 MarkerTypeNameFunction mMarkerTypeNameFunction
= nullptr;
60 MarkerSchemaFunction mMarkerSchemaFunction
= nullptr;
63 // A `DeserializerTag` will be added before the payload, to help select the
64 // correct deserializer when reading back the payload.
65 using DeserializerTag
= uint8_t;
67 // Store a deserializer (and other marker-type-specific functions) and get its
69 // This is intended to be only used once per deserializer when a new marker
70 // type is used for the first time, so it should be called to initialize a
71 // `static const` tag that will be re-used by all markers of the corresponding
72 // payload type -- see use below.
73 MFBT_API
static DeserializerTag
TagForMarkerTypeFunctions(
74 MarkerDataDeserializer aDeserializer
,
75 MarkerTypeNameFunction aMarkerTypeNameFunction
,
76 MarkerSchemaFunction aMarkerSchemaFunction
);
78 // Get the `MarkerDataDeserializer` for a given `DeserializerTag`.
79 MFBT_API
static MarkerDataDeserializer
DeserializerForTag(
80 DeserializerTag aTag
);
82 // Retrieve all MarkerTypeFunctions's.
83 MFBT_API
static Span
<const MarkerTypeFunctions
> MarkerTypeFunctionsArray();
86 // This helper will examine a marker type's `StreamJSONMarkerData` function, see
87 // specialization below.
89 struct StreamFunctionTypeHelper
;
91 // Helper specialization that takes the expected
92 // `StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter&, ...)` function and
93 // provide information about the `...` parameters.
94 template <typename R
, typename
... As
>
95 struct StreamFunctionTypeHelper
<R(baseprofiler::SpliceableJSONWriter
&, As
...)> {
96 constexpr static size_t scArity
= sizeof...(As
);
98 std::tuple
<std::remove_cv_t
<std::remove_reference_t
<As
>>...>;
100 // Serialization function that takes the exact same parameter types
101 // (const-ref'd) as `StreamJSONMarkerData`. This has to be inside the helper
102 // because only here can we access the raw parameter pack `As...`.
103 // And because we're using the same argument types through
104 // references-to-const, permitted implicit conversions can happen.
105 static ProfileBufferBlockIndex
Serialize(
106 ProfileChunkedBuffer
& aBuffer
, const ProfilerString8View
& aName
,
107 const MarkerCategory
& aCategory
, MarkerOptions
&& aOptions
,
108 Streaming::DeserializerTag aDeserializerTag
, const As
&... aAs
) {
109 // Note that options are first after the entry kind, because they contain
110 // the thread id, which is handled first to filter markers by threads.
111 return aBuffer
.PutObjects(ProfileBufferEntryKind::Marker
, aOptions
, aName
,
112 aCategory
, aDeserializerTag
, aAs
...);
116 // Helper for a marker type.
117 // A marker type is defined in a `struct` with some expected static member
118 // functions. See example in BaseProfilerMarkers.h.
119 template <typename MarkerType
>
120 struct MarkerTypeSerialization
{
121 // Definitions to access the expected
122 // `StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter&, ...)` function
123 // and its parameters.
124 using StreamFunctionType
=
125 StreamFunctionTypeHelper
<decltype(MarkerType::StreamJSONMarkerData
)>;
126 constexpr static size_t scStreamFunctionParameterCount
=
127 StreamFunctionType::scArity
;
128 using StreamFunctionUserParametersTuple
=
129 typename
StreamFunctionType::TupleType
;
131 using StreamFunctionParameter
=
132 std::tuple_element_t
<i
, StreamFunctionUserParametersTuple
>;
134 template <typename
... Ts
>
135 static ProfileBufferBlockIndex
Serialize(ProfileChunkedBuffer
& aBuffer
,
136 const ProfilerString8View
& aName
,
137 const MarkerCategory
& aCategory
,
138 MarkerOptions
&& aOptions
,
140 static_assert(!std::is_same_v
<MarkerType
,
141 ::mozilla::baseprofiler::markers::NoPayload
>,
142 "NoPayload should have been handled in the caller.");
143 // Register marker type functions, and get the tag for this deserializer.
144 // Note that the tag is stored in a function-static object, and this
145 // function is static in a templated struct, so there should only be one tag
147 // Making the tag class-static may have been more efficient (to avoid a
148 // thread-safe init check at every call), but random global static
149 // initialization order would make it more complex to coordinate with
150 // `Streaming::TagForMarkerTypeFunctions()`, and also would add a (small)
151 // cost for everybody, even the majority of users not using the profiler.
152 static const Streaming::DeserializerTag tag
=
153 Streaming::TagForMarkerTypeFunctions(Deserialize
,
154 MarkerType::MarkerTypeName
,
155 MarkerType::MarkerTypeDisplay
);
156 return StreamFunctionType::Serialize(aBuffer
, aName
, aCategory
,
157 std::move(aOptions
), tag
, aTs
...);
161 // This templated function will recursively deserialize each argument expected
162 // by `MarkerType::StreamJSONMarkerData()` on the stack, and call it at the
163 // end. E.g., for `StreamJSONMarkerData(int, char)`:
164 // - DeserializeArguments<0>(aER, aWriter) reads an int and calls:
165 // - DeserializeArguments<1>(aER, aWriter, const int&) reads a char and calls:
166 // - MarkerType::StreamJSONMarkerData(aWriter, const int&, const char&).
167 // Prototyping on godbolt showed that clang and gcc can flatten these
168 // recursive calls into one function with successive reads followed by the one
169 // stream call; tested up to 40 arguments: https://godbolt.org/z/5KeeM4
170 template <size_t i
= 0, typename
... Args
>
171 static void DeserializeArguments(ProfileBufferEntryReader
& aEntryReader
,
172 baseprofiler::SpliceableJSONWriter
& aWriter
,
173 const Args
&... aArgs
) {
174 static_assert(sizeof...(Args
) == i
,
175 "We should have collected `i` arguments so far");
176 if constexpr (i
< scStreamFunctionParameterCount
) {
177 // Deserialize the i-th argument on this stack.
178 auto argument
= aEntryReader
.ReadObject
<StreamFunctionParameter
<i
>>();
179 // Add our local argument to the next recursive call.
180 DeserializeArguments
<i
+ 1>(aEntryReader
, aWriter
, aArgs
..., argument
);
182 // We've read all the arguments, finally call the `StreamJSONMarkerData`
183 // function, which should write the appropriate JSON elements for this
184 // marker type. Note that the MarkerType-specific "type" element is
186 MarkerType::StreamJSONMarkerData(aWriter
, aArgs
...);
191 static void Deserialize(ProfileBufferEntryReader
& aEntryReader
,
192 baseprofiler::SpliceableJSONWriter
& aWriter
) {
193 aWriter
.StringProperty("type", MarkerType::MarkerTypeName());
194 DeserializeArguments(aEntryReader
, aWriter
);
199 struct MarkerTypeSerialization
<::mozilla::baseprofiler::markers::NoPayload
> {
200 // Nothing! NoPayload has special handling avoiding payload work.
203 template <typename MarkerType
, typename
... Ts
>
204 static ProfileBufferBlockIndex
AddMarkerWithOptionalStackToBuffer(
205 ProfileChunkedBuffer
& aBuffer
, const ProfilerString8View
& aName
,
206 const MarkerCategory
& aCategory
, MarkerOptions
&& aOptions
,
208 if constexpr (std::is_same_v
<MarkerType
,
209 ::mozilla::baseprofiler::markers::NoPayload
>) {
210 static_assert(sizeof...(Ts
) == 0,
211 "NoPayload does not accept any payload arguments.");
212 // Special case for NoPayload where there is a stack or inner window id:
213 // Because these options would be stored in the payload 'data' object, but
214 // there is no such object for NoPayload, we convert the marker to another
215 // type (without user fields in the 'data' object), so that the stack and/or
216 // inner window id are not lost.
217 // TODO: Remove this when bug 1646714 lands.
218 if (aOptions
.Stack().GetChunkedBuffer() ||
219 !aOptions
.InnerWindowId().IsUnspecified()) {
220 struct NoPayloadUserData
{
221 static constexpr Span
<const char> MarkerTypeName() {
222 return MakeStringSpan("NoPayloadUserData");
224 static void StreamJSONMarkerData(
225 baseprofiler::SpliceableJSONWriter
& aWriter
) {
228 static mozilla::MarkerSchema
MarkerTypeDisplay() {
229 using MS
= mozilla::MarkerSchema
;
230 MS schema
{MS::Location::markerChart
, MS::Location::markerTable
};
231 // No user data to display.
235 return MarkerTypeSerialization
<NoPayloadUserData
>::Serialize(
236 aBuffer
, aName
, aCategory
, std::move(aOptions
));
239 // Note that options are first after the entry kind, because they contain
240 // the thread id, which is handled first to filter markers by threads.
241 return aBuffer
.PutObjects(
242 ProfileBufferEntryKind::Marker
, std::move(aOptions
), aName
, aCategory
,
243 base_profiler_markers_detail::Streaming::DeserializerTag(0));
245 return MarkerTypeSerialization
<MarkerType
>::Serialize(
246 aBuffer
, aName
, aCategory
, std::move(aOptions
), aTs
...);
250 // Pointer to a function that can capture a backtrace into the provided
251 // `ProfileChunkedBuffer`, and returns true when successful.
252 using BacktraceCaptureFunction
= bool (*)(ProfileChunkedBuffer
&,
253 StackCaptureOptions
);
255 // Add a marker with the given name, options, and arguments to the given buffer.
256 // Because this may be called from either Base or Gecko Profiler functions, the
257 // appropriate backtrace-capturing function must also be provided.
258 template <typename MarkerType
, typename
... Ts
>
259 ProfileBufferBlockIndex
AddMarkerToBuffer(
260 ProfileChunkedBuffer
& aBuffer
, const ProfilerString8View
& aName
,
261 const MarkerCategory
& aCategory
, MarkerOptions
&& aOptions
,
262 BacktraceCaptureFunction aBacktraceCaptureFunction
, const Ts
&... aTs
) {
263 if (aOptions
.ThreadId().IsUnspecified()) {
264 // If yet unspecified, set thread to this thread where the marker is added.
265 aOptions
.Set(MarkerThreadId::CurrentThread());
268 if (aOptions
.IsTimingUnspecified()) {
269 // If yet unspecified, set timing to this instant of adding the marker.
270 aOptions
.Set(MarkerTiming::InstantNow());
273 StackCaptureOptions captureOptions
= aOptions
.Stack().CaptureOptions();
274 if (captureOptions
!= StackCaptureOptions::NoStack
) {
275 // A capture was requested, let's attempt to do it here&now. This avoids a
276 // lot of allocations that would be necessary if capturing a backtrace
278 // TODO use a local on-stack byte buffer to remove last allocation.
279 // TODO reduce internal profiler stack levels, see bug 1659872.
280 ProfileBufferChunkManagerSingle
chunkManager(
281 ProfileBufferChunkManager::scExpectedMaximumStackSize
);
282 ProfileChunkedBuffer
chunkedBuffer(
283 ProfileChunkedBuffer::ThreadSafety::WithoutMutex
, chunkManager
);
284 aOptions
.StackRef().UseRequestedBacktrace(
285 aBacktraceCaptureFunction(chunkedBuffer
, captureOptions
)
288 // This call must be made from here, while chunkedBuffer is in scope.
289 return AddMarkerWithOptionalStackToBuffer
<MarkerType
>(
290 aBuffer
, aName
, aCategory
, std::move(aOptions
), aTs
...);
293 return AddMarkerWithOptionalStackToBuffer
<MarkerType
>(
294 aBuffer
, aName
, aCategory
, std::move(aOptions
), aTs
...);
297 template <typename StackCallback
>
298 [[nodiscard
]] bool DeserializeAfterKindAndStream(
299 ProfileBufferEntryReader
& aEntryReader
,
300 baseprofiler::SpliceableJSONWriter
& aWriter
, int aThreadIdOrZero
,
301 StackCallback
&& aStackCallback
) {
302 // Each entry is made up of the following:
303 // ProfileBufferEntry::Kind::Marker, <- already read by caller
304 // options, <- next location in entries
307 const MarkerOptions options
= aEntryReader
.ReadObject
<MarkerOptions
>();
308 if (aThreadIdOrZero
!= 0 &&
309 options
.ThreadId().ThreadId() != aThreadIdOrZero
) {
310 // A specific thread is being read, we're not in it.
313 // Write the information to JSON with the following schema:
314 // [name, startTime, endTime, phase, category, data]
315 aWriter
.StartArrayElement();
317 aWriter
.UniqueStringElement(aEntryReader
.ReadObject
<ProfilerString8View
>());
319 const double startTime
= options
.Timing().GetStartTime();
320 aWriter
.DoubleElement(startTime
);
322 const double endTime
= options
.Timing().GetEndTime();
323 aWriter
.DoubleElement(endTime
);
325 aWriter
.IntElement(static_cast<int64_t>(options
.Timing().MarkerPhase()));
327 MarkerCategory category
= aEntryReader
.ReadObject
<MarkerCategory
>();
328 aWriter
.IntElement(static_cast<int64_t>(category
.GetCategory()));
331 aEntryReader
.ReadObject
<mozilla::base_profiler_markers_detail::
332 Streaming::DeserializerTag
>();
334 aWriter
.StartObjectElement(JSONWriter::SingleLineStyle
);
336 // Stream "common props".
338 // TODO: Move this to top-level tuple, when frontend supports it.
339 if (!options
.InnerWindowId().IsUnspecified()) {
340 // Here, we are converting uint64_t to double. Both Browsing Context
341 // and Inner Window IDs are created using
342 // `nsContentUtils::GenerateProcessSpecificId`, which is specifically
343 // designed to only use 53 of the 64 bits to be lossless when passed
344 // into and out of JS as a double.
345 aWriter
.DoubleProperty(
347 static_cast<double>(options
.InnerWindowId().Id()));
350 // TODO: Move this to top-level tuple, when frontend supports it.
351 if (ProfileChunkedBuffer
* chunkedBuffer
=
352 options
.Stack().GetChunkedBuffer();
354 aWriter
.StartObjectProperty("stack");
355 { std::forward
<StackCallback
>(aStackCallback
)(*chunkedBuffer
); }
359 // Stream the payload, including the type.
360 mozilla::base_profiler_markers_detail::Streaming::MarkerDataDeserializer
361 deserializer
= mozilla::base_profiler_markers_detail::Streaming::
362 DeserializerForTag(tag
);
363 MOZ_RELEASE_ASSERT(deserializer
);
364 deserializer(aEntryReader
, aWriter
);
373 } // namespace mozilla::base_profiler_markers_detail
377 // ----------------------------------------------------------------------------
378 // Serializer, Deserializer: ProfilerStringView<CHAR>
380 // The serialization starts with a ULEB128 number that encodes both whether the
381 // ProfilerStringView is literal (Least Significant Bit = 0) or not (LSB = 1),
382 // plus the string length (excluding null terminator) in bytes, shifted left by
383 // 1 bit. Following that number:
384 // - If literal, the string pointer value.
385 // - If non-literal, the contents as bytes (excluding null terminator if any).
386 template <typename CHAR
>
387 struct ProfileBufferEntryWriter::Serializer
<ProfilerStringView
<CHAR
>> {
388 static Length
Bytes(const ProfilerStringView
<CHAR
>& aString
) {
390 aString
.Length() < std::numeric_limits
<Length
>::max() / 2,
391 "Double the string length doesn't fit in Length type");
392 const Length stringLength
= static_cast<Length
>(aString
.Length());
393 if (aString
.IsLiteral()) {
394 // Literal -> Length shifted left and LSB=0, then pointer.
395 return ULEB128Size(stringLength
<< 1 | 0u) +
396 static_cast<ProfileChunkedBuffer::Length
>(sizeof(const CHAR
*));
398 // Non-literal -> Length shifted left and LSB=1, then string size in bytes.
399 return ULEB128Size((stringLength
<< 1) | 1u) + stringLength
* sizeof(CHAR
);
402 static void Write(ProfileBufferEntryWriter
& aEW
,
403 const ProfilerStringView
<CHAR
>& aString
) {
405 aString
.Length() < std::numeric_limits
<Length
>::max() / 2,
406 "Double the string length doesn't fit in Length type");
407 const Length stringLength
= static_cast<Length
>(aString
.Length());
408 if (aString
.IsLiteral()) {
409 // Literal -> Length shifted left and LSB=0, then pointer.
410 aEW
.WriteULEB128(stringLength
<< 1 | 0u);
411 aEW
.WriteObject(WrapProfileBufferRawPointer(aString
.Data()));
414 // Non-literal -> Length shifted left and LSB=1, then string size in bytes.
415 aEW
.WriteULEB128(stringLength
<< 1 | 1u);
416 aEW
.WriteBytes(aString
.Data(), stringLength
* sizeof(CHAR
));
420 template <typename CHAR
>
421 struct ProfileBufferEntryReader::Deserializer
<ProfilerStringView
<CHAR
>> {
422 static void ReadInto(ProfileBufferEntryReader
& aER
,
423 ProfilerStringView
<CHAR
>& aString
) {
424 const Length lengthAndIsLiteral
= aER
.ReadULEB128
<Length
>();
425 const Length stringLength
= lengthAndIsLiteral
>> 1;
426 if ((lengthAndIsLiteral
& 1u) == 0u) {
427 // LSB==0 -> Literal string, read the string pointer.
428 aString
.mStringView
= std::basic_string_view
<CHAR
>(
429 aER
.ReadObject
<const CHAR
*>(), stringLength
);
430 aString
.mOwnership
= ProfilerStringView
<CHAR
>::Ownership::Literal
;
433 // LSB==1 -> Not a literal string, allocate a buffer to store the string
434 // (plus terminal, for safety), and give it to the ProfilerStringView; Note
435 // that this is a secret use of ProfilerStringView, which is intended to
436 // only be used between deserialization and JSON streaming.
437 CHAR
* buffer
= new CHAR
[stringLength
+ 1];
438 aER
.ReadBytes(buffer
, stringLength
* sizeof(CHAR
));
439 buffer
[stringLength
] = CHAR(0);
440 aString
.mStringView
= std::basic_string_view
<CHAR
>(buffer
, stringLength
);
442 ProfilerStringView
<CHAR
>::Ownership::OwnedThroughStringView
;
445 static ProfilerStringView
<CHAR
> Read(ProfileBufferEntryReader
& aER
) {
446 const Length lengthAndIsLiteral
= aER
.ReadULEB128
<Length
>();
447 const Length stringLength
= lengthAndIsLiteral
>> 1;
448 if ((lengthAndIsLiteral
& 1u) == 0u) {
449 // LSB==0 -> Literal string, read the string pointer.
450 return ProfilerStringView
<CHAR
>(
451 aER
.ReadObject
<const CHAR
*>(), stringLength
,
452 ProfilerStringView
<CHAR
>::Ownership::Literal
);
454 // LSB==1 -> Not a literal string, allocate a buffer to store the string
455 // (plus terminal, for safety), and give it to the ProfilerStringView; Note
456 // that this is a secret use of ProfilerStringView, which is intended to
457 // only be used between deserialization and JSON streaming.
458 CHAR
* buffer
= new CHAR
[stringLength
+ 1];
459 aER
.ReadBytes(buffer
, stringLength
* sizeof(CHAR
));
460 buffer
[stringLength
] = CHAR(0);
461 return ProfilerStringView
<CHAR
>(
462 buffer
, stringLength
,
463 ProfilerStringView
<CHAR
>::Ownership::OwnedThroughStringView
);
467 // Serializer, Deserializer: MarkerCategory
469 // The serialization contains both category numbers encoded as ULEB128.
471 struct ProfileBufferEntryWriter::Serializer
<MarkerCategory
> {
472 static Length
Bytes(const MarkerCategory
& aCategory
) {
473 return ULEB128Size(static_cast<uint32_t>(aCategory
.CategoryPair()));
476 static void Write(ProfileBufferEntryWriter
& aEW
,
477 const MarkerCategory
& aCategory
) {
478 aEW
.WriteULEB128(static_cast<uint32_t>(aCategory
.CategoryPair()));
483 struct ProfileBufferEntryReader::Deserializer
<MarkerCategory
> {
484 static void ReadInto(ProfileBufferEntryReader
& aER
,
485 MarkerCategory
& aCategory
) {
486 aCategory
= Read(aER
);
489 static MarkerCategory
Read(ProfileBufferEntryReader
& aER
) {
490 return MarkerCategory(static_cast<baseprofiler::ProfilingCategoryPair
>(
491 aER
.ReadULEB128
<uint32_t>()));
495 // ----------------------------------------------------------------------------
496 // Serializer, Deserializer: MarkerTiming
498 // The serialization starts with the marker phase, followed by one or two
499 // timestamps as needed.
501 struct ProfileBufferEntryWriter::Serializer
<MarkerTiming
> {
502 static Length
Bytes(const MarkerTiming
& aTiming
) {
503 MOZ_ASSERT(!aTiming
.IsUnspecified());
504 const auto phase
= aTiming
.MarkerPhase();
506 case MarkerTiming::Phase::Instant
:
507 return SumBytes(phase
, aTiming
.StartTime());
508 case MarkerTiming::Phase::Interval
:
509 return SumBytes(phase
, aTiming
.StartTime(), aTiming
.EndTime());
510 case MarkerTiming::Phase::IntervalStart
:
511 return SumBytes(phase
, aTiming
.StartTime());
512 case MarkerTiming::Phase::IntervalEnd
:
513 return SumBytes(phase
, aTiming
.EndTime());
515 MOZ_RELEASE_ASSERT(phase
== MarkerTiming::Phase::Instant
||
516 phase
== MarkerTiming::Phase::Interval
||
517 phase
== MarkerTiming::Phase::IntervalStart
||
518 phase
== MarkerTiming::Phase::IntervalEnd
);
519 return 0; // Only to avoid build errors.
523 static void Write(ProfileBufferEntryWriter
& aEW
,
524 const MarkerTiming
& aTiming
) {
525 MOZ_ASSERT(!aTiming
.IsUnspecified());
526 const auto phase
= aTiming
.MarkerPhase();
528 case MarkerTiming::Phase::Instant
:
529 aEW
.WriteObjects(phase
, aTiming
.StartTime());
531 case MarkerTiming::Phase::Interval
:
532 aEW
.WriteObjects(phase
, aTiming
.StartTime(), aTiming
.EndTime());
534 case MarkerTiming::Phase::IntervalStart
:
535 aEW
.WriteObjects(phase
, aTiming
.StartTime());
537 case MarkerTiming::Phase::IntervalEnd
:
538 aEW
.WriteObjects(phase
, aTiming
.EndTime());
541 MOZ_RELEASE_ASSERT(phase
== MarkerTiming::Phase::Instant
||
542 phase
== MarkerTiming::Phase::Interval
||
543 phase
== MarkerTiming::Phase::IntervalStart
||
544 phase
== MarkerTiming::Phase::IntervalEnd
);
551 struct ProfileBufferEntryReader::Deserializer
<MarkerTiming
> {
552 static void ReadInto(ProfileBufferEntryReader
& aER
, MarkerTiming
& aTiming
) {
553 aTiming
.mPhase
= aER
.ReadObject
<MarkerTiming::Phase
>();
554 switch (aTiming
.mPhase
) {
555 case MarkerTiming::Phase::Instant
:
556 aTiming
.mStartTime
= aER
.ReadObject
<TimeStamp
>();
557 aTiming
.mEndTime
= TimeStamp
{};
559 case MarkerTiming::Phase::Interval
:
560 aTiming
.mStartTime
= aER
.ReadObject
<TimeStamp
>();
561 aTiming
.mEndTime
= aER
.ReadObject
<TimeStamp
>();
563 case MarkerTiming::Phase::IntervalStart
:
564 aTiming
.mStartTime
= aER
.ReadObject
<TimeStamp
>();
565 aTiming
.mEndTime
= TimeStamp
{};
567 case MarkerTiming::Phase::IntervalEnd
:
568 aTiming
.mStartTime
= TimeStamp
{};
569 aTiming
.mEndTime
= aER
.ReadObject
<TimeStamp
>();
572 MOZ_RELEASE_ASSERT(aTiming
.mPhase
== MarkerTiming::Phase::Instant
||
573 aTiming
.mPhase
== MarkerTiming::Phase::Interval
||
575 MarkerTiming::Phase::IntervalStart
||
576 aTiming
.mPhase
== MarkerTiming::Phase::IntervalEnd
);
581 static MarkerTiming
Read(ProfileBufferEntryReader
& aER
) {
584 auto phase
= aER
.ReadObject
<MarkerTiming::Phase
>();
586 case MarkerTiming::Phase::Instant
:
587 start
= aER
.ReadObject
<TimeStamp
>();
589 case MarkerTiming::Phase::Interval
:
590 start
= aER
.ReadObject
<TimeStamp
>();
591 end
= aER
.ReadObject
<TimeStamp
>();
593 case MarkerTiming::Phase::IntervalStart
:
594 start
= aER
.ReadObject
<TimeStamp
>();
596 case MarkerTiming::Phase::IntervalEnd
:
597 end
= aER
.ReadObject
<TimeStamp
>();
600 MOZ_RELEASE_ASSERT(phase
== MarkerTiming::Phase::Instant
||
601 phase
== MarkerTiming::Phase::Interval
||
602 phase
== MarkerTiming::Phase::IntervalStart
||
603 phase
== MarkerTiming::Phase::IntervalEnd
);
606 return MarkerTiming(start
, end
, phase
);
610 // ----------------------------------------------------------------------------
611 // Serializer, Deserializer: MarkerStack
613 // The serialization only contains the `ProfileChunkedBuffer` from the
614 // backtrace; if there is no backtrace or if it's empty, this will implicitly
615 // store a nullptr (see
616 // `ProfileBufferEntryWriter::Serializer<ProfilerChunkedBuffer*>`).
618 struct ProfileBufferEntryWriter::Serializer
<MarkerStack
> {
619 static Length
Bytes(const MarkerStack
& aStack
) {
620 return SumBytes(aStack
.GetChunkedBuffer());
623 static void Write(ProfileBufferEntryWriter
& aEW
, const MarkerStack
& aStack
) {
624 aEW
.WriteObject(aStack
.GetChunkedBuffer());
629 struct ProfileBufferEntryReader::Deserializer
<MarkerStack
> {
630 static void ReadInto(ProfileBufferEntryReader
& aER
, MarkerStack
& aStack
) {
634 static MarkerStack
Read(ProfileBufferEntryReader
& aER
) {
635 return MarkerStack(aER
.ReadObject
<UniquePtr
<ProfileChunkedBuffer
>>());
639 // ----------------------------------------------------------------------------
640 // Serializer, Deserializer: MarkerOptions
642 // The serialization contains all members (either trivially-copyable, or they
643 // provide their specialization above).
645 struct ProfileBufferEntryWriter::Serializer
<MarkerOptions
> {
646 static Length
Bytes(const MarkerOptions
& aOptions
) {
647 return SumBytes(aOptions
.ThreadId(), aOptions
.Timing(), aOptions
.Stack(),
648 aOptions
.InnerWindowId());
651 static void Write(ProfileBufferEntryWriter
& aEW
,
652 const MarkerOptions
& aOptions
) {
653 aEW
.WriteObjects(aOptions
.ThreadId(), aOptions
.Timing(), aOptions
.Stack(),
654 aOptions
.InnerWindowId());
659 struct ProfileBufferEntryReader::Deserializer
<MarkerOptions
> {
660 static void ReadInto(ProfileBufferEntryReader
& aER
, MarkerOptions
& aOptions
) {
661 aER
.ReadIntoObjects(aOptions
.mThreadId
, aOptions
.mTiming
, aOptions
.mStack
,
662 aOptions
.mInnerWindowId
);
665 static MarkerOptions
Read(ProfileBufferEntryReader
& aER
) {
666 MarkerOptions options
;
667 ReadInto(aER
, options
);
672 } // namespace mozilla
674 #endif // BaseProfilerMarkersDetail_h