summaryrefslogtreecommitdiffstats
path: root/tools/profiler/public/ProfilerState.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /tools/profiler/public/ProfilerState.h
parentInitial commit. (diff)
downloadfirefox-esr-upstream/115.8.0esr.tar.xz
firefox-esr-upstream/115.8.0esr.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/profiler/public/ProfilerState.h')
-rw-r--r--tools/profiler/public/ProfilerState.h399
1 files changed, 399 insertions, 0 deletions
diff --git a/tools/profiler/public/ProfilerState.h b/tools/profiler/public/ProfilerState.h
new file mode 100644
index 0000000000..7a9f3f5c73
--- /dev/null
+++ b/tools/profiler/public/ProfilerState.h
@@ -0,0 +1,399 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This header contains most functions that give information about the Profiler:
+// Whether it is active or not, paused, and the selected features.
+// It is safe to include unconditionally, but uses of structs and functions must
+// be guarded by `#ifdef MOZ_GECKO_PROFILER`.
+
+#ifndef ProfilerState_h
+#define ProfilerState_h
+
+#include <mozilla/DefineEnum.h>
+#include <mozilla/EnumSet.h>
+#include "mozilla/ProfilerUtils.h"
+
+#include <functional>
+
+//---------------------------------------------------------------------------
+// Profiler features
+//---------------------------------------------------------------------------
+
+#if defined(__APPLE__) && defined(__aarch64__)
+# define POWER_HELP "Sample per process power use"
+#elif defined(__APPLE__) && defined(__x86_64__)
+# define POWER_HELP \
+ "Record the power used by the entire system with each sample."
+#elif defined(__linux__) && defined(__x86_64__)
+# define POWER_HELP \
+ "Record the power used by the entire system with each sample. " \
+ "Only available with Intel CPUs and requires setting " \
+ "the sysctl kernel.perf_event_paranoid to 0."
+
+#elif defined(_MSC_VER)
+# define POWER_HELP \
+ "Record the value of every energy meter available on the system with " \
+ "each sample. Only available on Windows 11 with Intel CPUs."
+#else
+# define POWER_HELP "Not supported on this platform."
+#endif
+
+// Higher-order macro containing all the feature info in one place. Define
+// |MACRO| appropriately to extract the relevant parts. Note that the number
+// values are used internally only and so can be changed without consequence.
+// Any changes to this list should also be applied to the feature list in
+// toolkit/components/extensions/schemas/geckoProfiler.json.
+// *** Synchronize with lists in BaseProfilerState.h and geckoProfiler.json ***
+#define PROFILER_FOR_EACH_FEATURE(MACRO) \
+ MACRO(0, "java", Java, "Profile Java code, Android only") \
+ \
+ MACRO(1, "js", JS, \
+ "Get the JS engine to expose the JS stack to the profiler") \
+ \
+ MACRO(2, "mainthreadio", MainThreadIO, "Add main thread file I/O") \
+ \
+ MACRO(3, "fileio", FileIO, \
+ "Add file I/O from all profiled threads, implies mainthreadio") \
+ \
+ MACRO(4, "fileioall", FileIOAll, \
+ "Add file I/O from all threads, implies fileio") \
+ \
+ MACRO(5, "nomarkerstacks", NoMarkerStacks, \
+ "Markers do not capture stacks, to reduce overhead") \
+ \
+ MACRO(6, "screenshots", Screenshots, \
+ "Take a snapshot of the window on every composition") \
+ \
+ MACRO(7, "seqstyle", SequentialStyle, \
+ "Disable parallel traversal in styling") \
+ \
+ MACRO(8, "stackwalk", StackWalk, \
+ "Walk the C++ stack, not available on all platforms") \
+ \
+ MACRO(9, "jsallocations", JSAllocations, \
+ "Have the JavaScript engine track allocations") \
+ \
+ MACRO(10, "nostacksampling", NoStackSampling, \
+ "Disable all stack sampling: Cancels \"js\", \"stackwalk\" and " \
+ "labels") \
+ \
+ MACRO(11, "nativeallocations", NativeAllocations, \
+ "Collect the stacks from a smaller subset of all native " \
+ "allocations, biasing towards collecting larger allocations") \
+ \
+ MACRO(12, "ipcmessages", IPCMessages, \
+ "Have the IPC layer track cross-process messages") \
+ \
+ MACRO(13, "audiocallbacktracing", AudioCallbackTracing, \
+ "Audio callback tracing") \
+ \
+ MACRO(14, "cpu", CPUUtilization, "CPU utilization") \
+ \
+ MACRO(15, "notimerresolutionchange", NoTimerResolutionChange, \
+ "Do not adjust the timer resolution for sampling, so that other " \
+ "Firefox timers do not get affected") \
+ \
+ MACRO(16, "cpuallthreads", CPUAllThreads, \
+ "Sample the CPU utilization of all registered threads") \
+ \
+ MACRO(17, "samplingallthreads", SamplingAllThreads, \
+ "Sample the stacks of all registered threads") \
+ \
+ MACRO(18, "markersallthreads", MarkersAllThreads, \
+ "Record markers from all registered threads") \
+ \
+ MACRO(19, "unregisteredthreads", UnregisteredThreads, \
+ "Discover and profile unregistered threads -- beware: expensive!") \
+ \
+ MACRO(20, "processcpu", ProcessCPU, \
+ "Sample the CPU utilization of each process") \
+ \
+ MACRO(21, "power", Power, POWER_HELP)
+// *** Synchronize with lists in BaseProfilerState.h and geckoProfiler.json ***
+
+struct ProfilerFeature {
+#define DECLARE(n_, str_, Name_, desc_) \
+ static constexpr uint32_t Name_ = (1u << n_); \
+ [[nodiscard]] static constexpr bool Has##Name_(uint32_t aFeatures) { \
+ return aFeatures & Name_; \
+ } \
+ static constexpr void Set##Name_(uint32_t& aFeatures) { \
+ aFeatures |= Name_; \
+ } \
+ static constexpr void Clear##Name_(uint32_t& aFeatures) { \
+ aFeatures &= ~Name_; \
+ }
+
+ // Define a bitfield constant, a getter, and two setters for each feature.
+ PROFILER_FOR_EACH_FEATURE(DECLARE)
+
+#undef DECLARE
+};
+
+// clang-format off
+MOZ_DEFINE_ENUM_CLASS(ProfilingState,(
+ // A callback will be invoked ...
+ AlreadyActive, // if the profiler is active when the callback is added.
+ RemovingCallback, // when the callback is removed.
+ Started, // after the profiler has started.
+ Pausing, // before the profiler is paused.
+ Resumed, // after the profiler has resumed.
+ GeneratingProfile, // before a profile is created.
+ Stopping, // before the profiler stops (unless restarting afterward).
+ ShuttingDown // before the profiler is shut down.
+));
+// clang-format on
+
+[[nodiscard]] inline static const char* ProfilingStateToString(
+ ProfilingState aProfilingState) {
+ switch (aProfilingState) {
+ case ProfilingState::AlreadyActive:
+ return "Profiler already active";
+ case ProfilingState::RemovingCallback:
+ return "Callback being removed";
+ case ProfilingState::Started:
+ return "Profiler started";
+ case ProfilingState::Pausing:
+ return "Profiler pausing";
+ case ProfilingState::Resumed:
+ return "Profiler resumed";
+ case ProfilingState::GeneratingProfile:
+ return "Generating profile";
+ case ProfilingState::Stopping:
+ return "Profiler stopping";
+ case ProfilingState::ShuttingDown:
+ return "Profiler shutting down";
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected ProfilingState enum value");
+ return "?";
+ }
+}
+
+using ProfilingStateSet = mozilla::EnumSet<ProfilingState>;
+
+[[nodiscard]] constexpr ProfilingStateSet AllProfilingStates() {
+ ProfilingStateSet set;
+ using Value = std::underlying_type_t<ProfilingState>;
+ for (Value stateValue = 0;
+ stateValue <= static_cast<Value>(kHighestProfilingState); ++stateValue) {
+ set += static_cast<ProfilingState>(stateValue);
+ }
+ return set;
+}
+
+// Type of callbacks to be invoked at certain state changes.
+// It must NOT call profiler_add/remove_state_change_callback().
+using ProfilingStateChangeCallback = std::function<void(ProfilingState)>;
+
+#ifndef MOZ_GECKO_PROFILER
+
+[[nodiscard]] inline bool profiler_is_active() { return false; }
+[[nodiscard]] inline bool profiler_is_active_and_unpaused() { return false; }
+[[nodiscard]] inline bool profiler_feature_active(uint32_t aFeature) {
+ return false;
+}
+[[nodiscard]] inline bool profiler_is_locked_on_current_thread() {
+ return false;
+}
+inline void profiler_add_state_change_callback(
+ ProfilingStateSet aProfilingStateSet,
+ ProfilingStateChangeCallback&& aCallback, uintptr_t aUniqueIdentifier = 0) {
+}
+inline void profiler_remove_state_change_callback(uintptr_t aUniqueIdentifier) {
+}
+
+#else // !MOZ_GECKO_PROFILER
+
+# include "mozilla/Atomics.h"
+# include "mozilla/Maybe.h"
+
+# include <stdint.h>
+
+namespace mozilla::profiler::detail {
+
+// RacyFeatures is only defined in this header file so that its methods can
+// be inlined into profiler_is_active(). Please do not use anything from the
+// detail namespace outside the profiler.
+
+// Within the profiler's code, the preferred way to check profiler activeness
+// and features is via ActivePS(). However, that requires locking gPSMutex.
+// There are some hot operations where absolute precision isn't required, so we
+// duplicate the activeness/feature state in a lock-free manner in this class.
+class RacyFeatures {
+ public:
+ static void SetActive(uint32_t aFeatures) {
+ sActiveAndFeatures = Active | aFeatures;
+ }
+
+ static void SetInactive() { sActiveAndFeatures = 0; }
+
+ static void SetPaused() { sActiveAndFeatures |= Paused; }
+
+ static void SetUnpaused() { sActiveAndFeatures &= ~Paused; }
+
+ static void SetSamplingPaused() { sActiveAndFeatures |= SamplingPaused; }
+
+ static void SetSamplingUnpaused() { sActiveAndFeatures &= ~SamplingPaused; }
+
+ [[nodiscard]] static mozilla::Maybe<uint32_t> FeaturesIfActive() {
+ if (uint32_t af = sActiveAndFeatures; af & Active) {
+ // Active, remove the Active&Paused bits to get all features.
+ return Some(af & ~(Active | Paused | SamplingPaused));
+ }
+ return Nothing();
+ }
+
+ [[nodiscard]] static mozilla::Maybe<uint32_t> FeaturesIfActiveAndUnpaused() {
+ if (uint32_t af = sActiveAndFeatures; (af & (Active | Paused)) == Active) {
+ // Active but not fully paused, remove the Active and sampling-paused bits
+ // to get all features.
+ return Some(af & ~(Active | SamplingPaused));
+ }
+ return Nothing();
+ }
+
+ // This implementation must be kept in sync with `gecko_profiler::is_active`
+ // in the Profiler Rust API.
+ [[nodiscard]] static bool IsActive() {
+ return uint32_t(sActiveAndFeatures) & Active;
+ }
+
+ [[nodiscard]] static bool IsActiveWithFeature(uint32_t aFeature) {
+ uint32_t af = sActiveAndFeatures; // copy it first
+ return (af & Active) && (af & aFeature);
+ }
+
+ [[nodiscard]] static bool IsActiveWithoutFeature(uint32_t aFeature) {
+ uint32_t af = sActiveAndFeatures; // copy it first
+ return (af & Active) && !(af & aFeature);
+ }
+
+ // True if profiler is active, and not fully paused.
+ // Note that periodic sampling *could* be paused!
+ // This implementation must be kept in sync with
+ // `gecko_profiler::can_accept_markers` in the Profiler Rust API.
+ [[nodiscard]] static bool IsActiveAndUnpaused() {
+ uint32_t af = sActiveAndFeatures; // copy it first
+ return (af & Active) && !(af & Paused);
+ }
+
+ // True if profiler is active, and sampling is not paused (though generic
+ // `SetPaused()` or specific `SetSamplingPaused()`).
+ [[nodiscard]] static bool IsActiveAndSamplingUnpaused() {
+ uint32_t af = sActiveAndFeatures; // copy it first
+ return (af & Active) && !(af & (Paused | SamplingPaused));
+ }
+
+ private:
+ static constexpr uint32_t Active = 1u << 31;
+ static constexpr uint32_t Paused = 1u << 30;
+ static constexpr uint32_t SamplingPaused = 1u << 29;
+
+// Ensure Active/Paused don't overlap with any of the feature bits.
+# define NO_OVERLAP(n_, str_, Name_, desc_) \
+ static_assert(ProfilerFeature::Name_ != SamplingPaused, \
+ "bad feature value");
+
+ PROFILER_FOR_EACH_FEATURE(NO_OVERLAP);
+
+# undef NO_OVERLAP
+
+ // We combine the active bit with the feature bits so they can be read or
+ // written in a single atomic operation. Accesses to this atomic are not
+ // recorded by web replay as they may occur at non-deterministic points.
+ static mozilla::Atomic<uint32_t, mozilla::MemoryOrdering::Relaxed>
+ sActiveAndFeatures;
+};
+
+} // namespace mozilla::profiler::detail
+
+//---------------------------------------------------------------------------
+// Get information from the profiler
+//---------------------------------------------------------------------------
+
+// Is the profiler active? Note: the return value of this function can become
+// immediately out-of-date. E.g. the profile might be active but then
+// profiler_stop() is called immediately afterward. One common and reasonable
+// pattern of usage is the following:
+//
+// if (profiler_is_active()) {
+// ExpensiveData expensiveData = CreateExpensiveData();
+// PROFILER_OPERATION(expensiveData);
+// }
+//
+// where PROFILER_OPERATION is a no-op if the profiler is inactive. In this
+// case the profiler_is_active() check is just an optimization -- it prevents
+// us calling CreateExpensiveData() unnecessarily in most cases, but the
+// expensive data will end up being created but not used if another thread
+// stops the profiler between the CreateExpensiveData() and PROFILER_OPERATION
+// calls.
+[[nodiscard]] inline bool profiler_is_active() {
+ return mozilla::profiler::detail::RacyFeatures::IsActive();
+}
+
+// Same as profiler_is_active(), but also checks if the profiler is not paused.
+[[nodiscard]] inline bool profiler_is_active_and_unpaused() {
+ return mozilla::profiler::detail::RacyFeatures::IsActiveAndUnpaused();
+}
+
+// Is the profiler active and paused? Returns false if the profiler is inactive.
+[[nodiscard]] bool profiler_is_paused();
+
+// Is the profiler active and sampling is paused? Returns false if the profiler
+// is inactive.
+[[nodiscard]] bool profiler_is_sampling_paused();
+
+// Get all the features supported by the profiler that are accepted by
+// profiler_start(). The result is the same whether the profiler is active or
+// not.
+[[nodiscard]] uint32_t profiler_get_available_features();
+
+// Returns the full feature set if the profiler is active.
+// Note: the return value can become immediately out-of-date, much like the
+// return value of profiler_is_active().
+[[nodiscard]] inline mozilla::Maybe<uint32_t> profiler_features_if_active() {
+ return mozilla::profiler::detail::RacyFeatures::FeaturesIfActive();
+}
+
+// Returns the full feature set if the profiler is active and unpaused.
+// Note: the return value can become immediately out-of-date, much like the
+// return value of profiler_is_active().
+[[nodiscard]] inline mozilla::Maybe<uint32_t>
+profiler_features_if_active_and_unpaused() {
+ return mozilla::profiler::detail::RacyFeatures::FeaturesIfActiveAndUnpaused();
+}
+
+// Check if a profiler feature (specified via the ProfilerFeature type) is
+// active. Returns false if the profiler is inactive. Note: the return value
+// can become immediately out-of-date, much like the return value of
+// profiler_is_active().
+[[nodiscard]] bool profiler_feature_active(uint32_t aFeature);
+
+// Check if the profiler is active without a feature (specified via the
+// ProfilerFeature type). Note: the return value can become immediately
+// out-of-date, much like the return value of profiler_is_active().
+[[nodiscard]] bool profiler_active_without_feature(uint32_t aFeature);
+
+// Returns true if any of the profiler mutexes are currently locked *on the
+// current thread*. This may be used by re-entrant code that may call profiler
+// functions while the same of a different profiler mutex is locked, which could
+// deadlock.
+[[nodiscard]] bool profiler_is_locked_on_current_thread();
+
+// Install a callback to be invoked at any of the given profiling state changes.
+// An optional non-zero identifier may be given, to allow later removal of the
+// callback, the caller is responsible for making sure it's really unique (e.g.,
+// by using a pointer to an object it owns.)
+void profiler_add_state_change_callback(
+ ProfilingStateSet aProfilingStateSet,
+ ProfilingStateChangeCallback&& aCallback, uintptr_t aUniqueIdentifier = 0);
+
+// Remove the callback with the given non-zero identifier.
+void profiler_remove_state_change_callback(uintptr_t aUniqueIdentifier);
+
+#endif // MOZ_GECKO_PROFILER
+
+#endif // ProfilerState_h