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