BaseProfilerMarkers.h (13889B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // Markers are useful to delimit something important happening such as the first 8 // paint. Unlike labels, which are only recorded in the profile buffer if a 9 // sample is collected while the label is on the label stack, markers will 10 // always be recorded in the profile buffer. 11 // 12 // This header contains basic definitions necessary to create marker types, and 13 // to add markers to the profiler buffers. 14 // 15 // If basic marker types are needed, #include 16 // "mozilla/BaseProfilerMarkerTypes.h" instead. 17 // 18 // But if you want to create your own marker type locally, you can #include this 19 // header only; look at mozilla/BaseProfilerMarkerTypes.h for examples of how to 20 // define types, and mozilla/BaseProfilerMarkerPrerequisites.h for some 21 // supporting types. 22 // 23 // To then record markers: 24 // - Use `baseprofiler::AddMarker(...)` from mozglue or other libraries that 25 // are outside of xul, especially if they may happen outside of xpcom's 26 // lifetime (typically startup, shutdown, or tests). 27 // - Otherwise #include "ProfilerMarkers.h" instead, and use 28 // `profiler_add_marker(...)`. 29 // See these functions for more details. 30 31 #ifndef BaseProfilerMarkers_h 32 #define BaseProfilerMarkers_h 33 34 #include "mozilla/BaseProfilerMarkersDetail.h" 35 #include "mozilla/BaseProfilerLabels.h" 36 #include "mozilla/TimeStamp.h" 37 38 #include <functional> 39 #include <string> 40 #include <utility> 41 42 namespace mozilla::baseprofiler { 43 44 #ifdef MOZ_GECKO_PROFILER 45 // Forward-declaration. TODO: Move to more common header, see bug 1681416. 46 MFBT_API bool profiler_capture_backtrace_into( 47 ProfileChunkedBuffer& aChunkedBuffer, StackCaptureOptions aCaptureOptions); 48 49 // Add a marker to a given buffer. `AddMarker()` and related macros should be 50 // used in most cases, see below for more information about them and the 51 // parameters; This function may be useful when markers need to be recorded in a 52 // local buffer outside of the main profiler buffer. 53 template <typename MarkerType, typename... PayloadArguments> 54 ProfileBufferBlockIndex AddMarkerToBuffer( 55 ProfileChunkedBuffer& aBuffer, const ProfilerString8View& aName, 56 const MarkerCategory& aCategory, MarkerOptions&& aOptions, 57 MarkerType aMarkerType, const PayloadArguments&... aPayloadArguments) { 58 (void)aMarkerType; // Only the empty object type is useful. 59 AUTO_BASE_PROFILER_LABEL("baseprofiler::AddMarkerToBuffer", PROFILER); 60 return base_profiler_markers_detail::AddMarkerToBuffer<MarkerType>( 61 aBuffer, aName, aCategory, std::move(aOptions), 62 // Do not capture a stack if the NoMarkerStacks feature is set. 63 profiler_active_without_feature(ProfilerFeature::NoMarkerStacks) 64 ? ::mozilla::baseprofiler::profiler_capture_backtrace_into 65 : nullptr, 66 aPayloadArguments...); 67 } 68 69 // Add a marker (without payload) to a given buffer. 70 inline ProfileBufferBlockIndex AddMarkerToBuffer( 71 ProfileChunkedBuffer& aBuffer, const ProfilerString8View& aName, 72 const MarkerCategory& aCategory, MarkerOptions&& aOptions = {}) { 73 return AddMarkerToBuffer(aBuffer, aName, aCategory, std::move(aOptions), 74 markers::NoPayload{}); 75 } 76 #endif // MOZ_GECKO_PROFILER 77 78 // Add a marker to the Base Profiler buffer. 79 // - aName: Main name of this marker. 80 // - aCategory: Category for this marker. 81 // - aOptions: Optional settings (such as timing, inner window id, 82 // backtrace...), see `MarkerOptions` for details. 83 // - aMarkerType: Empty object that specifies the type of marker. 84 // - aPayloadArguments: Arguments expected by this marker type's 85 // ` StreamJSONMarkerData` function. 86 template <typename MarkerType, typename... PayloadArguments> 87 ProfileBufferBlockIndex AddMarker( 88 const ProfilerString8View& aName, const MarkerCategory& aCategory, 89 MarkerOptions&& aOptions, MarkerType aMarkerType, 90 const PayloadArguments&... aPayloadArguments) { 91 #ifndef MOZ_GECKO_PROFILER 92 return {}; 93 #else 94 // Record base markers whenever the core buffer is in session. 95 // TODO: When profiler_thread_is_being_profiled becomes available from 96 // mozglue, use it instead. 97 ProfileChunkedBuffer& coreBuffer = 98 ::mozilla::baseprofiler::profiler_get_core_buffer(); 99 if (!coreBuffer.IsInSession()) { 100 return {}; 101 } 102 return ::mozilla::baseprofiler::AddMarkerToBuffer( 103 coreBuffer, aName, aCategory, std::move(aOptions), aMarkerType, 104 aPayloadArguments...); 105 #endif 106 } 107 108 // Add a marker (without payload) to the Base Profiler buffer. 109 inline ProfileBufferBlockIndex AddMarker(const ProfilerString8View& aName, 110 const MarkerCategory& aCategory, 111 MarkerOptions&& aOptions = {}) { 112 return AddMarker(aName, aCategory, std::move(aOptions), markers::NoPayload{}); 113 } 114 115 } // namespace mozilla::baseprofiler 116 117 // Same as `AddMarker()` (without payload). This macro is safe to use even if 118 // MOZ_GECKO_PROFILER is not #defined. 119 #define BASE_PROFILER_MARKER_UNTYPED(markerName, categoryName, ...) \ 120 do { \ 121 AUTO_PROFILER_STATS(BASE_PROFILER_MARKER_UNTYPED); \ 122 ::mozilla::baseprofiler::AddMarker( \ 123 markerName, ::mozilla::baseprofiler::category::categoryName, \ 124 ##__VA_ARGS__); \ 125 } while (false) 126 127 // Same as `AddMarker()` (with payload). This macro is safe to use even if 128 // MOZ_GECKO_PROFILER is not #defined. 129 #define BASE_PROFILER_MARKER(markerName, categoryName, options, MarkerType, \ 130 ...) \ 131 do { \ 132 AUTO_PROFILER_STATS(BASE_PROFILER_MARKER_with_##MarkerType); \ 133 ::mozilla::baseprofiler::AddMarker( \ 134 markerName, ::mozilla::baseprofiler::category::categoryName, options, \ 135 ::mozilla::baseprofiler::markers::MarkerType{}, ##__VA_ARGS__); \ 136 } while (false) 137 138 namespace mozilla::baseprofiler::markers { 139 // Most common marker type. Others are in BaseProfilerMarkerTypes.h. 140 struct TextMarker : public BaseMarkerType<TextMarker> { 141 static constexpr const char* Name = "Text"; 142 // It's not possible to add a single meaningful description to this marker 143 // type since it can be used by various different markers. 144 static constexpr const char* Description = nullptr; 145 146 static constexpr bool StoreName = true; 147 148 using MS = MarkerSchema; 149 static constexpr MS::PayloadField PayloadFields[] = 150 // XXX - This is confusingly labeled 'name'. We probably want to fix that. 151 {{"name", MS::InputType::CString, "Details", MS::Format::String, 152 MS::PayloadFlags::Searchable}}; 153 154 static constexpr MS::Location Locations[] = {MS::Location::MarkerChart, 155 MS::Location::MarkerTable}; 156 157 static constexpr const char* ChartLabel = "{marker.data.name}"; 158 static constexpr const char* TableLabel = 159 "{marker.name} - {marker.data.name}"; 160 161 static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter, 162 const ProfilerString8View& aText) { 163 aWriter.StringProperty("name", aText); 164 } 165 }; 166 167 struct TextStackMarker : public BaseMarkerType<TextStackMarker> { 168 static constexpr const char* Name = "TextStack"; 169 // It's not possible to add a single meaningful description to this marker 170 // type since it can be used by various different markers. 171 static constexpr const char* Description = nullptr; 172 173 static constexpr bool StoreName = true; 174 175 using MS = MarkerSchema; 176 static constexpr MS::PayloadField PayloadFields[] = 177 // XXX - This is confusingly labeled 'name'. We probably want to fix that. 178 {{"name", MS::InputType::CString, "Details", MS::Format::String, 179 MS::PayloadFlags::Searchable}}; 180 181 static constexpr MS::Location Locations[] = {MS::Location::MarkerChart, 182 MS::Location::MarkerTable}; 183 184 static constexpr bool IsStackBased = true; 185 186 static constexpr const char* ChartLabel = "{marker.data.name}"; 187 static constexpr const char* TableLabel = 188 "{marker.name} - {marker.data.name}"; 189 190 static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter, 191 const ProfilerString8View& aText) { 192 aWriter.StringProperty("name", aText); 193 } 194 }; 195 196 // Keep this struct in sync with the `gecko_profiler::marker::Tracing` Rust 197 // counterpart. 198 struct Tracing : public BaseMarkerType<Tracing> { 199 static constexpr const char* Name = "tracing"; 200 // It's not possible to add a single meaningful description to this marker 201 // type since it can be used by various different markers. 202 static constexpr const char* Description = nullptr; 203 204 static constexpr bool StoreName = true; 205 206 using MS = MarkerSchema; 207 static constexpr MS::PayloadField PayloadFields[] = { 208 {"category", MS::InputType::CString, "Type", MS::Format::String, 209 MS::PayloadFlags::Searchable}}; 210 211 static constexpr MS::Location Locations[] = {MS::Location::MarkerChart, 212 MS::Location::MarkerTable, 213 MS::Location::TimelineOverview}; 214 215 static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, 216 const ProfilerString8View& aCategory) { 217 if (aCategory.Length() != 0) { 218 aWriter.StringProperty("category", aCategory); 219 } 220 } 221 }; 222 223 // This is the simplest stack based marker 224 struct StackMarker : public BaseMarkerType<StackMarker> { 225 static constexpr const char* Name = "StackMarker"; 226 // It's not possible to add a single meaningful description to this marker 227 // type since it can be used by various different markers. 228 static constexpr const char* Description = nullptr; 229 230 static constexpr bool StoreName = true; 231 232 using MS = MarkerSchema; 233 static constexpr MS::PayloadField PayloadFields[0] = {}; 234 235 static constexpr MS::Location Locations[] = {MS::Location::MarkerChart, 236 MS::Location::MarkerTable, 237 MS::Location::TimelineOverview}; 238 239 static constexpr bool IsStackBased = true; 240 241 static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter) {} 242 }; 243 244 } // namespace mozilla::baseprofiler::markers 245 246 // Add a text marker. This macro is safe to use even if MOZ_GECKO_PROFILER is 247 // not #defined. 248 #define BASE_PROFILER_MARKER_TEXT(markerName, categoryName, options, text) \ 249 do { \ 250 AUTO_PROFILER_STATS(BASE_PROFILER_MARKER_TEXT); \ 251 ::mozilla::baseprofiler::AddMarker( \ 252 markerName, ::mozilla::baseprofiler::category::categoryName, options, \ 253 ::mozilla::baseprofiler::markers::TextMarker{}, text); \ 254 } while (false) 255 256 namespace mozilla::baseprofiler { 257 258 // RAII object that adds a BASE_PROFILER_MARKER_TEXT when destroyed; the 259 // marker's timing will be the interval from construction (unless an instant or 260 // start time is already specified in the provided options) until destruction. 261 class MOZ_RAII AutoProfilerTextMarker { 262 public: 263 AutoProfilerTextMarker(const char* aMarkerName, 264 const MarkerCategory& aCategory, 265 MarkerOptions&& aOptions, const std::string& aText) 266 : mMarkerName(aMarkerName), 267 mCategory(aCategory), 268 mOptions(std::move(aOptions)), 269 mText(aText) { 270 MOZ_ASSERT(mOptions.Timing().EndTime().IsNull(), 271 "AutoProfilerTextMarker options shouldn't have an end time"); 272 if (profiler_is_active_and_unpaused() && 273 mOptions.Timing().StartTime().IsNull()) { 274 mOptions.Set(MarkerTiming::InstantNow()); 275 } 276 } 277 278 ~AutoProfilerTextMarker() { 279 if (profiler_is_active_and_unpaused()) { 280 mOptions.TimingRef().SetIntervalEnd(); 281 AUTO_PROFILER_STATS(AUTO_BASE_PROFILER_MARKER_TEXT); 282 AddMarker(ProfilerString8View::WrapNullTerminatedString(mMarkerName), 283 mCategory, std::move(mOptions), markers::TextStackMarker{}, 284 mText); 285 } 286 } 287 288 protected: 289 const char* mMarkerName; 290 MarkerCategory mCategory; 291 MarkerOptions mOptions; 292 std::string mText; 293 }; 294 295 #ifdef MOZ_GECKO_PROFILER 296 extern template MFBT_API ProfileBufferBlockIndex 297 AddMarker(const ProfilerString8View&, const MarkerCategory&, MarkerOptions&&, 298 markers::TextMarker, const std::string&); 299 300 extern template MFBT_API ProfileBufferBlockIndex 301 AddMarkerToBuffer(ProfileChunkedBuffer&, const ProfilerString8View&, 302 const MarkerCategory&, MarkerOptions&&, markers::NoPayload); 303 304 extern template MFBT_API ProfileBufferBlockIndex AddMarkerToBuffer( 305 ProfileChunkedBuffer&, const ProfilerString8View&, const MarkerCategory&, 306 MarkerOptions&&, markers::TextMarker, const std::string&); 307 #endif // MOZ_GECKO_PROFILER 308 309 } // namespace mozilla::baseprofiler 310 311 // Creates an AutoProfilerTextMarker RAII object. This macro is safe to use 312 // even if MOZ_GECKO_PROFILER is not #defined. 313 #define AUTO_BASE_PROFILER_MARKER_TEXT(markerName, categoryName, options, \ 314 text) \ 315 ::mozilla::baseprofiler::AutoProfilerTextMarker PROFILER_RAII( \ 316 markerName, ::mozilla::baseprofiler::category::categoryName, options, \ 317 text) 318 319 #endif // BaseProfilerMarkers_h