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"
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
{
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
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
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
{
79 MFBT_API
LockedMarkerTypeFunctionsList();
80 MFBT_API
~LockedMarkerTypeFunctionsList();
82 LockedMarkerTypeFunctionsList(const LockedMarkerTypeFunctionsList
&) =
84 LockedMarkerTypeFunctionsList
& operator=(
85 const LockedMarkerTypeFunctionsList
&) = delete;
87 auto begin() const { return mMarkerTypeFunctionsSpan
.begin(); }
88 auto end() const { return mMarkerTypeFunctionsSpan
.end(); }
91 Span
<const MarkerTypeFunctions
> mMarkerTypeFunctionsSpan
;
95 // This helper will examine a marker type's `StreamJSONMarkerData` function, see
96 // specialization below.
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
);
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
;
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
,
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
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
...);
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
);
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
196 MarkerType::StreamJSONMarkerData(aWriter
, aArgs
...);
201 static void Deserialize(ProfileBufferEntryReader
& aEntryReader
,
202 baseprofiler::SpliceableJSONWriter
& aWriter
) {
203 aWriter
.StringProperty("type", MarkerType::MarkerTypeName());
204 DeserializeArguments(aEntryReader
, aWriter
);
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
,
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
) {
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.
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));
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
,
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
300 // TODO reduce internal profiler stack levels, see bug 1659872.
301 auto CaptureStackAndAddMarker
= [&](ProfileChunkedBuffer
& aChunkedBuffer
) {
302 aOptions
.StackRef().UseRequestedBacktrace(
303 aOptionalBacktraceCaptureFunction(aChunkedBuffer
, captureOptions
)
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();
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
353 const MarkerOptions options
= aEntryReader
.ReadObject
<MarkerOptions
>();
355 baseprofiler::SpliceableJSONWriter
* writer
=
356 std::forward
<GetWriterForThreadCallback
>(aGetWriterForThreadCallback
)(
357 options
.ThreadId().ThreadId());
359 // No writer associated with this thread id, drop it.
360 aEntryReader
.SetRemainingBytes(0);
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()));
382 aEntryReader
.ReadObject
<mozilla::base_profiler_markers_detail::
383 Streaming::DeserializerTag
>();
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(
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();
405 writer
->StartObjectProperty("stack");
406 { std::forward
<StackCallback
>(aStackCallback
)(*chunkedBuffer
); }
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);
426 case mozilla::MarkerPayloadType::Rust
:
427 std::forward
<RustMarkerCallback
>(aRustMarkerCallback
)(tag
);
428 MOZ_ASSERT(aEntryReader
.RemainingBytes() == 0u);
431 MOZ_ASSERT_UNREACHABLE("Unknown payload type.");
439 MOZ_ASSERT(aEntryReader
.RemainingBytes() == 0u);
442 } // namespace mozilla::base_profiler_markers_detail
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
) {
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
) {
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()));
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
) {
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()) %
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
);
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.
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()));
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.
568 struct ProfileBufferEntryWriter::Serializer
<MarkerTiming
> {
569 static Length
Bytes(const MarkerTiming
& aTiming
) {
570 MOZ_ASSERT(!aTiming
.IsUnspecified());
571 const auto phase
= aTiming
.MarkerPhase();
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());
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();
595 case MarkerTiming::Phase::Instant
:
596 aEW
.WriteObjects(phase
, aTiming
.StartTime());
598 case MarkerTiming::Phase::Interval
:
599 aEW
.WriteObjects(phase
, aTiming
.StartTime(), aTiming
.EndTime());
601 case MarkerTiming::Phase::IntervalStart
:
602 aEW
.WriteObjects(phase
, aTiming
.StartTime());
604 case MarkerTiming::Phase::IntervalEnd
:
605 aEW
.WriteObjects(phase
, aTiming
.EndTime());
608 MOZ_RELEASE_ASSERT(phase
== MarkerTiming::Phase::Instant
||
609 phase
== MarkerTiming::Phase::Interval
||
610 phase
== MarkerTiming::Phase::IntervalStart
||
611 phase
== MarkerTiming::Phase::IntervalEnd
);
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
{};
626 case MarkerTiming::Phase::Interval
:
627 aTiming
.mStartTime
= aER
.ReadObject
<TimeStamp
>();
628 aTiming
.mEndTime
= aER
.ReadObject
<TimeStamp
>();
630 case MarkerTiming::Phase::IntervalStart
:
631 aTiming
.mStartTime
= aER
.ReadObject
<TimeStamp
>();
632 aTiming
.mEndTime
= TimeStamp
{};
634 case MarkerTiming::Phase::IntervalEnd
:
635 aTiming
.mStartTime
= TimeStamp
{};
636 aTiming
.mEndTime
= aER
.ReadObject
<TimeStamp
>();
639 MOZ_RELEASE_ASSERT(aTiming
.mPhase
== MarkerTiming::Phase::Instant
||
640 aTiming
.mPhase
== MarkerTiming::Phase::Interval
||
642 MarkerTiming::Phase::IntervalStart
||
643 aTiming
.mPhase
== MarkerTiming::Phase::IntervalEnd
);
648 static MarkerTiming
Read(ProfileBufferEntryReader
& aER
) {
651 auto phase
= aER
.ReadObject
<MarkerTiming::Phase
>();
653 case MarkerTiming::Phase::Instant
:
654 start
= aER
.ReadObject
<TimeStamp
>();
656 case MarkerTiming::Phase::Interval
:
657 start
= aER
.ReadObject
<TimeStamp
>();
658 end
= aER
.ReadObject
<TimeStamp
>();
660 case MarkerTiming::Phase::IntervalStart
:
661 start
= aER
.ReadObject
<TimeStamp
>();
663 case MarkerTiming::Phase::IntervalEnd
:
664 end
= aER
.ReadObject
<TimeStamp
>();
667 MOZ_RELEASE_ASSERT(phase
== MarkerTiming::Phase::Instant
||
668 phase
== MarkerTiming::Phase::Interval
||
669 phase
== MarkerTiming::Phase::IntervalStart
||
670 phase
== MarkerTiming::Phase::IntervalEnd
);
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*>`).
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());
696 struct ProfileBufferEntryReader::Deserializer
<MarkerStack
> {
697 static void ReadInto(ProfileBufferEntryReader
& aER
, MarkerStack
& aStack
) {
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).
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());
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
);
739 } // namespace mozilla
741 #endif // BaseProfilerMarkersDetail_h