Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / tools / profiler / public / ProfilerState.h
blob02f79e90779b98da72ceaa368700c16d0ab5cb0f
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 // This header contains most functions that give information about the Profiler:
8 // Whether it is active or not, paused, and the selected features.
9 // It is safe to include unconditionally, but uses of structs and functions must
10 // be guarded by `#ifdef MOZ_GECKO_PROFILER`.
12 #ifndef ProfilerState_h
13 #define ProfilerState_h
15 #include <mozilla/DefineEnum.h>
16 #include <mozilla/EnumSet.h>
17 #include "mozilla/ProfilerUtils.h"
18 #include "mozilla/Perfetto.h"
20 #include <functional>
22 //---------------------------------------------------------------------------
23 // Profiler features
24 //---------------------------------------------------------------------------
26 #if defined(__APPLE__) && defined(__aarch64__)
27 # define POWER_HELP "Sample per process power use"
28 #elif defined(__APPLE__) && defined(__x86_64__)
29 # define POWER_HELP \
30 "Record the power used by the entire system with each sample."
31 #elif defined(__linux__) && defined(__x86_64__)
32 # define POWER_HELP \
33 "Record the power used by the entire system with each sample. " \
34 "Only available with Intel CPUs and requires setting " \
35 "the sysctl kernel.perf_event_paranoid to 0."
37 #elif defined(_MSC_VER)
38 # define POWER_HELP \
39 "Record the value of every energy meter available on the system with " \
40 "each sample. Only available on Windows 11 with Intel CPUs."
41 #else
42 # define POWER_HELP "Not supported on this platform."
43 #endif
45 // Higher-order macro containing all the feature info in one place. Define
46 // |MACRO| appropriately to extract the relevant parts. Note that the number
47 // values are used internally only and so can be changed without consequence.
48 // Any changes to this list should also be applied to the feature list in
49 // toolkit/components/extensions/schemas/geckoProfiler.json.
50 // *** Synchronize with lists in BaseProfilerState.h and geckoProfiler.json ***
51 #define PROFILER_FOR_EACH_FEATURE(MACRO) \
52 MACRO(0, "java", Java, "Profile Java code, Android only") \
54 MACRO(1, "js", JS, \
55 "Get the JS engine to expose the JS stack to the profiler") \
57 MACRO(2, "mainthreadio", MainThreadIO, "Add main thread file I/O") \
59 MACRO(3, "fileio", FileIO, \
60 "Add file I/O from all profiled threads, implies mainthreadio") \
62 MACRO(4, "fileioall", FileIOAll, \
63 "Add file I/O from all threads, implies fileio") \
65 MACRO(5, "nomarkerstacks", NoMarkerStacks, \
66 "Markers do not capture stacks, to reduce overhead") \
68 MACRO(6, "screenshots", Screenshots, \
69 "Take a snapshot of the window on every composition") \
71 MACRO(7, "seqstyle", SequentialStyle, \
72 "Disable parallel traversal in styling") \
74 MACRO(8, "stackwalk", StackWalk, \
75 "Walk the C++ stack, not available on all platforms") \
77 MACRO(9, "jsallocations", JSAllocations, \
78 "Have the JavaScript engine track allocations") \
80 MACRO(10, "nostacksampling", NoStackSampling, \
81 "Disable all stack sampling: Cancels \"js\", \"stackwalk\" and " \
82 "labels") \
84 MACRO(11, "nativeallocations", NativeAllocations, \
85 "Collect the stacks from a smaller subset of all native " \
86 "allocations, biasing towards collecting larger allocations") \
88 MACRO(12, "ipcmessages", IPCMessages, \
89 "Have the IPC layer track cross-process messages") \
91 MACRO(13, "audiocallbacktracing", AudioCallbackTracing, \
92 "Audio callback tracing") \
94 MACRO(14, "cpu", CPUUtilization, "CPU utilization") \
96 MACRO(15, "notimerresolutionchange", NoTimerResolutionChange, \
97 "Do not adjust the timer resolution for sampling, so that other " \
98 "Firefox timers do not get affected") \
100 MACRO(16, "cpuallthreads", CPUAllThreads, \
101 "Sample the CPU utilization of all registered threads") \
103 MACRO(17, "samplingallthreads", SamplingAllThreads, \
104 "Sample the stacks of all registered threads") \
106 MACRO(18, "markersallthreads", MarkersAllThreads, \
107 "Record markers from all registered threads") \
109 MACRO(19, "unregisteredthreads", UnregisteredThreads, \
110 "Discover and profile unregistered threads -- beware: expensive!") \
112 MACRO(20, "processcpu", ProcessCPU, \
113 "Sample the CPU utilization of each process") \
115 MACRO(21, "power", Power, POWER_HELP) \
117 MACRO(22, "cpufreq", CPUFrequency, \
118 "Record the clock frequency of " \
119 "every CPU core for every profiler sample.") \
121 MACRO(23, "bandwidth", Bandwidth, \
122 "Record the network bandwidth used for every profiler sample.") \
124 MACRO(24, "memory", Memory, \
125 "Track the memory allocations and deallocations per process over " \
126 "time.") \
128 MACRO(25, "tracing", Tracing, \
129 "Instead of sampling periodically, captures information about " \
130 "every function executed for the duration (JS only)") \
132 MACRO(26, "sandbox", Sandbox, \
133 "Report sandbox syscalls and logs in the " \
134 "profiler.") \
136 MACRO(27, "flows", Flows, \
137 "Include all flow-related markers. These markers show the program" \
138 "better but can cause more overhead in some places than normal.")
140 // *** Synchronize with lists in BaseProfilerState.h and geckoProfiler.json ***
142 struct ProfilerFeature {
143 #define DECLARE(n_, str_, Name_, desc_) \
144 static constexpr uint32_t Name_ = (1u << n_); \
145 [[nodiscard]] static constexpr bool Has##Name_(uint32_t aFeatures) { \
146 return aFeatures & Name_; \
148 static constexpr void Set##Name_(uint32_t& aFeatures) { \
149 aFeatures |= Name_; \
151 static constexpr void Clear##Name_(uint32_t& aFeatures) { \
152 aFeatures &= ~Name_; \
155 // Define a bitfield constant, a getter, and two setters for each feature.
156 PROFILER_FOR_EACH_FEATURE(DECLARE)
158 #undef DECLARE
160 [[nodiscard]] static constexpr bool ShouldInstallMemoryHooks(
161 uint32_t aFeatures) {
162 return ProfilerFeature::HasMemory(aFeatures) ||
163 ProfilerFeature::HasNativeAllocations(aFeatures);
167 // clang-format off
168 MOZ_DEFINE_ENUM_CLASS(ProfilingState,(
169 // A callback will be invoked ...
170 AlreadyActive, // if the profiler is active when the callback is added.
171 RemovingCallback, // when the callback is removed.
172 Started, // after the profiler has started.
173 Pausing, // before the profiler is paused.
174 Resumed, // after the profiler has resumed.
175 GeneratingProfile, // before a profile is created.
176 Stopping, // before the profiler stops (unless restarting afterward).
177 ShuttingDown // before the profiler is shut down.
179 // clang-format on
181 [[nodiscard]] inline static const char* ProfilingStateToString(
182 ProfilingState aProfilingState) {
183 switch (aProfilingState) {
184 case ProfilingState::AlreadyActive:
185 return "Profiler already active";
186 case ProfilingState::RemovingCallback:
187 return "Callback being removed";
188 case ProfilingState::Started:
189 return "Profiler started";
190 case ProfilingState::Pausing:
191 return "Profiler pausing";
192 case ProfilingState::Resumed:
193 return "Profiler resumed";
194 case ProfilingState::GeneratingProfile:
195 return "Generating profile";
196 case ProfilingState::Stopping:
197 return "Profiler stopping";
198 case ProfilingState::ShuttingDown:
199 return "Profiler shutting down";
200 default:
201 MOZ_ASSERT_UNREACHABLE("Unexpected ProfilingState enum value");
202 return "?";
206 using ProfilingStateSet = mozilla::EnumSet<ProfilingState>;
208 [[nodiscard]] constexpr ProfilingStateSet AllProfilingStates() {
209 ProfilingStateSet set;
210 using Value = std::underlying_type_t<ProfilingState>;
211 for (Value stateValue = 0;
212 stateValue <= static_cast<Value>(kHighestProfilingState); ++stateValue) {
213 set += static_cast<ProfilingState>(stateValue);
215 return set;
218 // Type of callbacks to be invoked at certain state changes.
219 // It must NOT call profiler_add/remove_state_change_callback().
220 using ProfilingStateChangeCallback = std::function<void(ProfilingState)>;
222 #ifndef MOZ_GECKO_PROFILER
224 [[nodiscard]] inline bool profiler_is_active() { return false; }
225 [[nodiscard]] inline bool profiler_is_active_and_unpaused() { return false; }
226 [[nodiscard]] inline bool profiler_is_collecting_markers() { return false; }
227 [[nodiscard]] inline bool profiler_is_etw_collecting_markers() { return false; }
228 [[nodiscard]] inline bool profiler_is_perfetto_tracing() { return false; }
229 [[nodiscard]] inline bool profiler_feature_active(uint32_t aFeature) {
230 return false;
232 [[nodiscard]] inline bool profiler_is_locked_on_current_thread() {
233 return false;
235 inline void profiler_add_state_change_callback(
236 ProfilingStateSet aProfilingStateSet,
237 ProfilingStateChangeCallback&& aCallback, uintptr_t aUniqueIdentifier = 0) {
239 inline void profiler_remove_state_change_callback(uintptr_t aUniqueIdentifier) {
242 #else // !MOZ_GECKO_PROFILER
244 # include "mozilla/Atomics.h"
245 # include "mozilla/Maybe.h"
247 # include <stdint.h>
249 namespace mozilla::profiler::detail {
251 // RacyFeatures is only defined in this header file so that its methods can
252 // be inlined into profiler_is_active(). Please do not use anything from the
253 // detail namespace outside the profiler.
255 // Within the profiler's code, the preferred way to check profiler activeness
256 // and features is via ActivePS(). However, that requires locking gPSMutex.
257 // There are some hot operations where absolute precision isn't required, so we
258 // duplicate the activeness/feature state in a lock-free manner in this class.
259 class RacyFeatures {
260 public:
261 static void SetActive(uint32_t aFeatures) {
262 sActiveAndFeatures = Active | aFeatures;
265 static void SetETWCollectionActive() {
266 sActiveAndFeatures |= ETWCollectionEnabled;
269 static void SetETWCollectionInactive() {
270 sActiveAndFeatures &= ~ETWCollectionEnabled;
273 static void SetPerfettoTracingActive() {
274 sActiveAndFeatures |= PerfettoTracingEnabled;
277 static void SetPerfettoTracingInactive() {
278 sActiveAndFeatures &= ~PerfettoTracingEnabled;
281 static void SetInactive() { sActiveAndFeatures = 0; }
283 static void SetPaused() { sActiveAndFeatures |= Paused; }
285 static void SetUnpaused() { sActiveAndFeatures &= ~Paused; }
287 static void SetSamplingPaused() { sActiveAndFeatures |= SamplingPaused; }
289 static void SetSamplingUnpaused() { sActiveAndFeatures &= ~SamplingPaused; }
291 [[nodiscard]] static Maybe<uint32_t> FeaturesIfActive() {
292 if (uint32_t af = sActiveAndFeatures; af & Active) {
293 // Active, remove the Active&Paused bits to get all features.
294 return Some(af & ~(Active | Paused | SamplingPaused));
296 return Nothing();
299 [[nodiscard]] static Maybe<uint32_t> FeaturesIfActiveAndUnpaused() {
300 if (uint32_t af = sActiveAndFeatures; (af & (Active | Paused)) == Active) {
301 // Active but not fully paused, remove the Active and sampling-paused bits
302 // to get all features.
303 return Some(af & ~(Active | SamplingPaused));
305 return Nothing();
308 // This implementation must be kept in sync with `gecko_profiler::is_active`
309 // in the Profiler Rust API.
310 [[nodiscard]] static bool IsActive() {
311 return uint32_t(sActiveAndFeatures) & Active;
314 [[nodiscard]] static bool IsActiveWithFeature(uint32_t aFeature) {
315 uint32_t af = sActiveAndFeatures; // copy it first
316 return (af & Active) && (af & aFeature);
319 [[nodiscard]] static bool IsActiveWithoutFeature(uint32_t aFeature) {
320 uint32_t af = sActiveAndFeatures; // copy it first
321 return (af & Active) && !(af & aFeature);
324 // True if profiler is active, and not fully paused.
325 // Note that periodic sampling *could* be paused!
326 // This implementation must be kept in sync with
327 // `gecko_profiler::can_accept_markers` in the Profiler Rust API.
328 [[nodiscard]] static bool IsActiveAndUnpaused() {
329 uint32_t af = sActiveAndFeatures; // copy it first
330 return (af & Active) && !(af & Paused);
333 // True if profiler is active, and sampling is not paused (though generic
334 // `SetPaused()` or specific `SetSamplingPaused()`).
335 [[nodiscard]] static bool IsActiveAndSamplingUnpaused() {
336 uint32_t af = sActiveAndFeatures; // copy it first
337 return (af & Active) && !(af & (Paused | SamplingPaused));
340 [[nodiscard]] static bool IsCollectingMarkers() {
341 uint32_t af = sActiveAndFeatures; // copy it first
342 return ((af & Active) && !(af & Paused)) || (af & ETWCollectionEnabled) ||
343 (af & PerfettoTracingEnabled);
346 [[nodiscard]] static bool IsETWCollecting() {
347 uint32_t af = sActiveAndFeatures; // copy it first
348 return (af & ETWCollectionEnabled);
351 [[nodiscard]] static bool IsPerfettoTracing() {
352 uint32_t af = sActiveAndFeatures; // copy it first
353 return (af & PerfettoTracingEnabled);
356 private:
357 static constexpr uint32_t Active = 1u << 31;
358 static constexpr uint32_t Paused = 1u << 30;
359 static constexpr uint32_t SamplingPaused = 1u << 29;
360 static constexpr uint32_t ETWCollectionEnabled = 1u << 28;
361 static constexpr uint32_t PerfettoTracingEnabled = 1u << 27;
363 // Ensure Active/Paused don't overlap with any of the feature bits.
364 # define NO_OVERLAP(n_, str_, Name_, desc_) \
365 static_assert(ProfilerFeature::Name_ != SamplingPaused, \
366 "bad feature value");
368 PROFILER_FOR_EACH_FEATURE(NO_OVERLAP);
370 # undef NO_OVERLAP
372 // We combine the active bit with the feature bits so they can be read or
373 // written in a single atomic operation.
374 static Atomic<uint32_t, MemoryOrdering::Relaxed> sActiveAndFeatures;
377 } // namespace mozilla::profiler::detail
379 //---------------------------------------------------------------------------
380 // Get information from the profiler
381 //---------------------------------------------------------------------------
383 // Is the profiler active? Note: the return value of this function can become
384 // immediately out-of-date. E.g. the profile might be active but then
385 // profiler_stop() is called immediately afterward. One common and reasonable
386 // pattern of usage is the following:
388 // if (profiler_is_active()) {
389 // ExpensiveData expensiveData = CreateExpensiveData();
390 // PROFILER_OPERATION(expensiveData);
391 // }
393 // where PROFILER_OPERATION is a no-op if the profiler is inactive. In this
394 // case the profiler_is_active() check is just an optimization -- it prevents
395 // us calling CreateExpensiveData() unnecessarily in most cases, but the
396 // expensive data will end up being created but not used if another thread
397 // stops the profiler between the CreateExpensiveData() and PROFILER_OPERATION
398 // calls.
399 [[nodiscard]] inline bool profiler_is_active() {
400 return mozilla::profiler::detail::RacyFeatures::IsActive();
403 // Same as profiler_is_active(), but also checks if the profiler is not paused.
404 [[nodiscard]] inline bool profiler_is_active_and_unpaused() {
405 return mozilla::profiler::detail::RacyFeatures::IsActiveAndUnpaused();
408 // Same as profiler_is_active_and_unpaused(), but also checks if the ETW is
409 // collecting markers.
410 [[nodiscard]] inline bool profiler_is_collecting_markers() {
411 return mozilla::profiler::detail::RacyFeatures::IsCollectingMarkers();
414 // Reports if our ETW tracelogger is running.
415 [[nodiscard]] inline bool profiler_is_etw_collecting_markers() {
416 return mozilla::profiler::detail::RacyFeatures::IsETWCollecting();
419 [[nodiscard]] inline bool profiler_is_perfetto_tracing() {
420 return mozilla::profiler::detail::RacyFeatures::IsPerfettoTracing();
423 // Is the profiler active and paused? Returns false if the profiler is inactive.
424 [[nodiscard]] bool profiler_is_paused();
426 // Is the profiler active and sampling is paused? Returns false if the profiler
427 // is inactive.
428 [[nodiscard]] bool profiler_is_sampling_paused();
430 // Get all the features supported by the profiler that are accepted by
431 // profiler_start(). The result is the same whether the profiler is active or
432 // not.
433 [[nodiscard]] uint32_t profiler_get_available_features();
435 // Returns the full feature set if the profiler is active.
436 // Note: the return value can become immediately out-of-date, much like the
437 // return value of profiler_is_active().
438 [[nodiscard]] inline mozilla::Maybe<uint32_t> profiler_features_if_active() {
439 return mozilla::profiler::detail::RacyFeatures::FeaturesIfActive();
442 // Returns the full feature set if the profiler is active and unpaused.
443 // Note: the return value can become immediately out-of-date, much like the
444 // return value of profiler_is_active().
445 [[nodiscard]] inline mozilla::Maybe<uint32_t>
446 profiler_features_if_active_and_unpaused() {
447 return mozilla::profiler::detail::RacyFeatures::FeaturesIfActiveAndUnpaused();
450 // Check if a profiler feature (specified via the ProfilerFeature type) is
451 // active. Returns false if the profiler is inactive. Note: the return value
452 // can become immediately out-of-date, much like the return value of
453 // profiler_is_active().
454 [[nodiscard]] bool profiler_feature_active(uint32_t aFeature);
456 // Check if the profiler is active without a feature (specified via the
457 // ProfilerFeature type). Note: the return value can become immediately
458 // out-of-date, much like the return value of profiler_is_active().
459 [[nodiscard]] bool profiler_active_without_feature(uint32_t aFeature);
461 // Returns true if any of the profiler mutexes are currently locked *on the
462 // current thread*. This may be used by re-entrant code that may call profiler
463 // functions while the same of a different profiler mutex is locked, which could
464 // deadlock.
465 [[nodiscard]] bool profiler_is_locked_on_current_thread();
467 // Install a callback to be invoked at any of the given profiling state changes.
468 // An optional non-zero identifier may be given, to allow later removal of the
469 // callback, the caller is responsible for making sure it's really unique (e.g.,
470 // by using a pointer to an object it owns.)
471 void profiler_add_state_change_callback(
472 ProfilingStateSet aProfilingStateSet,
473 ProfilingStateChangeCallback&& aCallback, uintptr_t aUniqueIdentifier = 0);
475 // Remove the callback with the given non-zero identifier.
476 void profiler_remove_state_change_callback(uintptr_t aUniqueIdentifier);
478 #endif // MOZ_GECKO_PROFILER
480 #endif // ProfilerState_h