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