Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / tools / profiler / public / GeckoProfiler.h
bloba663bff70cbed666ee548b0420fdf8835a2423aa
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
23 // dependencies.
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()
54 namespace mozilla {
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() {
69 return nullptr;
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) {
79 return false;
81 static inline mozilla::UniquePtr<mozilla::ProfileChunkedBuffer>
82 profiler_capture_backtrace() {
83 return nullptr;
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"
118 # include "nscore.h"
119 # include "nsINamed.h"
120 # include "nsString.h"
121 # include "nsThreadUtils.h"
123 # include <functional>
124 # include <stdint.h>
126 class ProfilerBacktrace;
127 class ProfilerCodeAddressService;
128 struct JSContext;
130 namespace mozilla {
131 class ProfileBufferControlledChunkManager;
132 class ProfileChunkedBuffer;
133 namespace baseprofiler {
134 class SpliceableJSONWriter;
135 } // namespace baseprofiler
136 } // namespace mozilla
137 class nsIURI;
139 enum class ProfilerError {
140 IsInactive,
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) \
154 do { \
155 char stackTop; \
156 profiler_register_thread(name, &stackTop); \
157 } while (0)
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
176 // document.
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
206 // pause.
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
221 // is allowed.
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
227 // not.
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 {
252 public:
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
270 // returns.
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,
289 uint32_t aFeatures,
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
315 // inactive.
316 UniqueProfilerBacktrace profiler_get_backtrace();
318 struct ProfilerStats {
319 unsigned n = 0;
320 double sum = 0;
321 double min = std::numeric_limits<double>::max();
322 double max = 0;
323 void Count(double v) {
324 ++n;
325 sum += v;
326 if (v < min) {
327 min = v;
329 if (v > max) {
330 max = v;
335 struct ProfilerBufferInfo {
336 // Index of the oldest entry.
337 uint64_t mRangeStart;
338 // Index of the newest entry.
339 uint64_t mRangeEnd;
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
365 // called.
366 void profiler_record_wakeup_count(const nsACString& aProcessType);
368 //---------------------------------------------------------------------------
369 // Output profiles
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
397 // inactive.
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).
402 extern "C" {
403 void profiler_save_profile_to_file(const char* aFilename);
406 //---------------------------------------------------------------------------
407 // RAII classes
408 //---------------------------------------------------------------------------
410 namespace mozilla {
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 {
415 public:
416 explicit AutoProfilerRegisterThread(const char* aName) {
417 profiler_register_thread(aName, this);
420 ~AutoProfilerRegisterThread() { profiler_unregister_thread(); }
422 private:
423 AutoProfilerRegisterThread(const AutoProfilerRegisterThread&) = delete;
424 AutoProfilerRegisterThread& operator=(const AutoProfilerRegisterThread&) =
425 delete;
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