diff --git a/mozglue/baseprofiler/core/ProfileBufferEntry.cpp b/mozglue/baseprofiler/core/ProfileBufferEntry.cpp index 54b559af42eb..1dc5a223bb6b 100644 --- a/mozglue/baseprofiler/core/ProfileBufferEntry.cpp +++ b/mozglue/baseprofiler/core/ProfileBufferEntry.cpp @@ -15,10 +15,8 @@ #include "BaseProfiler.h" #include "BaseProfilerMarkerPayload.h" -#include "mozilla/BaseProfilerMarkers.h" #include "platform.h" #include "ProfileBuffer.h" -#include "ProfilerBacktrace.h" namespace mozilla { namespace baseprofiler { @@ -814,61 +812,33 @@ void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter, MOZ_ASSERT(static_cast(type) < static_cast( ProfileBufferEntry::Kind::MODERN_LIMIT)); - // Code should *break* from the switch if the entry was not fully read. - // Code should *return* from the switch if the entry was fully read. - switch (type) { - case ProfileBufferEntry::Kind::MarkerData: - if (aER.ReadObject() != aThreadId) { - break; - } - // Schema: - // [name, time, category, data] - aWriter.StartArrayElement(); - { - std::string name = aER.ReadObject(); - const ProfilingCategoryPairInfo& info = GetProfilingCategoryPairInfo( - static_cast(aER.ReadObject())); - auto payload = aER.ReadObject>(); - double time = aER.ReadObject(); - MOZ_ASSERT(aER.RemainingBytes() == 0); + if (type == ProfileBufferEntry::Kind::MarkerData && + aER.ReadObject() == aThreadId) { + // Schema: + // [name, time, category, data] - aUniqueStacks.mUniqueStrings->WriteElement(aWriter, name.c_str()); - aWriter.DoubleElement(time); - aWriter.IntElement(unsigned(info.mCategory)); - if (payload) { - aWriter.StartObjectElement(SpliceableJSONWriter::SingleLineStyle); - { - payload->StreamPayload(aWriter, aProcessStartTime, aUniqueStacks); - } - aWriter.EndObject(); - } - } - aWriter.EndArray(); - return; + aWriter.StartArrayElement(); + { + std::string name = aER.ReadObject(); + const ProfilingCategoryPairInfo& info = GetProfilingCategoryPairInfo( + static_cast(aER.ReadObject())); + auto payload = aER.ReadObject>(); + double time = aER.ReadObject(); + MOZ_ASSERT(aER.RemainingBytes() == 0); - case ProfileBufferEntry::Kind::Marker: - if (::mozilla::base_profiler_markers_detail:: - DeserializeAfterKindAndStream( - aER, aWriter, aThreadId, - [&](const mozilla::ProfilerString8View& aName) { - aUniqueStacks.mUniqueStrings->WriteElement( - aWriter, aName.String().c_str()); - }, - [&](ProfileChunkedBuffer& aChunkedBuffer) { - ProfilerBacktrace backtrace("", aThreadId, - &aChunkedBuffer); - backtrace.StreamJSON( - aWriter, TimeStamp::ProcessCreation(), aUniqueStacks); - })) { - return; + aUniqueStacks.mUniqueStrings->WriteElement(aWriter, name.c_str()); + aWriter.DoubleElement(time); + aWriter.IntElement(unsigned(info.mCategory)); + if (payload) { + aWriter.StartObjectElement(SpliceableJSONWriter::SingleLineStyle); + { payload->StreamPayload(aWriter, aProcessStartTime, aUniqueStacks); } + aWriter.EndObject(); } - break; - - default: - break; + } + aWriter.EndArray(); + } else { + aER.SetRemainingBytes(0); } - - aER.SetRemainingBytes(0); }); } diff --git a/mozglue/baseprofiler/core/ProfilerMarkers.cpp b/mozglue/baseprofiler/core/ProfilerMarkers.cpp deleted file mode 100644 index 8e5bdd891077..000000000000 --- a/mozglue/baseprofiler/core/ProfilerMarkers.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/BaseProfilerMarkers.h" - -#include "mozilla/Likely.h" - -#include - -namespace mozilla { -namespace base_profiler_markers_detail { - -// We need an atomic type that can hold a `DeserializerTag`. (Atomic doesn't -// work with too-small types.) -using DeserializerTagAtomic = unsigned; - -// Number of currently-registered deserializers. -static Atomic - sDeserializerCount{0}; - -// This needs to be big enough to handle all possible sub-types of -// ProfilerMarkerPayload. -static constexpr Streaming::DeserializerTag DeserializerMax = 255; - -static_assert( - DeserializerMax <= std::numeric_limits::max(), - "The maximum number of deserializers must fit in the DeserializerTag type"); - -// Array of deserializer. -// 1-based, i.e.: [0] -> tag 1, [DeserializerMax - 1] -> tag DeserializerMax. -static Streaming::Deserializer sDeserializers1Based[DeserializerMax]; - -/* static */ Streaming::DeserializerTag Streaming::TagForDeserializer( - Streaming::Deserializer aDeserializer) { - MOZ_RELEASE_ASSERT(!!aDeserializer); - - DeserializerTagAtomic tag = ++sDeserializerCount; - MOZ_RELEASE_ASSERT( - tag <= DeserializerMax, - "Too many deserializers, consider increasing DeserializerMax. " - "Or is a deserializer stored again and again?"); - sDeserializers1Based[tag - 1] = aDeserializer; - - return static_cast(tag); -} - -/* static */ Streaming::Deserializer Streaming::DeserializerForTag( - Streaming::DeserializerTag aTag) { - MOZ_RELEASE_ASSERT(aTag > 0); - MOZ_RELEASE_ASSERT(aTag <= DeserializerMax); - MOZ_RELEASE_ASSERT(aTag <= sDeserializerCount); - return sDeserializers1Based[aTag - 1]; -} - -} // namespace base_profiler_markers_detail -} // namespace mozilla diff --git a/mozglue/baseprofiler/core/platform.cpp b/mozglue/baseprofiler/core/platform.cpp index 9f9b134733b7..3cda00606587 100644 --- a/mozglue/baseprofiler/core/platform.cpp +++ b/mozglue/baseprofiler/core/platform.cpp @@ -419,7 +419,7 @@ class CorePS { PS_GET_LOCKLESS(int, MainThreadId) // No PSLockRef is needed for this field because it's immutable. - PS_GET_LOCKLESS(const TimeStamp&, ProcessStartTime) + PS_GET_LOCKLESS(TimeStamp, ProcessStartTime) // No PSLockRef is needed for this field because it's thread-safe. PS_GET_LOCKLESS(ProfileChunkedBuffer&, CoreBuffer) @@ -568,11 +568,6 @@ class CorePS { CorePS* CorePS::sInstance = nullptr; -ProfileChunkedBuffer& profiler_get_core_buffer() { - MOZ_ASSERT(CorePS::Exists()); - return CorePS::CoreBuffer(); -} - class SamplerThread; static SamplerThread* NewSamplerThread(PSLockRef aLock, uint32_t aGeneration, @@ -3535,7 +3530,59 @@ double profiler_time() { return delta.ToMilliseconds(); } -bool profiler_capture_backtrace_into(ProfileChunkedBuffer& aChunkedBuffer) { +static void locked_profiler_fill_backtrace(PSLockRef aLock, + RegisteredThread& aRegisteredThread, + ProfileBuffer& aProfileBuffer) { + Registers regs; +#if defined(HAVE_NATIVE_UNWIND) + regs.SyncPopulate(); +#else + regs.Clear(); +#endif + + DoSyncSample(aLock, aRegisteredThread, TimeStamp::NowUnfuzzed(), regs, + aProfileBuffer); +} + +static UniqueProfilerBacktrace locked_profiler_get_backtrace(PSLockRef aLock) { + MOZ_RELEASE_ASSERT(CorePS::Exists()); + + if (!ActivePS::Exists(aLock)) { + return nullptr; + } + + RegisteredThread* registeredThread = + TLSRegisteredThread::RegisteredThread(aLock); + if (!registeredThread) { + MOZ_ASSERT(registeredThread); + return nullptr; + } + + auto bufferManager = MakeUnique( + ProfileChunkedBuffer::ThreadSafety::WithoutMutex, + MakeUnique(scExpectedMaximumStackSize)); + ProfileBuffer buffer(*bufferManager); + + locked_profiler_fill_backtrace(aLock, *registeredThread, buffer); + + return UniqueProfilerBacktrace( + new ProfilerBacktrace("SyncProfile", registeredThread->Info()->ThreadId(), + std::move(bufferManager))); +} + +UniqueProfilerBacktrace profiler_get_backtrace() { + MOZ_RELEASE_ASSERT(CorePS::Exists()); + + PSAutoLock lock; + + return locked_profiler_get_backtrace(lock); +} + +void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) { + delete aBacktrace; +} + +bool profiler_capture_backtrace(ProfileChunkedBuffer& aChunkedBuffer) { MOZ_RELEASE_ASSERT(CorePS::Exists()); PSAutoLock lock; @@ -3553,53 +3600,11 @@ bool profiler_capture_backtrace_into(ProfileChunkedBuffer& aChunkedBuffer) { ProfileBuffer profileBuffer(aChunkedBuffer); - Registers regs; -#if defined(HAVE_NATIVE_UNWIND) - regs.SyncPopulate(); -#else - regs.Clear(); -#endif - - DoSyncSample(lock, *registeredThread, TimeStamp::NowUnfuzzed(), regs, - profileBuffer); + locked_profiler_fill_backtrace(lock, *registeredThread, profileBuffer); return true; } -UniquePtr profiler_capture_backtrace() { - MOZ_RELEASE_ASSERT(CorePS::Exists()); - - // Quick is-active check before allocating a buffer. - if (!profiler_is_active()) { - return nullptr; - } - - auto buffer = MakeUnique( - ProfileChunkedBuffer::ThreadSafety::WithoutMutex, - MakeUnique(scExpectedMaximumStackSize)); - - if (!profiler_capture_backtrace_into(*buffer)) { - return nullptr; - } - - return buffer; -} - -UniqueProfilerBacktrace profiler_get_backtrace() { - UniquePtr buffer = profiler_capture_backtrace(); - - if (!buffer) { - return nullptr; - } - - return UniqueProfilerBacktrace(new ProfilerBacktrace( - "SyncProfile", profiler_current_thread_id(), std::move(buffer))); -} - -void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) { - delete aBacktrace; -} - bool profiler_is_locked_on_current_thread() { // This function is used to help users avoid calling `profiler_...` functions // when the profiler may already have a lock in place, which would prevent a diff --git a/mozglue/baseprofiler/moz.build b/mozglue/baseprofiler/moz.build index b7babdd5cc14..229b858cd74d 100644 --- a/mozglue/baseprofiler/moz.build +++ b/mozglue/baseprofiler/moz.build @@ -26,7 +26,6 @@ if CONFIG['MOZ_GECKO_PROFILER']: 'core/ProfiledThreadData.cpp', 'core/ProfilerBacktrace.cpp', 'core/ProfilerMarkerPayload.cpp', - 'core/ProfilerMarkers.cpp', 'core/ProfilingCategory.cpp', 'core/ProfilingStack.cpp', 'core/RegisteredThread.cpp', @@ -83,10 +82,6 @@ EXPORTS.mozilla += [ 'public/BaseProfileJSONWriter.h', 'public/BaseProfilerCounts.h', 'public/BaseProfilerDetail.h', - 'public/BaseProfilerMarkers.h', - 'public/BaseProfilerMarkersDetail.h', - 'public/BaseProfilerMarkersPrerequisites.h', - 'public/BaseProfilerMarkerTypes.h', 'public/BlocksRingBuffer.h', 'public/leb128iterator.h', 'public/ModuloBuffer.h', diff --git a/mozglue/baseprofiler/public/BaseProfiler.h b/mozglue/baseprofiler/public/BaseProfiler.h index 1a83f176ffb4..f8641bd1752a 100644 --- a/mozglue/baseprofiler/public/BaseProfiler.h +++ b/mozglue/baseprofiler/public/BaseProfiler.h @@ -19,22 +19,18 @@ // This file is safe to include unconditionally, and only defines // empty macros if MOZ_GECKO_PROFILER is not set. -// These headers are also safe to include unconditionally, with empty macros if -// MOZ_GECKO_PROFILER is not set. +// BaseProfilerCounts.h is also safe to include unconditionally, with empty +// macros if MOZ_GECKO_PROFILER is not set. #include "mozilla/BaseProfilerCounts.h" -// BaseProfilerMarkers.h is #included in the middle of this header! -// #include "mozilla/BaseProfilerMarkers.h" - #ifndef MOZ_GECKO_PROFILER -# include "mozilla/BaseProfilerMarkers.h" # include "mozilla/UniquePtr.h" // This file can be #included unconditionally. However, everything within this // file must be guarded by a #ifdef MOZ_GECKO_PROFILER, *except* for the -// following macros and functions, which encapsulate the most common operations -// and thus avoid the need for many #ifdefs. +// following macros, which encapsulate the most common operations and thus +// avoid the need for many #ifdefs. # define AUTO_BASE_PROFILER_INIT @@ -66,30 +62,20 @@ # define AUTO_PROFILER_STATS(name) -// Function stubs for when MOZ_GECKO_PROFILER is not defined. - namespace mozilla { class ProfileChunkedBuffer; namespace baseprofiler { struct ProfilerBacktrace {}; using UniqueProfilerBacktrace = UniquePtr; - -// Get/Capture-backtrace functions can return nullptr or false, the result -// should be fed to another empty macro or stub anyway. - static inline UniqueProfilerBacktrace profiler_get_backtrace() { return nullptr; } -static inline bool profiler_capture_backtrace_into( +static inline bool profiler_capture_backtrace( ProfileChunkedBuffer& aChunkedBuffer) { return false; } - -static inline UniquePtr profiler_capture_backtrace() { - return nullptr; -} } // namespace baseprofiler } // namespace mozilla @@ -549,22 +535,10 @@ struct ProfilerBacktraceDestructor { using UniqueProfilerBacktrace = UniquePtr; -// Immediately capture the current thread's call stack, store it in the provided -// buffer (usually to avoid allocations if you can construct the buffer on the -// stack). Returns false if unsuccessful, or if the profiler is inactive. -MFBT_API bool profiler_capture_backtrace_into( - ProfileChunkedBuffer& aChunkedBuffer); - -// Immediately capture the current thread's call stack, and return it in a -// ProfileChunkedBuffer (usually for later use in MarkerStack::TakeBacktrace()). -// May be null if unsuccessful, or if the profiler is inactive. -MFBT_API UniquePtr profiler_capture_backtrace(); - -// Immediately capture the current thread's call stack, and return it in a -// ProfilerBacktrace (usually for later use in marker function that take a -// ProfilerBacktrace). May be null if unsuccessful, or if the profiler is -// inactive. +// Immediately capture the current thread's call stack and return it. A no-op +// if the profiler is inactive. MFBT_API UniqueProfilerBacktrace profiler_get_backtrace(); +MFBT_API bool profiler_capture_backtrace(ProfileChunkedBuffer& aChunkedBuffer); struct ProfilerStats { unsigned n = 0; @@ -702,16 +676,6 @@ class MOZ_RAII AutoProfilerStats { # endif // PROFILER_RUNTIME_STATS else -} // namespace baseprofiler -} // namespace mozilla - -// BaseProfilerMarkers.h requires some stuff from this header. -// TODO: Move common stuff to shared header, and move this #include to the top. -# include "mozilla/BaseProfilerMarkers.h" - -namespace mozilla { -namespace baseprofiler { - //--------------------------------------------------------------------------- // Put profiling data into the profiler (labels and markers) //--------------------------------------------------------------------------- diff --git a/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h b/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h deleted file mode 100644 index 188f611a7087..000000000000 --- a/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h +++ /dev/null @@ -1,129 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef BaseProfilerMarkerTypes_h -#define BaseProfilerMarkerTypes_h - -// This header contains common marker type definitions. -// -// It #include's "mozilla/BaseProfilerMarkers.h", see that file for how to -// define other marker types, and how to add markers to the profiler buffers. -// -// If you don't need to use these common types, #include -// "mozilla/BaseProfilerMarkers.h" instead. -// -// Types in this files can be defined without relying on xpcom. -// Others are defined in "ProfilerMarkerTypes.h". - -// /!\ WORK IN PROGRESS /!\ -// This file contains draft marker definitions, but most are not used yet. -// Further work is needed to complete these definitions, and use them to convert -// existing PROFILER_ADD_MARKER calls. See meta bug 1661394. - -#include "mozilla/BaseProfilerMarkers.h" - -#ifdef MOZ_GECKO_PROFILER - -namespace mozilla::baseprofiler::markers { - -struct Tracing { - static constexpr const char* MarkerTypeName() { return "tracing"; } - static void StreamJSONMarkerData(JSONWriter& aWriter, - const ProfilerString8View& aCategory) { - if (aCategory.Length() != 0) { - aWriter.StringProperty("category", aCategory.String().c_str()); - } - } -}; - -struct FileIO { - static constexpr const char* MarkerTypeName() { return "FileIO"; } - static void StreamJSONMarkerData(JSONWriter& aWriter, - const ProfilerString8View& aOperation, - const ProfilerString8View& aSource, - const ProfilerString8View& aFilename, - MarkerThreadId aOperationThreadId) { - aWriter.StringProperty("operation", aOperation.String().c_str()); - aWriter.StringProperty("source", aSource.String().c_str()); - if (aFilename.Length() != 0) { - aWriter.StringProperty("filename", aFilename.String().c_str()); - } - if (!aOperationThreadId.IsUnspecified()) { - aWriter.IntProperty("threadId", aOperationThreadId.ThreadId()); - } - } -}; - -struct UserTimingMark { - static constexpr const char* MarkerTypeName() { return "UserTiming"; } - static void StreamJSONMarkerData(JSONWriter& aWriter, - const ProfilerString8View& aName) { - aWriter.StringProperty("name", aName.String().c_str()); - aWriter.StringProperty("entryType", "mark"); - aWriter.NullProperty("startMark"); - aWriter.NullProperty("endMark"); - } -}; - -struct UserTimingMeasure { - static constexpr const char* MarkerTypeName() { return "UserTiming"; } - static void StreamJSONMarkerData(JSONWriter& aWriter, - const ProfilerString8View& aName, - const Maybe& aStartMark, - const Maybe& aEndMark) { - aWriter.StringProperty("name", aName.String().c_str()); - aWriter.StringProperty("entryType", "measure"); - - if (aStartMark.isSome()) { - aWriter.StringProperty("startMark", aStartMark->String().c_str()); - } else { - aWriter.NullProperty("startMark"); - } - if (aEndMark.isSome()) { - aWriter.StringProperty("endMark", aEndMark->String().c_str()); - } else { - aWriter.NullProperty("endMark"); - } - } -}; - -struct Hang { - static constexpr const char* MarkerTypeName() { return "BHR-detected hang"; } - static void StreamJSONMarkerData(JSONWriter& aWriter) {} -}; - -struct LongTask { - static constexpr const char* MarkerTypeName() { return "MainThreadLongTask"; } - static void StreamJSONMarkerData(JSONWriter& aWriter) { - aWriter.StringProperty("category", "LongTask"); - } -}; - -struct Log { - static constexpr const char* MarkerTypeName() { return "Log"; } - static void StreamJSONMarkerData(JSONWriter& aWriter, - const ProfilerString8View& aModule, - const ProfilerString8View& aText) { - aWriter.StringProperty("module", aModule.String().c_str()); - aWriter.StringProperty("name", aText.String().c_str()); - } -}; - -struct MediaSample { - static constexpr const char* MarkerTypeName() { return "MediaSample"; } - static void StreamJSONMarkerData(JSONWriter& aWriter, - int64_t aSampleStartTimeUs, - int64_t aSampleEndTimeUs) { - aWriter.IntProperty("sampleStartTimeUs", aSampleStartTimeUs); - aWriter.IntProperty("sampleEndTimeUs", aSampleEndTimeUs); - } -}; - -} // namespace mozilla::baseprofiler::markers - -#endif // MOZ_GECKO_PROFILER - -#endif // BaseProfilerMarkerTypes_h diff --git a/mozglue/baseprofiler/public/BaseProfilerMarkers.h b/mozglue/baseprofiler/public/BaseProfilerMarkers.h deleted file mode 100644 index 01333b271ec0..000000000000 --- a/mozglue/baseprofiler/public/BaseProfilerMarkers.h +++ /dev/null @@ -1,123 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// This header contains basic definitions necessary to create marker types, and -// to add markers to the profiler buffers. -// -// If basic marker types are needed, #include -// "mozilla/BaseProfilerMarkerTypes.h" instead. -// -// But if you want to create your own marker type locally, you can #include this -// header only; look at mozilla/BaseProfilerMarkerTypes.h for examples of how to -// define types, and mozilla/BaseProfilerMarkerPrerequisites.h for some -// supporting types. -// -// To then record markers: -// - Use `baseprofiler::AddMarker(...)` from -// mozglue or other libraries that are outside of xul, especially if they may -// happen outside of xpcom's lifetime (typically startup, shutdown, or tests). -// - Otherwise #include "ProfilerMarkers.h" instead, and use -// `profiler_add_marker(...)`. -// See these functions for more details. - -#ifndef BaseProfilerMarkers_h -#define BaseProfilerMarkers_h - -#include "mozilla/BaseProfilerMarkersDetail.h" - -// TODO: Move common stuff to shared header instead. -#include "BaseProfiler.h" - -#ifndef MOZ_GECKO_PROFILER - -# define BASE_PROFILER_MARKER_UNTYPED(markerName, options) -# define BASE_PROFILER_MARKER(markerName, options, MarkerType, ...) -# define BASE_PROFILER_MARKER_TEXT(markerName, options, text) - -#else // ndef MOZ_GECKO_PROFILER - -# include "mozilla/ProfileChunkedBuffer.h" -# include "mozilla/TimeStamp.h" - -# include -# include -# include - -namespace mozilla::baseprofiler { - -template -ProfileBufferBlockIndex AddMarkerToBuffer(ProfileChunkedBuffer& aBuffer, - const ProfilerString8View& aName, - MarkerOptions&& aOptions, - const Ts&... aTs) { - return base_profiler_markers_detail::AddMarkerToBuffer( - aBuffer, aName, std::move(aOptions), - ::mozilla::baseprofiler::profiler_capture_backtrace_into, aTs...); -} - -template -ProfileBufferBlockIndex AddMarker(const ProfilerString8View& aName, - MarkerOptions&& aOptions, const Ts&... aTs) { - if (!baseprofiler::profiler_can_accept_markers()) { - return {}; - } - return ::mozilla::baseprofiler::AddMarkerToBuffer( - base_profiler_markers_detail::CachedBaseCoreBuffer(), aName, - std::move(aOptions), aTs...); -} - -// Marker types' StreamJSONMarkerData functions should use this to correctly -// output timestamps as a JSON property. -inline void WritePropertyTime(JSONWriter& aWriter, const char* aName, - const TimeStamp& aTime) { - if (!aTime.IsNull()) { - aWriter.DoubleProperty( - aName, (aTime - TimeStamp::ProcessCreation()).ToMilliseconds()); - } -} - -} // namespace mozilla::baseprofiler - -# define BASE_PROFILER_MARKER_UNTYPED(markerName, options) \ - do { \ - AUTO_PROFILER_STATS(base_add_marker_v2); \ - ::mozilla::baseprofiler::AddMarker<>( \ - markerName, ::mozilla::baseprofiler::category::options); \ - } while (false) - -# define BASE_PROFILER_MARKER(markerName, options, MarkerType, ...) \ - do { \ - AUTO_PROFILER_STATS(base_add_marker_v2_with_##MarkerType); \ - ::mozilla::baseprofiler::AddMarker< \ - ::mozilla::baseprofiler::markers::MarkerType>( \ - markerName, ::mozilla::baseprofiler::category::options, \ - ##__VA_ARGS__); \ - } while (false) - -namespace mozilla::baseprofiler::markers { -// Most common marker type. Others are in BaseProfilerMarkerTypes.h. -struct Text { - static constexpr const char* MarkerTypeName() { return "Text"; } - static void StreamJSONMarkerData(JSONWriter& aWriter, - const ProfilerString8View& aText) { - aWriter.StringProperty("name", aText.String().c_str()); - } -}; -} // namespace mozilla::baseprofiler::markers - -# define BASE_PROFILER_MARKER_TEXT(markerName, options, text) \ - do { \ - AUTO_PROFILER_STATS(base_add_marker_v2_with_Text); \ - ::mozilla::baseprofiler::AddMarker< \ - ::mozilla::baseprofiler::markers::Text>( \ - markerName, ::mozilla::baseprofiler::category::options, text); \ - } while (false) - -#endif // nfed MOZ_GECKO_PROFILER else - -#endif // BaseProfilerMarkers_h diff --git a/mozglue/baseprofiler/public/BaseProfilerMarkersDetail.h b/mozglue/baseprofiler/public/BaseProfilerMarkersDetail.h deleted file mode 100644 index 3b2b1e90c115..000000000000 --- a/mozglue/baseprofiler/public/BaseProfilerMarkersDetail.h +++ /dev/null @@ -1,624 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef BaseProfilerMarkersDetail_h -#define BaseProfilerMarkersDetail_h - -#ifndef BaseProfilerMarkers_h -# error "This header should only be #included by BaseProfilerMarkers.h" -#endif - -#include "mozilla/BaseProfilerMarkersPrerequisites.h" - -#ifdef MOZ_GECKO_PROFILER - -// ~~ HERE BE DRAGONS ~~ -// -// Everything below is internal implementation detail, you shouldn't need to -// look at it unless working on the profiler code. - -# include "mozilla/JSONWriter.h" -# include "mozilla/ProfileBufferEntryKinds.h" - -# include -# include - -namespace mozilla::baseprofiler { -// Implemented in platform.cpp -MFBT_API ProfileChunkedBuffer& profiler_get_core_buffer(); -} // namespace mozilla::baseprofiler - -namespace mozilla::base_profiler_markers_detail { - -// Get the core buffer from the profiler, and cache it in a -// non-templated-function static reference. -inline ProfileChunkedBuffer& CachedBaseCoreBuffer() { - static ProfileChunkedBuffer& coreBuffer = - baseprofiler::profiler_get_core_buffer(); - return coreBuffer; -} - -struct Streaming { - // A `Deserializer` is a free function that can read a serialized payload from - // an `EntryReader` and streams it as JSON object properties. - using Deserializer = void (*)(ProfileBufferEntryReader&, JSONWriter&); - - // A `DeserializerTag` will be added before the payload, to help select the - // correct deserializer when reading back the payload. - using DeserializerTag = unsigned char; - - // Store a deserializer and get its `DeserializerTag`. - // This is intended to be only used once per deserializer, so it should be - // called to initialize a `static const` tag that will be re-used by all - // markers of the corresponding payload type. - MFBT_API static DeserializerTag TagForDeserializer( - Deserializer aDeserializer); - - // Get the `Deserializer` for a given `DeserializerTag`. - MFBT_API static Deserializer DeserializerForTag(DeserializerTag aTag); -}; - -// This helper will examine a marker type's `StreamJSONMarkerData` function, see -// specialization below. -template -struct StreamFunctionTypeHelper; - -// Helper specialization that takes the expected -// `StreamJSONMarkerData(JSONWriter&, ...)` function and provide information -// about the `...` parameters. -template -struct StreamFunctionTypeHelper { - constexpr static size_t scArity = sizeof...(As); - using TupleType = - std::tuple>...>; - - // Serialization function that takes the exact same parameter types - // (const-ref'd) as `StreamJSONMarkerData`. This has to be inside the helper - // because only here can we access the raw parameter pack `As...`. - // And because we're using the same argument types through - // references-to-const, permitted implicit conversions can happen. - static ProfileBufferBlockIndex Serialize( - ProfileChunkedBuffer& aBuffer, const ProfilerString8View& aName, - MarkerOptions&& aOptions, Streaming::DeserializerTag aDeserializerTag, - const As&... aAs) { - // Note that options are first after the entry kind, because they contain - // the thread id, which is handled first to filter markers by threads. - return aBuffer.PutObjects(ProfileBufferEntryKind::Marker, aOptions, aName, - aDeserializerTag, aAs...); - } -}; - -// Helper for a marker type. -// A marker type is defined in a `struct` with some expected static member -// functions. See example in BaseProfilerMarkers.h. -template -struct MarkerTypeSerialization { - // Definitions to access the expected `StreamJSONMarkerData(JSONWriter&, ...)` - // function and its parameters. - using StreamFunctionType = - StreamFunctionTypeHelper; - constexpr static size_t scStreamFunctionParameterCount = - StreamFunctionType::scArity; - using StreamFunctionUserParametersTuple = - typename StreamFunctionType::TupleType; - template - using StreamFunctionParameter = - std::tuple_element_t; - - template - static ProfileBufferBlockIndex Serialize(ProfileChunkedBuffer& aBuffer, - const ProfilerString8View& aName, - MarkerOptions&& aOptions, - const Ts&... aTs) { - static_assert(!std::is_same_v, - "NoPayload should have been handled in the caller."); - // Note that the tag is stored in a function-static object, and this - // function is static in a templated struct, so there should only be one tag - // per MarkerType. - // Making the tag class-static may have been more efficient (to avoid a - // thread-safe init check at every call), but random global static - // initialization order would make it more complex to coordinate with - // `Streaming::TagForDeserializer()`, and also would add a (small) cost for - // everybody, even the majority of users not using the profiler. - static const Streaming::DeserializerTag tag = - Streaming::TagForDeserializer(Deserialize); - return StreamFunctionType::Serialize(aBuffer, aName, std::move(aOptions), - tag, aTs...); - } - - private: - // This templated function will recursively deserialize each argument expected - // by `MarkerType::StreamJSONMarkerData()` on the stack, and call it at the - // end. E.g., for `StreamJSONMarkerData(int, char)`: - // - DeserializeArguments<0>(aER, aWriter) reads an int and calls: - // - DeserializeArguments<1>(aER, aWriter, const int&) reads a char and calls: - // - MarkerType::StreamJSONMarkerData(aWriter, const int&, const char&). - // Prototyping on godbolt showed that clang and gcc can flatten these - // recursive calls into one function with successive reads followed by the one - // stream call; tested up to 40 arguments: https://godbolt.org/z/5KeeM4 - template - static void DeserializeArguments(ProfileBufferEntryReader& aEntryReader, - JSONWriter& aWriter, const Args&... aArgs) { - static_assert(sizeof...(Args) == i, - "We should have collected `i` arguments so far"); - if constexpr (i < scStreamFunctionParameterCount) { - // Deserialize the i-th argument on this stack. - auto argument = aEntryReader.ReadObject>(); - // Add our local argument to the next recursive call. - DeserializeArguments(aEntryReader, aWriter, aArgs..., argument); - } else { - // We've read all the arguments, finally call the `StreamJSONMarkerData` - // function, which should write the appropriate JSON elements for this - // marker type. Note that the MarkerType-specific "type" element is - // already written. - MarkerType::StreamJSONMarkerData(aWriter, aArgs...); - } - } - - public: - static void Deserialize(ProfileBufferEntryReader& aEntryReader, - JSONWriter& aWriter) { - aWriter.StringProperty("type", MarkerType::MarkerTypeName()); - DeserializeArguments(aEntryReader, aWriter); - } -}; - -template <> -struct MarkerTypeSerialization<::mozilla::baseprofiler::markers::NoPayload> { - // Nothing! NoPayload has special handling avoiding payload work. -}; - -template -static ProfileBufferBlockIndex AddMarkerWithOptionalStackToBuffer( - ProfileChunkedBuffer& aBuffer, const ProfilerString8View& aName, - MarkerOptions&& aOptions, const Ts&... aTs) { - if constexpr (std::is_same_v) { - static_assert(sizeof...(Ts) == 0, - "NoPayload does not accept any payload arguments."); - // Note that options are first after the entry kind, because they contain - // the thread id, which is handled first to filter markers by threads. - return aBuffer.PutObjects( - ProfileBufferEntryKind::Marker, std::move(aOptions), aName, - base_profiler_markers_detail::Streaming::DeserializerTag(0)); - } else { - return MarkerTypeSerialization::Serialize( - aBuffer, aName, std::move(aOptions), aTs...); - } -} - -// Pointer to a function that can capture a backtrace into the provided -// `ProfileChunkedBuffer`, and returns true when successful. -using BacktraceCaptureFunction = bool (*)(ProfileChunkedBuffer&); - -// Add a marker with the given name, options, and arguments to the given buffer. -// Because this may be called from either Base or Gecko Profiler functions, the -// appropriate backtrace-capturing function must also be provided. -template -ProfileBufferBlockIndex AddMarkerToBuffer( - ProfileChunkedBuffer& aBuffer, const ProfilerString8View& aName, - MarkerOptions&& aOptions, - BacktraceCaptureFunction aBacktraceCaptureFunction, const Ts&... aTs) { - if (aOptions.ThreadId().IsUnspecified()) { - // If yet unspecified, set thread to this thread where the marker is added. - aOptions.Set(MarkerThreadId::CurrentThread()); - } - - if (aOptions.IsTimingUnspecified()) { - // If yet unspecified, set timing to this instant of adding the marker. - aOptions.Set(MarkerTiming::InstantNow()); - } - - if (aOptions.Stack().IsCaptureNeeded()) { - // A capture was requested, let's attempt to do it here&now. This avoids a - // lot of allocations that would be necessary if capturing a backtrace - // separately. - // TODO use a local on-stack byte buffer to remove last allocation. - // TODO reduce internal profiler stack levels, see bug 1659872. - ProfileBufferChunkManagerSingle chunkManager(64 * 1024); - ProfileChunkedBuffer chunkedBuffer( - ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager); - aOptions.Stack().UseRequestedBacktrace( - aBacktraceCaptureFunction(chunkedBuffer) ? &chunkedBuffer : nullptr); - // This call must be made from here, while chunkedBuffer is in scope. - return AddMarkerWithOptionalStackToBuffer( - aBuffer, aName, std::move(aOptions), aTs...); - } - - return AddMarkerWithOptionalStackToBuffer( - aBuffer, aName, std::move(aOptions), aTs...); -} - -template -[[nodiscard]] bool DeserializeAfterKindAndStream( - ProfileBufferEntryReader& aEntryReader, JSONWriter& aWriter, - int aThreadIdOrZero, NameCallback&& aNameCallback, - StackCallback&& aStackCallback) { - // Each entry is made up of the following: - // ProfileBufferEntry::Kind::Marker, <- already read by caller - // options, <- next location in entries - // name, - // payload - const MarkerOptions options = aEntryReader.ReadObject(); - if (aThreadIdOrZero != 0 && - options.ThreadId().ThreadId() != aThreadIdOrZero) { - // A specific thread is being read, we're not in it. - return false; - } - // Write the information to JSON with the following schema: - // [name, startTime, endTime, phase, category, data] - aWriter.StartArrayElement(); - { - std::forward(aNameCallback)( - aEntryReader.ReadObject()); - - const double startTime = options.Timing().GetStartTime(); - aWriter.DoubleElement(startTime); - - const double endTime = options.Timing().GetEndTime(); - aWriter.DoubleElement(endTime); - - aWriter.IntElement(static_cast(options.Timing().MarkerPhase())); - - aWriter.IntElement(static_cast(options.Category().Category())); - - if (const auto tag = - aEntryReader.ReadObject(); - tag != 0) { - aWriter.StartObjectElement(JSONWriter::SingleLineStyle); - { - // Stream "common props". - - // TODO: Move this to top-level tuple, when frontend supports it. - if (!options.InnerWindowId().IsUnspecified()) { - // Here, we are converting uint64_t to double. Both Browsing Context - // and Inner Window IDs are created using - // `nsContentUtils::GenerateProcessSpecificId`, which is specifically - // designed to only use 53 of the 64 bits to be lossless when passed - // into and out of JS as a double. - aWriter.DoubleProperty( - "innerWindowID", - static_cast(options.InnerWindowId().Id())); - } - - // TODO: Move this to top-level tuple, when frontend supports it. - if (ProfileChunkedBuffer* chunkedBuffer = - options.Stack().GetChunkedBuffer(); - chunkedBuffer) { - aWriter.StartObjectProperty("stack"); - { std::forward(aStackCallback)(*chunkedBuffer); } - aWriter.EndObject(); - } - - // Stream the payload, including the type. - mozilla::base_profiler_markers_detail::Streaming::Deserializer - deserializer = mozilla::base_profiler_markers_detail::Streaming:: - DeserializerForTag(tag); - MOZ_RELEASE_ASSERT(deserializer); - deserializer(aEntryReader, aWriter); - } - aWriter.EndObject(); - } - } - aWriter.EndArray(); - return true; -} - -} // namespace mozilla::base_profiler_markers_detail - -namespace mozilla { - -// ---------------------------------------------------------------------------- -// Serializer, Deserializer: ProfilerStringView - -// The serialization starts with a ULEB128 number that encodes both whether the -// ProfilerStringView is literal (Least Significant Bit = 0) or not (LSB = 1), -// plus the string length (excluding null terminator) in bytes, shifted left by -// 1 bit. Following that number: -// - If literal, the string pointer value. -// - If non-literal, the contents as bytes (excluding null terminator if any). -template -struct ProfileBufferEntryWriter::Serializer> { - static Length Bytes(const ProfilerStringView& aString) { - MOZ_RELEASE_ASSERT( - aString.Length() < std::numeric_limits::max() / 2, - "Double the string length doesn't fit in Length type"); - const Length stringLength = static_cast(aString.Length()); - if (aString.IsLiteral()) { - // Literal -> Length shifted left and LSB=0, then pointer. - return ULEB128Size(stringLength << 1 | 0u) + - static_cast(sizeof(const CHAR*)); - } - // Non-literal -> Length shifted left and LSB=1, then string size in bytes. - return ULEB128Size((stringLength << 1) | 1u) + stringLength * sizeof(CHAR); - } - - static void Write(ProfileBufferEntryWriter& aEW, - const ProfilerStringView& aString) { - MOZ_RELEASE_ASSERT( - aString.Length() < std::numeric_limits::max() / 2, - "Double the string length doesn't fit in Length type"); - const Length stringLength = static_cast(aString.Length()); - if (aString.IsLiteral()) { - // Literal -> Length shifted left and LSB=0, then pointer. - aEW.WriteULEB128(stringLength << 1 | 0u); - aEW.WriteObject(WrapProfileBufferRawPointer(aString.Data())); - return; - } - // Non-literal -> Length shifted left and LSB=1, then string size in bytes. - aEW.WriteULEB128(stringLength << 1 | 1u); - aEW.WriteBytes(aString.Data(), stringLength * sizeof(CHAR)); - } -}; - -template -struct ProfileBufferEntryReader::Deserializer> { - static void ReadInto(ProfileBufferEntryReader& aER, - ProfilerStringView& aString) { - const Length lengthAndIsLiteral = aER.ReadULEB128(); - const Length stringLength = lengthAndIsLiteral >> 1; - if ((lengthAndIsLiteral & 1u) == 0u) { - // LSB==0 -> Literal string, read the string pointer. - aString.mStringView = std::basic_string_view( - aER.ReadObject(), stringLength); - aString.mOwnership = ProfilerStringView::Ownership::Literal; - return; - } - // LSB==1 -> Not a literal string, allocate a buffer to store the string - // (plus terminal, for safety), and give it to the ProfilerStringView; Note - // that this is a secret use of ProfilerStringView, which is intended to - // only be used between deserialization and JSON streaming. - CHAR* buffer = new CHAR[stringLength + 1]; - aER.ReadBytes(buffer, stringLength * sizeof(CHAR)); - buffer[stringLength] = CHAR(0); - aString.mStringView = std::basic_string_view(buffer, stringLength); - aString.mOwnership = - ProfilerStringView::Ownership::OwnedThroughStringView; - } - - static ProfilerStringView Read(ProfileBufferEntryReader& aER) { - const Length lengthAndIsLiteral = aER.ReadULEB128(); - const Length stringLength = lengthAndIsLiteral >> 1; - if ((lengthAndIsLiteral & 1u) == 0u) { - // LSB==0 -> Literal string, read the string pointer. - return ProfilerStringView( - aER.ReadObject(), stringLength, - ProfilerStringView::Ownership::Literal); - } - // LSB==1 -> Not a literal string, allocate a buffer to store the string - // (plus terminal, for safety), and give it to the ProfilerStringView; Note - // that this is a secret use of ProfilerStringView, which is intended to - // only be used between deserialization and JSON streaming. - CHAR* buffer = new CHAR[stringLength + 1]; - aER.ReadBytes(buffer, stringLength * sizeof(CHAR)); - buffer[stringLength] = CHAR(0); - return ProfilerStringView( - buffer, stringLength, - ProfilerStringView::Ownership::OwnedThroughStringView); - } -}; - -// Serializer, Deserializer: MarkerCategory - -// The serialization contains both category numbers encoded as ULEB128. -template <> -struct ProfileBufferEntryWriter::Serializer { - static Length Bytes(const MarkerCategory& aCategory) { - return ULEB128Size(static_cast(aCategory.CategoryPair())) + - ULEB128Size(static_cast(aCategory.Category())); - } - - static void Write(ProfileBufferEntryWriter& aEW, - const MarkerCategory& aCategory) { - aEW.WriteULEB128(static_cast(aCategory.CategoryPair())); - aEW.WriteULEB128(static_cast(aCategory.Category())); - } -}; - -template <> -struct ProfileBufferEntryReader::Deserializer { - static void ReadInto(ProfileBufferEntryReader& aER, - MarkerCategory& aCategory) { - aCategory.mCategoryPair = static_cast( - aER.ReadULEB128()); - aCategory.mCategory = static_cast( - aER.ReadULEB128()); - } - - static MarkerCategory Read(ProfileBufferEntryReader& aER) { - return MarkerCategory(static_cast( - aER.ReadULEB128()), - static_cast( - aER.ReadULEB128())); - } -}; - -// ---------------------------------------------------------------------------- -// Serializer, Deserializer: MarkerTiming - -// The serialization starts with the marker phase, followed by one or two -// timestamps as needed. -template <> -struct ProfileBufferEntryWriter::Serializer { - static Length Bytes(const MarkerTiming& aTiming) { - MOZ_ASSERT(!aTiming.IsUnspecified()); - const auto phase = aTiming.MarkerPhase(); - switch (phase) { - case MarkerTiming::Phase::Instant: - return SumBytes(phase, aTiming.StartTime()); - case MarkerTiming::Phase::Interval: - return SumBytes(phase, aTiming.StartTime(), aTiming.EndTime()); - case MarkerTiming::Phase::IntervalStart: - return SumBytes(phase, aTiming.StartTime()); - case MarkerTiming::Phase::IntervalEnd: - return SumBytes(phase, aTiming.EndTime()); - default: - MOZ_RELEASE_ASSERT(phase == MarkerTiming::Phase::Instant || - phase == MarkerTiming::Phase::Interval || - phase == MarkerTiming::Phase::IntervalStart || - phase == MarkerTiming::Phase::IntervalEnd); - return 0; // Only to avoid build errors. - } - } - - static void Write(ProfileBufferEntryWriter& aEW, - const MarkerTiming& aTiming) { - MOZ_ASSERT(!aTiming.IsUnspecified()); - const auto phase = aTiming.MarkerPhase(); - switch (phase) { - case MarkerTiming::Phase::Instant: - aEW.WriteObjects(phase, aTiming.StartTime()); - return; - case MarkerTiming::Phase::Interval: - aEW.WriteObjects(phase, aTiming.StartTime(), aTiming.EndTime()); - return; - case MarkerTiming::Phase::IntervalStart: - aEW.WriteObjects(phase, aTiming.StartTime()); - return; - case MarkerTiming::Phase::IntervalEnd: - aEW.WriteObjects(phase, aTiming.EndTime()); - return; - default: - MOZ_RELEASE_ASSERT(phase == MarkerTiming::Phase::Instant || - phase == MarkerTiming::Phase::Interval || - phase == MarkerTiming::Phase::IntervalStart || - phase == MarkerTiming::Phase::IntervalEnd); - return; - } - } -}; - -template <> -struct ProfileBufferEntryReader::Deserializer { - static void ReadInto(ProfileBufferEntryReader& aER, MarkerTiming& aTiming) { - aTiming.mPhase = aER.ReadObject(); - switch (aTiming.mPhase) { - case MarkerTiming::Phase::Instant: - aTiming.mStartTime = aER.ReadObject(); - aTiming.mEndTime = TimeStamp{}; - break; - case MarkerTiming::Phase::Interval: - aTiming.mStartTime = aER.ReadObject(); - aTiming.mEndTime = aER.ReadObject(); - break; - case MarkerTiming::Phase::IntervalStart: - aTiming.mStartTime = aER.ReadObject(); - aTiming.mEndTime = TimeStamp{}; - break; - case MarkerTiming::Phase::IntervalEnd: - aTiming.mStartTime = TimeStamp{}; - aTiming.mEndTime = aER.ReadObject(); - break; - default: - MOZ_RELEASE_ASSERT(aTiming.mPhase == MarkerTiming::Phase::Instant || - aTiming.mPhase == MarkerTiming::Phase::Interval || - aTiming.mPhase == - MarkerTiming::Phase::IntervalStart || - aTiming.mPhase == MarkerTiming::Phase::IntervalEnd); - break; - } - } - - static MarkerTiming Read(ProfileBufferEntryReader& aER) { - TimeStamp start; - TimeStamp end; - auto phase = aER.ReadObject(); - switch (phase) { - case MarkerTiming::Phase::Instant: - start = aER.ReadObject(); - break; - case MarkerTiming::Phase::Interval: - start = aER.ReadObject(); - end = aER.ReadObject(); - break; - case MarkerTiming::Phase::IntervalStart: - start = aER.ReadObject(); - break; - case MarkerTiming::Phase::IntervalEnd: - end = aER.ReadObject(); - break; - default: - MOZ_RELEASE_ASSERT(phase == MarkerTiming::Phase::Instant || - phase == MarkerTiming::Phase::Interval || - phase == MarkerTiming::Phase::IntervalStart || - phase == MarkerTiming::Phase::IntervalEnd); - break; - } - return MarkerTiming(start, end, phase); - } -}; - -// ---------------------------------------------------------------------------- -// Serializer, Deserializer: MarkerStack - -// The serialization only contains the `ProfileChunkedBuffer` from the -// backtrace; if there is no backtrace or if it's empty, this will implicitly -// store a nullptr (see -// `ProfileBufferEntryWriter::Serializer`). -template <> -struct ProfileBufferEntryWriter::Serializer { - static Length Bytes(const MarkerStack& aStack) { - return SumBytes(aStack.GetChunkedBuffer()); - } - - static void Write(ProfileBufferEntryWriter& aEW, const MarkerStack& aStack) { - aEW.WriteObject(aStack.GetChunkedBuffer()); - } -}; - -template <> -struct ProfileBufferEntryReader::Deserializer { - static void ReadInto(ProfileBufferEntryReader& aER, MarkerStack& aStack) { - aStack = Read(aER); - } - - static MarkerStack Read(ProfileBufferEntryReader& aER) { - return MarkerStack(aER.ReadObject>()); - } -}; - -// ---------------------------------------------------------------------------- -// Serializer, Deserializer: MarkerOptions - -// The serialization contains all members (either trivially-copyable, or they -// provide their specialization above). -template <> -struct ProfileBufferEntryWriter::Serializer { - static Length Bytes(const MarkerOptions& aOptions) { - return SumBytes(aOptions.Category(), aOptions.ThreadId(), aOptions.Timing(), - aOptions.Stack(), aOptions.InnerWindowId()); - } - - static void Write(ProfileBufferEntryWriter& aEW, - const MarkerOptions& aOptions) { - aEW.WriteObjects(aOptions.Category(), aOptions.ThreadId(), - aOptions.Timing(), aOptions.Stack(), - aOptions.InnerWindowId()); - } -}; - -template <> -struct ProfileBufferEntryReader::Deserializer { - static void ReadInto(ProfileBufferEntryReader& aER, MarkerOptions& aOptions) { - aER.ReadIntoObjects(aOptions.mCategory, aOptions.mThreadId, - aOptions.mTiming, aOptions.mStack, - aOptions.mInnerWindowId); - } - - static MarkerOptions Read(ProfileBufferEntryReader& aER) { - MarkerOptions options; - ReadInto(aER, options); - return options; - } -}; - -} // namespace mozilla - -#endif // MOZ_GECKO_PROFILER - -#endif // BaseProfilerMarkersDetail_h diff --git a/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h b/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h deleted file mode 100644 index 3b711a3e1c33..000000000000 --- a/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h +++ /dev/null @@ -1,646 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// This header contains basic definitions required to create marker types, and -// to add markers to the profiler buffers. -// -// In most cases, #include "mozilla/BaseProfilerMarkers.h" instead, or -// #include "mozilla/BaseProfilerMarkerTypes.h" for common marker types. - -#ifndef BaseProfilerMarkersPrerequisites_h -#define BaseProfilerMarkersPrerequisites_h - -#ifdef MOZ_GECKO_PROFILER - -# include "BaseProfilingCategory.h" -# include "mozilla/ProfileChunkedBuffer.h" -# include "mozilla/TimeStamp.h" -# include "mozilla/UniquePtr.h" - -# include -# include -# include -# include - -namespace mozilla::baseprofiler { -// Implemented in platform.cpp -MFBT_API int profiler_current_thread_id(); -} // namespace mozilla::baseprofiler - -namespace mozilla { - -// Return a NotNull pointing at the literal empty string `""`. -template -constexpr const CHAR* LiteralEmptyStringPointer() { - static_assert(std::is_same_v || std::is_same_v, - "Only char and char16_t are supported in Firefox"); - if constexpr (std::is_same_v) { - return ""; - } - if constexpr (std::is_same_v) { - return u""; - } -} - -// Return a string_view pointing at the literal empty string. -template -constexpr std::basic_string_view LiteralEmptyStringView() { - static_assert(std::is_same_v || std::is_same_v, - "Only char and char16_t are supported in Firefox"); - // Use `operator""sv()` from . - using namespace std::literals::string_view_literals; - if constexpr (std::is_same_v) { - return ""sv; - } - if constexpr (std::is_same_v) { - return u""sv; - } -} - -// General string view, optimized for short on-stack life before serialization, -// and between deserialization and JSON-streaming. -template -class MOZ_STACK_CLASS ProfilerStringView { - public: - // Default constructor points at "" (literal empty string). - constexpr ProfilerStringView() = default; - - // Don't allow copy. - ProfilerStringView(const ProfilerStringView&) = delete; - ProfilerStringView& operator=(const ProfilerStringView&) = delete; - - // Allow move. For consistency the moved-from string is always reset to "". - constexpr ProfilerStringView(ProfilerStringView&& aOther) - : mStringView(std::move(aOther.mStringView)), - mOwnership(aOther.mOwnership) { - if (mOwnership == Ownership::OwnedThroughStringView) { - // We now own the buffer, make the other point at the literal "". - aOther.mStringView = LiteralEmptyStringView(); - aOther.mOwnership = Ownership::Literal; - } - } - constexpr ProfilerStringView& operator=(ProfilerStringView&& aOther) { - mStringView = std::move(aOther.mStringView); - mOwnership = aOther.mOwnership; - if (mOwnership == Ownership::OwnedThroughStringView) { - // We now own the buffer, make the other point at the literal "". - aOther.mStringView = LiteralEmptyStringView(); - aOther.mOwnership = Ownership::Literal; - } - return *this; - } - - ~ProfilerStringView() { - if (MOZ_UNLIKELY(mOwnership == Ownership::OwnedThroughStringView)) { - // We own the buffer pointed at by mStringView, destroy it. - // This is only used between deserialization and streaming. - delete mStringView.data(); - } - } - - // Implicit construction from nullptr, points at "" (literal empty string). - constexpr MOZ_IMPLICIT ProfilerStringView(decltype(nullptr)) {} - - // Implicit constructor from a literal string. - template - constexpr MOZ_IMPLICIT ProfilerStringView(const CHAR (&aLiteralString)[Np1]) - : ProfilerStringView(aLiteralString, Np1 - 1, Ownership::Literal) {} - - // Constructor from a non-literal string. - constexpr ProfilerStringView(const CHAR* aString, size_t aLength) - : ProfilerStringView(aString, aLength, Ownership::Reference) {} - - // Implicit constructor from a string_view. - constexpr MOZ_IMPLICIT ProfilerStringView( - const std::basic_string_view& aStringView) - : ProfilerStringView(aStringView.data(), aStringView.length(), - Ownership::Reference) {} - - // Implicit constructor from an expiring string_view. We assume that the - // pointed-at string will outlive this ProfilerStringView. - constexpr MOZ_IMPLICIT ProfilerStringView( - std::basic_string_view&& aStringView) - : ProfilerStringView(aStringView.data(), aStringView.length(), - Ownership::Reference) {} - - // Implicit constructor from std::string. - constexpr MOZ_IMPLICIT ProfilerStringView(const std::string& aString) - : ProfilerStringView(aString.data(), aString.length(), - Ownership::Reference) {} - - // Construction from a raw pointer to a null-terminated string. - // This is a named class-static function to make it more obvious where work is - // being done (to determine the string length), and encourage users to instead - // provide a length, if already known. - // TODO: Find callers and convert them to constructor instead if possible. - static constexpr ProfilerStringView WrapNullTerminatedString( - const CHAR* aString) { - return ProfilerStringView( - aString, aString ? std::char_traits::length(aString) : 0, - Ownership::Reference); - } - - // Implicit constructor for an object with member functions `Data()` - // `Length()`, and `IsLiteral()`, common in xpcom strings. - template < - typename String, - typename DataReturnType = decltype(std::declval().Data()), - typename LengthReturnType = - decltype(std::declval().Length()), - typename IsLiteralReturnType = - decltype(std::declval().IsLiteral()), - typename = - std::enable_if_t && - std::is_integral_v && - std::is_same_v>> - constexpr MOZ_IMPLICIT ProfilerStringView(const String& aString) - : ProfilerStringView( - static_cast(aString.Data()), aString.Length(), - aString.IsLiteral() ? Ownership::Literal : Ownership::Reference) {} - - [[nodiscard]] constexpr const std::basic_string_view& StringView() - const { - return mStringView; - } - - [[nodiscard]] constexpr const CHAR* Data() const { - return mStringView.data(); - } - - [[nodiscard]] constexpr size_t Length() const { return mStringView.length(); } - - [[nodiscard]] constexpr bool IsLiteral() const { - return mOwnership == Ownership::Literal; - } - [[nodiscard]] constexpr bool IsReference() const { - return mOwnership == Ownership::Reference; - } - // No `IsOwned...()` because it's a secret, only used internally! - - [[nodiscard]] std::basic_string String() const { - return std::basic_string(mStringView); - } - - private: - enum class Ownership { Literal, Reference, OwnedThroughStringView }; - - // Allow deserializer to store anything here. - friend ProfileBufferEntryReader::Deserializer; - - constexpr ProfilerStringView(const CHAR* aString, size_t aLength, - Ownership aOwnership) - : mStringView(aString ? std::basic_string_view(aString, aLength) - : LiteralEmptyStringView()), - mOwnership(aString ? aOwnership : Ownership::Literal) {} - - // String view to an outside string (literal or reference). - // We may actually own the pointed-at buffer, but it is only used internally - // between deserialization and JSON streaming. - std::basic_string_view mStringView = LiteralEmptyStringView(); - - Ownership mOwnership = Ownership::Literal; -}; - -using ProfilerString8View = ProfilerStringView; -using ProfilerString16View = ProfilerStringView; - -// The classes below are all embedded in a `MarkerOptions` object. -class MarkerOptions; - -// This compulsory marker option contains the required category information. -class MarkerCategory { - public: - // Constructor from category pair (aka sub-category) and category. - constexpr MarkerCategory(baseprofiler::ProfilingCategoryPair aCategoryPair, - baseprofiler::ProfilingCategory aCategory) - : mCategoryPair(aCategoryPair), mCategory(aCategory) {} - - constexpr baseprofiler::ProfilingCategoryPair CategoryPair() const { - return mCategoryPair; - } - - constexpr baseprofiler::ProfilingCategory Category() const { - return mCategory; - } - - // Create a MarkerOptions object from this category and options. - // Definition under MarkerOptions below. - template - MarkerOptions WithOptions(Options&&... aOptions) const; - - private: - // The default constructor is only used during deserialization of - // MarkerOptions. - friend MarkerOptions; - constexpr MarkerCategory() = default; - - friend ProfileBufferEntryReader::Deserializer; - - baseprofiler::ProfilingCategoryPair mCategoryPair = - baseprofiler::ProfilingCategoryPair::OTHER; - baseprofiler::ProfilingCategory mCategory = - baseprofiler::ProfilingCategory::OTHER; -}; - -namespace baseprofiler::category { -// Each category-pair (aka subcategory) name constructs a MarkerCategory. -// E.g.: mozilla::baseprofiler::category::OTHER_Profiling -// Profiler macros will take the category name alone. -// E.g.: `PROFILER_MARKER_UNTYPED("name", OTHER_Profiling)` -# define CATEGORY_ENUM_BEGIN_CATEGORY(name, labelAsString, color) -# define CATEGORY_ENUM_SUBCATEGORY(supercategory, name, labelAsString) \ - static constexpr MarkerCategory name{ProfilingCategoryPair::name, \ - ProfilingCategory::supercategory}; -# define CATEGORY_ENUM_END_CATEGORY -MOZ_PROFILING_CATEGORY_LIST(CATEGORY_ENUM_BEGIN_CATEGORY, - CATEGORY_ENUM_SUBCATEGORY, - CATEGORY_ENUM_END_CATEGORY) -# undef CATEGORY_ENUM_BEGIN_CATEGORY -# undef CATEGORY_ENUM_SUBCATEGORY -# undef CATEGORY_ENUM_END_CATEGORY -} // namespace baseprofiler::category - -// This marker option captures a given thread id. -// If left unspecified (by default construction) during the add-marker call, the -// current thread id will be used then. -class MarkerThreadId { - public: - // Default constructor, keeps the thread id unspecified. - constexpr MarkerThreadId() = default; - - // Constructor from a given thread id. - constexpr explicit MarkerThreadId(int aThreadId) : mThreadId(aThreadId) {} - - // Use the current thread's id. - static MarkerThreadId CurrentThread() { - return MarkerThreadId(baseprofiler::profiler_current_thread_id()); - } - - [[nodiscard]] constexpr int ThreadId() const { return mThreadId; } - - [[nodiscard]] constexpr bool IsUnspecified() const { return mThreadId == 0; } - - private: - int mThreadId = 0; -}; - -// This marker option contains marker timing information. -// This class encapsulates the logic for correctly storing a marker based on its -// Use the static methods to create the MarkerTiming. This is a transient object -// that is being used to enforce the constraints of the combinations of the -// data. -class MarkerTiming { - public: - // The following static methods are used to create the MarkerTiming based on - // the type that it is. - - static MarkerTiming InstantAt(const TimeStamp& aTime) { - MOZ_ASSERT(!aTime.IsNull(), "Time is null for an instant marker."); - return MarkerTiming{aTime, TimeStamp{}, MarkerTiming::Phase::Instant}; - } - - static MarkerTiming InstantNow() { - return InstantAt(TimeStamp::NowUnfuzzed()); - } - - static MarkerTiming Interval(const TimeStamp& aStartTime, - const TimeStamp& aEndTime) { - MOZ_ASSERT(!aStartTime.IsNull(), - "Start time is null for an interval marker."); - MOZ_ASSERT(!aEndTime.IsNull(), "End time is null for an interval marker."); - return MarkerTiming{aStartTime, aEndTime, MarkerTiming::Phase::Interval}; - } - - static MarkerTiming IntervalUntilNowFrom(const TimeStamp& aStartTime) { - return Interval(aStartTime, TimeStamp::NowUnfuzzed()); - } - - static MarkerTiming IntervalStart( - const TimeStamp& aTime = TimeStamp::NowUnfuzzed()) { - MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval start marker."); - return MarkerTiming{aTime, TimeStamp{}, MarkerTiming::Phase::IntervalStart}; - } - - static MarkerTiming IntervalEnd( - const TimeStamp& aTime = TimeStamp::NowUnfuzzed()) { - MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval end marker."); - return MarkerTiming{TimeStamp{}, aTime, MarkerTiming::Phase::IntervalEnd}; - } - - [[nodiscard]] const TimeStamp& StartTime() const { return mStartTime; } - [[nodiscard]] const TimeStamp& EndTime() const { return mEndTime; } - - enum class Phase : uint8_t { - Instant = 0, - Interval = 1, - IntervalStart = 2, - IntervalEnd = 3, - }; - - [[nodiscard]] Phase MarkerPhase() const { - MOZ_ASSERT(!IsUnspecified()); - return mPhase; - } - - // The following getter methods are used to put the value into the buffer for - // storage. - [[nodiscard]] double GetStartTime() const { - MOZ_ASSERT(!IsUnspecified()); - // If mStartTime is null (e.g., for IntervalEnd), this will output 0.0 as - // expected. - return MarkerTiming::timeStampToDouble(mStartTime); - } - - [[nodiscard]] double GetEndTime() const { - MOZ_ASSERT(!IsUnspecified()); - // If mEndTime is null (e.g., for Instant or IntervalStart), this will - // output 0.0 as expected. - return MarkerTiming::timeStampToDouble(mEndTime); - } - - [[nodiscard]] uint8_t GetPhase() const { - MOZ_ASSERT(!IsUnspecified()); - return static_cast(mPhase); - } - - private: - friend ProfileBufferEntryWriter::Serializer; - friend ProfileBufferEntryReader::Deserializer; - friend MarkerOptions; - - // Default timing leaves it internally "unspecified", serialization getters - // and add-marker functions will default to `InstantNow()`. - constexpr MarkerTiming() = default; - - // This should only be used by internal profiler code. - [[nodiscard]] bool IsUnspecified() const { - return mStartTime.IsNull() && mEndTime.IsNull(); - } - - // Full constructor, used by static factory functions. - constexpr MarkerTiming(const TimeStamp& aStartTime, const TimeStamp& aEndTime, - Phase aPhase) - : mStartTime(aStartTime), mEndTime(aEndTime), mPhase(aPhase) {} - - static double timeStampToDouble(const TimeStamp& time) { - if (time.IsNull()) { - // The Phase lets us know not to use this value. - return 0; - } - return (time - TimeStamp::ProcessCreation()).ToMilliseconds(); - } - - TimeStamp mStartTime; - TimeStamp mEndTime; - Phase mPhase = Phase::Instant; -}; - -// This marker option allows three cases: -// - By default, no stacks are captured. -// - The caller can request a stack capture, and the add-marker code will take -// care of it in the most efficient way. -// - The caller can still provide an existing backtrace, for cases where a -// marker reports something that happened elsewhere. -class MarkerStack { - public: - // Default constructor, no capture. - constexpr MarkerStack() = default; - - // Disallow copy. - MarkerStack(const MarkerStack&) = delete; - MarkerStack& operator=(const MarkerStack&) = delete; - - // Allow move. - MarkerStack(MarkerStack&& aOther) - : mIsCaptureRequested(aOther.mIsCaptureRequested), - mOptionalChunkedBufferStorage( - std::move(aOther.mOptionalChunkedBufferStorage)), - mChunkedBuffer(aOther.mChunkedBuffer) { - AssertInvariants(); - aOther.Clear(); - } - MarkerStack& operator=(MarkerStack&& aOther) { - mIsCaptureRequested = aOther.mIsCaptureRequested; - mOptionalChunkedBufferStorage = - std::move(aOther.mOptionalChunkedBufferStorage); - mChunkedBuffer = aOther.mChunkedBuffer; - AssertInvariants(); - aOther.Clear(); - return *this; - } - - // Take ownership of a backtrace. If null or empty, equivalent to NoStack(). - explicit MarkerStack(UniquePtr&& aExternalChunkedBuffer) - : mIsCaptureRequested(false), - mOptionalChunkedBufferStorage( - (!aExternalChunkedBuffer || aExternalChunkedBuffer->IsEmpty()) - ? nullptr - : std::move(aExternalChunkedBuffer)), - mChunkedBuffer(mOptionalChunkedBufferStorage.get()) { - AssertInvariants(); - } - - // Use an existing backtrace stored elsewhere, which the user must guarantee - // is alive during the add-marker call. If empty, equivalent to NoStack(). - explicit MarkerStack(ProfileChunkedBuffer& aExternalChunkedBuffer) - : mIsCaptureRequested(false), - mChunkedBuffer(aExternalChunkedBuffer.IsEmpty() - ? nullptr - : &aExternalChunkedBuffer) { - AssertInvariants(); - } - - // Don't capture a stack in this marker. - static MarkerStack NoStack() { return MarkerStack(false); } - - // Capture a stack when adding this marker. - static MarkerStack Capture() { - // Actual capture will be handled inside profiler_add_marker. - return MarkerStack(true); - } - - // Use an existing backtrace stored elsewhere, which the user must guarantee - // is alive during the add-marker call. If empty, equivalent to NoStack(). - static MarkerStack UseBacktrace( - ProfileChunkedBuffer& aExternalChunkedBuffer) { - return MarkerStack(aExternalChunkedBuffer); - } - - // Take ownership of a backtrace previously captured with - // `profiler_capture_backtrace()`. If null, equivalent to NoStack(). - static MarkerStack TakeBacktrace( - UniquePtr&& aExternalChunkedBuffer) { - return MarkerStack(std::move(aExternalChunkedBuffer)); - } - - [[nodiscard]] bool IsCaptureNeeded() const { - // If the chunked buffer already contains something, consider the capture - // request already fulfilled. - return mIsCaptureRequested; - } - - ProfileChunkedBuffer* GetChunkedBuffer() const { return mChunkedBuffer; } - - // Use backtrace after a request. If null, equivalent to NoStack(). - void UseRequestedBacktrace(ProfileChunkedBuffer* aExternalChunkedBuffer) { - MOZ_RELEASE_ASSERT(IsCaptureNeeded()); - mIsCaptureRequested = false; - if (aExternalChunkedBuffer && !aExternalChunkedBuffer->IsEmpty()) { - // We only need to use the provided buffer if it is not empty. - mChunkedBuffer = aExternalChunkedBuffer; - } - AssertInvariants(); - } - - void Clear() { - mIsCaptureRequested = false; - mOptionalChunkedBufferStorage.reset(); - mChunkedBuffer = nullptr; - AssertInvariants(); - } - - private: - explicit MarkerStack(bool aIsCaptureRequested) - : mIsCaptureRequested(aIsCaptureRequested) { - AssertInvariants(); - } - - // This should be called after every constructor and non-const function. - void AssertInvariants() const { -# ifdef DEBUG - if (mIsCaptureRequested) { - MOZ_ASSERT(!mOptionalChunkedBufferStorage, - "We should not hold a buffer when capture is requested"); - MOZ_ASSERT(!mChunkedBuffer, - "We should not point at a buffer when capture is requested"); - } else { - if (mOptionalChunkedBufferStorage) { - MOZ_ASSERT(mChunkedBuffer == mOptionalChunkedBufferStorage.get(), - "Non-null mOptionalChunkedBufferStorage must be pointed-at " - "by mChunkedBuffer"); - } - if (mChunkedBuffer) { - MOZ_ASSERT(!mChunkedBuffer->IsEmpty(), - "Non-null mChunkedBuffer must not be empty"); - } - } -# endif // DEBUG - } - - // True if a capture is requested when marker is added to the profile buffer. - bool mIsCaptureRequested = false; - - // Optional storage for the backtrace, in case it was captured before the - // add-marker call. - UniquePtr mOptionalChunkedBufferStorage; - - // If not null, this points to the backtrace. It may point to a backtrace - // temporarily stored on the stack, or to mOptionalChunkedBufferStorage. - ProfileChunkedBuffer* mChunkedBuffer = nullptr; -}; - -// This marker option captures a given inner window id. -class MarkerInnerWindowId { - public: - // Default constructor, it leaves the id unspecified. - constexpr MarkerInnerWindowId() = default; - - // Constructor with a specified inner window id. - constexpr explicit MarkerInnerWindowId(uint64_t i) : mInnerWindowId(i) {} - - // Explicit option with unspecified id. - constexpr static MarkerInnerWindowId NoId() { return MarkerInnerWindowId{}; } - - [[nodiscard]] bool IsUnspecified() const { return mInnerWindowId == scNoId; } - - [[nodiscard]] constexpr uint64_t Id() const { return mInnerWindowId; } - - private: - static constexpr uint64_t scNoId = 0; - uint64_t mInnerWindowId = scNoId; -}; - -// This class combines a compulsory category with the above marker options. -// To provide options to add-marker functions, first pick a MarkerCategory -// object, then options may be added with WithOptions(), e.g.: -// `mozilla::baseprofiler::category::OTHER_profiling` -// `mozilla::baseprofiler::category::DOM.WithOptions( -// MarkerThreadId(1), MarkerTiming::IntervalStart())` -class MarkerOptions { - public: - // Implicit constructor from category. - constexpr MOZ_IMPLICIT MarkerOptions(const MarkerCategory& aCategory) - : mCategory(aCategory) {} - - // Constructor from category and other options. - template - explicit MarkerOptions(const MarkerCategory& aCategory, Options&&... aOptions) - : mCategory(aCategory) { - (Set(std::forward(aOptions)), ...); - } - - // Disallow copy. - MarkerOptions(const MarkerOptions&) = delete; - MarkerOptions& operator=(const MarkerOptions&) = delete; - - // Allow move. - MarkerOptions(MarkerOptions&&) = default; - MarkerOptions& operator=(MarkerOptions&&) = default; - - // The embedded `MarkerTiming` hasn't been specified yet. - [[nodiscard]] bool IsTimingUnspecified() const { - return mTiming.IsUnspecified(); - } - - // Each options may be added in a chain by e.g.: `Set(MarkerThreadId(123))`. - // Read them with the option name (without "Marker"), e.g.: `ThreadId()`. -# define FUNCTION_ON_MEMBER(NAME) \ - MarkerOptions&& Set(Marker##NAME&& a##NAME) { \ - m##NAME = std::move(a##NAME); \ - return std::move(*this); \ - } \ - \ - const Marker##NAME& NAME() const { return m##NAME; } \ - Marker##NAME& NAME() { return m##NAME; } - - FUNCTION_ON_MEMBER(Category); - FUNCTION_ON_MEMBER(ThreadId); - FUNCTION_ON_MEMBER(Timing); - FUNCTION_ON_MEMBER(Stack); - FUNCTION_ON_MEMBER(InnerWindowId); -# undef FUNCTION_ON_MEMBER - - private: - friend ProfileBufferEntryReader::Deserializer; - - // The default constructor is only used during deserialization. - constexpr MarkerOptions() = default; - - MarkerCategory mCategory; - MarkerThreadId mThreadId; - MarkerTiming mTiming; - MarkerStack mStack; - MarkerInnerWindowId mInnerWindowId; -}; - -template -MarkerOptions MarkerCategory::WithOptions(Options&&... aOptions) const { - return MarkerOptions(*this, std::forward(aOptions)...); -} - -} // namespace mozilla - -namespace mozilla::baseprofiler::markers { - -// Default marker payload types, with no extra information, not even a marker -// type and payload. This is intended for label-only markers. -struct NoPayload final {}; - -} // namespace mozilla::baseprofiler::markers - -#endif // MOZ_GECKO_PROFILER - -#endif // BaseProfilerMarkersPrerequisites_h diff --git a/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h b/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h index 86c8b160bfd0..523ba0c9d6e2 100644 --- a/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h +++ b/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h @@ -73,9 +73,6 @@ enum class ProfileBufferEntryKind : ProfileBufferEntryKindUnderlyingType { // Marker data, including payload. MarkerData = LEGACY_LIMIT, - // Markers from 2.0 specs. - Marker, - // Optional between TimeBeforeCompactStack and CompactStack. UnresponsiveDurationMs, diff --git a/mozglue/baseprofiler/public/ProfileChunkedBuffer.h b/mozglue/baseprofiler/public/ProfileChunkedBuffer.h index f66dea184874..ba863759c5ef 100644 --- a/mozglue/baseprofiler/public/ProfileChunkedBuffer.h +++ b/mozglue/baseprofiler/public/ProfileChunkedBuffer.h @@ -587,11 +587,6 @@ class ProfileChunkedBuffer { return {mRangeStart, mRangeEnd, mPushedBlockCount, mClearedBlockCount}; } - [[nodiscard]] bool IsEmpty() const { - baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex); - return mRangeStart == mRangeEnd; - } - // True if this buffer is already locked on this thread. // This should be used if some functions may call an already-locked buffer, // e.g.: Put -> memory hook -> profiler_add_native_allocation_marker -> Put. @@ -1749,33 +1744,6 @@ struct ProfileBufferEntryWriter::Serializer> { } }; -// Serialization of a raw pointer to ProfileChunkedBuffer. -// Use Deserializer> to read it back. -template <> -struct ProfileBufferEntryWriter::Serializer { - static Length Bytes(ProfileChunkedBuffer* aBufferUPtr) { - if (!aBufferUPtr) { - // Null pointer, treat it like an empty buffer, i.e., write length of 0. - return ULEB128Size(0); - } - // Otherwise write the pointed-at ProfileChunkedBuffer (which could be - // out-of-session or empty.) - return SumBytes(*aBufferUPtr); - } - - static void Write(ProfileBufferEntryWriter& aEW, - ProfileChunkedBuffer* aBufferUPtr) { - if (!aBufferUPtr) { - // Null pointer, treat it like an empty buffer, i.e., write length of 0. - aEW.WriteULEB128(0); - return; - } - // Otherwise write the pointed-at ProfileChunkedBuffer (which could be - // out-of-session or empty.) - aEW.WriteObject(*aBufferUPtr); - } -}; - template <> struct ProfileBufferEntryReader::Deserializer> { static void ReadInto(ProfileBufferEntryReader& aER, diff --git a/mozglue/tests/TestBaseProfiler.cpp b/mozglue/tests/TestBaseProfiler.cpp index add9c4f64dba..ce6d6e75f5a5 100644 --- a/mozglue/tests/TestBaseProfiler.cpp +++ b/mozglue/tests/TestBaseProfiler.cpp @@ -11,7 +11,6 @@ #ifdef MOZ_GECKO_PROFILER # include "BaseProfilerMarkerPayload.h" -# include "mozilla/BaseProfilerMarkerTypes.h" # include "mozilla/BlocksRingBuffer.h" # include "mozilla/leb128iterator.h" # include "mozilla/ModuloBuffer.h" @@ -37,7 +36,6 @@ #include #include -#include #include #include #include @@ -2985,215 +2983,6 @@ void TestBlocksRingBufferSerialization() { printf("TestBlocksRingBufferSerialization done\n"); } -void TestLiteralEmptyStringView() { - printf("TestLiteralEmptyStringView...\n"); - - static_assert(mozilla::LiteralEmptyStringView() == - std::string_view("")); - static_assert(!!mozilla::LiteralEmptyStringView().data()); - static_assert(mozilla::LiteralEmptyStringView().length() == 0); - - static_assert(mozilla::LiteralEmptyStringView() == - std::basic_string_view(u"")); - static_assert(!!mozilla::LiteralEmptyStringView().data()); - static_assert(mozilla::LiteralEmptyStringView().length() == 0); - - printf("TestLiteralEmptyStringView done\n"); -} - -void TestProfilerStringView() { - printf("TestProfilerStringView...\n"); - - // Used to verify implicit constructions, as this will normally be used in - // function parameters. - auto BS8V = [](mozilla::ProfilerString8View&& aBS8V) { - return std::move(aBS8V); - }; - - // Literal empty string. - MOZ_RELEASE_ASSERT(BS8V("").Data()); - MOZ_RELEASE_ASSERT(BS8V("").Data()[0] == '\0'); - MOZ_RELEASE_ASSERT(BS8V("").Length() == 0); - MOZ_RELEASE_ASSERT(BS8V("").IsLiteral()); - MOZ_RELEASE_ASSERT(!BS8V("").IsReference()); - - // Literal non-empty string. - MOZ_RELEASE_ASSERT(BS8V("hi").Data()); - MOZ_RELEASE_ASSERT(BS8V("hi").Data()[0] == 'h'); - MOZ_RELEASE_ASSERT(BS8V("hi").Data()[1] == 'i'); - MOZ_RELEASE_ASSERT(BS8V("hi").Data()[2] == '\0'); - MOZ_RELEASE_ASSERT(BS8V("hi").Length() == 2); - MOZ_RELEASE_ASSERT(BS8V("hi").IsLiteral()); - MOZ_RELEASE_ASSERT(!BS8V("hi").IsReference()); - - // std::string_view to a literal empty string. - MOZ_RELEASE_ASSERT(BS8V(std::string_view("")).Data()); - MOZ_RELEASE_ASSERT(BS8V(std::string_view("")).Data()[0] == '\0'); - MOZ_RELEASE_ASSERT(BS8V(std::string_view("")).Length() == 0); - MOZ_RELEASE_ASSERT(!BS8V(std::string_view("")).IsLiteral()); - MOZ_RELEASE_ASSERT(BS8V(std::string_view("")).IsReference()); - - // std::string_view to a literal non-empty string. - MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Data()); - MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Data()[0] == 'h'); - MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Data()[1] == 'i'); - MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Data()[2] == '\0'); - MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Length() == 2); - MOZ_RELEASE_ASSERT(!BS8V(std::string_view("hi")).IsLiteral()); - MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).IsReference()); - - // Default std::string_view points at nullptr, ProfilerStringView converts it - // to the literal empty string. - MOZ_RELEASE_ASSERT(!std::string_view().data()); - MOZ_RELEASE_ASSERT(BS8V(std::string_view()).Data()); - MOZ_RELEASE_ASSERT(BS8V(std::string_view()).Data()[0] == '\0'); - MOZ_RELEASE_ASSERT(BS8V(std::string_view()).Length() == 0); - MOZ_RELEASE_ASSERT(BS8V(std::string_view()).IsLiteral()); - MOZ_RELEASE_ASSERT(!BS8V(std::string_view()).IsReference()); - - // std::string to a literal empty string. - MOZ_RELEASE_ASSERT(BS8V(std::string("")).Data()); - MOZ_RELEASE_ASSERT(BS8V(std::string("")).Data()[0] == '\0'); - MOZ_RELEASE_ASSERT(BS8V(std::string("")).Length() == 0); - MOZ_RELEASE_ASSERT(!BS8V(std::string("")).IsLiteral()); - MOZ_RELEASE_ASSERT(BS8V(std::string("")).IsReference()); - - // std::string to a literal non-empty string. - MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()); - MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()[0] == 'h'); - MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()[1] == 'i'); - MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()[2] == '\0'); - MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Length() == 2); - MOZ_RELEASE_ASSERT(!BS8V(std::string("hi")).IsLiteral()); - MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).IsReference()); - - // Default std::string_view contains an empty null-terminated string. - MOZ_RELEASE_ASSERT(std::string().data()); - MOZ_RELEASE_ASSERT(BS8V(std::string()).Data()); - MOZ_RELEASE_ASSERT(BS8V(std::string()).Data()[0] == '\0'); - MOZ_RELEASE_ASSERT(BS8V(std::string()).Length() == 0); - MOZ_RELEASE_ASSERT(!BS8V(std::string()).IsLiteral()); - MOZ_RELEASE_ASSERT(BS8V(std::string()).IsReference()); - - class FakeNsCString { - public: - FakeNsCString(const char* aData, size_t aLength, bool aIsLiteral) - : mData(aData), mLength(aLength), mIsLiteral(aIsLiteral) {} - - const char* Data() const { return mData; } - size_t Length() const { return mLength; } - bool IsLiteral() const { return mIsLiteral; } - - private: - const char* mData; - size_t mLength; - bool mIsLiteral; - }; - - // FakeNsCString to nullptr. - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).Data()); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).Data()[0] == '\0'); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).Length() == 0); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).IsLiteral()); - MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString(nullptr, 0, true)).IsReference()); - - // FakeNsCString to a literal empty string. - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).Data()); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).Data()[0] == '\0'); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).Length() == 0); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).IsLiteral()); - MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString("", 0, true)).IsReference()); - - // FakeNsCString to a literal non-empty string. - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()[0] == 'h'); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()[1] == 'i'); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()[2] == '\0'); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Length() == 2); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).IsLiteral()); - MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString("hi", 2, true)).IsReference()); - - // FakeNsCString to a non-literal non-empty string. - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()[0] == 'h'); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()[1] == 'i'); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()[2] == '\0'); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Length() == 2); - MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString("hi", 2, false)).IsLiteral()); - MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).IsReference()); - - // Serialization and deserialization (with ownership). - constexpr size_t bufferMaxSize = 1024; - constexpr ProfileChunkedBuffer::Length chunkMinSize = 128; - ProfileBufferChunkManagerWithLocalLimit cm(bufferMaxSize, chunkMinSize); - ProfileChunkedBuffer cb(ProfileChunkedBuffer::ThreadSafety::WithMutex, cm); - - // Literal string, serialized as raw pointer. - MOZ_RELEASE_ASSERT(cb.PutObject(BS8V("hi"))); - { - unsigned read = 0; - ProfilerString8View outerBS8V; - cb.ReadEach([&](ProfileBufferEntryReader& aER) { - ++read; - auto bs8v = aER.ReadObject(); - MOZ_RELEASE_ASSERT(bs8v.Data()); - MOZ_RELEASE_ASSERT(bs8v.Data()[0] == 'h'); - MOZ_RELEASE_ASSERT(bs8v.Data()[1] == 'i'); - MOZ_RELEASE_ASSERT(bs8v.Data()[2] == '\0'); - MOZ_RELEASE_ASSERT(bs8v.Length() == 2); - MOZ_RELEASE_ASSERT(bs8v.IsLiteral()); - MOZ_RELEASE_ASSERT(!bs8v.IsReference()); - outerBS8V = std::move(bs8v); - }); - MOZ_RELEASE_ASSERT(read == 1); - MOZ_RELEASE_ASSERT(outerBS8V.Data()); - MOZ_RELEASE_ASSERT(outerBS8V.Data()[0] == 'h'); - MOZ_RELEASE_ASSERT(outerBS8V.Data()[1] == 'i'); - MOZ_RELEASE_ASSERT(outerBS8V.Data()[2] == '\0'); - MOZ_RELEASE_ASSERT(outerBS8V.Length() == 2); - MOZ_RELEASE_ASSERT(outerBS8V.IsLiteral()); - MOZ_RELEASE_ASSERT(!outerBS8V.IsReference()); - } - - cb.Clear(); - - // Non-literal string, content is serialized. - std::string hiString("hi"); - MOZ_RELEASE_ASSERT(cb.PutObject(BS8V(hiString))); - { - unsigned read = 0; - ProfilerString8View outerBS8V; - cb.ReadEach([&](ProfileBufferEntryReader& aER) { - ++read; - auto bs8v = aER.ReadObject(); - MOZ_RELEASE_ASSERT(bs8v.Data()); - MOZ_RELEASE_ASSERT(bs8v.Data() != hiString.data()); - MOZ_RELEASE_ASSERT(bs8v.Data()[0] == 'h'); - MOZ_RELEASE_ASSERT(bs8v.Data()[1] == 'i'); - MOZ_RELEASE_ASSERT(bs8v.Data()[2] == '\0'); - MOZ_RELEASE_ASSERT(bs8v.Length() == 2); - // Special ownership case, neither a literal nor a reference! - MOZ_RELEASE_ASSERT(!bs8v.IsLiteral()); - MOZ_RELEASE_ASSERT(!bs8v.IsReference()); - // Test move of ownership. - outerBS8V = std::move(bs8v); - // NOLINTNEXTLINE(bugprone-use-after-move, clang-analyzer-cplusplus.Move) - MOZ_RELEASE_ASSERT(bs8v.Length() == 0); - }); - MOZ_RELEASE_ASSERT(read == 1); - MOZ_RELEASE_ASSERT(outerBS8V.Data()); - MOZ_RELEASE_ASSERT(outerBS8V.Data() != hiString.data()); - MOZ_RELEASE_ASSERT(outerBS8V.Data()[0] == 'h'); - MOZ_RELEASE_ASSERT(outerBS8V.Data()[1] == 'i'); - MOZ_RELEASE_ASSERT(outerBS8V.Data()[2] == '\0'); - MOZ_RELEASE_ASSERT(outerBS8V.Length() == 2); - MOZ_RELEASE_ASSERT(!outerBS8V.IsLiteral()); - MOZ_RELEASE_ASSERT(!outerBS8V.IsReference()); - } - - printf("TestProfilerStringView done\n"); -} - void TestProfilerDependencies() { TestPowerOfTwoMask(); TestPowerOfTwo(); @@ -3210,8 +2999,6 @@ void TestProfilerDependencies() { TestBlocksRingBufferUnderlyingBufferChanges(); TestBlocksRingBufferThreading(); TestBlocksRingBufferSerialization(); - TestLiteralEmptyStringView(); - TestProfilerStringView(); } class BaseTestMarkerPayload : public baseprofiler::ProfilerMarkerPayload { @@ -3398,7 +3185,6 @@ void TestProfiler() { static const unsigned long long fibStart = 37; printf("Fibonacci(%llu)...\n", fibStart); AUTO_BASE_PROFILER_LABEL("Label around Fibonacci", OTHER); - unsigned long long f = Fibonacci(fibStart); printf("Fibonacci(%llu) = %llu\n", fibStart, f); }); @@ -3456,20 +3242,6 @@ void TestProfiler() { baseprofiler::FileIOMarkerPayload( "operation", "source", "filename", TimeStamp::NowUnfuzzed(), TimeStamp::NowUnfuzzed(), std::move(cause))); - baseprofiler::AddMarker( - "m2fileio", mozilla::baseprofiler::category::OTHER, "op2", "src2", "f2", - MarkerThreadId{}); - baseprofiler::AddMarker( - "m2fileio-capture", - mozilla::baseprofiler::category::OTHER.WithOptions( - MarkerStack::Capture()), - "op2", "src2", "f2", MarkerThreadId{}); - baseprofiler::AddMarker( - "m2fileio-take-backtrace", - mozilla::baseprofiler::category::OTHER.WithOptions( - MarkerStack::TakeBacktrace( - baseprofiler::profiler_capture_backtrace())), - "op2", "src2", "f2", MarkerThreadId{}); baseprofiler::profiler_add_marker( "UserTimingMarkerPayload", baseprofiler::ProfilingCategoryPair::OTHER, @@ -3499,67 +3271,6 @@ void TestProfiler() { baseprofiler::LogMarkerPayload("module", "text", TimeStamp::NowUnfuzzed())); - MOZ_RELEASE_ASSERT(baseprofiler::AddMarker( - "markers 2.0 without options", mozilla::baseprofiler::category::OTHER)); - - MOZ_RELEASE_ASSERT(baseprofiler::AddMarker( - "markers 2.0 with option", - mozilla::baseprofiler::category::OTHER.WithOptions( - MarkerInnerWindowId(123)))); - - MOZ_RELEASE_ASSERT(baseprofiler::AddMarker<>( - "default-templated markers 2.0 without options", - mozilla::baseprofiler::category::OTHER)); - - MOZ_RELEASE_ASSERT(baseprofiler::AddMarker<>( - "default-templated markers 2.0 with option", - mozilla::baseprofiler::category::OTHER.WithOptions( - MarkerInnerWindowId(123)))); - - MOZ_RELEASE_ASSERT( - baseprofiler::AddMarker<::mozilla::baseprofiler::markers::NoPayload>( - "explicitly-default-templated markers 2.0 without options", - mozilla::baseprofiler::category::OTHER)); - - MOZ_RELEASE_ASSERT( - baseprofiler::AddMarker<::mozilla::baseprofiler::markers::NoPayload>( - "explicitly-default-templated markers 2.0 with option", - mozilla::baseprofiler::category::OTHER.WithOptions( - MarkerInnerWindowId(123)))); - - MOZ_RELEASE_ASSERT( - baseprofiler::AddMarker( - "tracing", mozilla::baseprofiler::category::OTHER, "category")); - - MOZ_RELEASE_ASSERT( - baseprofiler::AddMarker( - "mark", mozilla::baseprofiler::category::OTHER, "mark name")); - - MOZ_RELEASE_ASSERT(baseprofiler::AddMarker< - mozilla::baseprofiler::markers::UserTimingMeasure>( - "measure", mozilla::baseprofiler::category::OTHER, "measure name", - Some(ProfilerString8View("start")), Some(ProfilerString8View("end")))); - - MOZ_RELEASE_ASSERT( - baseprofiler::AddMarker( - "hang", mozilla::baseprofiler::category::OTHER)); - - MOZ_RELEASE_ASSERT( - baseprofiler::AddMarker( - "longtask", mozilla::baseprofiler::category::OTHER)); - - MOZ_RELEASE_ASSERT( - baseprofiler::AddMarker( - "text", mozilla::baseprofiler::category::OTHER, "text text")); - - MOZ_RELEASE_ASSERT( - baseprofiler::AddMarker( - "log", mozilla::baseprofiler::category::OTHER, "module", "text")); - - MOZ_RELEASE_ASSERT( - baseprofiler::AddMarker( - "media sample", mozilla::baseprofiler::category::OTHER, 123, 456)); - printf("Sleep 1s...\n"); { AUTO_BASE_PROFILER_THREAD_SLEEP; @@ -3615,252 +3326,6 @@ void TestProfiler() { printf("TestProfiler done\n"); } -void StreamMarkers(const mozilla::ProfileChunkedBuffer& aBuffer, - mozilla::JSONWriter& aWriter) { - aWriter.Start(); - { - aWriter.StartArrayProperty("data"); - { - aBuffer.ReadEach([&](mozilla::ProfileBufferEntryReader& aEntryReader) { - mozilla::ProfileBufferEntryKind entryKind = - aEntryReader.ReadObject(); - MOZ_RELEASE_ASSERT(entryKind == - mozilla::ProfileBufferEntryKind::Marker); - - const bool success = mozilla::base_profiler_markers_detail:: - DeserializeAfterKindAndStream( - aEntryReader, aWriter, 0, - [&](const mozilla::ProfilerString8View& aName) { - aWriter.StringElement(aName.String().c_str()); - }, - [&](mozilla::ProfileChunkedBuffer&) { - aWriter.StringElement("Real backtrace would be here"); - }); - MOZ_RELEASE_ASSERT(success); - }); - } - aWriter.EndArray(); - } - aWriter.End(); -} - -void PrintMarkers(const mozilla::ProfileChunkedBuffer& aBuffer) { - mozilla::baseprofiler::SpliceableJSONWriter writer( - mozilla::MakeUnique( - std::cout)); - StreamMarkers(aBuffer, writer); -} - -void TestMarkerNoPayload() { - printf("TestMarkerNoPayload...\n"); - - mozilla::ProfileBufferChunkManagerSingle chunkManager(512); - mozilla::ProfileChunkedBuffer buffer( - mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager); - - mozilla::ProfileBufferBlockIndex i0 = - mozilla::baseprofiler::AddMarkerToBuffer<>( - buffer, "literal", mozilla::baseprofiler::category::OTHER_Profiling); - MOZ_RELEASE_ASSERT(i0); - - const std::string dynamic = "dynamic"; - mozilla::ProfileBufferBlockIndex i1 = - mozilla::baseprofiler::AddMarkerToBuffer<>( - buffer, dynamic, - mozilla::baseprofiler::category::GRAPHICS_FlushingAsyncPaints); - MOZ_RELEASE_ASSERT(i1); - MOZ_RELEASE_ASSERT(i1 > i0); - - mozilla::ProfileBufferBlockIndex i2 = - mozilla::baseprofiler::AddMarkerToBuffer<>( - buffer, std::string_view("string_view"), - mozilla::baseprofiler::category::GRAPHICS_FlushingAsyncPaints); - MOZ_RELEASE_ASSERT(i2); - MOZ_RELEASE_ASSERT(i2 > i1); - -# ifdef DEBUG - buffer.Dump(); -# endif - - PrintMarkers(buffer); - - printf("TestMarkerNoPayload done\n"); -} - -void TestUserMarker() { - printf("TestUserMarker...\n"); - - // User-defined marker type with text. If there are no `Convert` functions, - // it's fine to define it right in the function where it's used. - struct MarkerTypeTestMinimal { - static constexpr const char* MarkerTypeName() { return "test-minimal"; } - static void StreamJSONMarkerData(mozilla::JSONWriter& aWriter, - const std::string& aText) { - aWriter.StringProperty("text", aText.c_str()); - } - }; - - mozilla::ProfileBufferChunkManagerSingle chunkManager(1024); - mozilla::ProfileChunkedBuffer buffer( - mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager); - - MOZ_RELEASE_ASSERT( - mozilla::baseprofiler::AddMarkerToBuffer( - buffer, "test2", mozilla::baseprofiler::category::OTHER_Profiling, - std::string("payload text"))); - - MOZ_RELEASE_ASSERT( - mozilla::baseprofiler::AddMarkerToBuffer( - buffer, "test2", - mozilla::baseprofiler::category::OTHER_Profiling.WithOptions( - mozilla::MarkerThreadId(123)), - std::string("ThreadId(123)"))); - - auto start = mozilla::TimeStamp::NowUnfuzzed(); - - MOZ_RELEASE_ASSERT( - mozilla::baseprofiler::AddMarkerToBuffer( - buffer, "test2", - mozilla::baseprofiler::category::OTHER_Profiling.WithOptions( - mozilla::MarkerTiming::InstantAt(start)), - std::string("InstantAt(start)"))); - - auto then = mozilla::TimeStamp::NowUnfuzzed(); - - MOZ_RELEASE_ASSERT( - mozilla::baseprofiler::AddMarkerToBuffer( - buffer, "test2", - mozilla::baseprofiler::category::OTHER_Profiling.WithOptions( - mozilla::MarkerTiming::IntervalStart(start)), - std::string("IntervalStart(start)"))); - - MOZ_RELEASE_ASSERT( - mozilla::baseprofiler::AddMarkerToBuffer( - buffer, "test2", - mozilla::baseprofiler::category::OTHER_Profiling.WithOptions( - mozilla::MarkerTiming::IntervalEnd(then)), - std::string("IntervalEnd(then)"))); - - MOZ_RELEASE_ASSERT( - mozilla::baseprofiler::AddMarkerToBuffer( - buffer, "test2", - mozilla::baseprofiler::category::OTHER_Profiling.WithOptions( - mozilla::MarkerTiming::Interval(start, then)), - std::string("Interval(start, then)"))); - - MOZ_RELEASE_ASSERT( - mozilla::baseprofiler::AddMarkerToBuffer( - buffer, "test2", - mozilla::baseprofiler::category::OTHER_Profiling.WithOptions( - mozilla::MarkerTiming::IntervalUntilNowFrom(start)), - std::string("IntervalUntilNowFrom(start)"))); - - MOZ_RELEASE_ASSERT( - mozilla::baseprofiler::AddMarkerToBuffer( - buffer, "test2", - mozilla::baseprofiler::category::OTHER_Profiling.WithOptions( - mozilla::MarkerStack::NoStack()), - std::string("NoStack"))); - // Note: We cannot test stack-capture here, because the profiler is not - // initialized. - - MOZ_RELEASE_ASSERT( - mozilla::baseprofiler::AddMarkerToBuffer( - buffer, "test2", - mozilla::baseprofiler::category::OTHER_Profiling.WithOptions( - mozilla::MarkerInnerWindowId(123)), - std::string("InnerWindowId(123)"))); - -# ifdef DEBUG - buffer.Dump(); -# endif - - PrintMarkers(buffer); - - printf("TestUserMarker done\n"); -} - -void TestPredefinedMarkers() { - printf("TestPredefinedMarkers...\n"); - - // User-defined marker type with text. If there are no `Convert` functions, - // it's fine to define it right in the function where it's used. - struct MarkerTypeTestMinimal { - static constexpr const char* MarkerTypeName() { return "test-minimal"; } - static void StreamJSONMarkerData(mozilla::JSONWriter& aWriter, - const std::string& aText) { - aWriter.StringProperty("text", aText.c_str()); - } - }; - - mozilla::ProfileBufferChunkManagerSingle chunkManager(1024); - mozilla::ProfileChunkedBuffer buffer( - mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager); - - MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer< - mozilla::baseprofiler::markers::Tracing>( - buffer, std::string_view("tracing"), - mozilla::baseprofiler::category::OTHER, "category")); - - MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer< - mozilla::baseprofiler::markers::UserTimingMark>( - buffer, std::string_view("mark"), mozilla::baseprofiler::category::OTHER, - "mark name")); - - MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer< - mozilla::baseprofiler::markers::UserTimingMeasure>( - buffer, std::string_view("measure"), - mozilla::baseprofiler::category::OTHER, "measure name ", - mozilla::Some(mozilla::ProfilerString8View(" start ")), - mozilla::Some(mozilla::ProfilerString8View("end")))); - - MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer< - mozilla::baseprofiler::markers::Hang>( - buffer, std::string_view("hang"), - mozilla::baseprofiler::category::OTHER)); - - MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer< - mozilla::baseprofiler::markers::LongTask>( - buffer, std::string_view("long task"), - mozilla::baseprofiler::category::OTHER)); - - MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer< - mozilla::baseprofiler::markers::Text>( - buffer, std::string_view("text"), mozilla::baseprofiler::category::OTHER, - "text text")); - - MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer< - mozilla::baseprofiler::markers::Log>( - buffer, std::string_view("log"), mozilla::baseprofiler::category::OTHER, - "module", "text")); - - MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer< - mozilla::baseprofiler::markers::MediaSample>( - buffer, std::string_view("media"), mozilla::baseprofiler::category::OTHER, - 123, 456)); - -# ifdef DEBUG - buffer.Dump(); -# endif - - PrintMarkers(buffer); - - printf("TestPredefinedMarkers done\n"); -} - -void TestProfilerMarkers() { - printf("TestProfilerMarkers -- pid: %d, tid: %d\n", - mozilla::baseprofiler::profiler_current_process_id(), - mozilla::baseprofiler::profiler_current_thread_id()); - // ::SleepMilli(10000); - - TestMarkerNoPayload(); - TestUserMarker(); - TestPredefinedMarkers(); - - printf("TestProfilerMarkers done\n"); -} - #else // MOZ_GECKO_PROFILER // Testing that macros are still #defined (but do nothing) when @@ -3868,50 +3333,14 @@ void TestProfilerMarkers() { void TestProfiler() { // These don't need to make sense, we just want to know that they're defined // and don't do anything. - -# ifndef AUTO_BASE_PROFILER_INIT -# error AUTO_BASE_PROFILER_INIT not #defined -# endif // AUTO_BASE_PROFILER_INIT AUTO_BASE_PROFILER_INIT; // This wouldn't build if the macro did output its arguments. -# ifndef AUTO_BASE_PROFILER_TEXT_MARKER_CAUSE -# error AUTO_BASE_PROFILER_TEXT_MARKER_CAUSE not #defined -# endif // AUTO_BASE_PROFILER_TEXT_MARKER_CAUSE AUTO_BASE_PROFILER_TEXT_MARKER_CAUSE(catch, catch, catch, catch); -# ifndef AUTO_BASE_PROFILER_LABEL -# error AUTO_BASE_PROFILER_LABEL not #defined -# endif // AUTO_BASE_PROFILER_LABEL AUTO_BASE_PROFILER_LABEL(catch, catch); -# ifndef AUTO_BASE_PROFILER_THREAD_SLEEP -# error AUTO_BASE_PROFILER_THREAD_SLEEP not #defined -# endif // AUTO_BASE_PROFILER_THREAD_SLEEP AUTO_BASE_PROFILER_THREAD_SLEEP; - -# ifndef BASE_PROFILER_MARKER_UNTYPED -# error BASE_PROFILER_MARKER_UNTYPED not #defined -# endif // BASE_PROFILER_MARKER_UNTYPED - BASE_PROFILER_MARKER_UNTYPED(catch, catch); - -# ifndef BASE_PROFILER_MARKER -# error BASE_PROFILER_MARKER not #defined -# endif // BASE_PROFILER_MARKER - BASE_PROFILER_MARKER(catch, catch, catch); - BASE_PROFILER_MARKER(catch, catch, catch, catch); - -# ifndef BASE_PROFILER_MARKER_TEXT -# error BASE_PROFILER_MARKER_TEXT not #defined -# endif // BASE_PROFILER_MARKER_TEXT - BASE_PROFILER_MARKER_TEXT(catch, catch, catch); -} - -// Testing that macros are still #defined (but do nothing) when -// MOZ_GECKO_PROFILER is disabled. -void TestProfilerMarkers() { - // These don't need to make sense, we just want to know that they're defined - // and don't do anything. } #endif // MOZ_GECKO_PROFILER else @@ -3929,10 +3358,9 @@ int main() // ::SleepMilli(10000); #endif // MOZ_GECKO_PROFILER - // Note that there are two `TestProfiler{,Markers}` functions above, depending - // on whether MOZ_GECKO_PROFILER is #defined. + // Note that there are two `TestProfiler` functions above, depending on + // whether MOZ_GECKO_PROFILER is #defined. TestProfiler(); - TestProfilerMarkers(); return 0; } diff --git a/tools/profiler/core/ProfileBufferEntry.cpp b/tools/profiler/core/ProfileBufferEntry.cpp index 53460ea56f20..0019a080df5b 100644 --- a/tools/profiler/core/ProfileBufferEntry.cpp +++ b/tools/profiler/core/ProfileBufferEntry.cpp @@ -6,10 +6,8 @@ #include "ProfileBufferEntry.h" -#include "mozilla/ProfilerMarkers.h" #include "platform.h" #include "ProfileBuffer.h" -#include "ProfilerBacktrace.h" #include "ProfilerMarkerPayload.h" #include "jsapi.h" @@ -674,7 +672,6 @@ class EntryGetter { // */ // ) // | MarkerData -// | Marker // | ( /* Counters */ // CounterId // Time @@ -1184,83 +1181,51 @@ void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter, MOZ_ASSERT(static_cast(type) < static_cast( ProfileBufferEntry::Kind::MODERN_LIMIT)); - // Code should *return* from the switch if the entry was fully read. - // Code should *break* from the switch if the entry was not fully read (we - // then need to adjust the reader position to the end of the entry, as - // expected by the reader code.) - switch (type) { - case ProfileBufferEntry::Kind::MarkerData: - if (aER.ReadObject() != aThreadId) { - break; // Entry not fully read. + if (type == ProfileBufferEntry::Kind::MarkerData && + aER.ReadObject() == aThreadId) { + aWriter.StartArrayElement(); + { + // Extract the information from the buffer: + // Each entry is made up of the following: + // + // [ + // ProfileBufferEntry::Kind::MarkerData, <- already read + // threadId, <- already read + // name, <- next location in entries + // startTime, + // endTime, + // phase, + // categoryPair, + // payload, + // ] + auto name = aER.ReadObject(); + auto startTime = aER.ReadObject(); + auto endTime = aER.ReadObject(); + auto phase = aER.ReadObject(); + const JS::ProfilingCategoryPairInfo& info = + GetProfilingCategoryPairInfo(static_cast( + aER.ReadObject())); + auto payload = aER.ReadObject>(); + + MOZ_ASSERT(aER.RemainingBytes() == 0); + + // Now write this information to JSON with the following schema: + // [name, startTime, endTime, phase, category, data] + aUniqueStacks.mUniqueStrings->WriteElement(aWriter, name.c_str()); + aWriter.DoubleElement(startTime); + aWriter.DoubleElement(endTime); + aWriter.IntElement(phase); + aWriter.IntElement(unsigned(info.mCategory)); + if (payload) { + aWriter.StartObjectElement(SpliceableJSONWriter::SingleLineStyle); + { payload->StreamPayload(aWriter, aProcessStartTime, aUniqueStacks); } + aWriter.EndObject(); } - aWriter.StartArrayElement(); - { - // Extract the information from the buffer: - // Each entry is made up of the following: - // - // [ - // ProfileBufferEntry::Kind::MarkerData, <- already read - // threadId, <- already read - // name, <- next location in entries - // startTime, - // endTime, - // phase, - // categoryPair, - // payload - // ] - auto name = aER.ReadObject(); - auto startTime = aER.ReadObject(); - auto endTime = aER.ReadObject(); - auto phase = aER.ReadObject(); - const JS::ProfilingCategoryPairInfo& info = - GetProfilingCategoryPairInfo( - static_cast( - aER.ReadObject())); - auto payload = aER.ReadObject>(); - - MOZ_ASSERT(aER.RemainingBytes() == 0); - - // Now write this information to JSON with the following schema: - // [name, startTime, endTime, phase, category, data] - aUniqueStacks.mUniqueStrings->WriteElement(aWriter, name.c_str()); - aWriter.DoubleElement(startTime); - aWriter.DoubleElement(endTime); - aWriter.IntElement(phase); - aWriter.IntElement(unsigned(info.mCategory)); - if (payload) { - aWriter.StartObjectElement(SpliceableJSONWriter::SingleLineStyle); - { - payload->StreamPayload(aWriter, aProcessStartTime, aUniqueStacks); - } - aWriter.EndObject(); - } - } - aWriter.EndArray(); - return; // Entry fully read. - - case ProfileBufferEntry::Kind::Marker: - if (mozilla::base_profiler_markers_detail:: - DeserializeAfterKindAndStream( - aER, aWriter, aThreadId, - [&](const mozilla::ProfilerString8View& aName) { - aUniqueStacks.mUniqueStrings->WriteElement( - aWriter, aName.String().c_str()); - }, - [&](ProfileChunkedBuffer& aChunkedBuffer) { - ProfilerBacktrace backtrace("", aThreadId, - &aChunkedBuffer); - backtrace.StreamJSON(aWriter, aProcessStartTime, - aUniqueStacks); - })) { - return; // Entry fully read. - } - break; // Entry not fully read. - - default: - break; // Entry not fully read. + } + aWriter.EndArray(); + } else { + aER.SetRemainingBytes(0); } - - aER.SetRemainingBytes(0); }); } diff --git a/tools/profiler/core/ProfilerMarkerPayload.cpp b/tools/profiler/core/ProfilerMarkerPayload.cpp index 4963859902b0..155730b14977 100644 --- a/tools/profiler/core/ProfilerMarkerPayload.cpp +++ b/tools/profiler/core/ProfilerMarkerPayload.cpp @@ -139,8 +139,8 @@ ProfilerMarkerPayload::DeserializeCommonProps( // Deprecated: This function is providing a way for a few payloads to use the // start time and end time in their payloads, which is currently deprecated. // The startTime and endTime were removed from most payloads, in favor of -// the MarkerTiming::Phase idea. However, IPC and Network markers still have -// them as it was harder to upgrade the front-end without them. +// the MarkerPhase idea. However, IPC and Network markers still have them as +// it was harder to upgrade the front-end without them. void ProfilerMarkerPayload::StreamStartEndTime( SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime) const { WriteTime(aWriter, aProcessStartTime, mCommonProps.mStartTime, "startTime"); diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp index 37b751e21fd2..28ac4fdca91a 100644 --- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -695,11 +695,6 @@ class CorePS { CorePS* CorePS::sInstance = nullptr; -ProfileChunkedBuffer& profiler_get_core_buffer() { - MOZ_ASSERT(CorePS::Exists()); - return CorePS::CoreBuffer(); -} - class SamplerThread; static SamplerThread* NewSamplerThread(PSLockRef aLock, uint32_t aGeneration, @@ -1588,6 +1583,80 @@ void ProfilingStackOwner::DumpStackAndCrash() const { // The name of the main thread. static const char* const kMainThreadName = "GeckoMain"; +enum class MarkerPhase : uint8_t { + Instant = 0, + Interval = 1, + IntervalStart = 2, + IntervalEnd = 3, +}; + +// This class encapsulates the logic for correctly storing a marker based on its +// timing type, based on the constraints of that type. Use the static methods to +// create the MarkerTiming. This is a transient object that is being used to +// enforce the constraints of the combinations of the data. +class MarkerTiming { + public: + // The following static methods are used to create the MarkerTiming based on + // the type that it is. + static MarkerTiming Instant( + const TimeStamp& aTime = TimeStamp::NowUnfuzzed()) { + MOZ_ASSERT(!aTime.IsNull(), "Time is null for an instant marker."); + return MarkerTiming{aTime, TimeStamp{}, MarkerPhase::Instant}; + } + + static MarkerTiming Interval( + const TimeStamp& aStartTime, + const TimeStamp& aEndTime = TimeStamp::NowUnfuzzed()) { + MOZ_ASSERT(!aStartTime.IsNull(), + "Start time is null for an interval marker."); + MOZ_ASSERT(!aEndTime.IsNull(), "End time is null for an interval marker."); + return MarkerTiming{aStartTime, aEndTime, MarkerPhase::Interval}; + } + + static MarkerTiming IntervalStart( + const TimeStamp& aTime = TimeStamp::NowUnfuzzed()) { + MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval start marker."); + return MarkerTiming{aTime, TimeStamp{}, MarkerPhase::IntervalStart}; + } + + static MarkerTiming IntervalEnd( + const TimeStamp& aTime = TimeStamp::NowUnfuzzed()) { + MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval end marker."); + return MarkerTiming{TimeStamp{}, aTime, MarkerPhase::IntervalEnd}; + } + + // The following getter methods are used to put the value into the buffer for + // storage. + double GetStartTime() const { + return MarkerTiming::timeStampToDouble(mStartTime); + } + + double GetEndTime() const { + return MarkerTiming::timeStampToDouble(mEndTime); + } + + uint8_t GetMarkerPhase() const { return static_cast(mMarkerPhase); } + + private: + MarkerTiming(TimeStamp aStartTime, TimeStamp aEndTime, + MarkerPhase aMarkerPhase) + : mStartTime(aStartTime), + mEndTime(aEndTime), + mMarkerPhase(aMarkerPhase) {} + + static double timeStampToDouble(TimeStamp time) { + if (time.IsNull()) { + // The MarkerPhase lets us know not to use this value. + return 0; + } + return (time - CorePS::ProcessStartTime()).ToMilliseconds(); + } + + TimeStamp mStartTime; + TimeStamp mEndTime; + MarkerPhase mMarkerPhase; +}; + // TODO - It is better to have the marker timing created by the original callers // of the profiler_add_marker API, rather than deduce it from the payload. This // is a bigger code diff for adding MarkerTiming, so do that work in a @@ -1599,7 +1668,7 @@ MarkerTiming get_marker_timing_from_payload( if (start.IsNull()) { if (end.IsNull()) { // The payload contains no time information, use the current time. - return MarkerTiming::InstantAt(TimeStamp::NowUnfuzzed()); + return MarkerTiming::Instant(TimeStamp::NowUnfuzzed()); } return MarkerTiming::IntervalEnd(end); } @@ -1607,7 +1676,7 @@ MarkerTiming get_marker_timing_from_payload( return MarkerTiming::IntervalStart(start); } if (start == end) { - return MarkerTiming::InstantAt(start); + return MarkerTiming::Instant(start); } return MarkerTiming::Interval(start, end); } @@ -1619,11 +1688,12 @@ static void StoreMarker(ProfileChunkedBuffer& aChunkedBuffer, int aThreadId, const MarkerTiming& aMarkerTiming, JS::ProfilingCategoryPair aCategoryPair, const ProfilerMarkerPayload* aPayload) { - aChunkedBuffer.PutObjects( - ProfileBufferEntry::Kind::MarkerData, aThreadId, - WrapProfileBufferUnownedCString(aMarkerName), - aMarkerTiming.GetStartTime(), aMarkerTiming.GetEndTime(), - aMarkerTiming.GetPhase(), static_cast(aCategoryPair), aPayload); + aChunkedBuffer.PutObjects(ProfileBufferEntry::Kind::MarkerData, aThreadId, + WrapProfileBufferUnownedCString(aMarkerName), + aMarkerTiming.GetStartTime(), + aMarkerTiming.GetEndTime(), + aMarkerTiming.GetMarkerPhase(), + static_cast(aCategoryPair), aPayload); } //////////////////////////////////////////////////////////////////////// @@ -2713,7 +2783,7 @@ static void CollectJavaThreadProfileData(ProfileBuffer& aProfileBuffer) { : CorePS::ProcessStartTime() + TimeDuration::FromMilliseconds(endTimeMs); MarkerTiming timing = endTimeMs == 0 - ? MarkerTiming::InstantAt(startTime) + ? MarkerTiming::Instant(startTime) : MarkerTiming::Interval(startTime, endTime); if (!text) { @@ -5279,7 +5349,65 @@ double profiler_time() { return delta.ToMilliseconds(); } -bool profiler_capture_backtrace_into(ProfileChunkedBuffer& aChunkedBuffer) { +static void locked_profiler_fill_backtrace(PSLockRef aLock, + RegisteredThread& aRegisteredThread, + ProfileBuffer& aProfileBuffer) { + Registers regs; +#if defined(HAVE_NATIVE_UNWIND) + regs.SyncPopulate(); +#else + regs.Clear(); +#endif + + DoSyncSample(aLock, aRegisteredThread, TimeStamp::NowUnfuzzed(), regs, + aProfileBuffer); +} + +static UniqueProfilerBacktrace locked_profiler_get_backtrace(PSLockRef aLock) { + if (!ActivePS::Exists(aLock)) { + return nullptr; + } + + RegisteredThread* registeredThread = + TLSRegisteredThread::RegisteredThread(aLock); + if (!registeredThread) { + // If this was called from a non-registered thread, return a nullptr + // and do no more work. This can happen from a memory hook. Before + // the allocation tracking there was a MOZ_ASSERT() here checking + // for the existence of a registeredThread. + return nullptr; + } + + auto bufferManager = MakeUnique( + ProfileChunkedBuffer::ThreadSafety::WithoutMutex, + MakeUnique(scExpectedMaximumStackSize)); + ProfileBuffer buffer(*bufferManager); + + locked_profiler_fill_backtrace(aLock, *registeredThread, buffer); + + return UniqueProfilerBacktrace( + new ProfilerBacktrace("SyncProfile", registeredThread->Info()->ThreadId(), + std::move(bufferManager))); +} + +UniqueProfilerBacktrace profiler_get_backtrace() { + MOZ_RELEASE_ASSERT(CorePS::Exists()); + + // Fast racy early return. + if (!profiler_is_active()) { + return nullptr; + } + + PSAutoLock lock(gPSMutex); + + return locked_profiler_get_backtrace(lock); +} + +void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) { + delete aBacktrace; +} + +bool profiler_capture_backtrace(ProfileChunkedBuffer& aChunkedBuffer) { MOZ_RELEASE_ASSERT(CorePS::Exists()); PSAutoLock lock(gPSMutex); @@ -5291,62 +5419,17 @@ bool profiler_capture_backtrace_into(ProfileChunkedBuffer& aChunkedBuffer) { RegisteredThread* registeredThread = TLSRegisteredThread::RegisteredThread(lock); if (!registeredThread) { - // If this was called from a non-registered thread, return false and do no - // more work. This can happen from a memory hook. Before the allocation - // tracking there was a MOZ_ASSERT() here checking for the existence of a - // registeredThread. + MOZ_ASSERT(registeredThread); return false; } ProfileBuffer profileBuffer(aChunkedBuffer); - Registers regs; -#if defined(HAVE_NATIVE_UNWIND) - regs.SyncPopulate(); -#else - regs.Clear(); -#endif - - DoSyncSample(lock, *registeredThread, TimeStamp::NowUnfuzzed(), regs, - profileBuffer); + locked_profiler_fill_backtrace(lock, *registeredThread, profileBuffer); return true; } -UniquePtr profiler_capture_backtrace() { - MOZ_RELEASE_ASSERT(CorePS::Exists()); - - // Quick is-active check before allocating a buffer. - if (!profiler_is_active()) { - return nullptr; - } - - auto buffer = MakeUnique( - ProfileChunkedBuffer::ThreadSafety::WithoutMutex, - MakeUnique(scExpectedMaximumStackSize)); - - if (!profiler_capture_backtrace_into(*buffer)) { - return nullptr; - } - - return buffer; -} - -UniqueProfilerBacktrace profiler_get_backtrace() { - UniquePtr buffer = profiler_capture_backtrace(); - - if (!buffer) { - return nullptr; - } - - return UniqueProfilerBacktrace(new ProfilerBacktrace( - "SyncProfile", profiler_current_thread_id(), std::move(buffer))); -} - -void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) { - delete aBacktrace; -} - static void racy_profiler_add_marker(const char* aMarkerName, JS::ProfilingCategoryPair aCategoryPair, const ProfilerMarkerPayload* aPayload) { @@ -5369,7 +5452,7 @@ static void racy_profiler_add_marker(const char* aMarkerName, const MarkerTiming markerTiming = aPayload ? get_marker_timing_from_payload(*aPayload) - : MarkerTiming::InstantNow(); + : MarkerTiming::Instant(); StoreMarker(CorePS::CoreBuffer(), racyRegisteredThread->ThreadId(), aMarkerName, markerTiming, aCategoryPair, aPayload); @@ -5519,20 +5602,21 @@ bool profiler_add_native_allocation_marker(int aMainThreadId, int64_t aSize, // locking the profiler mutex here could end up causing a deadlock if another // mutex is taken, which the profiler may indirectly need elsewhere. // See bug 1642726 for such a scenario. - // So instead we bail out if the mutex is already locked. Native allocations - // are statistically sampled anyway, so missing a few because of this is - // acceptable. - if (gPSMutex.IsLockedOnCurrentThread()) { + // So instead we only try to lock, and bail out if the mutex is already + // locked. Native allocations are statistically sampled anyway, so missing a + // few because of this is acceptable. + PSAutoTryLock tryLock(gPSMutex); + if (!tryLock.IsLocked()) { return false; } AUTO_PROFILER_STATS(add_marker_with_NativeAllocationMarkerPayload); maybelocked_profiler_add_marker_for_thread( aMainThreadId, JS::ProfilingCategoryPair::OTHER, "Native allocation", - NativeAllocationMarkerPayload(TimeStamp::Now(), aSize, aMemoryAddress, - profiler_current_thread_id(), - profiler_get_backtrace()), - nullptr); + NativeAllocationMarkerPayload( + TimeStamp::Now(), aSize, aMemoryAddress, profiler_current_thread_id(), + locked_profiler_get_backtrace(tryLock.LockRef())), + &tryLock.LockRef()); return true; } diff --git a/tools/profiler/moz.build b/tools/profiler/moz.build index 7d3bdcea0d72..0f5f44ed6d1e 100644 --- a/tools/profiler/moz.build +++ b/tools/profiler/moz.build @@ -139,10 +139,6 @@ EXPORTS.mozilla += [ 'public/ProfileJSONWriter.h', # vm/GeckoProfiler.h needs to include this and doesn't like #include "ProfilerCounts.h" 'public/ProfilerCounts.h', - 'public/ProfilerMarkers.h', - 'public/ProfilerMarkersDetail.h', - 'public/ProfilerMarkersPrerequisites.h', - 'public/ProfilerMarkerTypes.h', ] if CONFIG['MOZ_VTUNE']: diff --git a/tools/profiler/public/GeckoProfiler.h b/tools/profiler/public/GeckoProfiler.h index 0b9d5ab7e656..aefc660d6382 100644 --- a/tools/profiler/public/GeckoProfiler.h +++ b/tools/profiler/public/GeckoProfiler.h @@ -18,21 +18,16 @@ // everything in here is also safe to include unconditionally, and only defines // empty macros if MOZ_GECKO_PROFILER is unset -#include "BaseProfiler.h" #include "mozilla/ProfilerCounts.h" -// ProfilerMarkers.h is #included in the middle of this header! -// #include "mozilla/ProfilerMarkers.h" - #ifndef MOZ_GECKO_PROFILER -# include "mozilla/ProfilerMarkers.h" # include "mozilla/UniquePtr.h" // This file can be #included unconditionally. However, everything within this // file must be guarded by a #ifdef MOZ_GECKO_PROFILER, *except* for the -// following macros and functions, which encapsulate the most common operations -// and thus avoid the need for many #ifdefs. +// following macros, which encapsulate the most common operations and thus +// avoid the need for many #ifdefs. # define AUTO_PROFILER_INIT # define AUTO_PROFILER_INIT2 @@ -82,14 +77,8 @@ # define AUTO_PROFILER_TEXT_MARKER_DOCSHELL_CAUSE( \ markerName, text, categoryPair, docShell, cause) -// Function stubs for when MOZ_GECKO_PROFILER is not defined. - struct ProfilerBacktrace {}; using UniqueProfilerBacktrace = mozilla::UniquePtr; - -// Get/Capture-backtrace functions can return nullptr or false, the result -// should be fed to another empty macro or stub anyway. - static inline UniqueProfilerBacktrace profiler_get_backtrace() { return nullptr; } @@ -97,17 +86,14 @@ static inline UniqueProfilerBacktrace profiler_get_backtrace() { namespace mozilla { class ProfileChunkedBuffer; } // namespace mozilla -static inline bool profiler_capture_backtrace_into( +static inline bool profiler_capture_backtrace( mozilla::ProfileChunkedBuffer& aChunkedBuffer) { return false; } -static inline mozilla::UniquePtr -profiler_capture_backtrace() { - return nullptr; -} #else // !MOZ_GECKO_PROFILER +# include "BaseProfiler.h" # include "js/AllocationRecording.h" # include "js/ProfilingFrameIterator.h" # include "js/ProfilingStack.h" @@ -698,22 +684,10 @@ struct ProfilerBacktraceDestructor { using UniqueProfilerBacktrace = mozilla::UniquePtr; -// Immediately capture the current thread's call stack, store it in the provided -// buffer (usually to avoid allocations if you can construct the buffer on the -// stack). Returns false if unsuccessful, or if the profiler is inactive. -bool profiler_capture_backtrace_into( - mozilla::ProfileChunkedBuffer& aChunkedBuffer); - -// Immediately capture the current thread's call stack, and return it in a -// ProfileChunkedBuffer (usually for later use in MarkerStack::TakeBacktrace()). -// May be null if unsuccessful, or if the profiler is inactive. -mozilla::UniquePtr profiler_capture_backtrace(); - -// Immediately capture the current thread's call stack, and return it in a -// ProfilerBacktrace (usually for later use in marker function that take a -// ProfilerBacktrace). May be null if unsuccessful, or if the profiler is -// inactive. +// Immediately capture the current thread's call stack and return it. A no-op +// if the profiler is inactive. UniqueProfilerBacktrace profiler_get_backtrace(); +bool profiler_capture_backtrace(mozilla::ProfileChunkedBuffer& aChunkedBuffer); struct ProfilerStats { unsigned n = 0; @@ -761,10 +735,6 @@ struct ProfilerBufferInfo { // buffer is being written to, and how much data is visible. mozilla::Maybe profiler_get_buffer_info(); -// ProfilerMarkers.h requires some stuff from this header. -// TODO: Move common stuff to shared header, and move this #include to the top. -# include "mozilla/ProfilerMarkers.h" - //--------------------------------------------------------------------------- // Put profiling data into the profiler (labels and markers) //--------------------------------------------------------------------------- diff --git a/tools/profiler/public/ProfilerMarkerTypes.h b/tools/profiler/public/ProfilerMarkerTypes.h deleted file mode 100644 index e54e05d18372..000000000000 --- a/tools/profiler/public/ProfilerMarkerTypes.h +++ /dev/null @@ -1,437 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef ProfilerMarkerTypes_h -#define ProfilerMarkerTypes_h - -// This header contains common marker type definitions that rely on xpcom. -// -// It #include's "mozilla/BaseProfilerMarkerTypess.h" and "ProfilerMarkers.h", -// see these files for more marker types, how to define other marker types, and -// how to add markers to the profiler buffers. - -// /!\ WORK IN PROGRESS /!\ -// This file contains draft marker definitions, but most are not used yet. -// Further work is needed to complete these definitions, and use them to convert -// existing PROFILER_ADD_MARKER calls. See meta bug 1661394. - -#include "mozilla/BaseProfilerMarkerTypes.h" -#include "mozilla/ProfilerMarkers.h" - -#ifdef MOZ_GECKO_PROFILER - -# include "gfxASurface.h" -# include "js/AllocationRecording.h" -# include "js/ProfilingFrameIterator.h" -# include "js/Utility.h" -# include "Layers.h" -# include "mozilla/ipc/ProtocolUtils.h" -# include "mozilla/net/HttpBaseChannel.h" -# include "mozilla/Preferences.h" -# include "mozilla/ServoTraversalStatistics.h" - -namespace geckoprofiler::markers { - -// Import some common markers from mozilla::baseprofiler::markers. -using Tracing = mozilla::baseprofiler::markers::Tracing; -using FileIO = mozilla::baseprofiler::markers::FileIO; -using UserTimingMark = mozilla::baseprofiler::markers::UserTimingMark; -using UserTimingMeasure = mozilla::baseprofiler::markers::UserTimingMeasure; -using Hang = mozilla::baseprofiler::markers::Hang; -using LongTask = mozilla::baseprofiler::markers::LongTask; -using Log = mozilla::baseprofiler::markers::Log; -using MediaSample = mozilla::baseprofiler::markers::MediaSample; - -struct Budget { - static constexpr const char* MarkerTypeName() { return "Budget"; } - static void StreamJSONMarkerData(mozilla::JSONWriter& aWriter) {} -}; - -struct DOMEvent { - static constexpr const char* MarkerTypeName() { - // Note: DOMEventMarkerPayload wase originally a sub-class of - // TracingMarkerPayload, so it uses the same payload type. - // TODO: Change to its own distinct type, but this will require front-end - // changes. - return "tracing"; - } - static void StreamJSONMarkerData( - mozilla::JSONWriter& aWriter, - const mozilla::ProfilerString16View& aEventType, - const mozilla::TimeStamp& aEventTimeStamp, - const mozilla::ProfilerString8View& aTracingCategory) { - aWriter.StringProperty( - "eventType", - NS_ConvertUTF16toUTF8(aEventType.Data(), aEventType.Length()).get()); - // Note: This is the event *creation* timestamp, which should be before the - // marker's own timestamp. It is used to compute the event processing - // latency. - mozilla::baseprofiler::WritePropertyTime(aWriter, "timeStamp", - aEventTimeStamp); - // TODO: This is from the old TracingMarkerPayload legacy, is it still - // needed, once the one location where DOMEvent is used has transitioned? - if (aTracingCategory.Length() != 0) { - // Note: This is *not* the MarkerCategory, it's a identifier used to - // combine pairs of markers. This should disappear after "set index" is - // implemented in bug 1661114. - aWriter.StringProperty("category", aTracingCategory.String().c_str()); - } - } -}; - -struct Pref { - static constexpr const char* MarkerTypeName() { return "PreferenceRead"; } - static void StreamJSONMarkerData( - mozilla::JSONWriter& aWriter, - const mozilla::ProfilerString8View& aPrefName, - const mozilla::Maybe& aPrefKind, - const mozilla::Maybe& aPrefType, - const mozilla::ProfilerString8View& aPrefValue, - const mozilla::TimeStamp& aPrefAccessTime) { - // TODO: This looks like it's always `Now()`, so it could probably be - // removed; but the frontend may need updating first. - mozilla::baseprofiler::WritePropertyTime(aWriter, "prefAccessTime", - aPrefAccessTime); - aWriter.StringProperty("prefName", aPrefName.String().c_str()); - aWriter.StringProperty("prefKind", PrefValueKindToString(aPrefKind)); - aWriter.StringProperty("prefType", PrefTypeToString(aPrefType)); - aWriter.StringProperty("prefValue", aPrefValue.String().c_str()); - } - - private: - static const char* PrefValueKindToString( - const mozilla::Maybe& aKind) { - if (aKind) { - return *aKind == mozilla::PrefValueKind::Default ? "Default" : "User"; - } - return "Shared"; - } - - static const char* PrefTypeToString( - const mozilla::Maybe& type) { - if (type) { - switch (*type) { - case mozilla::PrefType::None: - return "None"; - case mozilla::PrefType::Int: - return "Int"; - case mozilla::PrefType::Bool: - return "Bool"; - case mozilla::PrefType::String: - return "String"; - default: - MOZ_ASSERT_UNREACHABLE("Unknown preference type."); - } - } - return "Preference not found"; - } -}; - -// Contains the translation applied to a 2d layer so we can track the layer -// position at each frame. -struct LayerTranslation { - static constexpr const char* MarkerTypeName() { return "LayerTranslation"; } - static void StreamJSONMarkerData(mozilla::JSONWriter& aWriter, - mozilla::layers::Layer* aLayer, - mozilla::gfx::Point aPoint) { - const size_t bufferSize = 32; - char buffer[bufferSize]; - SprintfLiteral(buffer, "%p", aLayer); - - aWriter.StringProperty("layer", buffer); - aWriter.IntProperty("x", aPoint.x); - aWriter.IntProperty("y", aPoint.y); - } -}; - -// Tracks when a vsync occurs according to the HardwareComposer. -struct Vsync { - static constexpr const char* MarkerTypeName() { return "VsyncTimestamp"; } - static void StreamJSONMarkerData(mozilla::JSONWriter& aWriter) {} -}; - -struct Network { - static constexpr const char* MarkerTypeName() { return "Network"; } - static void StreamJSONMarkerData( - mozilla::JSONWriter& aWriter, int64_t aID, - const mozilla::ProfilerString8View& aURI, NetworkLoadType aType, - const mozilla::TimeStamp& aStartTime, const mozilla::TimeStamp& aEndTime, - int32_t aPri, int64_t aCount, - mozilla::net::CacheDisposition aCacheDisposition, uint64_t aInnerWindowID, - const mozilla::net::TimingStruct& aTimings, - const mozilla::ProfilerString8View& aRedirectURI, - UniqueProfilerBacktrace aSource, - const mozilla::ProfilerString8View& aContentType) { - // TODO: Remove these Legacy start&end times when frontend is updated. - mozilla::baseprofiler::WritePropertyTime(aWriter, "startTime", aStartTime); - mozilla::baseprofiler::WritePropertyTime(aWriter, "endTime", aEndTime); - - aWriter.IntProperty("id", aID); - const char* typeString = GetNetworkState(aType); - const char* cacheString = GetCacheState(aCacheDisposition); - // want to use aUniqueStacks.mUniqueStrings->WriteElement(aWriter, - // typeString); - aWriter.StringProperty("status", typeString); - if (cacheString) { - aWriter.StringProperty("cache", cacheString); - } - aWriter.IntProperty("pri", aPri); - if (aCount > 0) { - aWriter.IntProperty("count", aCount); - } - if (aURI.Length() != 0) { - aWriter.StringProperty("URI", aURI.String().c_str()); - } - if (aRedirectURI.Length() != 0) { - aWriter.StringProperty("RedirectURI", aRedirectURI.String().c_str()); - } - - if (aContentType.Length() != 0) { - aWriter.StringProperty("contentType", aContentType.String().c_str()); - } else { - aWriter.NullProperty("contentType"); - } - - if (aType != NetworkLoadType::LOAD_START) { - mozilla::baseprofiler::WritePropertyTime(aWriter, "domainLookupStart", - aTimings.domainLookupStart); - mozilla::baseprofiler::WritePropertyTime(aWriter, "domainLookupEnd", - aTimings.domainLookupEnd); - mozilla::baseprofiler::WritePropertyTime(aWriter, "connectStart", - aTimings.connectStart); - mozilla::baseprofiler::WritePropertyTime(aWriter, "tcpConnectEnd", - aTimings.tcpConnectEnd); - mozilla::baseprofiler::WritePropertyTime(aWriter, "secureConnectionStart", - aTimings.secureConnectionStart); - mozilla::baseprofiler::WritePropertyTime(aWriter, "connectEnd", - aTimings.connectEnd); - mozilla::baseprofiler::WritePropertyTime(aWriter, "requestStart", - aTimings.requestStart); - mozilla::baseprofiler::WritePropertyTime(aWriter, "responseStart", - aTimings.responseStart); - mozilla::baseprofiler::WritePropertyTime(aWriter, "responseEnd", - aTimings.responseEnd); - } - } - - private: - static const char* GetNetworkState(NetworkLoadType aType) { - switch (aType) { - case NetworkLoadType::LOAD_START: - return "STATUS_START"; - case NetworkLoadType::LOAD_STOP: - return "STATUS_STOP"; - case NetworkLoadType::LOAD_REDIRECT: - return "STATUS_REDIRECT"; - } - return ""; - } - - static const char* GetCacheState( - mozilla::net::CacheDisposition aCacheDisposition) { - switch (aCacheDisposition) { - case mozilla::net::kCacheUnresolved: - return "Unresolved"; - case mozilla::net::kCacheHit: - return "Hit"; - case mozilla::net::kCacheHitViaReval: - return "HitViaReval"; - case mozilla::net::kCacheMissedViaReval: - return "MissedViaReval"; - case mozilla::net::kCacheMissed: - return "Missed"; - case mozilla::net::kCacheUnknown: - default: - return nullptr; - } - } -}; - -struct ScreenshotPayload { - static constexpr const char* MarkerTypeName() { - return "CompositorScreenshot"; - } - static void StreamJSONMarkerData( - mozilla::JSONWriter& aWriter, - const mozilla::ProfilerString8View& aScreenshotDataURL, - const mozilla::gfx::IntSize& aWindowSize, uintptr_t aWindowIdentifier) { - // TODO: Use UniqueStacks&Strings - // aUniqueStacks.mUniqueStrings->WriteProperty(aWriter, "url", - // mScreenshotDataURL.get()); - aWriter.StringProperty("url", aScreenshotDataURL.String().c_str()); - - char hexWindowID[32]; - SprintfLiteral(hexWindowID, "0x%" PRIXPTR, aWindowIdentifier); - aWriter.StringProperty("windowID", hexWindowID); - aWriter.DoubleProperty("windowWidth", aWindowSize.width); - aWriter.DoubleProperty("windowHeight", aWindowSize.height); - } -}; - -struct GCSlice { - static constexpr const char* MarkerTypeName() { return "GCSlice"; } - static void StreamJSONMarkerData( - mozilla::JSONWriter& aWriter, - const mozilla::ProfilerString8View& aTimingJSON) { - if (aTimingJSON.Length() != 0) { - // TODO: Is SplicedJSONProperty necessary here? (Guessing yes!) - // aWriter.SplicedJSONProperty("timings", aTimingJSON.String().c_str()); - aWriter.StringProperty("timings", aTimingJSON.String().c_str()); - } else { - aWriter.NullProperty("timings"); - } - } -}; - -struct GCMajor { - static constexpr const char* MarkerTypeName() { return "GCMajor"; } - static void StreamJSONMarkerData( - mozilla::JSONWriter& aWriter, - const mozilla::ProfilerString8View& aTimingJSON) { - if (aTimingJSON.Length() != 0) { - // TODO: Is SplicedJSONProperty necessary here? (Guessing yes!) - // aWriter.SplicedJSONProperty("timings", aTimingJSON.String().c_str()); - aWriter.StringProperty("timings", aTimingJSON.String().c_str()); - } else { - aWriter.NullProperty("timings"); - } - } -}; - -struct GCMinor { - static constexpr const char* MarkerTypeName() { return "GCMinor"; } - static void StreamJSONMarkerData( - mozilla::JSONWriter& aWriter, - const mozilla::ProfilerString8View& aTimingJSON) { - if (aTimingJSON.Length() != 0) { - // TODO: Is SplicedJSONProperty necessary here? (Guessing yes!) - // aWriter.SplicedJSONProperty("nursery", aTimingJSON.String().c_str()); - aWriter.StringProperty("nursery", aTimingJSON.String().c_str()); - } else { - aWriter.NullProperty("nursery"); - } - } -}; - -struct StyleMarkerPayload { - static constexpr const char* MarkerTypeName() { return "Styles"; } - static void StreamJSONMarkerData( - mozilla::JSONWriter& aWriter, - const mozilla::ServoTraversalStatistics& aStats) { - aWriter.StringProperty("category", "Paint"); - aWriter.IntProperty("elementsTraversed", aStats.mElementsTraversed); - aWriter.IntProperty("elementsStyled", aStats.mElementsStyled); - aWriter.IntProperty("elementsMatched", aStats.mElementsMatched); - aWriter.IntProperty("stylesShared", aStats.mStylesShared); - aWriter.IntProperty("stylesReused", aStats.mStylesReused); - } -}; - -class JsAllocationMarkerPayload { - static constexpr const char* MarkerTypeName() { return "JS allocation"; } - static void StreamJSONMarkerData( - mozilla::JSONWriter& aWriter, - const mozilla::ProfilerString16View& aTypeName, - const mozilla::ProfilerString8View& aClassName, - const mozilla::ProfilerString16View& aDescriptiveTypeName, - const mozilla::ProfilerString8View& aCoarseType, uint64_t aSize, - bool aInNursery) { - if (aClassName.Length() != 0) { - aWriter.StringProperty("className", aClassName.String().c_str()); - } - if (aTypeName.Length() != 0) { - aWriter.StringProperty( - "typeName", - NS_ConvertUTF16toUTF8(aTypeName.Data(), aTypeName.Length()).get()); - } - if (aDescriptiveTypeName.Length() != 0) { - aWriter.StringProperty( - "descriptiveTypeName", - NS_ConvertUTF16toUTF8(aDescriptiveTypeName.Data(), - aDescriptiveTypeName.Length()) - .get()); - } - aWriter.StringProperty("coarseType", aCoarseType.String().c_str()); - aWriter.IntProperty("size", aSize); - aWriter.BoolProperty("inNursery", aInNursery); - } -}; - -// This payload is for collecting information about native allocations. There is -// a memory hook into malloc and other memory functions that can sample a subset -// of the allocations. This information is then stored in this payload. -struct NativeAllocationMarkerPayload { - static constexpr const char* MarkerTypeName() { return "Native allocation"; } - static void StreamJSONMarkerData(mozilla::JSONWriter& aWriter, int64_t aSize, - uintptr_t aMemoryAddress, int aThreadId) { - aWriter.IntProperty("size", aSize); - aWriter.IntProperty("memoryAddress", static_cast(aMemoryAddress)); - aWriter.IntProperty("threadId", aThreadId); - } -}; - -struct IPCMarkerPayload { - static constexpr const char* MarkerTypeName() { return "IPC"; } - static void StreamJSONMarkerData(mozilla::JSONWriter& aWriter, - int32_t aOtherPid, int32_t aMessageSeqno, - IPC::Message::msgid_t aMessageType, - mozilla::ipc::Side aSide, - mozilla::ipc::MessageDirection aDirection, - mozilla::ipc::MessagePhase aPhase, - bool aSync, - const mozilla::TimeStamp& aTime) { - // TODO: Remove these Legacy times when frontend is updated. - mozilla::baseprofiler::WritePropertyTime(aWriter, "startTime", aTime); - mozilla::baseprofiler::WritePropertyTime(aWriter, "endTime", aTime); - - using namespace mozilla::ipc; - aWriter.IntProperty("otherPid", aOtherPid); - aWriter.IntProperty("messageSeqno", aMessageSeqno); - aWriter.StringProperty("messageType", - IPC::StringFromIPCMessageType(aMessageType)); - aWriter.StringProperty("side", IPCSideToString(aSide)); - aWriter.StringProperty("direction", aDirection == MessageDirection::eSending - ? "sending" - : "receiving"); - aWriter.StringProperty("phase", IPCPhaseToString(aPhase)); - aWriter.BoolProperty("sync", aSync); - } - - private: - static const char* IPCSideToString(mozilla::ipc::Side aSide) { - switch (aSide) { - case mozilla::ipc::ParentSide: - return "parent"; - case mozilla::ipc::ChildSide: - return "child"; - case mozilla::ipc::UnknownSide: - return "unknown"; - default: - MOZ_ASSERT_UNREACHABLE("Invalid IPC side"); - return ""; - } - } - - static const char* IPCPhaseToString(mozilla::ipc::MessagePhase aPhase) { - switch (aPhase) { - case mozilla::ipc::MessagePhase::Endpoint: - return "endpoint"; - case mozilla::ipc::MessagePhase::TransferStart: - return "transferStart"; - case mozilla::ipc::MessagePhase::TransferEnd: - return "transferEnd"; - default: - MOZ_ASSERT_UNREACHABLE("Invalid IPC phase"); - return ""; - } - } -}; - -} // namespace geckoprofiler::markers - -#endif // MOZ_GECKO_PROFILER - -#endif // ProfilerMarkerTypes_h diff --git a/tools/profiler/public/ProfilerMarkers.h b/tools/profiler/public/ProfilerMarkers.h deleted file mode 100644 index 561db49ad0f4..000000000000 --- a/tools/profiler/public/ProfilerMarkers.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// This header contains definitions necessary to add markers to the Gecko -// Profiler buffer. -// -// It #include's "mozilla/BaseProfilerMarkers.h", see that header for base -// definitions necessary to create marker types. -// -// If common marker types are needed, #include "ProfilerMarkerTypes.h" instead. -// -// But if you want to create your own marker type locally, you can #include this -// header only; look at ProfilerMarkerTypes.h for examples of how to define -// types. -// -// To then record markers: -// - Use `baseprofiler::AddMarker(...)` from -// mozglue or other libraries that are outside of xul, especially if they may -// happen outside of xpcom's lifetime (typically startup, shutdown, or tests). -// - Otherwise #include "ProfilerMarkers.h" instead, and use -// `profiler_add_marker(...)`. -// See these functions for more details. - -#ifndef ProfilerMarkers_h -#define ProfilerMarkers_h - -#include "mozilla/BaseProfilerMarkers.h" -#include "mozilla/ProfilerMarkersDetail.h" - -// TODO: Move common stuff to shared header instead. -#include "GeckoProfiler.h" - -#ifndef MOZ_GECKO_PROFILER - -# define PROFILER_MARKER_UNTYPED(markerName, options) -# define PROFILER_MARKER(markerName, options, MarkerType, ...) -# define PROFILER_MARKER_TEXT(markerName, options, text) - -#else // ndef MOZ_GECKO_PROFILER - -// Bring category names from Base Profiler into the geckoprofiler::category -// namespace, for consistency with other Gecko Profiler identifiers. -namespace geckoprofiler::category { -using namespace ::mozilla::baseprofiler::category; -} - -template -mozilla::ProfileBufferBlockIndex AddMarkerToBuffer( - mozilla::ProfileChunkedBuffer& aBuffer, - const mozilla::ProfilerString8View& aName, - mozilla::MarkerOptions&& aOptions, const Ts&... aTs) { - return mozilla::base_profiler_markers_detail::AddMarkerToBuffer( - aBuffer, aName, std::move(aOptions), ::profiler_capture_backtrace_into, - aTs...); -} - -template -mozilla::ProfileBufferBlockIndex profiler_add_marker( - const mozilla::ProfilerString8View& aName, - mozilla::MarkerOptions&& aOptions, const Ts&... aTs) { - if (!profiler_can_accept_markers()) { - return {}; - } - return ::AddMarkerToBuffer( - profiler_markers_detail::CachedCoreBuffer(), aName, std::move(aOptions), - aTs...); -} - -# define PROFILER_MARKER_UNTYPED(markerName, options) \ - do { \ - AUTO_PROFILER_STATS(add_marker_v2); \ - ::profiler_add_marker<>(markerName, ::geckoprofiler::category::options); \ - } while (false) - -# define PROFILER_MARKER(markerName, options, MarkerType, ...) \ - do { \ - AUTO_PROFILER_STATS(add_marker_v2_with_##MarkerType); \ - ::profiler_add_marker<::geckoprofiler::markers::MarkerType>( \ - markerName, ::geckoprofiler::category::options, ##__VA_ARGS__); \ - } while (false) - -namespace geckoprofiler::markers { -// Most common marker type. Others are in ProfilerMarkerTypes.h. -using Text = ::mozilla::baseprofiler::markers::Text; -} // namespace geckoprofiler::markers - -# define PROFILER_MARKER_TEXT(markerName, options, text) \ - do { \ - AUTO_PROFILER_STATS(add_marker_v2_with_Text); \ - ::profiler_add_marker<::geckoprofiler::markers::Text>( \ - markerName, ::geckoprofiler::category::options, text); \ - } while (false) - -#endif // nfed MOZ_GECKO_PROFILER else - -#endif // ProfilerMarkers_h diff --git a/tools/profiler/public/ProfilerMarkersDetail.h b/tools/profiler/public/ProfilerMarkersDetail.h deleted file mode 100644 index 22396b5d2f64..000000000000 --- a/tools/profiler/public/ProfilerMarkersDetail.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef ProfilerMarkersDetail_h -#define ProfilerMarkersDetail_h - -#ifndef ProfilerMarkers_h -# error "This header should only be #included by ProfilerMarkers.h" -#endif - -#include "mozilla/ProfilerMarkersPrerequisites.h" - -#ifdef MOZ_GECKO_PROFILER - -// ~~ HERE BE DRAGONS ~~ -// -// Everything below is internal implementation detail, you shouldn't need to -// look at it unless working on the profiler code. - -// Header that specializes the (de)serializers for xpcom types. -# include "mozilla/ProfileBufferEntrySerializationGeckoExtensions.h" - -// Implemented in platform.cpp -mozilla::ProfileChunkedBuffer& profiler_get_core_buffer(); - -namespace profiler_markers_detail { - -// Get the core buffer from the profiler, and cache it in a -// non-templated-function static reference. -inline mozilla::ProfileChunkedBuffer& CachedCoreBuffer() { - static mozilla::ProfileChunkedBuffer& coreBuffer = profiler_get_core_buffer(); - return coreBuffer; -} - -} // namespace profiler_markers_detail - -#endif // MOZ_GECKO_PROFILER - -#endif // ProfilerMarkersDetail_h diff --git a/tools/profiler/public/ProfilerMarkersPrerequisites.h b/tools/profiler/public/ProfilerMarkersPrerequisites.h deleted file mode 100644 index 93868b05240b..000000000000 --- a/tools/profiler/public/ProfilerMarkersPrerequisites.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// This header contains basic definitions required to create marker types, and -// to add markers to the profiler buffers. -// -// In most cases, #include "mozilla/ProfilerMarkers.h" instead, or -// #include "mozilla/ProfilerMarkerTypes.h" for common marker types. - -#ifndef ProfilerMarkersPrerequisites_h -#define ProfilerMarkersPrerequisites_h - -#include "mozilla/BaseProfilerMarkersPrerequisites.h" - -#ifdef MOZ_GECKO_PROFILER - -namespace geckoprofiler::markers { - -// Default marker payload types, with no extra information, not even a marker -// type and payload. This is intended for label-only markers. -using NoPayload = ::mozilla::baseprofiler::markers::NoPayload; - -} // namespace geckoprofiler::markers - -#endif // MOZ_GECKO_PROFILER - -#endif // ProfilerMarkersPrerequisites_h diff --git a/tools/profiler/tests/gtest/GeckoProfiler.cpp b/tools/profiler/tests/gtest/GeckoProfiler.cpp index f857786134ab..e4fd5d6ac056 100644 --- a/tools/profiler/tests/gtest/GeckoProfiler.cpp +++ b/tools/profiler/tests/gtest/GeckoProfiler.cpp @@ -10,7 +10,6 @@ // profiler internals. #include "GeckoProfiler.h" -#include "mozilla/ProfilerMarkerTypes.h" #include "platform.h" #include "ProfileBuffer.h" #include "ProfilerMarkerPayload.h" @@ -543,9 +542,6 @@ TEST(GeckoProfiler, Pause) PROFILER_ADD_MARKER("Paused", OTHER); info2 = profiler_get_buffer_info(); ASSERT_TRUE(info1->mRangeEnd == info2->mRangeEnd); - PROFILER_MARKER_UNTYPED("Paused v2", OTHER); - Maybe info3 = profiler_get_buffer_info(); - ASSERT_TRUE(info2->mRangeEnd == info3->mRangeEnd); profiler_resume(); @@ -701,22 +697,6 @@ TEST(GeckoProfiler, Markers) ASSERT_EQ(longstrCut[kMax - 1], 'c'); longstrCut[kMax - 1] = '\0'; - // Test basic markers 2.0. - MOZ_RELEASE_ASSERT( - profiler_add_marker<>("default-templated markers 2.0 with empty options", - geckoprofiler::category::OTHER)); - - PROFILER_MARKER_UNTYPED("default-templated markers 2.0 with option", - OTHER.WithOptions(MarkerStack::TakeBacktrace( - profiler_capture_backtrace()))); - - PROFILER_MARKER("explicitly-default-templated markers 2.0 with empty options", - OTHER, NoPayload); - - MOZ_RELEASE_ASSERT(profiler_add_marker<::geckoprofiler::markers::NoPayload>( - "explicitly-default-templated markers 2.0 with option", - geckoprofiler::category::OTHER)); - // Used in markers below. TimeStamp ts1 = TimeStamp::NowUnfuzzed(); @@ -737,32 +717,16 @@ TEST(GeckoProfiler, Markers) "FileIOMarkerPayload marker", OTHER, FileIOMarkerPayload, ("operation", "source", "filename", ts1, ts2, nullptr)); - MOZ_RELEASE_ASSERT(profiler_add_marker( - "FileIOMarkerPayload marker 2.0", - geckoprofiler::category::OTHER.WithOptions( - MarkerTiming::Interval(ts1, ts2)), - "operation", "source", "filename", MarkerThreadId{})); - PROFILER_ADD_MARKER_WITH_PAYLOAD( "FileIOMarkerPayload marker off-MT", OTHER, FileIOMarkerPayload, ("operation2", "source2", "filename2", ts1, ts2, nullptr, Some(123))); - MOZ_RELEASE_ASSERT(profiler_add_marker( - "FileIOMarkerPayload marker 2.0 off-MT", - geckoprofiler::category::OTHER.WithOptions( - MarkerTiming::Interval(ts1, ts2)), - "operation2", "source2", "filename2", MarkerThreadId{123})); - // Other markers in alphabetical order of payload class names. PROFILER_ADD_MARKER_WITH_PAYLOAD( "DOMEventMarkerPayload marker", OTHER, DOMEventMarkerPayload, (u"dom event"_ns, ts1, "category", TRACING_EVENT, mozilla::Nothing())); - MOZ_RELEASE_ASSERT(profiler_add_marker( - "DOMEventMarkerPayload marker 2.0", geckoprofiler::category::OTHER, - u"dom event"_ns, ts1, "category")); - { const char gcMajorJSON[] = "42"; const auto len = strlen(gcMajorJSON); @@ -865,41 +829,6 @@ TEST(GeckoProfiler, Markers) mozilla::ipc::MessageDirection::eSending, mozilla::ipc::MessagePhase::Endpoint, false, ts1)); - MOZ_RELEASE_ASSERT(profiler_add_marker( - "Tracing", geckoprofiler::category::OTHER, "category")); - - MOZ_RELEASE_ASSERT( - profiler_add_marker( - "UserTimingMark", geckoprofiler::category::OTHER, "mark name")); - - MOZ_RELEASE_ASSERT( - profiler_add_marker( - "UserTimingMeasure", geckoprofiler::category::OTHER, "measure name", - Some(mozilla::ProfilerString8View("start")), - Some(mozilla::ProfilerString8View("end")))); - - MOZ_RELEASE_ASSERT(profiler_add_marker( - "Hang", geckoprofiler::category::OTHER)); - - MOZ_RELEASE_ASSERT(profiler_add_marker( - "LongTask", geckoprofiler::category::OTHER)); - - MOZ_RELEASE_ASSERT(profiler_add_marker( - "Text", geckoprofiler::category::OTHER, "Text text")); - - MOZ_RELEASE_ASSERT(profiler_add_marker( - "Log", geckoprofiler::category::OTHER, "module", "log text")); - - MOZ_RELEASE_ASSERT(profiler_add_marker( - "MediaSample", geckoprofiler::category::OTHER, 123, 456)); - - MOZ_RELEASE_ASSERT(profiler_add_marker( - "Budget", geckoprofiler::category::OTHER)); - - MOZ_RELEASE_ASSERT(profiler_add_marker( - "DOMEvent", geckoprofiler::category::OTHER, u"event"_ns, ts2, - "category")); - SpliceableChunkedJSONWriter w; w.Start(); EXPECT_TRUE(::profiler_stream_json_for_this_process(w)); @@ -938,16 +867,9 @@ TEST(GeckoProfiler, Markers) S_M5_gtest7, S_M5_gtest8, S_M5_gtest9, - S_Markers2DefaultEmptyOptions, - S_Markers2DefaultWithOptions, - S_Markers2ExplicitDefaultEmptyOptions, - S_Markers2ExplicitDefaultWithOptions, S_FileIOMarkerPayload, - S_FileIOMarker2, S_FileIOMarkerPayloadOffMT, - S_FileIOMarker2OffMT, S_DOMEventMarkerPayload, - S_DOMEventMarker2, S_GCMajorMarkerPayload, S_GCMinorMarkerPayload, S_GCSliceMarkerPayload, @@ -1152,24 +1074,6 @@ TEST(GeckoProfiler, Markers) } else if (nameString == "M3") { ASSERT_EQ(state, S_M3); state = State(state + 1); - } else if (nameString == - "default-templated markers 2.0 with empty options") { - EXPECT_EQ(state, S_Markers2DefaultEmptyOptions); - state = State(S_Markers2DefaultEmptyOptions + 1); - } else if (nameString == - "default-templated markers 2.0 with option") { - EXPECT_EQ(state, S_Markers2DefaultWithOptions); - state = State(S_Markers2DefaultWithOptions + 1); - } else if (nameString == - "explicitly-default-templated markers 2.0 with empty " - "options") { - EXPECT_EQ(state, S_Markers2ExplicitDefaultEmptyOptions); - state = State(S_Markers2ExplicitDefaultEmptyOptions + 1); - } else if (nameString == - "explicitly-default-templated markers 2.0 with " - "option") { - EXPECT_EQ(state, S_Markers2ExplicitDefaultWithOptions); - state = State(S_Markers2ExplicitDefaultWithOptions + 1); } } else { // root.threads[0].markers.data[i] is an array with 6 elements, @@ -1303,17 +1207,6 @@ TEST(GeckoProfiler, Markers) EXPECT_EQ_JSON(payload["filename"], String, "filename"); EXPECT_FALSE(payload.isMember("threadId")); - } else if (nameString == "FileIOMarkerPayload marker 2.0") { - EXPECT_EQ(state, S_FileIOMarker2); - state = State(S_FileIOMarker2 + 1); - EXPECT_EQ(typeString, "FileIO"); - EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double); - EXPECT_TRUE(payload["stack"].isNull()); - EXPECT_EQ_JSON(payload["operation"], String, "operation"); - EXPECT_EQ_JSON(payload["source"], String, "source"); - EXPECT_EQ_JSON(payload["filename"], String, "filename"); - EXPECT_FALSE(payload.isMember("threadId")); - } else if (nameString == "FileIOMarkerPayload marker off-MT") { EXPECT_EQ(state, S_FileIOMarkerPayloadOffMT); state = State(S_FileIOMarkerPayloadOffMT + 1); @@ -1325,18 +1218,6 @@ TEST(GeckoProfiler, Markers) EXPECT_EQ_JSON(payload["filename"], String, "filename2"); EXPECT_EQ_JSON(payload["threadId"], Int, 123); - } else if (nameString == - "FileIOMarkerPayload marker 2.0 off-MT") { - EXPECT_EQ(state, S_FileIOMarker2OffMT); - state = State(S_FileIOMarker2OffMT + 1); - EXPECT_EQ(typeString, "FileIO"); - EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double); - EXPECT_TRUE(payload["stack"].isNull()); - EXPECT_EQ_JSON(payload["operation"], String, "operation2"); - EXPECT_EQ_JSON(payload["source"], String, "source2"); - EXPECT_EQ_JSON(payload["filename"], String, "filename2"); - EXPECT_EQ_JSON(payload["threadId"], Int, 123); - } else if (nameString == "DOMEventMarkerPayload marker") { EXPECT_EQ(state, S_DOMEventMarkerPayload); state = State(S_DOMEventMarkerPayload + 1); @@ -1347,15 +1228,6 @@ TEST(GeckoProfiler, Markers) EXPECT_EQ_JSON(payload["timeStamp"], Double, ts1Double); EXPECT_EQ_JSON(payload["eventType"], String, "dom event"); - } else if (nameString == "DOMEventMarkerPayload marker 2.0") { - EXPECT_EQ(state, S_DOMEventMarker2); - state = State(S_DOMEventMarker2 + 1); - EXPECT_EQ(typeString, "tracing"); - EXPECT_TRUE(payload["stack"].isNull()); - EXPECT_EQ_JSON(payload["category"], String, "category"); - EXPECT_EQ_JSON(payload["timeStamp"], Double, ts1Double); - EXPECT_EQ_JSON(payload["eventType"], String, "dom event"); - } else if (nameString == "GCMajorMarkerPayload marker") { EXPECT_EQ(state, S_GCMajorMarkerPayload); state = State(S_GCMajorMarkerPayload + 1);