BaseProfiler.h (20784B)
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 // The Gecko Profiler is an always-on profiler that takes fast and low overhead 8 // samples of the program execution using only userspace functionality for 9 // portability. The goal of this module is to provide performance data in a 10 // generic cross-platform way without requiring custom tools or kernel support. 11 // 12 // Samples are collected to form a timeline with optional timeline event 13 // (markers) used for filtering. The samples include both native stacks and 14 // platform-independent "label stack" frames. 15 16 #ifndef BaseProfiler_h 17 #define BaseProfiler_h 18 19 // This file is safe to include unconditionally, and only defines 20 // empty macros if MOZ_GECKO_PROFILER is not set. 21 22 // These headers are also safe to include unconditionally, with empty macros if 23 // MOZ_GECKO_PROFILER is not set. 24 // If your file only uses particular APIs (e.g., only markers), please consider 25 // including only the needed headers instead of this one, to reduce compilation 26 // dependencies. 27 #include "mozilla/BaseProfilerCounts.h" 28 #include "mozilla/BaseProfilerLabels.h" 29 #include "mozilla/BaseProfilerMarkers.h" 30 #include "mozilla/BaseProfilerState.h" 31 32 #ifndef MOZ_GECKO_PROFILER 33 34 # include "mozilla/UniquePtr.h" 35 36 // This file can be #included unconditionally. However, everything within this 37 // file must be guarded by a #ifdef MOZ_GECKO_PROFILER, *except* for the 38 // following macros and functions, which encapsulate the most common operations 39 // and thus avoid the need for many #ifdefs. 40 41 # define AUTO_BASE_PROFILER_INIT \ 42 ::mozilla::baseprofiler::profiler_init_main_thread_id() 43 44 # define BASE_PROFILER_REGISTER_THREAD(name) 45 # define BASE_PROFILER_UNREGISTER_THREAD() 46 # define AUTO_BASE_PROFILER_REGISTER_THREAD(name) 47 48 # define AUTO_BASE_PROFILER_THREAD_SLEEP 49 # define AUTO_BASE_PROFILER_THREAD_WAKE 50 51 // Function stubs for when MOZ_GECKO_PROFILER is not defined. 52 53 namespace mozilla { 54 55 namespace baseprofiler { 56 // This won't be used, it's just there to allow the empty definition of 57 // `profiler_get_backtrace`. 58 struct ProfilerBacktrace {}; 59 using UniqueProfilerBacktrace = UniquePtr<ProfilerBacktrace>; 60 61 // Get/Capture-backtrace functions can return nullptr or false, the result 62 // should be fed to another empty macro or stub anyway. 63 64 static inline UniqueProfilerBacktrace profiler_get_backtrace() { 65 return nullptr; 66 } 67 68 static inline bool profiler_capture_backtrace_into( 69 ProfileChunkedBuffer& aChunkedBuffer, StackCaptureOptions aCaptureOptions) { 70 return false; 71 } 72 73 static inline UniquePtr<ProfileChunkedBuffer> profiler_capture_backtrace() { 74 return nullptr; 75 } 76 77 static inline void profiler_init(void* stackTop) {} 78 79 static inline void profiler_shutdown() {} 80 81 } // namespace baseprofiler 82 } // namespace mozilla 83 84 #else // !MOZ_GECKO_PROFILER 85 86 # include "BaseProfilingStack.h" 87 88 # include "mozilla/Assertions.h" 89 # include "mozilla/Attributes.h" 90 # include "mozilla/BaseProfilerRAIIMacro.h" 91 # include "mozilla/Maybe.h" 92 # include "mozilla/PowerOfTwo.h" 93 # include "mozilla/TimeStamp.h" 94 # include "mozilla/UniquePtr.h" 95 96 # include <functional> 97 # include <stdint.h> 98 # include <string> 99 100 namespace mozilla { 101 102 class MallocAllocPolicy; 103 class ProfileChunkedBuffer; 104 enum class StackCaptureOptions; 105 template <class T, size_t MinInlineCapacity, class AllocPolicy> 106 class Vector; 107 108 namespace baseprofiler { 109 110 class ProfilerBacktrace; 111 class SpliceableJSONWriter; 112 113 //--------------------------------------------------------------------------- 114 // Start and stop the profiler 115 //--------------------------------------------------------------------------- 116 117 static constexpr PowerOfTwo32 BASE_PROFILER_DEFAULT_ENTRIES = 118 # if !defined(GP_PLAT_arm_android) 119 MakePowerOfTwo32<16 * 1024 * 1024>(); // 16M entries = 128MiB 120 # else 121 MakePowerOfTwo32<4 * 1024 * 1024>(); // 4M entries = 32MiB 122 # endif 123 124 // Startup profiling usually need to capture more data, especially on slow 125 // systems. 126 // Note: Keep in sync with GeckoThread.maybeStartGeckoProfiler: 127 // https://searchfox.org/mozilla-central/source/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java 128 static constexpr PowerOfTwo32 BASE_PROFILER_DEFAULT_STARTUP_ENTRIES = 129 # if !defined(GP_PLAT_arm_android) 130 mozilla::MakePowerOfTwo32<64 * 1024 * 1024>(); // 64M entries = 512MiB 131 # else 132 mozilla::MakePowerOfTwo32<16 * 1024 * 1024>(); // 16M entries = 128MiB 133 # endif 134 135 // Note: Keep in sync with GeckoThread.maybeStartGeckoProfiler: 136 // https://searchfox.org/mozilla-central/source/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java 137 # define BASE_PROFILER_DEFAULT_INTERVAL 1 /* millisecond */ 138 # define BASE_PROFILER_MAX_INTERVAL 5000 /* milliseconds */ 139 140 // Initialize the profiler. If MOZ_PROFILER_STARTUP is set the profiler will 141 // also be started. This call must happen before any other profiler calls 142 // (except profiler_start(), which will call profiler_init() if it hasn't 143 // already run). 144 MFBT_API void profiler_init(void* stackTop); 145 146 # define AUTO_BASE_PROFILER_INIT \ 147 ::mozilla::baseprofiler::AutoProfilerInit PROFILER_RAII 148 149 // Clean up the profiler module, stopping it if required. This function may 150 // also save a shutdown profile if requested. No profiler calls should happen 151 // after this point and all profiling stack labels should have been popped. 152 MFBT_API void profiler_shutdown(); 153 154 // Start the profiler -- initializing it first if necessary -- with the 155 // selected options. Stops and restarts the profiler if it is already active. 156 // After starting the profiler is "active". The samples will be recorded in a 157 // circular buffer. 158 // "aCapacity" is the maximum number of 8-byte entries in the profiler's 159 // circular buffer. 160 // "aInterval" the sampling interval, measured in millseconds. 161 // "aFeatures" is the feature set. Features unsupported by this 162 // platform/configuration are ignored. 163 // "aFilters" is the list of thread filters. Threads that do not match any 164 // of the filters are not profiled. A filter matches a thread if 165 // (a) the thread name contains the filter as a case-insensitive 166 // substring, or 167 // (b) the filter is of the form "pid:<n>" where n is the process 168 // id of the process that the thread is running in. 169 // "aDuration" is the duration of entries in the profiler's circular buffer. 170 MFBT_API void profiler_start(PowerOfTwo32 aCapacity, double aInterval, 171 uint32_t aFeatures, const char** aFilters, 172 uint32_t aFilterCount, 173 const Maybe<double>& aDuration = Nothing()); 174 175 // Stop the profiler and discard the profile without saving it. A no-op if the 176 // profiler is inactive. After stopping the profiler is "inactive". 177 MFBT_API void profiler_stop(); 178 179 // If the profiler is inactive, start it. If it's already active, restart it if 180 // the requested settings differ from the current settings. Both the check and 181 // the state change are performed while the profiler state is locked. 182 // The only difference to profiler_start is that the current buffer contents are 183 // not discarded if the profiler is already running with the requested settings. 184 MFBT_API void profiler_ensure_started( 185 PowerOfTwo32 aCapacity, double aInterval, uint32_t aFeatures, 186 const char** aFilters, uint32_t aFilterCount, 187 const Maybe<double>& aDuration = Nothing()); 188 189 //--------------------------------------------------------------------------- 190 // Control the profiler 191 //--------------------------------------------------------------------------- 192 193 // Register/unregister threads with the profiler. Both functions operate the 194 // same whether the profiler is active or inactive. 195 # define BASE_PROFILER_REGISTER_THREAD(name) \ 196 do { \ 197 char stackTop; \ 198 ::mozilla::baseprofiler::profiler_register_thread(name, &stackTop); \ 199 } while (0) 200 # define BASE_PROFILER_UNREGISTER_THREAD() \ 201 ::mozilla::baseprofiler::profiler_unregister_thread() 202 MFBT_API ProfilingStack* profiler_register_thread(const char* name, 203 void* guessStackTop); 204 MFBT_API void profiler_unregister_thread(); 205 206 // Registers a DOM Window (the JS global `window`) with the profiler. Each 207 // Window _roughly_ corresponds to a single document loaded within a 208 // browsing context. Both the Window Id and Browser Id are recorded to allow 209 // correlating different Windows loaded within the same tab or frame element. 210 // 211 // We register pages for each navigations but we do not register 212 // history.pushState or history.replaceState since they correspond to the same 213 // Inner Window ID. When a browsing context is first loaded, the first url 214 // loaded in it will be about:blank. Because of that, this call keeps the first 215 // non-about:blank registration of window and discards the previous one. 216 // 217 // "aTabID" is the BrowserId of that document belongs to. 218 // That's used to determine the tab of that page. 219 // "aInnerWindowID" is the ID of the `window` global object of that 220 // document. 221 // "aUrl" is the URL of the page. 222 // "aEmbedderInnerWindowID" is the inner window id of embedder. It's used to 223 // determine sub documents of a page. 224 MFBT_API void profiler_register_page(uint64_t aTabD, uint64_t aInnerWindowID, 225 const std::string& aUrl, 226 uint64_t aEmbedderInnerWindowID); 227 228 // Unregister page with the profiler. 229 // 230 // Take a Inner Window ID and unregister the page entry that has the same ID. 231 MFBT_API void profiler_unregister_page(uint64_t aRegisteredInnerWindowID); 232 233 // Remove all registered and unregistered pages in the profiler. 234 void profiler_clear_all_pages(); 235 236 class BaseProfilerCount; 237 MFBT_API void profiler_add_sampled_counter(BaseProfilerCount* aCounter); 238 MFBT_API void profiler_remove_sampled_counter(BaseProfilerCount* aCounter); 239 240 // Register and unregister a thread within a scope. 241 # define AUTO_BASE_PROFILER_REGISTER_THREAD(name) \ 242 ::mozilla::baseprofiler::AutoProfilerRegisterThread PROFILER_RAII(name) 243 244 // Pause and resume the profiler. No-ops if the profiler is inactive. While 245 // paused the profile will not take any samples and will not record any data 246 // into its buffers. The profiler remains fully initialized in this state. 247 // This feature will keep JavaScript profiling enabled, thus allowing toggling 248 // the profiler without invalidating the JIT. 249 MFBT_API void profiler_pause(); 250 MFBT_API void profiler_resume(); 251 252 // Only pause and resume the periodic sampling loop, including stack sampling, 253 // counters, and profiling overheads. 254 MFBT_API void profiler_pause_sampling(); 255 MFBT_API void profiler_resume_sampling(); 256 257 // These functions tell the profiler that a thread went to sleep so that we can 258 // avoid sampling it while it's sleeping. Calling profiler_thread_sleep() 259 // twice without an intervening profiler_thread_wake() is an error. All three 260 // functions operate the same whether the profiler is active or inactive. 261 MFBT_API void profiler_thread_sleep(); 262 MFBT_API void profiler_thread_wake(); 263 264 // Mark a thread as asleep/awake within a scope. 265 # define AUTO_BASE_PROFILER_THREAD_SLEEP \ 266 ::mozilla::baseprofiler::AutoProfilerThreadSleep PROFILER_RAII 267 # define AUTO_BASE_PROFILER_THREAD_WAKE \ 268 ::mozilla::baseprofiler::AutoProfilerThreadWake PROFILER_RAII 269 270 //--------------------------------------------------------------------------- 271 // Get information from the profiler 272 //--------------------------------------------------------------------------- 273 274 // Get the params used to start the profiler. Returns 0 and an empty vector 275 // (via outparams) if the profile is inactive. It's possible that the features 276 // returned may be slightly different to those requested due to required 277 // adjustments. 278 MFBT_API void profiler_get_start_params( 279 int* aEntrySize, Maybe<double>* aDuration, double* aInterval, 280 uint32_t* aFeatures, Vector<const char*, 0, MallocAllocPolicy>* aFilters); 281 282 // The number of milliseconds since the process started. Operates the same 283 // whether the profiler is active or inactive. 284 MFBT_API double profiler_time(); 285 286 // An object of this class is passed to profiler_suspend_and_sample_thread(). 287 // For each stack frame, one of the Collect methods will be called. 288 class ProfilerStackCollector { 289 public: 290 // Some collectors need to worry about possibly overwriting previous 291 // generations of data. If that's not an issue, this can return Nothing, 292 // which is the default behaviour. 293 virtual Maybe<uint64_t> SamplePositionInBuffer() { return Nothing(); } 294 virtual Maybe<uint64_t> BufferRangeStart() { return Nothing(); } 295 296 // This method will be called once if the thread being suspended is the main 297 // thread. Default behaviour is to do nothing. 298 virtual void SetIsMainThread() {} 299 300 // WARNING: The target thread is suspended when the Collect methods are 301 // called. Do not try to allocate or acquire any locks, or you could 302 // deadlock. The target thread will have resumed by the time this function 303 // returns. 304 305 virtual void CollectNativeLeafAddr(void* aAddr) = 0; 306 307 virtual void CollectProfilingStackFrame( 308 const ProfilingStackFrame& aFrame) = 0; 309 }; 310 311 // This method suspends the thread identified by aThreadId, samples its 312 // profiling stack, JS stack, and (optionally) native stack, passing the 313 // collected frames into aCollector. aFeatures dictates which compiler features 314 // are used. |Leaf| is the only relevant one. 315 // Use `aThreadId`=0 to sample the current thread. 316 MFBT_API void profiler_suspend_and_sample_thread( 317 int aThreadId, uint32_t aFeatures, ProfilerStackCollector& aCollector, 318 bool aSampleNative = true); 319 320 struct ProfilerBacktraceDestructor { 321 MFBT_API void operator()(ProfilerBacktrace*); 322 }; 323 324 using UniqueProfilerBacktrace = 325 UniquePtr<ProfilerBacktrace, ProfilerBacktraceDestructor>; 326 327 // Immediately capture the current thread's call stack, store it in the provided 328 // buffer (usually to avoid allocations if you can construct the buffer on the 329 // stack). Returns false if unsuccessful, if the profiler is inactive, or if 330 // aCaptureOptions is NoStack. 331 MFBT_API bool profiler_capture_backtrace_into( 332 ProfileChunkedBuffer& aChunkedBuffer, StackCaptureOptions aCaptureOptions); 333 334 // Immediately capture the current thread's call stack, and return it in a 335 // ProfileChunkedBuffer (usually for later use in MarkerStack::TakeBacktrace()). 336 // May be null if unsuccessful, or if the profiler is inactive. 337 MFBT_API UniquePtr<ProfileChunkedBuffer> profiler_capture_backtrace(); 338 339 // Immediately capture the current thread's call stack, and return it in a 340 // ProfilerBacktrace (usually for later use in marker function that take a 341 // ProfilerBacktrace). May be null if unsuccessful, or if the profiler is 342 // inactive. 343 MFBT_API UniqueProfilerBacktrace profiler_get_backtrace(); 344 345 struct ProfilerStats { 346 unsigned n = 0; 347 double sum = 0; 348 double min = std::numeric_limits<double>::max(); 349 double max = 0; 350 void Count(double v) { 351 ++n; 352 sum += v; 353 if (v < min) { 354 min = v; 355 } 356 if (v > max) { 357 max = v; 358 } 359 } 360 }; 361 362 struct ProfilerBufferInfo { 363 // Index of the oldest entry. 364 uint64_t mRangeStart; 365 // Index of the newest entry. 366 uint64_t mRangeEnd; 367 // Buffer capacity in number of 8-byte entries. 368 uint32_t mEntryCount; 369 // Sampling stats: Interval (us) between successive samplings. 370 ProfilerStats mIntervalsUs; 371 // Sampling stats: Total duration (us) of each sampling. (Split detail below.) 372 ProfilerStats mOverheadsUs; 373 // Sampling stats: Time (us) to acquire the lock before sampling. 374 ProfilerStats mLockingsUs; 375 // Sampling stats: Time (us) to discard expired data. 376 ProfilerStats mCleaningsUs; 377 // Sampling stats: Time (us) to collect counter data. 378 ProfilerStats mCountersUs; 379 // Sampling stats: Time (us) to sample thread stacks. 380 ProfilerStats mThreadsUs; 381 }; 382 383 // Get information about the current buffer status. 384 // Returns Nothing() if the profiler is inactive. 385 // 386 // This information may be useful to a user-interface displaying the current 387 // status of the profiler, allowing the user to get a sense for how fast the 388 // buffer is being written to, and how much data is visible. 389 MFBT_API Maybe<ProfilerBufferInfo> profiler_get_buffer_info(); 390 391 } // namespace baseprofiler 392 } // namespace mozilla 393 394 namespace mozilla { 395 namespace baseprofiler { 396 397 //--------------------------------------------------------------------------- 398 // Put profiling data into the profiler (markers) 399 //--------------------------------------------------------------------------- 400 401 MFBT_API void profiler_add_js_marker(const char* aMarkerName, 402 const char* aMarkerText); 403 404 //--------------------------------------------------------------------------- 405 // Output profiles 406 //--------------------------------------------------------------------------- 407 408 // Set a user-friendly process name, used in JSON stream. 409 MFBT_API void profiler_set_process_name(const std::string& aProcessName, 410 const std::string* aETLDplus1); 411 412 // Get the profile encoded as a JSON string. A no-op (returning nullptr) if the 413 // profiler is inactive. 414 // If aIsShuttingDown is true, the current time is included as the process 415 // shutdown time in the JSON's "meta" object. 416 MFBT_API UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0, 417 bool aIsShuttingDown = false, 418 bool aOnlyThreads = false); 419 420 // Write the profile for this process (excluding subprocesses) into aWriter. 421 // Returns false if the profiler is inactive. 422 MFBT_API bool profiler_stream_json_for_this_process( 423 SpliceableJSONWriter& aWriter, double aSinceTime = 0, 424 bool aIsShuttingDown = false, bool aOnlyThreads = false); 425 426 // Get the profile and write it into a file. A no-op if the profile is 427 // inactive. 428 // Prefixed with "base" to avoid clashing with Gecko Profiler's extern "C" 429 // profiler_save_profile_to_file when called from debugger. 430 MFBT_API void baseprofiler_save_profile_to_file(const char* aFilename); 431 432 //--------------------------------------------------------------------------- 433 // RAII classes 434 //--------------------------------------------------------------------------- 435 436 class MOZ_RAII AutoProfilerInit { 437 public: 438 explicit AutoProfilerInit() { profiler_init(this); } 439 440 ~AutoProfilerInit() { profiler_shutdown(); } 441 442 private: 443 }; 444 445 // Convenience class to register and unregister a thread with the profiler. 446 // Needs to be the first object on the stack of the thread. 447 class MOZ_RAII AutoProfilerRegisterThread final { 448 public: 449 explicit AutoProfilerRegisterThread(const char* aName) { 450 profiler_register_thread(aName, this); 451 } 452 453 ~AutoProfilerRegisterThread() { profiler_unregister_thread(); } 454 455 private: 456 AutoProfilerRegisterThread(const AutoProfilerRegisterThread&) = delete; 457 AutoProfilerRegisterThread& operator=(const AutoProfilerRegisterThread&) = 458 delete; 459 }; 460 461 class MOZ_RAII AutoProfilerThreadSleep { 462 public: 463 explicit AutoProfilerThreadSleep() { profiler_thread_sleep(); } 464 465 ~AutoProfilerThreadSleep() { profiler_thread_wake(); } 466 467 private: 468 }; 469 470 // Temporarily wake up the profiling of a thread while servicing events such as 471 // Asynchronous Procedure Calls (APCs). 472 class MOZ_RAII AutoProfilerThreadWake { 473 public: 474 explicit AutoProfilerThreadWake() 475 : mIssuedWake(profiler_thread_is_sleeping()) { 476 if (mIssuedWake) { 477 profiler_thread_wake(); 478 } 479 } 480 481 ~AutoProfilerThreadWake() { 482 if (mIssuedWake) { 483 MOZ_ASSERT(!profiler_thread_is_sleeping()); 484 profiler_thread_sleep(); 485 } 486 } 487 488 private: 489 bool mIssuedWake; 490 }; 491 492 // Get the MOZ_PROFILER_STARTUP* environment variables that should be 493 // supplied to a child process that is about to be launched, in order 494 // to make that child process start with the same profiler settings as 495 // in the current process. The given function is invoked once for 496 // each variable to be set. 497 MFBT_API void GetProfilerEnvVarsForChildProcess( 498 std::function<void(const char* key, const char* value)>&& aSetEnv); 499 500 } // namespace baseprofiler 501 } // namespace mozilla 502 503 #endif // !MOZ_GECKO_PROFILER 504 505 #endif // BaseProfiler_h