tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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