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/. */
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.
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.
16 #ifndef GeckoProfiler_h
17 #define GeckoProfiler_h
19 // Everything in here is also safe to include unconditionally, and only defines
20 // empty macros if MOZ_GECKO_PROFILER is unset.
21 // If your file only uses particular APIs (e.g., only markers), please consider
22 // including only the needed headers instead of this one, to reduce compilation
24 #include "BaseProfiler.h"
25 #include "ProfileAdditionalInformation.h"
26 #include "mozilla/ProfilerCounts.h"
27 #include "mozilla/ProfilerLabels.h"
28 #include "mozilla/ProfilerMarkers.h"
29 #include "mozilla/ProfilerState.h"
30 #include "mozilla/ProfilerThreadSleep.h"
31 #include "mozilla/ProfilerThreadState.h"
32 #include "mozilla/ProgressLogger.h"
33 #include "mozilla/Result.h"
34 #include "mozilla/ResultVariant.h"
36 #ifndef MOZ_GECKO_PROFILER
38 # include "mozilla/UniquePtr.h"
40 // This file can be #included unconditionally. However, everything within this
41 // file must be guarded by a #ifdef MOZ_GECKO_PROFILER, *except* for the
42 // following macros and functions, which encapsulate the most common operations
43 // and thus avoid the need for many #ifdefs.
45 # define PROFILER_REGISTER_THREAD(name)
46 # define PROFILER_UNREGISTER_THREAD()
47 # define AUTO_PROFILER_REGISTER_THREAD(name)
49 # define PROFILER_JS_INTERRUPT_CALLBACK()
51 # define PROFILER_SET_JS_CONTEXT(cx)
52 # define PROFILER_CLEAR_JS_CONTEXT()
55 class CycleCollectedJSContext
;
58 // Function stubs for when MOZ_GECKO_PROFILER is not defined.
60 // This won't be used, it's just there to allow the empty definition of
61 // `profiler_get_backtrace`.
62 struct ProfilerBacktrace
{};
63 using UniqueProfilerBacktrace
= mozilla::UniquePtr
<ProfilerBacktrace
>;
65 // Get/Capture-backtrace functions can return nullptr or false, the result
66 // should be fed to another empty macro or stub anyway.
68 static inline UniqueProfilerBacktrace
profiler_get_backtrace() {
72 // This won't be used, it's just there to allow the empty definitions of
73 // `profiler_capture_backtrace_into` and `profiler_capture_backtrace`.
74 struct ProfileChunkedBuffer
{};
76 static inline bool profiler_capture_backtrace_into(
77 mozilla::ProfileChunkedBuffer
& aChunkedBuffer
,
78 mozilla::StackCaptureOptions aCaptureOptions
) {
81 static inline mozilla::UniquePtr
<mozilla::ProfileChunkedBuffer
>
82 profiler_capture_backtrace() {
86 static inline void profiler_set_process_name(
87 const nsACString
& aProcessName
, const nsACString
* aETLDplus1
= nullptr) {}
89 static inline void profiler_received_exit_profile(
90 const nsACString
& aExitProfile
) {}
92 static inline void profiler_register_page(uint64_t aTabID
,
93 uint64_t aInnerWindowID
,
94 const nsCString
& aUrl
,
95 uint64_t aEmbedderInnerWindowID
,
96 bool aIsPrivateBrowsing
) {}
97 static inline void profiler_unregister_page(uint64_t aRegisteredInnerWindowID
) {
100 static inline void GetProfilerEnvVarsForChildProcess(
101 std::function
<void(const char* key
, const char* value
)>&& aSetEnv
) {}
103 static inline void profiler_record_wakeup_count(
104 const nsACString
& aProcessType
) {}
106 #else // !MOZ_GECKO_PROFILER
108 # include "js/ProfilingStack.h"
109 # include "mozilla/Assertions.h"
110 # include "mozilla/Atomics.h"
111 # include "mozilla/Attributes.h"
112 # include "mozilla/BaseProfilerRAIIMacro.h"
113 # include "mozilla/Maybe.h"
114 # include "mozilla/PowerOfTwo.h"
115 # include "mozilla/ThreadLocal.h"
116 # include "mozilla/TimeStamp.h"
117 # include "mozilla/UniquePtr.h"
119 # include "nsINamed.h"
120 # include "nsString.h"
121 # include "nsThreadUtils.h"
123 # include <functional>
126 class ProfilerBacktrace
;
127 class ProfilerCodeAddressService
;
131 class ProfileBufferControlledChunkManager
;
132 class ProfileChunkedBuffer
;
133 namespace baseprofiler
{
134 class SpliceableJSONWriter
;
135 } // namespace baseprofiler
136 } // namespace mozilla
139 enum class ProfilerError
{
141 JsonGenerationFailed
,
144 template <typename T
>
145 using ProfilerResult
= mozilla::Result
<T
, ProfilerError
>;
147 //---------------------------------------------------------------------------
148 // Give information to the profiler
149 //---------------------------------------------------------------------------
151 // Register/unregister threads with the profiler. Both functions operate the
152 // same whether the profiler is active or inactive.
153 # define PROFILER_REGISTER_THREAD(name) \
156 profiler_register_thread(name, &stackTop); \
158 # define PROFILER_UNREGISTER_THREAD() profiler_unregister_thread()
159 ProfilingStack
* profiler_register_thread(const char* name
, void* guessStackTop
);
160 void profiler_unregister_thread();
162 // Registers a DOM Window (the JS global `window`) with the profiler. Each
163 // Window _roughly_ corresponds to a single document loaded within a
164 // browsing context. Both the Window Id and Browser Id are recorded to allow
165 // correlating different Windows loaded within the same tab or frame element.
167 // We register pages for each navigations but we do not register
168 // history.pushState or history.replaceState since they correspond to the same
169 // Inner Window ID. When a browsing context is first loaded, the first url
170 // loaded in it will be about:blank. Because of that, this call keeps the first
171 // non-about:blank registration of window and discards the previous one.
173 // "aTabID" is the BrowserId of that document belongs to.
174 // That's used to determine the tab of that page.
175 // "aInnerWindowID" is the ID of the `window` global object of that
177 // "aUrl" is the URL of the page.
178 // "aEmbedderInnerWindowID" is the inner window id of embedder. It's used to
179 // determine sub documents of a page.
180 // "aIsPrivateBrowsing" is true if this browsing context happens in a
181 // private browsing context.
182 void profiler_register_page(uint64_t aTabID
, uint64_t aInnerWindowID
,
183 const nsCString
& aUrl
,
184 uint64_t aEmbedderInnerWindowID
,
185 bool aIsPrivateBrowsing
);
186 // Unregister page with the profiler.
188 // Take a Inner Window ID and unregister the page entry that has the same ID.
189 void profiler_unregister_page(uint64_t aRegisteredInnerWindowID
);
191 // Remove all registered and unregistered pages in the profiler.
192 void profiler_clear_all_pages();
194 class BaseProfilerCount
;
195 void profiler_add_sampled_counter(BaseProfilerCount
* aCounter
);
196 void profiler_remove_sampled_counter(BaseProfilerCount
* aCounter
);
198 // Register and unregister a thread within a scope.
199 # define AUTO_PROFILER_REGISTER_THREAD(name) \
200 mozilla::AutoProfilerRegisterThread PROFILER_RAII(name)
202 enum class SamplingState
{
203 JustStopped
, // Sampling loop has just stopped without sampling, between the
204 // callback registration and now.
205 SamplingPaused
, // Profiler is active but sampling loop has gone through a
207 NoStackSamplingCompleted
, // A full sampling loop has completed in
208 // no-stack-sampling mode.
209 SamplingCompleted
// A full sampling loop has completed.
212 using PostSamplingCallback
= std::function
<void(SamplingState
)>;
214 // Install a callback to be invoked at the end of the next sampling loop.
215 // - `false` if profiler is not active, `aCallback` will stay untouched.
216 // - `true` if `aCallback` was successfully moved-from into internal storage,
217 // and *will* be invoked at the end of the next sampling cycle. Note that this
218 // will happen on the Sampler thread, and will block further sampling, so
219 // please be mindful not to block for a long time (e.g., just dispatch a
220 // runnable to another thread.) Calling profiler functions from the callback
222 [[nodiscard
]] bool profiler_callback_after_sampling(
223 PostSamplingCallback
&& aCallback
);
225 // Called by the JSRuntime's operation callback. This is used to start profiling
226 // on auxiliary threads. Operates the same whether the profiler is active or
228 # define PROFILER_JS_INTERRUPT_CALLBACK() profiler_js_interrupt_callback()
229 void profiler_js_interrupt_callback();
231 // Set and clear the current thread's JSContext.
232 # define PROFILER_SET_JS_CONTEXT(cx) profiler_set_js_context(cx)
233 # define PROFILER_CLEAR_JS_CONTEXT() profiler_clear_js_context()
234 void profiler_set_js_context(mozilla::CycleCollectedJSContext
* aCx
);
235 void profiler_clear_js_context();
237 //---------------------------------------------------------------------------
238 // Get information from the profiler
239 //---------------------------------------------------------------------------
241 // Get the chunk manager used in the current profiling session, or null.
242 mozilla::ProfileBufferControlledChunkManager
*
243 profiler_get_controlled_chunk_manager();
245 // The number of milliseconds since the process started. Operates the same
246 // whether the profiler is active or inactive.
247 double profiler_time();
249 // An object of this class is passed to profiler_suspend_and_sample_thread().
250 // For each stack frame, one of the Collect methods will be called.
251 class ProfilerStackCollector
{
253 // Some collectors need to worry about possibly overwriting previous
254 // generations of data. If that's not an issue, this can return Nothing,
255 // which is the default behaviour.
256 virtual mozilla::Maybe
<uint64_t> SamplePositionInBuffer() {
257 return mozilla::Nothing();
259 virtual mozilla::Maybe
<uint64_t> BufferRangeStart() {
260 return mozilla::Nothing();
263 // This method will be called once if the thread being suspended is the main
264 // thread. Default behaviour is to do nothing.
265 virtual void SetIsMainThread() {}
267 // WARNING: The target thread is suspended when the Collect methods are
268 // called. Do not try to allocate or acquire any locks, or you could
269 // deadlock. The target thread will have resumed by the time this function
272 virtual void CollectNativeLeafAddr(void* aAddr
) = 0;
274 virtual void CollectJitReturnAddr(void* aAddr
) = 0;
276 virtual void CollectWasmFrame(JS::ProfilingCategoryPair aCategory
,
277 const char* aLabel
) = 0;
279 virtual void CollectProfilingStackFrame(
280 const js::ProfilingStackFrame
& aFrame
) = 0;
283 // This method suspends the thread identified by aThreadId, samples its
284 // profiling stack, JS stack, and (optionally) native stack, passing the
285 // collected frames into aCollector. aFeatures dictates which compiler features
286 // are used. |Leaf| is the only relevant one.
287 // Use `ProfilerThreadId{}` (unspecified) to sample the current thread.
288 void profiler_suspend_and_sample_thread(ProfilerThreadId aThreadId
,
290 ProfilerStackCollector
& aCollector
,
291 bool aSampleNative
= true);
293 struct ProfilerBacktraceDestructor
{
294 void operator()(ProfilerBacktrace
*);
297 using UniqueProfilerBacktrace
=
298 mozilla::UniquePtr
<ProfilerBacktrace
, ProfilerBacktraceDestructor
>;
300 // Immediately capture the current thread's call stack, store it in the provided
301 // buffer (usually to avoid allocations if you can construct the buffer on the
302 // stack). Returns false if unsuccessful, or if the profiler is inactive.
303 bool profiler_capture_backtrace_into(
304 mozilla::ProfileChunkedBuffer
& aChunkedBuffer
,
305 mozilla::StackCaptureOptions aCaptureOptions
);
307 // Immediately capture the current thread's call stack, and return it in a
308 // ProfileChunkedBuffer (usually for later use in MarkerStack::TakeBacktrace()).
309 // May be null if unsuccessful, or if the profiler is inactive.
310 mozilla::UniquePtr
<mozilla::ProfileChunkedBuffer
> profiler_capture_backtrace();
312 // Immediately capture the current thread's call stack, and return it in a
313 // ProfilerBacktrace (usually for later use in marker function that take a
314 // ProfilerBacktrace). May be null if unsuccessful, or if the profiler is
316 UniqueProfilerBacktrace
profiler_get_backtrace();
318 struct ProfilerStats
{
321 double min
= std::numeric_limits
<double>::max();
323 void Count(double v
) {
335 struct ProfilerBufferInfo
{
336 // Index of the oldest entry.
337 uint64_t mRangeStart
;
338 // Index of the newest entry.
340 // Buffer capacity in number of 8-byte entries.
341 uint32_t mEntryCount
;
342 // Sampling stats: Interval between successive samplings.
343 ProfilerStats mIntervalsUs
;
344 // Sampling stats: Total sampling duration. (Split detail below.)
345 ProfilerStats mOverheadsUs
;
346 // Sampling stats: Time to acquire the lock before sampling.
347 ProfilerStats mLockingsUs
;
348 // Sampling stats: Time to discard expired data.
349 ProfilerStats mCleaningsUs
;
350 // Sampling stats: Time to collect counter data.
351 ProfilerStats mCountersUs
;
352 // Sampling stats: Time to sample thread stacks.
353 ProfilerStats mThreadsUs
;
356 // Get information about the current buffer status.
357 // Returns Nothing() if the profiler is inactive.
359 // This information may be useful to a user-interface displaying the current
360 // status of the profiler, allowing the user to get a sense for how fast the
361 // buffer is being written to, and how much data is visible.
362 mozilla::Maybe
<ProfilerBufferInfo
> profiler_get_buffer_info();
364 // Record through glean how many times profiler_thread_wake has been
366 void profiler_record_wakeup_count(const nsACString
& aProcessType
);
368 //---------------------------------------------------------------------------
370 //---------------------------------------------------------------------------
372 // Set a user-friendly process name, used in JSON stream. Allows an optional
373 // detailed name which may include private info (eTLD+1 in fission)
374 void profiler_set_process_name(const nsACString
& aProcessName
,
375 const nsACString
* aETLDplus1
= nullptr);
377 // Record an exit profile from a child process.
378 void profiler_received_exit_profile(const nsACString
& aExitProfile
);
380 // Get the profile encoded as a JSON string. A no-op (returning nullptr) if the
381 // profiler is inactive.
382 // If aIsShuttingDown is true, the current time is included as the process
383 // shutdown time in the JSON's "meta" object.
384 mozilla::UniquePtr
<char[]> profiler_get_profile(double aSinceTime
= 0,
385 bool aIsShuttingDown
= false);
387 // Write the profile for this process (excluding subprocesses) into aWriter.
388 // Returns a failed result if the profiler is inactive.
389 ProfilerResult
<mozilla::ProfileGenerationAdditionalInformation
>
390 profiler_stream_json_for_this_process(
391 mozilla::baseprofiler::SpliceableJSONWriter
& aWriter
, double aSinceTime
= 0,
392 bool aIsShuttingDown
= false,
393 ProfilerCodeAddressService
* aService
= nullptr,
394 mozilla::ProgressLogger aProgressLogger
= {});
396 // Get the profile and write it into a file. A no-op if the profile is
399 // This function is 'extern "C"' so that it is easily callable from a debugger
400 // in a build without debugging information (a workaround for
401 // http://llvm.org/bugs/show_bug.cgi?id=22211).
403 void profiler_save_profile_to_file(const char* aFilename
);
406 //---------------------------------------------------------------------------
408 //---------------------------------------------------------------------------
412 // Convenience class to register and unregister a thread with the profiler.
413 // Needs to be the first object on the stack of the thread.
414 class MOZ_RAII AutoProfilerRegisterThread final
{
416 explicit AutoProfilerRegisterThread(const char* aName
) {
417 profiler_register_thread(aName
, this);
420 ~AutoProfilerRegisterThread() { profiler_unregister_thread(); }
423 AutoProfilerRegisterThread(const AutoProfilerRegisterThread
&) = delete;
424 AutoProfilerRegisterThread
& operator=(const AutoProfilerRegisterThread
&) =
428 // Get the MOZ_PROFILER_STARTUP* environment variables that should be
429 // supplied to a child process that is about to be launched, in order
430 // to make that child process start with the same profiler settings as
431 // in the current process. The given function is invoked once for
432 // each variable to be set.
433 void GetProfilerEnvVarsForChildProcess(
434 std::function
<void(const char* key
, const char* value
)>&& aSetEnv
);
436 } // namespace mozilla
438 #endif // !MOZ_GECKO_PROFILER
440 #endif // GeckoProfiler_h