/* -*- 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/. */ // Markers are useful to delimit something important happening such as the first // paint. Unlike labels, which are only recorded in the profile buffer if a // sample is collected while the label is on the label stack, markers will // always be recorded in the profile buffer. // // 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" #include "mozilla/ProfilerLabels.h" #include "nsJSUtils.h" // for nsJSUtils::GetCurrentlyRunningCodeInnerWindowID class nsIDocShell; namespace geckoprofiler::markers::detail { // Please do not use anything from the detail namespace outside the profiler. #ifdef MOZ_GECKO_PROFILER mozilla::Maybe profiler_get_inner_window_id_from_docshell( nsIDocShell* aDocshell); #else inline mozilla::Maybe profiler_get_inner_window_id_from_docshell( nsIDocShell* aDocshell) { return mozilla::Nothing(); } #endif // MOZ_GECKO_PROFILER } // namespace geckoprofiler::markers::detail // This is a helper function to get the Inner Window ID from DocShell but it's // not a recommended method to get it and it's not encouraged to use this // function. If there is a computed inner window ID, `window`, or `Document` // available in the call site, please use them. Use this function as a last // resort. inline mozilla::MarkerInnerWindowId MarkerInnerWindowIdFromDocShell( nsIDocShell* aDocshell) { mozilla::Maybe id = geckoprofiler::markers::detail:: profiler_get_inner_window_id_from_docshell(aDocshell); if (!id) { return mozilla::MarkerInnerWindowId::NoId(); } return mozilla::MarkerInnerWindowId(*id); } // This is a helper function to get the Inner Window ID from a JS Context but // it's not a recommended method to get it and it's not encouraged to use this // function. If there is a computed inner window ID, `window`, or `Document` // available in the call site, please use them. Use this function as a last // resort. inline mozilla::MarkerInnerWindowId MarkerInnerWindowIdFromJSContext( JSContext* aContext) { return mozilla::MarkerInnerWindowId( nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(aContext)); } // 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; } #ifdef MOZ_GECKO_PROFILER // Forward-declaration. TODO: Move to more common header, see bug 1681416. bool profiler_capture_backtrace_into( mozilla::ProfileChunkedBuffer& aChunkedBuffer, mozilla::StackCaptureOptions aCaptureOptions); // Add a marker to a given buffer. `AddMarker()` and related macros should be // used in most cases, see below for more information about them and the // paramters; This function may be useful when markers need to be recorded in a // local buffer outside of the main profiler buffer. template mozilla::ProfileBufferBlockIndex AddMarkerToBuffer( mozilla::ProfileChunkedBuffer& aBuffer, const mozilla::ProfilerString8View& aName, const mozilla::MarkerCategory& aCategory, mozilla::MarkerOptions&& aOptions, MarkerType aMarkerType, const PayloadArguments&... aPayloadArguments) { AUTO_PROFILER_LABEL("AddMarkerToBuffer", PROFILER); mozilla::Unused << aMarkerType; // Only the empty object type is useful. return mozilla::base_profiler_markers_detail::AddMarkerToBuffer( aBuffer, aName, aCategory, std::move(aOptions), profiler_active_without_feature(ProfilerFeature::NoMarkerStacks) ? ::profiler_capture_backtrace_into : nullptr, aPayloadArguments...); } // Add a marker (without payload) to a given buffer. inline mozilla::ProfileBufferBlockIndex AddMarkerToBuffer( mozilla::ProfileChunkedBuffer& aBuffer, const mozilla::ProfilerString8View& aName, const mozilla::MarkerCategory& aCategory, mozilla::MarkerOptions&& aOptions = {}) { return AddMarkerToBuffer(aBuffer, aName, aCategory, std::move(aOptions), mozilla::baseprofiler::markers::NoPayload{}); } #endif [[nodiscard]] inline bool profiler_thread_is_being_profiled_for_markers() { return profiler_thread_is_being_profiled(ThreadProfilingFeatures::Markers); } [[nodiscard]] inline bool profiler_thread_is_being_profiled_for_markers( const ProfilerThreadId& aThreadId) { return profiler_thread_is_being_profiled(aThreadId, ThreadProfilingFeatures::Markers); } // Add a marker to the Gecko Profiler buffer. // - aName: Main name of this marker. // - aCategory: Category for this marker. // - aOptions: Optional settings (such as timing, inner window id, // backtrace...), see `MarkerOptions` for details. // - aMarkerType: Empty object that specifies the type of marker. // - aPayloadArguments: Arguments expected by this marker type's // ` StreamJSONMarkerData` function. template mozilla::ProfileBufferBlockIndex profiler_add_marker( const mozilla::ProfilerString8View& aName, const mozilla::MarkerCategory& aCategory, mozilla::MarkerOptions&& aOptions, MarkerType aMarkerType, const PayloadArguments&... aPayloadArguments) { #ifndef MOZ_GECKO_PROFILER return {}; #else if (!profiler_thread_is_being_profiled_for_markers( aOptions.ThreadId().ThreadId())) { return {}; } AUTO_PROFILER_LABEL("profiler_add_marker", PROFILER); return ::AddMarkerToBuffer(profiler_get_core_buffer(), aName, aCategory, std::move(aOptions), aMarkerType, aPayloadArguments...); #endif } // Add a marker (without payload) to the Gecko Profiler buffer. inline mozilla::ProfileBufferBlockIndex profiler_add_marker( const mozilla::ProfilerString8View& aName, const mozilla::MarkerCategory& aCategory, mozilla::MarkerOptions&& aOptions = {}) { return profiler_add_marker(aName, aCategory, std::move(aOptions), mozilla::baseprofiler::markers::NoPayload{}); } // Same as `profiler_add_marker()` (without payload). This macro is safe to use // even if MOZ_GECKO_PROFILER is not #defined. #define PROFILER_MARKER_UNTYPED(markerName, categoryName, ...) \ do { \ AUTO_PROFILER_STATS(PROFILER_MARKER_UNTYPED); \ ::profiler_add_marker(markerName, ::geckoprofiler::category::categoryName, \ ##__VA_ARGS__); \ } while (false) // Same as `profiler_add_marker()` (with payload). This macro is safe to use // even if MOZ_GECKO_PROFILER is not #defined. #define PROFILER_MARKER(markerName, categoryName, options, MarkerType, ...) \ do { \ AUTO_PROFILER_STATS(PROFILER_MARKER_with_##MarkerType); \ ::profiler_add_marker(markerName, ::geckoprofiler::category::categoryName, \ options, ::geckoprofiler::markers::MarkerType{}, \ ##__VA_ARGS__); \ } while (false) namespace geckoprofiler::markers { // Most common marker types. Others are in ProfilerMarkerTypes.h. using TextMarker = ::mozilla::baseprofiler::markers::TextMarker; using Tracing = mozilla::baseprofiler::markers::Tracing; } // namespace geckoprofiler::markers // Add a text marker. This macro is safe to use even if MOZ_GECKO_PROFILER is // not #defined. #define PROFILER_MARKER_TEXT(markerName, categoryName, options, text) \ do { \ AUTO_PROFILER_STATS(PROFILER_MARKER_TEXT); \ ::profiler_add_marker(markerName, ::geckoprofiler::category::categoryName, \ options, ::geckoprofiler::markers::TextMarker{}, \ text); \ } while (false) // RAII object that adds a PROFILER_MARKER_TEXT when destroyed; the marker's // timing will be the interval from construction (unless an instant or start // time is already specified in the provided options) until destruction. class MOZ_RAII AutoProfilerTextMarker { public: AutoProfilerTextMarker(const char* aMarkerName, const mozilla::MarkerCategory& aCategory, mozilla::MarkerOptions&& aOptions, const nsACString& aText) : mMarkerName(aMarkerName), mCategory(aCategory), mOptions(std::move(aOptions)), mText(aText) { MOZ_ASSERT(mOptions.Timing().EndTime().IsNull(), "AutoProfilerTextMarker options shouldn't have an end time"); if (profiler_is_active_and_unpaused() && mOptions.Timing().StartTime().IsNull()) { mOptions.Set(mozilla::MarkerTiming::InstantNow()); } } ~AutoProfilerTextMarker() { if (profiler_is_active_and_unpaused()) { AUTO_PROFILER_LABEL("TextMarker", PROFILER); mOptions.TimingRef().SetIntervalEnd(); AUTO_PROFILER_STATS(AUTO_PROFILER_MARKER_TEXT); profiler_add_marker( mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName), mCategory, std::move(mOptions), geckoprofiler::markers::TextMarker{}, mText); } } protected: const char* mMarkerName; mozilla::MarkerCategory mCategory; mozilla::MarkerOptions mOptions; nsCString mText; }; // Creates an AutoProfilerTextMarker RAII object. This macro is safe to use // even if MOZ_GECKO_PROFILER is not #defined. #define AUTO_PROFILER_MARKER_TEXT(markerName, categoryName, options, text) \ AutoProfilerTextMarker PROFILER_RAII( \ markerName, ::mozilla::baseprofiler::category::categoryName, options, \ text) class MOZ_RAII AutoProfilerTracing { public: AutoProfilerTracing(const char* aCategoryString, const char* aMarkerName, mozilla::MarkerCategory aCategoryPair, const mozilla::Maybe& aInnerWindowID) : mCategoryString(aCategoryString), mMarkerName(aMarkerName), mCategoryPair(aCategoryPair), mInnerWindowID(aInnerWindowID) { profiler_add_marker( mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName), mCategoryPair, {mozilla::MarkerTiming::IntervalStart(), mozilla::MarkerInnerWindowId(mInnerWindowID)}, geckoprofiler::markers::Tracing{}, mozilla::ProfilerString8View::WrapNullTerminatedString( mCategoryString)); } AutoProfilerTracing( const char* aCategoryString, const char* aMarkerName, mozilla::MarkerCategory aCategoryPair, mozilla::UniquePtr aBacktrace, const mozilla::Maybe& aInnerWindowID) : mCategoryString(aCategoryString), mMarkerName(aMarkerName), mCategoryPair(aCategoryPair), mInnerWindowID(aInnerWindowID) { profiler_add_marker( mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName), mCategoryPair, {mozilla::MarkerTiming::IntervalStart(), mozilla::MarkerInnerWindowId(mInnerWindowID), mozilla::MarkerStack::TakeBacktrace(std::move(aBacktrace))}, geckoprofiler::markers::Tracing{}, mozilla::ProfilerString8View::WrapNullTerminatedString( mCategoryString)); } ~AutoProfilerTracing() { profiler_add_marker( mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName), mCategoryPair, {mozilla::MarkerTiming::IntervalEnd(), mozilla::MarkerInnerWindowId(mInnerWindowID)}, geckoprofiler::markers::Tracing{}, mozilla::ProfilerString8View::WrapNullTerminatedString( mCategoryString)); } protected: const char* mCategoryString; const char* mMarkerName; const mozilla::MarkerCategory mCategoryPair; const mozilla::Maybe mInnerWindowID; }; // Adds a START/END pair of tracing markers. #define AUTO_PROFILER_TRACING_MARKER(categoryString, markerName, categoryPair) \ AutoProfilerTracing PROFILER_RAII(categoryString, markerName, \ geckoprofiler::category::categoryPair, \ mozilla::Nothing()) #define AUTO_PROFILER_TRACING_MARKER_INNERWINDOWID( \ categoryString, markerName, categoryPair, innerWindowId) \ AutoProfilerTracing PROFILER_RAII(categoryString, markerName, \ geckoprofiler::category::categoryPair, \ mozilla::Some(innerWindowId)) #define AUTO_PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \ categoryPair, docShell) \ AutoProfilerTracing PROFILER_RAII( \ categoryString, markerName, geckoprofiler::category::categoryPair, \ geckoprofiler::markers::detail:: \ profiler_get_inner_window_id_from_docshell(docShell)) #ifdef MOZ_GECKO_PROFILER extern template mozilla::ProfileBufferBlockIndex AddMarkerToBuffer( mozilla::ProfileChunkedBuffer&, const mozilla::ProfilerString8View&, const mozilla::MarkerCategory&, mozilla::MarkerOptions&&, mozilla::baseprofiler::markers::NoPayload); extern template mozilla::ProfileBufferBlockIndex AddMarkerToBuffer( mozilla::ProfileChunkedBuffer&, const mozilla::ProfilerString8View&, const mozilla::MarkerCategory&, mozilla::MarkerOptions&&, mozilla::baseprofiler::markers::TextMarker, const std::string&); extern template mozilla::ProfileBufferBlockIndex profiler_add_marker( const mozilla::ProfilerString8View&, const mozilla::MarkerCategory&, mozilla::MarkerOptions&&, mozilla::baseprofiler::markers::TextMarker, const std::string&); extern template mozilla::ProfileBufferBlockIndex profiler_add_marker( const mozilla::ProfilerString8View&, const mozilla::MarkerCategory&, mozilla::MarkerOptions&&, mozilla::baseprofiler::markers::TextMarker, const nsCString&); extern template mozilla::ProfileBufferBlockIndex profiler_add_marker( const mozilla::ProfilerString8View&, const mozilla::MarkerCategory&, mozilla::MarkerOptions&&, mozilla::baseprofiler::markers::Tracing, const mozilla::ProfilerString8View&); #endif // MOZ_GECKO_PROFILER #endif // ProfilerMarkers_h