summaryrefslogtreecommitdiffstats
path: root/tools/profiler/public
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /tools/profiler/public
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/profiler/public')
-rw-r--r--tools/profiler/public/ChildProfilerController.h51
-rw-r--r--tools/profiler/public/GeckoProfiler.h1172
-rw-r--r--tools/profiler/public/GeckoProfilerReporter.h26
-rw-r--r--tools/profiler/public/ProfileBufferEntrySerializationGeckoExtensions.h160
-rw-r--r--tools/profiler/public/ProfileJSONWriter.h19
-rw-r--r--tools/profiler/public/ProfilerChild.h91
-rw-r--r--tools/profiler/public/ProfilerCodeAddressService.h52
-rw-r--r--tools/profiler/public/ProfilerCounts.h274
-rw-r--r--tools/profiler/public/ProfilerMarkerTypes.h41
-rw-r--r--tools/profiler/public/ProfilerMarkers.h294
-rw-r--r--tools/profiler/public/ProfilerMarkersDetail.h42
-rw-r--r--tools/profiler/public/ProfilerMarkersPrerequisites.h30
-rw-r--r--tools/profiler/public/ProfilerParent.h95
-rw-r--r--tools/profiler/public/shared-libraries.h155
14 files changed, 2502 insertions, 0 deletions
diff --git a/tools/profiler/public/ChildProfilerController.h b/tools/profiler/public/ChildProfilerController.h
new file mode 100644
index 0000000000..8be428b18f
--- /dev/null
+++ b/tools/profiler/public/ChildProfilerController.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef ChildProfilerController_h
+#define ChildProfilerController_h
+
+#include "base/process.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
+#include "nsStringFwd.h"
+
+namespace mozilla {
+
+class ProfilerChild;
+class PProfilerChild;
+class PProfilerParent;
+
+// ChildProfilerController manages the setup and teardown of ProfilerChild.
+// It's used on the main thread.
+// It manages a background thread that ProfilerChild runs on.
+class ChildProfilerController final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChildProfilerController)
+
+ static already_AddRefed<ChildProfilerController> Create(
+ mozilla::ipc::Endpoint<PProfilerChild>&& aEndpoint);
+
+ [[nodiscard]] nsCString GrabShutdownProfileAndShutdown();
+ void Shutdown();
+
+ private:
+ ChildProfilerController();
+ ~ChildProfilerController();
+ void Init(mozilla::ipc::Endpoint<PProfilerChild>&& aEndpoint);
+ void ShutdownAndMaybeGrabShutdownProfileFirst(nsCString* aOutShutdownProfile);
+
+ // Called on mThread:
+ void SetupProfilerChild(mozilla::ipc::Endpoint<PProfilerChild>&& aEndpoint);
+ void ShutdownProfilerChild(nsCString* aOutShutdownProfile);
+
+ RefPtr<ProfilerChild> mProfilerChild; // only accessed on mThread
+ RefPtr<nsIThread> mThread;
+};
+
+} // namespace mozilla
+
+#endif // ChildProfilerController_h
diff --git a/tools/profiler/public/GeckoProfiler.h b/tools/profiler/public/GeckoProfiler.h
new file mode 100644
index 0000000000..7433a3d2ea
--- /dev/null
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -0,0 +1,1172 @@
+/* -*- 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/. */
+
+// The Gecko Profiler is an always-on profiler that takes fast and low overhead
+// samples of the program execution using only userspace functionality for
+// portability. The goal of this module is to provide performance data in a
+// generic cross-platform way without requiring custom tools or kernel support.
+//
+// Samples are collected to form a timeline with optional timeline event
+// (markers) used for filtering. The samples include both native stacks and
+// platform-independent "label stack" frames.
+
+#ifndef GeckoProfiler_h
+#define GeckoProfiler_h
+
+// everything in here is also safe to include unconditionally, and only defines
+// empty macros if MOZ_GECKO_PROFILER is unset
+#include "BaseProfiler.h"
+#include "mozilla/ProfilerCounts.h"
+
+// ProfilerMarkers.h is #included in the middle of this header!
+// #include "mozilla/ProfilerMarkers.h"
+
+#ifndef MOZ_GECKO_PROFILER
+
+# include "mozilla/ProfilerMarkers.h"
+# include "mozilla/UniquePtr.h"
+
+// This file can be #included unconditionally. However, everything within this
+// file must be guarded by a #ifdef MOZ_GECKO_PROFILER, *except* for the
+// following macros and functions, which encapsulate the most common operations
+// and thus avoid the need for many #ifdefs.
+
+# define AUTO_PROFILER_INIT
+# define AUTO_PROFILER_INIT2
+
+# define PROFILER_REGISTER_THREAD(name)
+# define PROFILER_UNREGISTER_THREAD()
+# define AUTO_PROFILER_REGISTER_THREAD(name)
+
+# define AUTO_PROFILER_THREAD_SLEEP
+# define AUTO_PROFILER_THREAD_WAKE
+
+# define PROFILER_JS_INTERRUPT_CALLBACK()
+
+# define PROFILER_SET_JS_CONTEXT(cx)
+# define PROFILER_CLEAR_JS_CONTEXT()
+
+# define AUTO_PROFILER_LABEL(label, categoryPair)
+# define AUTO_PROFILER_LABEL_CATEGORY_PAIR(categoryPair)
+# define AUTO_PROFILER_LABEL_DYNAMIC_CSTR(label, categoryPair, cStr)
+# define AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(label, categoryPair, \
+ cStr)
+# define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(label, categoryPair, nsCStr)
+# define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE( \
+ label, categoryPair, nsCStr)
+# define AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(label, categoryPair, nsStr)
+# define AUTO_PROFILER_LABEL_FAST(label, categoryPair, ctx)
+# define AUTO_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString, categoryPair, \
+ ctx, flags)
+
+// Function stubs for when MOZ_GECKO_PROFILER is not defined.
+
+// This won't be used, it's just there to allow the empty definition of
+// `profiler_get_backtrace`.
+struct ProfilerBacktrace {};
+using UniqueProfilerBacktrace = mozilla::UniquePtr<int>;
+
+// Get/Capture-backtrace functions can return nullptr or false, the result
+// should be fed to another empty macro or stub anyway.
+
+static inline UniqueProfilerBacktrace profiler_get_backtrace() {
+ return nullptr;
+}
+
+static inline bool profiler_capture_backtrace_into(
+ mozilla::ProfileChunkedBuffer& aChunkedBuffer) {
+ return false;
+}
+static inline mozilla::UniquePtr<mozilla::ProfileChunkedBuffer>
+profiler_capture_backtrace() {
+ return nullptr;
+}
+
+#else // !MOZ_GECKO_PROFILER
+
+# include "js/ProfilingCategory.h"
+# include "js/ProfilingStack.h"
+# include "js/RootingAPI.h"
+# include "mozilla/Assertions.h"
+# include "mozilla/Atomics.h"
+# include "mozilla/Attributes.h"
+# include "mozilla/Maybe.h"
+# include "mozilla/PowerOfTwo.h"
+# include "mozilla/Sprintf.h"
+# include "mozilla/ThreadLocal.h"
+# include "mozilla/TimeStamp.h"
+# include "mozilla/UniquePtr.h"
+# include "nscore.h"
+# include "nsString.h"
+
+# include <functional>
+# include <stdint.h>
+
+class ProfilerBacktrace;
+class ProfilerCodeAddressService;
+struct JSContext;
+
+namespace JS {
+struct RecordAllocationInfo;
+}
+
+namespace mozilla {
+class ProfileBufferControlledChunkManager;
+class ProfileChunkedBuffer;
+namespace baseprofiler {
+class SpliceableJSONWriter;
+} // namespace baseprofiler
+namespace net {
+struct TimingStruct;
+enum CacheDisposition : uint8_t;
+} // namespace net
+} // namespace mozilla
+class nsIURI;
+class nsIDocShell;
+
+namespace mozilla {
+class MallocAllocPolicy;
+template <class T, size_t MinInlineCapacity, class AllocPolicy>
+class Vector;
+} // namespace mozilla
+
+// Macros used by the AUTO_PROFILER_* macros below.
+# define PROFILER_RAII_PASTE(id, line) id##line
+# define PROFILER_RAII_EXPAND(id, line) PROFILER_RAII_PASTE(id, line)
+# define PROFILER_RAII PROFILER_RAII_EXPAND(raiiObject, __LINE__)
+
+//---------------------------------------------------------------------------
+// Profiler features
+//---------------------------------------------------------------------------
+
+// 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.
+# 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") \
+ \
+ /* The DevTools profiler doesn't want the native addresses. */ \
+ MACRO(2, "leaf", Leaf, "Include the C++ leaf node if not stackwalking") \
+ \
+ MACRO(3, "mainthreadio", MainThreadIO, "Add main thread file I/O") \
+ \
+ MACRO(4, "fileio", FileIO, \
+ "Add file I/O from all profiled threads, implies mainthreadio") \
+ \
+ MACRO(5, "fileioall", FileIOAll, \
+ "Add file I/O from all threads, implies fileio") \
+ \
+ MACRO(6, "noiostacks", NoIOStacks, \
+ "File I/O markers do not capture stacks, to reduce overhead") \
+ \
+ MACRO(7, "screenshots", Screenshots, \
+ "Take a snapshot of the window on every composition") \
+ \
+ MACRO(8, "seqstyle", SequentialStyle, \
+ "Disable parallel traversal in styling") \
+ \
+ MACRO(9, "stackwalk", StackWalk, \
+ "Walk the C++ stack, not available on all platforms") \
+ \
+ MACRO(10, "tasktracer", TaskTracer, \
+ "Start profiling with feature TaskTracer") \
+ \
+ MACRO(11, "threads", Threads, "Profile the registered secondary threads") \
+ \
+ MACRO(12, "jstracer", JSTracer, "Enable tracing of the JavaScript engine") \
+ \
+ MACRO(13, "jsallocations", JSAllocations, \
+ "Have the JavaScript engine track allocations") \
+ \
+ MACRO(14, "nostacksampling", NoStackSampling, \
+ "Disable all stack sampling: Cancels \"js\", \"leaf\", " \
+ "\"stackwalk\" and labels") \
+ \
+ MACRO(15, "preferencereads", PreferenceReads, \
+ "Track when preferences are read") \
+ \
+ MACRO(16, "nativeallocations", NativeAllocations, \
+ "Collect the stacks from a smaller subset of all native " \
+ "allocations, biasing towards collecting larger allocations") \
+ \
+ MACRO(17, "ipcmessages", IPCMessages, \
+ "Have the IPC layer track cross-process messages") \
+ \
+ MACRO(18, "audiocallbacktracing", AudioCallbackTracing, \
+ "Audio callback tracing") \
+ \
+ MACRO(19, "cpu", CPUUtilization, "CPU utilization")
+
+struct ProfilerFeature {
+# define DECLARE(n_, str_, Name_, desc_) \
+ static constexpr uint32_t Name_ = (1u << n_); \
+ 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
+};
+
+namespace mozilla {
+namespace profiler {
+namespace 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; }
+
+ 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();
+ }
+
+ 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();
+ }
+
+ static bool IsActive() { return uint32_t(sActiveAndFeatures) & Active; }
+
+ static bool IsActiveWithFeature(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!
+ 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()`).
+ 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;
+};
+
+bool IsThreadBeingProfiled();
+bool IsThreadRegistered();
+
+} // namespace detail
+} // namespace profiler
+} // namespace mozilla
+
+//---------------------------------------------------------------------------
+// Start and stop the profiler
+//---------------------------------------------------------------------------
+
+static constexpr mozilla::PowerOfTwo32 PROFILER_DEFAULT_ENTRIES =
+# if !defined(GP_PLAT_arm_android)
+ mozilla::MakePowerOfTwo32<8 * 1024 * 1024>(); // 8M entries = 64MB
+# else
+ mozilla::MakePowerOfTwo32<2 * 1024 * 1024>(); // 2M entries = 16MB
+# endif
+
+// Startup profiling usually need to capture more data, especially on slow
+// systems.
+// Note: Keep in sync with GeckoThread.maybeStartGeckoProfiler:
+// https://searchfox.org/mozilla-central/source/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
+static constexpr mozilla::PowerOfTwo32 PROFILER_DEFAULT_STARTUP_ENTRIES =
+# if !defined(GP_PLAT_arm_android)
+ mozilla::MakePowerOfTwo32<64 * 1024 * 1024>(); // 64M entries = 512MB
+# else
+ mozilla::MakePowerOfTwo32<8 * 1024 * 1024>(); // 8M entries = 64MB
+# endif
+
+# define PROFILER_DEFAULT_DURATION 20 /* seconds, for tests only */
+// Note: Keep in sync with GeckoThread.maybeStartGeckoProfiler:
+// https://searchfox.org/mozilla-central/source/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
+# define PROFILER_DEFAULT_INTERVAL 1 /* millisecond */
+# define PROFILER_MAX_INTERVAL 5000 /* milliseconds */
+
+// Initialize the profiler. If MOZ_PROFILER_STARTUP is set the profiler will
+// also be started. This call must happen before any other profiler calls
+// (except profiler_start(), which will call profiler_init() if it hasn't
+// already run).
+void profiler_init(void* stackTop);
+void profiler_init_threadmanager();
+
+// Call this as early as possible
+# define AUTO_PROFILER_INIT mozilla::AutoProfilerInit PROFILER_RAII
+// Call this after the nsThreadManager is Init()ed
+# define AUTO_PROFILER_INIT2 mozilla::AutoProfilerInit2 PROFILER_RAII
+
+enum class IsFastShutdown {
+ No,
+ Yes,
+};
+
+// Clean up the profiler module, stopping it if required. This function may
+// also save a shutdown profile if requested. No profiler calls should happen
+// after this point and all profiling stack labels should have been popped.
+void profiler_shutdown(IsFastShutdown aIsFastShutdown = IsFastShutdown::No);
+
+// Start the profiler -- initializing it first if necessary -- with the
+// selected options. Stops and restarts the profiler if it is already active.
+// After starting the profiler is "active". The samples will be recorded in a
+// circular buffer.
+// "aCapacity" is the maximum number of 8-bytes entries in the profiler's
+// circular buffer.
+// "aInterval" the sampling interval, measured in millseconds.
+// "aFeatures" is the feature set. Features unsupported by this
+// platform/configuration are ignored.
+// "aFilters" is the list of thread filters. Threads that do not match any
+// of the filters are not profiled. A filter matches a thread if
+// (a) the thread name contains the filter as a case-insensitive
+// substring, or
+// (b) the filter is of the form "pid:<n>" where n is the process
+// id of the process that the thread is running in.
+// "aActiveBrowsingContextID" Browsing Context of the active browser screen's
+// active tab. It's being used to determine the profiled tab.
+// It's "0" if we failed to get the ID.
+// "aDuration" is the duration of entries in the profiler's circular buffer.
+void profiler_start(
+ mozilla::PowerOfTwo32 aCapacity, double aInterval, uint32_t aFeatures,
+ const char** aFilters, uint32_t aFilterCount,
+ uint64_t aActiveBrowsingContextID,
+ const mozilla::Maybe<double>& aDuration = mozilla::Nothing());
+
+// Stop the profiler and discard the profile without saving it. A no-op if the
+// profiler is inactive. After stopping the profiler is "inactive".
+void profiler_stop();
+
+// If the profiler is inactive, start it. If it's already active, restart it if
+// the requested settings differ from the current settings. Both the check and
+// the state change are performed while the profiler state is locked.
+// The only difference to profiler_start is that the current buffer contents are
+// not discarded if the profiler is already running with the requested settings.
+void profiler_ensure_started(
+ mozilla::PowerOfTwo32 aCapacity, double aInterval, uint32_t aFeatures,
+ const char** aFilters, uint32_t aFilterCount,
+ uint64_t aActiveBrowsingContextID,
+ const mozilla::Maybe<double>& aDuration = mozilla::Nothing());
+
+//---------------------------------------------------------------------------
+// Control the profiler
+//---------------------------------------------------------------------------
+
+// Register/unregister threads with the profiler. Both functions operate the
+// same whether the profiler is active or inactive.
+# define PROFILER_REGISTER_THREAD(name) \
+ do { \
+ char stackTop; \
+ profiler_register_thread(name, &stackTop); \
+ } while (0)
+# define PROFILER_UNREGISTER_THREAD() profiler_unregister_thread()
+ProfilingStack* profiler_register_thread(const char* name, void* guessStackTop);
+void profiler_unregister_thread();
+
+// Registers a DOM Window (the JS global `window`) with the profiler. Each
+// Window _roughly_ corresponds to a single document loaded within a
+// BrowsingContext. The unique IDs for both the Window and BrowsingContext are
+// recorded to allow correlating different Windows loaded within the same tab or
+// frame element.
+//
+// We register pages for each navigations but we do not register
+// history.pushState or history.replaceState since they correspond to the same
+// Inner Window ID. When a Browsing context is first loaded, the first url
+// loaded in it will be about:blank. Because of that, this call keeps the first
+// non-about:blank registration of window and discards the previous one.
+//
+// "aBrowsingContextID" is the ID of the browsing context that document
+// belongs to. That's used to determine the tab of
+// that page.
+// "aInnerWindowID" is the ID of the `window` global object of that
+// document.
+// "aUrl" is the URL of the page.
+// "aEmbedderInnerWindowID" is the inner window id of embedder. It's used to
+// determine sub documents of a page.
+void profiler_register_page(uint64_t aBrowsingContextID,
+ uint64_t aInnerWindowID, const nsCString& aUrl,
+ uint64_t aEmbedderInnerWindowID);
+// Unregister page with the profiler.
+//
+// Take a Inner Window ID and unregister the page entry that has the same ID.
+void profiler_unregister_page(uint64_t aRegisteredInnerWindowID);
+
+// Remove all registered and unregistered pages in the profiler.
+void profiler_clear_all_pages();
+
+class BaseProfilerCount;
+void profiler_add_sampled_counter(BaseProfilerCount* aCounter);
+void profiler_remove_sampled_counter(BaseProfilerCount* aCounter);
+
+// Register and unregister a thread within a scope.
+# define AUTO_PROFILER_REGISTER_THREAD(name) \
+ mozilla::AutoProfilerRegisterThread PROFILER_RAII(name)
+
+enum class SamplingState {
+ JustStopped, // Sampling loop has just stopped without sampling, between the
+ // callback registration and now.
+ SamplingPaused, // Profiler is active but sampling loop has gone through a
+ // pause.
+ NoStackSamplingCompleted, // A full sampling loop has completed in
+ // no-stack-sampling mode.
+ SamplingCompleted // A full sampling loop has completed.
+};
+
+using PostSamplingCallback = std::function<void(SamplingState)>;
+
+// Install a callback to be invoked at the end of the next sampling loop.
+// - `false` if profiler is not active, `aCallback` will stay untouched.
+// - `true` if `aCallback` was successfully moved-from into internal storage,
+// and *will* be invoked at the end of the next sampling cycle. Note that this
+// will happen on the Sampler thread, and will block further sampling, so
+// please be mindful not to block for a long time (e.g., just dispatch a
+// runnable to another thread.) Calling profiler functions from the callback
+// is allowed.
+[[nodiscard]] bool profiler_callback_after_sampling(
+ PostSamplingCallback&& aCallback);
+
+// Pause and resume the profiler. No-ops if the profiler is inactive. While
+// paused the profile will not take any samples and will not record any data
+// into its buffers. The profiler remains fully initialized in this state.
+// Timeline markers will still be stored. This feature will keep JavaScript
+// profiling enabled, thus allowing toggling the profiler without invalidating
+// the JIT.
+void profiler_pause();
+void profiler_resume();
+
+// Only pause and resume the periodic sampling loop, including stack sampling,
+// counters, and profiling overheads.
+void profiler_pause_sampling();
+void profiler_resume_sampling();
+
+// These functions tell the profiler that a thread went to sleep so that we can
+// avoid sampling it while it's sleeping. Calling profiler_thread_sleep()
+// twice without an intervening profiler_thread_wake() is an error. All three
+// functions operate the same whether the profiler is active or inactive.
+void profiler_thread_sleep();
+void profiler_thread_wake();
+
+// Mark a thread as asleep/awake within a scope.
+# define AUTO_PROFILER_THREAD_SLEEP \
+ mozilla::AutoProfilerThreadSleep PROFILER_RAII
+# define AUTO_PROFILER_THREAD_WAKE \
+ mozilla::AutoProfilerThreadWake PROFILER_RAII
+
+// Called by the JSRuntime's operation callback. This is used to start profiling
+// on auxiliary threads. Operates the same whether the profiler is active or
+// not.
+# define PROFILER_JS_INTERRUPT_CALLBACK() profiler_js_interrupt_callback()
+void profiler_js_interrupt_callback();
+
+// Set and clear the current thread's JSContext.
+# define PROFILER_SET_JS_CONTEXT(cx) profiler_set_js_context(cx)
+# define PROFILER_CLEAR_JS_CONTEXT() profiler_clear_js_context()
+void profiler_set_js_context(JSContext* aCx);
+void profiler_clear_js_context();
+
+//---------------------------------------------------------------------------
+// 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.
+inline bool profiler_is_active() {
+ return mozilla::profiler::detail::RacyFeatures::IsActive();
+}
+
+// Same as profiler_is_active(), but with the same extra checks that determine
+// if the profiler would currently store markers. So this should be used before
+// doing some potentially-expensive work that's used in a marker. E.g.:
+//
+// if (profiler_can_accept_markers()) {
+// ExpensiveMarkerPayload expensivePayload = CreateExpensivePayload();
+// BASE_PROFILER_ADD_MARKER_WITH_PAYLOAD(name, OTHER, expensivePayload);
+// }
+inline bool profiler_can_accept_markers() {
+ return mozilla::profiler::detail::RacyFeatures::IsActiveAndUnpaused();
+}
+
+// Is the profiler active, and is the current thread being profiled?
+// (Same caveats and recommented usage as profiler_is_active().)
+inline bool profiler_thread_is_being_profiled() {
+ return profiler_is_active() &&
+ mozilla::profiler::detail::IsThreadBeingProfiled();
+}
+
+// During profiling, if the current thread is registered, return true
+// (regardless of whether it is actively being profiled).
+// (Same caveats and recommented usage as profiler_is_active().)
+inline bool profiler_is_active_and_thread_is_registered() {
+ return profiler_is_active() &&
+ mozilla::profiler::detail::IsThreadRegistered();
+}
+
+// Is the profiler active and paused? Returns false if the profiler is inactive.
+bool profiler_is_paused();
+
+// Is the profiler active and sampling is paused? Returns false if the profiler
+// is inactive.
+bool profiler_is_sampling_paused();
+
+// Is the current thread sleeping?
+bool profiler_thread_is_sleeping();
+
+// 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.
+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().
+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().
+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().
+bool profiler_feature_active(uint32_t aFeature);
+
+// Get the params used to start the profiler. Returns 0 and an empty vector
+// (via outparams) if the profile is inactive. It's possible that the features
+// returned may be slightly different to those requested due to required
+// adjustments.
+void profiler_get_start_params(
+ int* aEntrySize, mozilla::Maybe<double>* aDuration, double* aInterval,
+ uint32_t* aFeatures,
+ mozilla::Vector<const char*, 0, mozilla::MallocAllocPolicy>* aFilters,
+ uint64_t* aActiveBrowsingContextID);
+
+// Get the chunk manager used in the current profiling session, or null.
+mozilla::ProfileBufferControlledChunkManager*
+profiler_get_controlled_chunk_manager();
+
+// The number of milliseconds since the process started. Operates the same
+// whether the profiler is active or inactive.
+double profiler_time();
+
+// Get the current process's ID.
+int profiler_current_process_id();
+
+// Get the current thread's ID.
+int profiler_current_thread_id();
+
+// Statically initialized to 0, then set once from profiler_init(), which should
+// be called from the main thread before any other use of the profiler.
+extern int scProfilerMainThreadId;
+
+inline int profiler_main_thread_id() { return scProfilerMainThreadId; }
+
+inline bool profiler_is_main_thread() {
+ return profiler_current_thread_id() == profiler_main_thread_id();
+}
+
+// An object of this class is passed to profiler_suspend_and_sample_thread().
+// For each stack frame, one of the Collect methods will be called.
+class ProfilerStackCollector {
+ public:
+ // Some collectors need to worry about possibly overwriting previous
+ // generations of data. If that's not an issue, this can return Nothing,
+ // which is the default behaviour.
+ virtual mozilla::Maybe<uint64_t> SamplePositionInBuffer() {
+ return mozilla::Nothing();
+ }
+ virtual mozilla::Maybe<uint64_t> BufferRangeStart() {
+ return mozilla::Nothing();
+ }
+
+ // This method will be called once if the thread being suspended is the main
+ // thread. Default behaviour is to do nothing.
+ virtual void SetIsMainThread() {}
+
+ // WARNING: The target thread is suspended when the Collect methods are
+ // called. Do not try to allocate or acquire any locks, or you could
+ // deadlock. The target thread will have resumed by the time this function
+ // returns.
+
+ virtual void CollectNativeLeafAddr(void* aAddr) = 0;
+
+ virtual void CollectJitReturnAddr(void* aAddr) = 0;
+
+ virtual void CollectWasmFrame(const char* aLabel) = 0;
+
+ virtual void CollectProfilingStackFrame(
+ const js::ProfilingStackFrame& aFrame) = 0;
+};
+
+// This method suspends the thread identified by aThreadId, samples its
+// profiling stack, JS stack, and (optionally) native stack, passing the
+// collected frames into aCollector. aFeatures dictates which compiler features
+// are used. |Leaf| is the only relevant one.
+void profiler_suspend_and_sample_thread(int aThreadId, uint32_t aFeatures,
+ ProfilerStackCollector& aCollector,
+ bool aSampleNative = true);
+
+struct ProfilerBacktraceDestructor {
+ void operator()(ProfilerBacktrace*);
+};
+
+using UniqueProfilerBacktrace =
+ mozilla::UniquePtr<ProfilerBacktrace, ProfilerBacktraceDestructor>;
+
+// Immediately capture the current thread's call stack, store it in the provided
+// buffer (usually to avoid allocations if you can construct the buffer on the
+// stack). Returns false if unsuccessful, or if the profiler is inactive.
+bool profiler_capture_backtrace_into(
+ mozilla::ProfileChunkedBuffer& aChunkedBuffer);
+
+// Immediately capture the current thread's call stack, and return it in a
+// ProfileChunkedBuffer (usually for later use in MarkerStack::TakeBacktrace()).
+// May be null if unsuccessful, or if the profiler is inactive.
+mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> profiler_capture_backtrace();
+
+// Immediately capture the current thread's call stack, and return it in a
+// ProfilerBacktrace (usually for later use in marker function that take a
+// ProfilerBacktrace). May be null if unsuccessful, or if the profiler is
+// inactive.
+UniqueProfilerBacktrace profiler_get_backtrace();
+
+struct ProfilerStats {
+ unsigned n = 0;
+ double sum = 0;
+ double min = std::numeric_limits<double>::max();
+ double max = 0;
+ void Count(double v) {
+ ++n;
+ sum += v;
+ if (v < min) {
+ min = v;
+ }
+ if (v > max) {
+ max = v;
+ }
+ }
+};
+
+struct ProfilerBufferInfo {
+ // Index of the oldest entry.
+ uint64_t mRangeStart;
+ // Index of the newest entry.
+ uint64_t mRangeEnd;
+ // Buffer capacity in number of 8-byte entries.
+ uint32_t mEntryCount;
+ // Sampling stats: Interval between successive samplings.
+ ProfilerStats mIntervalsUs;
+ // Sampling stats: Total sampling duration. (Split detail below.)
+ ProfilerStats mOverheadsUs;
+ // Sampling stats: Time to acquire the lock before sampling.
+ ProfilerStats mLockingsUs;
+ // Sampling stats: Time to discard expired data.
+ ProfilerStats mCleaningsUs;
+ // Sampling stats: Time to collect counter data.
+ ProfilerStats mCountersUs;
+ // Sampling stats: Time to sample thread stacks.
+ ProfilerStats mThreadsUs;
+};
+
+// Get information about the current buffer status.
+// Returns Nothing() if the profiler is inactive.
+//
+// This information may be useful to a user-interface displaying the current
+// status of the profiler, allowing the user to get a sense for how fast the
+// buffer is being written to, and how much data is visible.
+mozilla::Maybe<ProfilerBufferInfo> profiler_get_buffer_info();
+
+// ProfilerMarkers.h requires some stuff from this header.
+// TODO: Move common stuff to shared header, and move this #include to the top.
+# include "mozilla/ProfilerMarkers.h"
+
+//---------------------------------------------------------------------------
+// Put profiling data into the profiler (labels and markers)
+//---------------------------------------------------------------------------
+
+// Insert an RAII object in this scope to enter a label stack frame. Any
+// samples collected in this scope will contain this label in their stack.
+// The label argument must be a static C string. It is usually of the
+// form "ClassName::FunctionName". (Ideally we'd use the compiler to provide
+// that for us, but __func__ gives us the function name without the class
+// name.) If the label applies to only part of a function, you can qualify it
+// like this: "ClassName::FunctionName:PartName".
+//
+// Use AUTO_PROFILER_LABEL_DYNAMIC_* if you want to add additional / dynamic
+// information to the label stack frame.
+# define AUTO_PROFILER_LABEL(label, categoryPair) \
+ mozilla::AutoProfilerLabel PROFILER_RAII( \
+ label, nullptr, JS::ProfilingCategoryPair::categoryPair)
+
+// Similar to AUTO_PROFILER_LABEL, but with only one argument: the category
+// pair. The label string is taken from the category pair. This is convenient
+// for labels like AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_LayerBuilding)
+// which would otherwise just repeat the string.
+# define AUTO_PROFILER_LABEL_CATEGORY_PAIR(categoryPair) \
+ mozilla::AutoProfilerLabel PROFILER_RAII( \
+ "", nullptr, JS::ProfilingCategoryPair::categoryPair, \
+ uint32_t(js::ProfilingStackFrame::Flags:: \
+ LABEL_DETERMINED_BY_CATEGORY_PAIR))
+
+// Similar to AUTO_PROFILER_LABEL, but with an additional string. The inserted
+// RAII object stores the cStr pointer in a field; it does not copy the string.
+//
+// WARNING: This means that the string you pass to this macro needs to live at
+// least until the end of the current scope. Be careful using this macro with
+// ns[C]String; the other AUTO_PROFILER_LABEL_DYNAMIC_* macros below are
+// preferred because they avoid this problem.
+//
+// If the profiler samples the current thread and walks the label stack while
+// this RAII object is on the stack, it will copy the supplied string into the
+// profile buffer. So there's one string copy operation, and it happens at
+// sample time.
+//
+// Compare this to the plain AUTO_PROFILER_LABEL macro, which only accepts
+// literal strings: When the label stack frames generated by
+// AUTO_PROFILER_LABEL are sampled, no string copy needs to be made because the
+// profile buffer can just store the raw pointers to the literal strings.
+// Consequently, AUTO_PROFILER_LABEL frames take up considerably less space in
+// the profile buffer than AUTO_PROFILER_LABEL_DYNAMIC_* frames.
+# define AUTO_PROFILER_LABEL_DYNAMIC_CSTR(label, categoryPair, cStr) \
+ mozilla::AutoProfilerLabel PROFILER_RAII( \
+ label, cStr, JS::ProfilingCategoryPair::categoryPair)
+
+// Like AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but with the NONSENSITIVE flag to
+// note that it does not contain sensitive information (so we can include it
+// in, for example, the BackgroundHangMonitor)
+# define AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(label, categoryPair, \
+ cStr) \
+ mozilla::AutoProfilerLabel PROFILER_RAII( \
+ label, cStr, JS::ProfilingCategoryPair::categoryPair, \
+ uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE))
+
+// Similar to AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but takes an nsACString.
+//
+// Note: The use of the Maybe<>s ensures the scopes for the dynamic string and
+// the AutoProfilerLabel are appropriate, while also not incurring the runtime
+// cost of the string assignment unless the profiler is active. Therefore,
+// unlike AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC_CSTR, this macro
+// doesn't push/pop a label when the profiler is inactive.
+# define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(label, categoryPair, nsCStr) \
+ mozilla::Maybe<nsAutoCString> autoCStr; \
+ mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectNsCString; \
+ if (profiler_is_active()) { \
+ autoCStr.emplace(nsCStr); \
+ raiiObjectNsCString.emplace(label, autoCStr->get(), \
+ JS::ProfilingCategoryPair::categoryPair); \
+ }
+
+// See note above AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE
+# define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE( \
+ label, categoryPair, nsCStr) \
+ mozilla::Maybe<nsAutoCString> autoCStr; \
+ mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectNsCString; \
+ if (profiler_is_active()) { \
+ autoCStr.emplace(nsCStr); \
+ raiiObjectNsCString.emplace( \
+ label, autoCStr->get(), JS::ProfilingCategoryPair::categoryPair, \
+ uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE)); \
+ }
+
+// Similar to AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but takes an nsString that is
+// is lossily converted to an ASCII string.
+//
+// Note: The use of the Maybe<>s ensures the scopes for the converted dynamic
+// string and the AutoProfilerLabel are appropriate, while also not incurring
+// the runtime cost of the string conversion unless the profiler is active.
+// Therefore, unlike AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC_CSTR,
+// this macro doesn't push/pop a label when the profiler is inactive.
+# define AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(label, categoryPair, \
+ nsStr) \
+ mozilla::Maybe<NS_LossyConvertUTF16toASCII> asciiStr; \
+ mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectLossyNsString; \
+ if (profiler_is_active()) { \
+ asciiStr.emplace(nsStr); \
+ raiiObjectLossyNsString.emplace( \
+ label, asciiStr->get(), JS::ProfilingCategoryPair::categoryPair); \
+ }
+
+// Similar to AUTO_PROFILER_LABEL, but accepting a JSContext* parameter, and a
+// no-op if the profiler is disabled.
+// Used to annotate functions for which overhead in the range of nanoseconds is
+// noticeable. It avoids overhead from the TLS lookup because it can get the
+// ProfilingStack from the JS context, and avoids almost all overhead in the
+// case where the profiler is disabled.
+# define AUTO_PROFILER_LABEL_FAST(label, categoryPair, ctx) \
+ mozilla::AutoProfilerLabel PROFILER_RAII( \
+ ctx, label, nullptr, JS::ProfilingCategoryPair::categoryPair)
+
+// Similar to AUTO_PROFILER_LABEL_FAST, but also takes an extra string and an
+// additional set of flags. The flags parameter should carry values from the
+// js::ProfilingStackFrame::Flags enum.
+# define AUTO_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString, categoryPair, \
+ ctx, flags) \
+ mozilla::AutoProfilerLabel PROFILER_RAII( \
+ ctx, label, dynamicString, JS::ProfilingCategoryPair::categoryPair, \
+ flags)
+
+void profiler_add_js_marker(const char* aMarkerName, const char* aMarkerText);
+void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info);
+
+// Returns true or or false depending on whether the marker was actually added
+// or not.
+bool profiler_add_native_allocation_marker(int64_t aSize,
+ uintptr_t aMemoryAddress);
+
+// 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.
+bool profiler_is_locked_on_current_thread();
+
+enum class NetworkLoadType { LOAD_START, LOAD_STOP, LOAD_REDIRECT };
+
+void profiler_add_network_marker(
+ nsIURI* aURI, const nsACString& aRequestMethod, int32_t aPriority,
+ uint64_t aChannelId, NetworkLoadType aType, mozilla::TimeStamp aStart,
+ mozilla::TimeStamp aEnd, int64_t aCount,
+ mozilla::net::CacheDisposition aCacheDisposition, uint64_t aInnerWindowID,
+ const mozilla::net::TimingStruct* aTimings = nullptr,
+ nsIURI* aRedirectURI = nullptr,
+ mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource = nullptr,
+ const mozilla::Maybe<nsDependentCString>& aContentType =
+ mozilla::Nothing());
+
+enum TracingKind {
+ TRACING_EVENT,
+ TRACING_INTERVAL_START,
+ TRACING_INTERVAL_END,
+};
+
+// This is a helper function to get the Inner Window ID from DocShell but it's
+// not a recommended method to get it and it's not encouraged to use this
+// function. If there is a computed inner window ID, `window`, or `Document`
+// available in the call site, please use them. Use this function as a last
+// resort.
+mozilla::Maybe<uint64_t> profiler_get_inner_window_id_from_docshell(
+ nsIDocShell* aDocshell);
+
+inline mozilla::MarkerInnerWindowId MarkerInnerWindowIdFromDocShell(
+ nsIDocShell* aDocshell) {
+ mozilla::Maybe<uint64_t> id =
+ profiler_get_inner_window_id_from_docshell(aDocshell);
+ if (!id) {
+ return mozilla::MarkerInnerWindowId::NoId();
+ }
+ return mozilla::MarkerInnerWindowId(*id);
+}
+
+//---------------------------------------------------------------------------
+// Output profiles
+//---------------------------------------------------------------------------
+
+// Set a user-friendly process name, used in JSON stream. Allows an optional
+// detailed name which may include private info (eTLD+1 in fission)
+void profiler_set_process_name(const nsACString& aProcessName,
+ const nsACString* aETLDplus1 = nullptr);
+
+// Get the profile encoded as a JSON string. A no-op (returning nullptr) if the
+// profiler is inactive.
+// If aIsShuttingDown is true, the current time is included as the process
+// shutdown time in the JSON's "meta" object.
+mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0,
+ bool aIsShuttingDown = false);
+
+// Write the profile for this process (excluding subprocesses) into aWriter.
+// Returns false if the profiler is inactive.
+bool profiler_stream_json_for_this_process(
+ mozilla::baseprofiler::SpliceableJSONWriter& aWriter, double aSinceTime = 0,
+ bool aIsShuttingDown = false,
+ ProfilerCodeAddressService* aService = nullptr);
+
+// Get the profile and write it into a file. A no-op if the profile is
+// inactive.
+//
+// This function is 'extern "C"' so that it is easily callable from a debugger
+// in a build without debugging information (a workaround for
+// http://llvm.org/bugs/show_bug.cgi?id=22211).
+extern "C" {
+void profiler_save_profile_to_file(const char* aFilename);
+}
+
+//---------------------------------------------------------------------------
+// RAII classes
+//---------------------------------------------------------------------------
+
+class TLSRegisteredThread; // Needed for friendship in ProfilingStackOwnerTLS.
+
+namespace mozilla {
+
+class MOZ_RAII AutoProfilerInit {
+ public:
+ explicit AutoProfilerInit() { profiler_init(this); }
+
+ ~AutoProfilerInit() { profiler_shutdown(); }
+
+ private:
+};
+
+class MOZ_RAII AutoProfilerInit2 {
+ public:
+ explicit AutoProfilerInit2() { profiler_init_threadmanager(); }
+
+ private:
+};
+
+// Convenience class to register and unregister a thread with the profiler.
+// Needs to be the first object on the stack of the thread.
+class MOZ_RAII AutoProfilerRegisterThread final {
+ public:
+ explicit AutoProfilerRegisterThread(const char* aName) {
+ profiler_register_thread(aName, this);
+ }
+
+ ~AutoProfilerRegisterThread() { profiler_unregister_thread(); }
+
+ private:
+ AutoProfilerRegisterThread(const AutoProfilerRegisterThread&) = delete;
+ AutoProfilerRegisterThread& operator=(const AutoProfilerRegisterThread&) =
+ delete;
+};
+
+class MOZ_RAII AutoProfilerThreadSleep {
+ public:
+ explicit AutoProfilerThreadSleep() { profiler_thread_sleep(); }
+
+ ~AutoProfilerThreadSleep() { profiler_thread_wake(); }
+
+ private:
+};
+
+// Temporarily wake up the profiling of a thread while servicing events such as
+// Asynchronous Procedure Calls (APCs).
+class MOZ_RAII AutoProfilerThreadWake {
+ public:
+ explicit AutoProfilerThreadWake()
+ : mIssuedWake(profiler_thread_is_sleeping()) {
+ if (mIssuedWake) {
+ profiler_thread_wake();
+ }
+ }
+
+ ~AutoProfilerThreadWake() {
+ if (mIssuedWake) {
+ MOZ_ASSERT(!profiler_thread_is_sleeping());
+ profiler_thread_sleep();
+ }
+ }
+
+ private:
+ bool mIssuedWake;
+};
+
+// Ref-counted shell around a `ProfilingStack`, to be used by the owning
+// (Racy)RegisteredThread and AutoProfilerLabel.
+class ProfilingStackOwner {
+ public:
+ class ProfilingStack& ProfilingStack() {
+ return mProfilingStack;
+ }
+
+ // Using hand-rolled ref-counting, to evade leak checking (emergency patch
+ // for bug 1445822).
+ // TODO: Eliminate all/most leaks if possible.
+ void AddRef() const { ++mRefCnt; }
+ void Release() const {
+ MOZ_ASSERT(int32_t(mRefCnt) > 0);
+ if (--mRefCnt == 0) {
+ if (mProfilingStack.stackSize() > 0) {
+ DumpStackAndCrash();
+ }
+ delete this;
+ }
+ }
+
+ private:
+ ~ProfilingStackOwner() = default;
+
+ MOZ_NORETURN void DumpStackAndCrash() const;
+
+ class ProfilingStack mProfilingStack;
+
+ mutable Atomic<int32_t, MemoryOrdering::ReleaseAcquire> mRefCnt;
+};
+
+// This class creates a non-owning ProfilingStack reference. Objects of this
+// class are stack-allocated, and so exist within a thread, and are thus bounded
+// by the lifetime of the thread, which ensures that the references held can't
+// be used after the ProfilingStack is destroyed.
+class MOZ_RAII AutoProfilerLabel {
+ public:
+ // This is the AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC variant.
+ AutoProfilerLabel(const char* aLabel, const char* aDynamicString,
+ JS::ProfilingCategoryPair aCategoryPair,
+ uint32_t aFlags = 0) {
+ // Get the ProfilingStack from TLS.
+ ProfilingStackOwner* profilingStackOwner = ProfilingStackOwnerTLS::Get();
+ Push(profilingStackOwner ? &profilingStackOwner->ProfilingStack() : nullptr,
+ aLabel, aDynamicString, aCategoryPair, aFlags);
+ }
+
+ // This is the AUTO_PROFILER_LABEL_FAST variant. It retrieves the
+ // ProfilingStack from the JSContext and does nothing if the profiler is
+ // inactive.
+ AutoProfilerLabel(JSContext* aJSContext, const char* aLabel,
+ const char* aDynamicString,
+ JS::ProfilingCategoryPair aCategoryPair, uint32_t aFlags) {
+ Push(js::GetContextProfilingStackIfEnabled(aJSContext), aLabel,
+ aDynamicString, aCategoryPair, aFlags);
+ }
+
+ void Push(ProfilingStack* aProfilingStack, const char* aLabel,
+ const char* aDynamicString, JS::ProfilingCategoryPair aCategoryPair,
+ uint32_t aFlags = 0) {
+ // This function runs both on and off the main thread.
+
+ mProfilingStack = aProfilingStack;
+ if (mProfilingStack) {
+ mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this,
+ aCategoryPair, aFlags);
+ }
+ }
+
+ ~AutoProfilerLabel() {
+ // This function runs both on and off the main thread.
+
+ if (mProfilingStack) {
+ mProfilingStack->pop();
+ }
+ }
+
+ private:
+ // We save a ProfilingStack pointer in the ctor so we don't have to redo the
+ // TLS lookup in the dtor.
+ ProfilingStack* mProfilingStack;
+
+ public:
+ // See the comment on the definition in platform.cpp for details about this.
+ class ProfilingStackOwnerTLS {
+ public:
+ static ProfilingStackOwner* Get() {
+ MOZ_ASSERT(
+ sState != State::Uninitialized,
+ "ProfilingStackOwnerTLS::Get() should only be called after Init()");
+ if (sState != State::Initialized) {
+ return nullptr;
+ }
+ return sProfilingStackOwnerTLS.get();
+ }
+
+ static void Set(ProfilingStackOwner* aProfilingStackOwner) {
+ MOZ_ASSERT(
+ sState != State::Uninitialized,
+ "ProfilingStackOwnerTLS::Set() should only be called after Init()");
+ MOZ_DIAGNOSTIC_ASSERT(sState == State::Initialized,
+ "ProfilingStackOwnerTLS::Set() should only be "
+ "called after a successful Init()");
+ sProfilingStackOwnerTLS.set(aProfilingStackOwner);
+ }
+
+ private:
+ friend TLSRegisteredThread;
+ static void Init();
+
+ enum class State { Uninitialized = 0, Initialized, Unavailable };
+ static State sState;
+
+ static MOZ_THREAD_LOCAL(ProfilingStackOwner*) sProfilingStackOwnerTLS;
+ };
+};
+
+// Get the MOZ_PROFILER_STARTUP* environment variables that should be
+// supplied to a child process that is about to be launched, in order
+// to make that child process start with the same profiler settings as
+// in the current process. The given function is invoked once for
+// each variable to be set.
+void GetProfilerEnvVarsForChildProcess(
+ std::function<void(const char* key, const char* value)>&& aSetEnv);
+
+} // namespace mozilla
+
+#endif // !MOZ_GECKO_PROFILER
+
+#endif // GeckoProfiler_h
diff --git a/tools/profiler/public/GeckoProfilerReporter.h b/tools/profiler/public/GeckoProfilerReporter.h
new file mode 100644
index 0000000000..f5bf41f223
--- /dev/null
+++ b/tools/profiler/public/GeckoProfilerReporter.h
@@ -0,0 +1,26 @@
+/* -*- 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/. */
+
+#ifndef GeckoProfilerReporter_h
+#define GeckoProfilerReporter_h
+
+#include "nsIMemoryReporter.h"
+
+class GeckoProfilerReporter final : public nsIMemoryReporter {
+ public:
+ NS_DECL_ISUPPORTS
+
+ GeckoProfilerReporter() {}
+
+ NS_IMETHOD
+ CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+ bool aAnonymize) override;
+
+ private:
+ ~GeckoProfilerReporter() {}
+};
+
+#endif
diff --git a/tools/profiler/public/ProfileBufferEntrySerializationGeckoExtensions.h b/tools/profiler/public/ProfileBufferEntrySerializationGeckoExtensions.h
new file mode 100644
index 0000000000..1578bd2ddc
--- /dev/null
+++ b/tools/profiler/public/ProfileBufferEntrySerializationGeckoExtensions.h
@@ -0,0 +1,160 @@
+/* -*- 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/. */
+
+#ifndef ProfileBufferEntrySerializationGeckoExtensions_h
+#define ProfileBufferEntrySerializationGeckoExtensions_h
+
+#include "mozilla/ProfileBufferEntrySerialization.h"
+
+#include "js/AllocPolicy.h"
+#include "js/Utility.h"
+#include "nsString.h"
+
+namespace mozilla {
+
+// ----------------------------------------------------------------------------
+// ns[C]String
+
+// nsString or nsCString contents are serialized as the number of bytes (encoded
+// as ULEB128) and all the characters in the string. The terminal '\0' is
+// omitted.
+// Make sure you write and read with the same character type!
+//
+// Usage: `nsCString s = ...; aEW.WriteObject(s);`
+template <typename CHAR>
+struct ProfileBufferEntryWriter::Serializer<nsTString<CHAR>> {
+ static Length Bytes(const nsTString<CHAR>& aS) {
+ const auto length = aS.Length();
+ return ProfileBufferEntryWriter::ULEB128Size(length) +
+ static_cast<Length>(length * sizeof(CHAR));
+ }
+
+ static void Write(ProfileBufferEntryWriter& aEW, const nsTString<CHAR>& aS) {
+ const auto length = aS.Length();
+ aEW.WriteULEB128(length);
+ // Copy the bytes from the string's buffer.
+ aEW.WriteBytes(aS.Data(), length * sizeof(CHAR));
+ }
+};
+
+template <typename CHAR>
+struct ProfileBufferEntryReader::Deserializer<nsTString<CHAR>> {
+ static void ReadInto(ProfileBufferEntryReader& aER, nsTString<CHAR>& aS) {
+ aS = Read(aER);
+ }
+
+ static nsTString<CHAR> Read(ProfileBufferEntryReader& aER) {
+ const Length length = aER.ReadULEB128<Length>();
+ nsTString<CHAR> s;
+ // BulkWrite is the most efficient way to copy bytes into the target string.
+ auto writerOrErr = s.BulkWrite(length, 0, true);
+ MOZ_RELEASE_ASSERT(!writerOrErr.isErr());
+
+ auto writer = writerOrErr.unwrap();
+
+ aER.ReadBytes(writer.Elements(), length * sizeof(CHAR));
+ writer.Finish(length, true);
+ return s;
+ }
+};
+
+// ----------------------------------------------------------------------------
+// nsAuto[C]String
+
+// nsAuto[C]String contents are serialized as the number of bytes (encoded as
+// ULEB128) and all the characters in the string. The terminal '\0' is omitted.
+// Make sure you write and read with the same character type!
+//
+// Usage: `nsAutoCString s = ...; aEW.WriteObject(s);`
+template <typename CHAR, size_t N>
+struct ProfileBufferEntryWriter::Serializer<nsTAutoStringN<CHAR, N>> {
+ static Length Bytes(const nsTAutoStringN<CHAR, N>& aS) {
+ const auto length = aS.Length();
+ return ProfileBufferEntryWriter::ULEB128Size(length) +
+ static_cast<Length>(length * sizeof(CHAR));
+ }
+
+ static void Write(ProfileBufferEntryWriter& aEW,
+ const nsTAutoStringN<CHAR, N>& aS) {
+ const auto length = aS.Length();
+ aEW.WriteULEB128(length);
+ // Copy the bytes from the string's buffer.
+ aEW.WriteBytes(aS.BeginReading(), length * sizeof(CHAR));
+ }
+};
+
+template <typename CHAR, size_t N>
+struct ProfileBufferEntryReader::Deserializer<nsTAutoStringN<CHAR, N>> {
+ static void ReadInto(ProfileBufferEntryReader& aER,
+ nsTAutoStringN<CHAR, N>& aS) {
+ aS = Read(aER);
+ }
+
+ static nsTAutoStringN<CHAR, N> Read(ProfileBufferEntryReader& aER) {
+ const auto length = aER.ReadULEB128<Length>();
+ nsTAutoStringN<CHAR, N> s;
+ // BulkWrite is the most efficient way to copy bytes into the target string.
+ auto writerOrErr = s.BulkWrite(length, 0, true);
+ MOZ_RELEASE_ASSERT(!writerOrErr.isErr());
+
+ auto writer = writerOrErr.unwrap();
+ aER.ReadBytes(writer.Elements(), length * sizeof(CHAR));
+ writer.Finish(length, true);
+ return s;
+ }
+};
+
+// ----------------------------------------------------------------------------
+// JS::UniqueChars
+
+// JS::UniqueChars contents are serialized as the number of bytes (encoded as
+// ULEB128) and all the characters in the string. The terminal '\0' is omitted.
+// Note: A nullptr pointer will be serialized like an empty string, so when
+// deserializing it will result in an allocated buffer only containing a
+// single null terminator.
+//
+// Usage: `JS::UniqueChars s = ...; aEW.WriteObject(s);`
+template <>
+struct ProfileBufferEntryWriter::Serializer<JS::UniqueChars> {
+ static Length Bytes(const JS::UniqueChars& aS) {
+ if (!aS) {
+ return ProfileBufferEntryWriter::ULEB128Size<Length>(0);
+ }
+ const auto len = static_cast<Length>(strlen(aS.get()));
+ return ProfileBufferEntryWriter::ULEB128Size(len) + len;
+ }
+
+ static void Write(ProfileBufferEntryWriter& aEW, const JS::UniqueChars& aS) {
+ if (!aS) {
+ aEW.WriteULEB128<Length>(0);
+ return;
+ }
+ const auto len = static_cast<Length>(strlen(aS.get()));
+ aEW.WriteULEB128(len);
+ aEW.WriteBytes(aS.get(), len);
+ }
+};
+
+template <>
+struct ProfileBufferEntryReader::Deserializer<JS::UniqueChars> {
+ static void ReadInto(ProfileBufferEntryReader& aER, JS::UniqueChars& aS) {
+ aS = Read(aER);
+ }
+
+ static JS::UniqueChars Read(ProfileBufferEntryReader& aER) {
+ const auto len = aER.ReadULEB128<Length>();
+ // Use the same allocation policy as JS_smprintf.
+ char* buffer =
+ static_cast<char*>(js::SystemAllocPolicy{}.pod_malloc<char>(len + 1));
+ aER.ReadBytes(buffer, len);
+ buffer[len] = '\0';
+ return JS::UniqueChars(buffer);
+ }
+};
+
+} // namespace mozilla
+
+#endif // ProfileBufferEntrySerializationGeckoExtensions_h
diff --git a/tools/profiler/public/ProfileJSONWriter.h b/tools/profiler/public/ProfileJSONWriter.h
new file mode 100644
index 0000000000..8d23d7a890
--- /dev/null
+++ b/tools/profiler/public/ProfileJSONWriter.h
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef PROFILEJSONWRITER_H
+#define PROFILEJSONWRITER_H
+
+#include "mozilla/BaseProfileJSONWriter.h"
+
+using ChunkedJSONWriteFunc = mozilla::baseprofiler::ChunkedJSONWriteFunc;
+using JSONSchemaWriter = mozilla::baseprofiler::JSONSchemaWriter;
+using OStreamJSONWriteFunc = mozilla::baseprofiler::OStreamJSONWriteFunc;
+using SpliceableChunkedJSONWriter =
+ mozilla::baseprofiler::SpliceableChunkedJSONWriter;
+using SpliceableJSONWriter = mozilla::baseprofiler::SpliceableJSONWriter;
+using UniqueJSONStrings = mozilla::baseprofiler::UniqueJSONStrings;
+
+#endif // PROFILEJSONWRITER_H
diff --git a/tools/profiler/public/ProfilerChild.h b/tools/profiler/public/ProfilerChild.h
new file mode 100644
index 0000000000..4e4875f909
--- /dev/null
+++ b/tools/profiler/public/ProfilerChild.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; 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/. */
+
+#ifndef ProfilerChild_h
+#define ProfilerChild_h
+
+#include "mozilla/BaseProfilerDetail.h"
+#include "mozilla/DataMutex.h"
+#include "mozilla/PProfilerChild.h"
+#include "mozilla/ProfileBufferControlledChunkManager.h"
+#include "mozilla/RefPtr.h"
+
+class nsIThread;
+
+namespace mozilla {
+
+// The ProfilerChild actor is created in all processes except for the main
+// process. The corresponding ProfilerParent actor is created in the main
+// process, and it will notify us about profiler state changes and request
+// profiles from us.
+class ProfilerChild final : public PProfilerChild,
+ public mozilla::ipc::IShmemAllocator {
+ NS_INLINE_DECL_REFCOUNTING(ProfilerChild)
+
+ ProfilerChild();
+
+ // Collects and returns a profile.
+ // This method can be used to grab a profile just before PProfiler is torn
+ // down. The collected profile should then be sent through a different
+ // message channel that is guaranteed to stay open long enough.
+ nsCString GrabShutdownProfile();
+
+ void Destroy();
+
+ // This should be called regularly from outside of the profiler lock.
+ static void ProcessPendingUpdate();
+
+ static bool IsLockedOnCurrentThread();
+
+ private:
+ virtual ~ProfilerChild();
+
+ mozilla::ipc::IPCResult RecvStart(const ProfilerInitParams& params) override;
+ mozilla::ipc::IPCResult RecvEnsureStarted(
+ const ProfilerInitParams& params) override;
+ mozilla::ipc::IPCResult RecvStop() override;
+ mozilla::ipc::IPCResult RecvPause() override;
+ mozilla::ipc::IPCResult RecvResume() override;
+ mozilla::ipc::IPCResult RecvPauseSampling() override;
+ mozilla::ipc::IPCResult RecvResumeSampling() override;
+ mozilla::ipc::IPCResult RecvAwaitNextChunkManagerUpdate(
+ AwaitNextChunkManagerUpdateResolver&& aResolve) override;
+ mozilla::ipc::IPCResult RecvDestroyReleasedChunksAtOrBefore(
+ const TimeStamp& aTimeStamp) override;
+ mozilla::ipc::IPCResult RecvGatherProfile(
+ GatherProfileResolver&& aResolve) override;
+ mozilla::ipc::IPCResult RecvClearAllPages() override;
+
+ void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
+
+ FORWARD_SHMEM_ALLOCATOR_TO(PProfilerChild)
+
+ void SetupChunkManager();
+ void ResetChunkManager();
+ void ResolveChunkUpdate(
+ PProfilerChild::AwaitNextChunkManagerUpdateResolver& aResolve);
+ void ProcessChunkManagerUpdate(
+ ProfileBufferControlledChunkManager::Update&& aUpdate);
+
+ nsCOMPtr<nsIThread> mThread;
+ bool mDestroyed;
+
+ ProfileBufferControlledChunkManager* mChunkManager = nullptr;
+ AwaitNextChunkManagerUpdateResolver mAwaitNextChunkManagerUpdateResolver;
+ ProfileBufferControlledChunkManager::Update mChunkManagerUpdate;
+
+ struct ProfilerChildAndUpdate {
+ RefPtr<ProfilerChild> mProfilerChild;
+ ProfileBufferControlledChunkManager::Update mUpdate;
+ };
+ static DataMutexBase<ProfilerChildAndUpdate,
+ baseprofiler::detail::BaseProfilerMutex>
+ sPendingChunkManagerUpdate;
+};
+
+} // namespace mozilla
+
+#endif // ProfilerChild_h
diff --git a/tools/profiler/public/ProfilerCodeAddressService.h b/tools/profiler/public/ProfilerCodeAddressService.h
new file mode 100644
index 0000000000..9d75c363b3
--- /dev/null
+++ b/tools/profiler/public/ProfilerCodeAddressService.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; 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/. */
+
+#ifndef ProfilerCodeAddressService_h
+#define ProfilerCodeAddressService_h
+
+#include "CodeAddressService.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+// This SymbolTable struct, and the CompactSymbolTable struct in the
+// profiler rust module, have the exact same memory layout.
+// nsTArray and ThinVec are FFI-compatible, because the thin-vec crate is
+// being compiled with the "gecko-ffi" feature enabled.
+struct SymbolTable {
+ SymbolTable() = default;
+ SymbolTable(SymbolTable&& aOther) = default;
+
+ nsTArray<uint32_t> mAddrs;
+ nsTArray<uint32_t> mIndex;
+ nsTArray<uint8_t> mBuffer;
+};
+
+} // namespace mozilla
+
+/**
+ * Cache and look up function symbol names.
+ *
+ * We don't template this on AllocPolicy since we need to use nsTArray in
+ * SymbolTable above, which doesn't work with AllocPolicy. (We can't switch
+ * to Vector, as we would lose FFI compatibility with ThinVec.)
+ */
+class ProfilerCodeAddressService : public mozilla::CodeAddressService<> {
+ public:
+ // Like GetLocation, but only returns the symbol name.
+ bool GetFunction(const void* aPc, nsACString& aResult);
+
+ private:
+#if defined(XP_LINUX) || defined(XP_FREEBSD)
+ // Map of library names (owned by mLibraryStrings) to SymbolTables filled
+ // in by profiler_get_symbol_table.
+ mozilla::HashMap<const char*, mozilla::SymbolTable,
+ mozilla::DefaultHasher<const char*>, AllocPolicy>
+ mSymbolTables;
+#endif
+};
+
+#endif // ProfilerCodeAddressService_h
diff --git a/tools/profiler/public/ProfilerCounts.h b/tools/profiler/public/ProfilerCounts.h
new file mode 100644
index 0000000000..a86cf19709
--- /dev/null
+++ b/tools/profiler/public/ProfilerCounts.h
@@ -0,0 +1,274 @@
+/* -*- 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/. */
+
+#ifndef ProfilerCounts_h
+#define ProfilerCounts_h
+
+#ifndef MOZ_GECKO_PROFILER
+
+# define PROFILER_DEFINE_COUNT_TOTAL(label, category, description)
+# define PROFILER_DEFINE_COUNT(label, category, description)
+# define PROFILER_DEFINE_STATIC_COUNT_TOTAL(label, category, description)
+# define AUTO_PROFILER_TOTAL(label, count)
+# define AUTO_PROFILER_COUNT(label)
+# define AUTO_PROFILER_STATIC_COUNT(label, count)
+
+#else
+
+# include "mozilla/Atomics.h"
+
+class BaseProfilerCount;
+void profiler_add_sampled_counter(BaseProfilerCount* aCounter);
+void profiler_remove_sampled_counter(BaseProfilerCount* aCounter);
+
+typedef mozilla::Atomic<int64_t, mozilla::MemoryOrdering::Relaxed>
+ ProfilerAtomicSigned;
+typedef mozilla::Atomic<uint64_t, mozilla::MemoryOrdering::Relaxed>
+ ProfilerAtomicUnsigned;
+
+// Counter support
+// There are two types of counters:
+// 1) a simple counter which can be added to or subtracted from. This could
+// track the number of objects of a type, the number of calls to something
+// (reflow, JIT, etc).
+// 2) a combined counter which has the above, plus a number-of-calls counter
+// that is incremented by 1 for each call to modify the count. This provides
+// an optional source for a 'heatmap' of access. This can be used (for
+// example) to track the amount of memory allocated, and provide a heatmap of
+// memory operations (allocs/frees).
+//
+// Counters are sampled by the profiler once per sample-period. At this time,
+// all counters are global to the process. In the future, there might be more
+// versions with per-thread or other discriminators.
+//
+// Typical usage:
+// There are two ways to use counters: With heap-created counter objects,
+// or using macros. Note: the macros use statics, and will be slightly
+// faster/smaller, and you need to care about creating them before using
+// them. They're similar to the use-pattern for the other AUTO_PROFILER*
+// macros, but they do need the PROFILER_DEFINE* to be use to instantiate
+// the statics.
+//
+// PROFILER_DEFINE_COUNT(mything, "JIT", "Some JIT byte count")
+// ...
+// void foo() { ... AUTO_PROFILER_COUNT(mything, number_of_bytes_used); ... }
+//
+// or (to also get a heatmap)
+//
+// PROFILER_DEFINE_COUNT_TOTAL(mything, "JIT", "Some JIT byte count")
+// ...
+// void foo() {
+// ...
+// AUTO_PROFILER_COUNT_TOTAL(mything, number_of_bytes_generated);
+// ...
+// }
+//
+// To use without statics/macros:
+//
+// UniquePtr<ProfilerCounter> myCounter;
+// ...
+// myCounter =
+// MakeUnique<ProfilerCounter>("mything", "JIT", "Some JIT byte count"));
+// ...
+// void foo() { ... myCounter->Add(number_of_bytes_generated0; ... }
+
+class BaseProfilerCount {
+ public:
+ BaseProfilerCount(const char* aLabel, ProfilerAtomicSigned* aCounter,
+ ProfilerAtomicUnsigned* aNumber, const char* aCategory,
+ const char* aDescription)
+ : mLabel(aLabel),
+ mCategory(aCategory),
+ mDescription(aDescription),
+ mCounter(aCounter),
+ mNumber(aNumber) {
+# define COUNTER_CANARY 0xDEADBEEF
+# ifdef DEBUG
+ mCanary = COUNTER_CANARY;
+ mPrevNumber = 0;
+# endif
+ // Can't call profiler_* here since this may be non-xul-library
+ }
+
+ virtual ~BaseProfilerCount() {
+# ifdef DEBUG
+ mCanary = 0;
+# endif
+ }
+
+ virtual void Sample(int64_t& aCounter, uint64_t& aNumber) {
+ MOZ_ASSERT(mCanary == COUNTER_CANARY);
+
+ aCounter = *mCounter;
+ aNumber = mNumber ? *mNumber : 0;
+# ifdef DEBUG
+ MOZ_ASSERT(aNumber >= mPrevNumber);
+ mPrevNumber = aNumber;
+# endif
+ }
+
+ // We don't define ++ and Add() here, since the static defines directly
+ // increment the atomic counters, and the subclasses implement ++ and
+ // Add() directly.
+
+ // These typically are static strings (for example if you use the macros
+ // below)
+ const char* mLabel;
+ const char* mCategory;
+ const char* mDescription;
+ // We're ok with these being un-ordered in race conditions. These are
+ // pointers because we want to be able to use statics and increment them
+ // directly. Otherwise we could just have them inline, and not need the
+ // constructor args.
+ // These can be static globals (using the macros below), though they
+ // don't have to be - their lifetime must be longer than the use of them
+ // by the profiler (see profiler_add/remove_sampled_counter()). If you're
+ // using a lot of these, they probably should be allocated at runtime (see
+ // class ProfilerCountOnly below).
+ ProfilerAtomicSigned* mCounter;
+ ProfilerAtomicUnsigned* mNumber; // may be null
+
+# ifdef DEBUG
+ uint32_t mCanary;
+ uint64_t mPrevNumber; // value of number from the last Sample()
+# endif
+};
+
+// Designed to be allocated dynamically, and simply incremented with obj++
+// or obj->Add(n)
+class ProfilerCounter final : public BaseProfilerCount {
+ public:
+ ProfilerCounter(const char* aLabel, const char* aCategory,
+ const char* aDescription)
+ : BaseProfilerCount(aLabel, &mCounter, nullptr, aCategory, aDescription) {
+ // Assume we're in libxul
+ profiler_add_sampled_counter(this);
+ }
+
+ virtual ~ProfilerCounter() { profiler_remove_sampled_counter(this); }
+
+ BaseProfilerCount& operator++() {
+ Add(1);
+ return *this;
+ }
+
+ void Add(int64_t aNumber) { mCounter += aNumber; }
+
+ ProfilerAtomicSigned mCounter;
+};
+
+// Also keeps a heatmap (number of calls to ++/Add())
+class ProfilerCounterTotal final : public BaseProfilerCount {
+ public:
+ ProfilerCounterTotal(const char* aLabel, const char* aCategory,
+ const char* aDescription)
+ : BaseProfilerCount(aLabel, &mCounter, &mNumber, aCategory,
+ aDescription) {
+ // Assume we're in libxul
+ profiler_add_sampled_counter(this);
+ }
+
+ virtual ~ProfilerCounterTotal() { profiler_remove_sampled_counter(this); }
+
+ BaseProfilerCount& operator++() {
+ Add(1);
+ return *this;
+ }
+
+ void Add(int64_t aNumber) {
+ mCounter += aNumber;
+ mNumber++;
+ }
+
+ ProfilerAtomicSigned mCounter;
+ ProfilerAtomicUnsigned mNumber;
+};
+
+// Defines a counter that is sampled on each profiler tick, with a running
+// count (signed), and number-of-instances. Note that because these are two
+// independent Atomics, there is a possiblity that count will not include
+// the last call, but number of uses will. I think this is not worth
+// worrying about
+# define PROFILER_DEFINE_COUNT_TOTAL(label, category, description) \
+ ProfilerAtomicSigned profiler_count_##label(0); \
+ ProfilerAtomicUnsigned profiler_number_##label(0); \
+ const char profiler_category_##label[] = category; \
+ const char profiler_description_##label[] = description; \
+ mozilla::UniquePtr<BaseProfilerCount> AutoCount_##label;
+
+// This counts, but doesn't keep track of the number of calls to
+// AUTO_PROFILER_COUNT()
+# define PROFILER_DEFINE_COUNT(label, category, description) \
+ ProfilerAtomicSigned profiler_count_##label(0); \
+ const char profiler_category_##label[] = category; \
+ const char profiler_description_##label[] = description; \
+ mozilla::UniquePtr<BaseProfilerCount> AutoCount_##label;
+
+// This will create a static initializer if used, but avoids a possible
+// allocation.
+# define PROFILER_DEFINE_STATIC_COUNT_TOTAL(label, category, description) \
+ ProfilerAtomicSigned profiler_count_##label(0); \
+ ProfilerAtomicUnsigned profiler_number_##label(0); \
+ BaseProfilerCount AutoCount_##label(#label, &profiler_count_##label, \
+ &profiler_number_##label, category, \
+ description);
+
+// If we didn't care about static initializers, we could avoid the need for
+// a ptr to the BaseProfilerCount object.
+
+// XXX It would be better to do this without the if() and without the
+// theoretical race to set the UniquePtr (i.e. possible leak).
+# define AUTO_PROFILER_COUNT_TOTAL(label, count) \
+ do { \
+ profiler_number_##label++; /* do this first*/ \
+ profiler_count_##label += count; \
+ if (!AutoCount_##label) { \
+ /* Ignore that we could call this twice in theory, and that we leak \
+ * them \
+ */ \
+ AutoCount_##label.reset(new BaseProfilerCount( \
+ #label, &profiler_count_##label, &profiler_number_##label, \
+ profiler_category_##label, profiler_description_##label)); \
+ profiler_add_sampled_counter(AutoCount_##label.get()); \
+ } \
+ } while (0)
+
+# define AUTO_PROFILER_COUNT(label, count) \
+ do { \
+ profiler_count_##label += count; /* do this first*/ \
+ if (!AutoCount_##label) { \
+ /* Ignore that we could call this twice in theory, and that we leak \
+ * them \
+ */ \
+ AutoCount_##label.reset(new BaseProfilerCount( \
+ #label, nullptr, &profiler_number_##label, \
+ profiler_category_##label, profiler_description_##label)); \
+ profiler_add_sampled_counter(AutoCount_##label.get()); \
+ } \
+ } while (0)
+
+# define AUTO_PROFILER_STATIC_COUNT(label, count) \
+ do { \
+ profiler_number_##label++; /* do this first*/ \
+ profiler_count_##label += count; \
+ } while (0)
+
+// if we need to force the allocation
+# define AUTO_PROFILER_FORCE_ALLOCATION(label) \
+ do { \
+ if (!AutoCount_##label) { \
+ /* Ignore that we could call this twice in theory, and that we leak \
+ * them \
+ */ \
+ AutoCount_##label.reset(new BaseProfilerCount( \
+ #label, &profiler_count_##label, &profiler_number_##label, \
+ profiler_category_##label, profiler_description_##label)); \
+ } \
+ } while (0)
+
+#endif // !MOZ_GECKO_PROFILER
+
+#endif // ProfilerCounts_h
diff --git a/tools/profiler/public/ProfilerMarkerTypes.h b/tools/profiler/public/ProfilerMarkerTypes.h
new file mode 100644
index 0000000000..347f58839c
--- /dev/null
+++ b/tools/profiler/public/ProfilerMarkerTypes.h
@@ -0,0 +1,41 @@
+/* -*- 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/. */
+
+#ifndef ProfilerMarkerTypes_h
+#define ProfilerMarkerTypes_h
+
+// This header contains common marker type definitions that rely on xpcom.
+//
+// It #include's "mozilla/BaseProfilerMarkerTypess.h" and "ProfilerMarkers.h",
+// see these files for more marker types, how to define other marker types, and
+// how to add markers to the profiler buffers.
+
+// !!! /!\ WORK IN PROGRESS /!\ !!!
+// This file contains draft marker definitions, but most are not used yet.
+// Further work is needed to complete these definitions, and use them to convert
+// existing PROFILER_ADD_MARKER calls. See meta bug 1661394.
+
+#include "mozilla/BaseProfilerMarkerTypes.h"
+#include "mozilla/ProfilerMarkers.h"
+
+#ifdef MOZ_GECKO_PROFILER
+
+# include "js/ProfilingFrameIterator.h"
+# include "js/Utility.h"
+# include "mozilla/Preferences.h"
+# include "mozilla/ServoTraversalStatistics.h"
+
+namespace geckoprofiler::markers {
+
+// Import some common markers from mozilla::baseprofiler::markers.
+using MediaSampleMarker = mozilla::baseprofiler::markers::MediaSampleMarker;
+using ContentBuildMarker = mozilla::baseprofiler::markers::ContentBuildMarker;
+
+} // namespace geckoprofiler::markers
+
+#endif // MOZ_GECKO_PROFILER
+
+#endif // ProfilerMarkerTypes_h
diff --git a/tools/profiler/public/ProfilerMarkers.h b/tools/profiler/public/ProfilerMarkers.h
new file mode 100644
index 0000000000..de2e6f881d
--- /dev/null
+++ b/tools/profiler/public/ProfilerMarkers.h
@@ -0,0 +1,294 @@
+/* -*- 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/. */
+
+// Markers are useful to delimit something important happening such as the first
+// paint. Unlike labels, which are only recorded in the profile buffer if a
+// sample is collected while the label is on the label stack, markers will
+// always be recorded in the profile buffer.
+//
+// This header contains definitions necessary to add markers to the Gecko
+// Profiler buffer.
+//
+// It #include's "mozilla/BaseProfilerMarkers.h", see that header for base
+// definitions necessary to create marker types.
+//
+// If common marker types are needed, #include "ProfilerMarkerTypes.h" instead.
+//
+// But if you want to create your own marker type locally, you can #include this
+// header only; look at ProfilerMarkerTypes.h for examples of how to define
+// types.
+//
+// To then record markers:
+// - Use `baseprofiler::AddMarker(...)` from mozglue or other libraries that are
+// outside of xul, especially if they may happen outside of xpcom's lifetime
+// (typically startup, shutdown, or tests).
+// - Otherwise #include "ProfilerMarkers.h" instead, and use
+// `profiler_add_marker(...)`.
+// See these functions for more details.
+
+#ifndef ProfilerMarkers_h
+#define ProfilerMarkers_h
+
+#include "mozilla/BaseProfilerMarkers.h"
+#include "mozilla/ProfilerMarkersDetail.h"
+
+#ifndef MOZ_GECKO_PROFILER
+
+# define PROFILER_MARKER_UNTYPED(markerName, categoryName, ...)
+# define PROFILER_MARKER(markerName, categoryName, options, MarkerType, ...)
+# define PROFILER_MARKER_TEXT(markerName, categoryName, options, text)
+# define AUTO_PROFILER_MARKER_TEXT(markerName, categoryName, options, text)
+# define AUTO_PROFILER_TRACING_MARKER(categoryString, markerName, categoryPair)
+# define AUTO_PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \
+ categoryPair, docShell)
+
+#else // ndef MOZ_GECKO_PROFILER
+
+namespace mozilla {
+class ProfileChunkedBuffer;
+}
+
+bool profiler_can_accept_markers();
+bool profiler_capture_backtrace_into(
+ mozilla::ProfileChunkedBuffer& aChunkedBuffer);
+
+// Bring category names from Base Profiler into the geckoprofiler::category
+// namespace, for consistency with other Gecko Profiler identifiers.
+namespace geckoprofiler::category {
+using namespace ::mozilla::baseprofiler::category;
+}
+
+// Add a marker to a given buffer. `AddMarker()` and related macros should be
+// used in most cases, see below for more information about them and the
+// paramters; This function may be useful when markers need to be recorded in a
+// local buffer outside of the main profiler buffer.
+template <typename MarkerType, typename... PayloadArguments>
+mozilla::ProfileBufferBlockIndex AddMarkerToBuffer(
+ mozilla::ProfileChunkedBuffer& aBuffer,
+ const mozilla::ProfilerString8View& aName,
+ const mozilla::MarkerCategory& aCategory, mozilla::MarkerOptions&& aOptions,
+ MarkerType aMarkerType, const PayloadArguments&... aPayloadArguments) {
+ mozilla::Unused << aMarkerType; // Only the empty object type is useful.
+ return mozilla::base_profiler_markers_detail::AddMarkerToBuffer<MarkerType>(
+ aBuffer, aName, aCategory, std::move(aOptions),
+ ::profiler_capture_backtrace_into, aPayloadArguments...);
+}
+
+// Add a marker (without payload) to a given buffer.
+inline mozilla::ProfileBufferBlockIndex AddMarkerToBuffer(
+ mozilla::ProfileChunkedBuffer& aBuffer,
+ const mozilla::ProfilerString8View& aName,
+ const mozilla::MarkerCategory& aCategory,
+ mozilla::MarkerOptions&& aOptions = {}) {
+ return AddMarkerToBuffer(aBuffer, aName, aCategory, std::move(aOptions),
+ mozilla::baseprofiler::markers::NoPayload{});
+}
+
+// Add a marker to the Gecko Profiler buffer.
+// - aName: Main name of this marker.
+// - aCategory: Category for this marker.
+// - aOptions: Optional settings (such as timing, inner window id,
+// backtrace...), see `MarkerOptions` for details.
+// - aMarkerType: Empty object that specifies the type of marker.
+// - aPayloadArguments: Arguments expected by this marker type's
+// ` StreamJSONMarkerData` function.
+template <typename MarkerType, typename... PayloadArguments>
+mozilla::ProfileBufferBlockIndex profiler_add_marker(
+ const mozilla::ProfilerString8View& aName,
+ const mozilla::MarkerCategory& aCategory, mozilla::MarkerOptions&& aOptions,
+ MarkerType aMarkerType, const PayloadArguments&... aPayloadArguments) {
+ if (!profiler_can_accept_markers()) {
+ return {};
+ }
+ return ::AddMarkerToBuffer(profiler_markers_detail::CachedCoreBuffer(), aName,
+ aCategory, std::move(aOptions), aMarkerType,
+ aPayloadArguments...);
+}
+
+// Add a marker (without payload) to the Gecko Profiler buffer.
+inline mozilla::ProfileBufferBlockIndex profiler_add_marker(
+ const mozilla::ProfilerString8View& aName,
+ const mozilla::MarkerCategory& aCategory,
+ mozilla::MarkerOptions&& aOptions = {}) {
+ return profiler_add_marker(aName, aCategory, std::move(aOptions),
+ mozilla::baseprofiler::markers::NoPayload{});
+}
+
+// Same as `profiler_add_marker()` (without payload). This macro is safe to use
+// even if MOZ_GECKO_PROFILER is not #defined.
+# define PROFILER_MARKER_UNTYPED(markerName, categoryName, ...) \
+ do { \
+ AUTO_PROFILER_STATS(PROFILER_MARKER_UNTYPED); \
+ ::profiler_add_marker( \
+ markerName, ::geckoprofiler::category::categoryName, ##__VA_ARGS__); \
+ } while (false)
+
+// Same as `profiler_add_marker()` (with payload). This macro is safe to use
+// even if MOZ_GECKO_PROFILER is not #defined.
+# define PROFILER_MARKER(markerName, categoryName, options, MarkerType, ...) \
+ do { \
+ AUTO_PROFILER_STATS(PROFILER_MARKER_with_##MarkerType); \
+ ::profiler_add_marker( \
+ markerName, ::geckoprofiler::category::categoryName, options, \
+ ::geckoprofiler::markers::MarkerType{}, ##__VA_ARGS__); \
+ } while (false)
+
+namespace geckoprofiler::markers {
+// Most common marker types. Others are in ProfilerMarkerTypes.h.
+using TextMarker = ::mozilla::baseprofiler::markers::TextMarker;
+using Tracing = mozilla::baseprofiler::markers::Tracing;
+} // namespace geckoprofiler::markers
+
+// Add a text marker. This macro is safe to use even if MOZ_GECKO_PROFILER is
+// not #defined.
+# define PROFILER_MARKER_TEXT(markerName, categoryName, options, text) \
+ do { \
+ AUTO_PROFILER_STATS(PROFILER_MARKER_TEXT); \
+ ::profiler_add_marker(markerName, \
+ ::geckoprofiler::category::categoryName, options, \
+ ::geckoprofiler::markers::TextMarker{}, text); \
+ } while (false)
+
+// RAII object that adds a PROFILER_MARKER_TEXT when destroyed; the marker's
+// timing will be the interval from construction (unless an instant or start
+// time is already specified in the provided options) until destruction.
+class MOZ_RAII AutoProfilerTextMarker {
+ public:
+ AutoProfilerTextMarker(const char* aMarkerName,
+ const mozilla::MarkerCategory& aCategory,
+ mozilla::MarkerOptions&& aOptions,
+ const nsACString& aText)
+ : mMarkerName(aMarkerName),
+ mCategory(aCategory),
+ mOptions(std::move(aOptions)),
+ mText(aText) {
+ MOZ_ASSERT(mOptions.Timing().EndTime().IsNull(),
+ "AutoProfilerTextMarker options shouldn't have an end time");
+ if (mOptions.Timing().StartTime().IsNull()) {
+ mOptions.Set(mozilla::MarkerTiming::InstantNow());
+ }
+ }
+
+ ~AutoProfilerTextMarker() {
+ mOptions.TimingRef().SetIntervalEnd();
+ AUTO_PROFILER_STATS(AUTO_PROFILER_MARKER_TEXT);
+ profiler_add_marker(
+ mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName),
+ mCategory, std::move(mOptions), geckoprofiler::markers::TextMarker{},
+ mText);
+ }
+
+ protected:
+ const char* mMarkerName;
+ mozilla::MarkerCategory mCategory;
+ mozilla::MarkerOptions mOptions;
+ nsCString mText;
+};
+
+// Creates an AutoProfilerTextMarker RAII object. This macro is safe to use
+// even if MOZ_GECKO_PROFILER is not #defined.
+# define AUTO_PROFILER_MARKER_TEXT(markerName, categoryName, options, text) \
+ AutoProfilerTextMarker PROFILER_RAII( \
+ markerName, ::mozilla::baseprofiler::category::categoryName, options, \
+ text)
+
+class MOZ_RAII AutoProfilerTracing {
+ public:
+ AutoProfilerTracing(const char* aCategoryString, const char* aMarkerName,
+ mozilla::MarkerCategory aCategoryPair,
+ const mozilla::Maybe<uint64_t>& aInnerWindowID)
+ : mCategoryString(aCategoryString),
+ mMarkerName(aMarkerName),
+ mCategoryPair(aCategoryPair),
+ mInnerWindowID(aInnerWindowID) {
+ profiler_add_marker(
+ mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName),
+ mCategoryPair,
+ {mozilla::MarkerTiming::IntervalStart(),
+ mozilla::MarkerInnerWindowId(mInnerWindowID)},
+ geckoprofiler::markers::Tracing{},
+ mozilla::ProfilerString8View::WrapNullTerminatedString(
+ mCategoryString));
+ }
+
+ AutoProfilerTracing(
+ const char* aCategoryString, const char* aMarkerName,
+ mozilla::MarkerCategory aCategoryPair,
+ mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aBacktrace,
+ const mozilla::Maybe<uint64_t>& aInnerWindowID)
+ : mCategoryString(aCategoryString),
+ mMarkerName(aMarkerName),
+ mCategoryPair(aCategoryPair),
+ mInnerWindowID(aInnerWindowID) {
+ profiler_add_marker(
+ mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName),
+ mCategoryPair,
+ {mozilla::MarkerTiming::IntervalStart(),
+ mozilla::MarkerInnerWindowId(mInnerWindowID),
+ mozilla::MarkerStack::TakeBacktrace(std::move(aBacktrace))},
+ geckoprofiler::markers::Tracing{},
+ mozilla::ProfilerString8View::WrapNullTerminatedString(
+ mCategoryString));
+ }
+
+ ~AutoProfilerTracing() {
+ profiler_add_marker(
+ mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName),
+ mCategoryPair,
+ {mozilla::MarkerTiming::IntervalEnd(),
+ mozilla::MarkerInnerWindowId(mInnerWindowID)},
+ geckoprofiler::markers::Tracing{},
+ mozilla::ProfilerString8View::WrapNullTerminatedString(
+ mCategoryString));
+ }
+
+ protected:
+ const char* mCategoryString;
+ const char* mMarkerName;
+ const mozilla::MarkerCategory mCategoryPair;
+ const mozilla::Maybe<uint64_t> mInnerWindowID;
+};
+
+// Adds a START/END pair of tracing markers.
+# define AUTO_PROFILER_TRACING_MARKER(categoryString, markerName, \
+ categoryPair) \
+ AutoProfilerTracing PROFILER_RAII(categoryString, markerName, \
+ geckoprofiler::category::categoryPair, \
+ mozilla::Nothing())
+# define AUTO_PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \
+ categoryPair, docShell) \
+ AutoProfilerTracing PROFILER_RAII( \
+ categoryString, markerName, geckoprofiler::category::categoryPair, \
+ profiler_get_inner_window_id_from_docshell(docShell))
+
+extern template mozilla::ProfileBufferBlockIndex AddMarkerToBuffer(
+ mozilla::ProfileChunkedBuffer&, const mozilla::ProfilerString8View&,
+ const mozilla::MarkerCategory&, mozilla::MarkerOptions&&,
+ mozilla::baseprofiler::markers::NoPayload);
+
+extern template mozilla::ProfileBufferBlockIndex AddMarkerToBuffer(
+ mozilla::ProfileChunkedBuffer&, const mozilla::ProfilerString8View&,
+ const mozilla::MarkerCategory&, mozilla::MarkerOptions&&,
+ mozilla::baseprofiler::markers::TextMarker, const std::string&);
+
+extern template mozilla::ProfileBufferBlockIndex profiler_add_marker(
+ const mozilla::ProfilerString8View&, const mozilla::MarkerCategory&,
+ mozilla::MarkerOptions&&, mozilla::baseprofiler::markers::TextMarker,
+ const std::string&);
+
+extern template mozilla::ProfileBufferBlockIndex profiler_add_marker(
+ const mozilla::ProfilerString8View&, const mozilla::MarkerCategory&,
+ mozilla::MarkerOptions&&, mozilla::baseprofiler::markers::TextMarker,
+ const nsCString&);
+
+extern template mozilla::ProfileBufferBlockIndex profiler_add_marker(
+ const mozilla::ProfilerString8View&, const mozilla::MarkerCategory&,
+ mozilla::MarkerOptions&&, mozilla::baseprofiler::markers::Tracing,
+ const mozilla::ProfilerString8View&);
+
+#endif // nfed MOZ_GECKO_PROFILER else
+
+#endif // ProfilerMarkers_h
diff --git a/tools/profiler/public/ProfilerMarkersDetail.h b/tools/profiler/public/ProfilerMarkersDetail.h
new file mode 100644
index 0000000000..22396b5d2f
--- /dev/null
+++ b/tools/profiler/public/ProfilerMarkersDetail.h
@@ -0,0 +1,42 @@
+/* -*- 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/. */
+
+#ifndef ProfilerMarkersDetail_h
+#define ProfilerMarkersDetail_h
+
+#ifndef ProfilerMarkers_h
+# error "This header should only be #included by ProfilerMarkers.h"
+#endif
+
+#include "mozilla/ProfilerMarkersPrerequisites.h"
+
+#ifdef MOZ_GECKO_PROFILER
+
+// ~~ HERE BE DRAGONS ~~
+//
+// Everything below is internal implementation detail, you shouldn't need to
+// look at it unless working on the profiler code.
+
+// Header that specializes the (de)serializers for xpcom types.
+# include "mozilla/ProfileBufferEntrySerializationGeckoExtensions.h"
+
+// Implemented in platform.cpp
+mozilla::ProfileChunkedBuffer& profiler_get_core_buffer();
+
+namespace profiler_markers_detail {
+
+// Get the core buffer from the profiler, and cache it in a
+// non-templated-function static reference.
+inline mozilla::ProfileChunkedBuffer& CachedCoreBuffer() {
+ static mozilla::ProfileChunkedBuffer& coreBuffer = profiler_get_core_buffer();
+ return coreBuffer;
+}
+
+} // namespace profiler_markers_detail
+
+#endif // MOZ_GECKO_PROFILER
+
+#endif // ProfilerMarkersDetail_h
diff --git a/tools/profiler/public/ProfilerMarkersPrerequisites.h b/tools/profiler/public/ProfilerMarkersPrerequisites.h
new file mode 100644
index 0000000000..93868b0524
--- /dev/null
+++ b/tools/profiler/public/ProfilerMarkersPrerequisites.h
@@ -0,0 +1,30 @@
+/* -*- 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 basic definitions required to create marker types, and
+// to add markers to the profiler buffers.
+//
+// In most cases, #include "mozilla/ProfilerMarkers.h" instead, or
+// #include "mozilla/ProfilerMarkerTypes.h" for common marker types.
+
+#ifndef ProfilerMarkersPrerequisites_h
+#define ProfilerMarkersPrerequisites_h
+
+#include "mozilla/BaseProfilerMarkersPrerequisites.h"
+
+#ifdef MOZ_GECKO_PROFILER
+
+namespace geckoprofiler::markers {
+
+// Default marker payload types, with no extra information, not even a marker
+// type and payload. This is intended for label-only markers.
+using NoPayload = ::mozilla::baseprofiler::markers::NoPayload;
+
+} // namespace geckoprofiler::markers
+
+#endif // MOZ_GECKO_PROFILER
+
+#endif // ProfilerMarkersPrerequisites_h
diff --git a/tools/profiler/public/ProfilerParent.h b/tools/profiler/public/ProfilerParent.h
new file mode 100644
index 0000000000..fe9457e678
--- /dev/null
+++ b/tools/profiler/public/ProfilerParent.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; 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/. */
+
+#ifndef ProfilerParent_h
+#define ProfilerParent_h
+
+#include "mozilla/PProfilerParent.h"
+#include "mozilla/RefPtr.h"
+
+class nsIProfilerStartParams;
+
+namespace mozilla {
+
+class ProfileBufferGlobalController;
+class ProfilerParentTracker;
+
+// This is the main process side of the PProfiler protocol.
+// ProfilerParent instances only exist on the main thread of the main process.
+// The other side (ProfilerChild) lives on a background thread in the other
+// process.
+// The creation of PProfiler actors is initiated from the main process, after
+// the other process has been launched.
+// ProfilerParent instances are destroyed once the message channel closes,
+// which can be triggered by either process, depending on which one shuts down
+// first.
+// All ProfilerParent instances are registered with a manager class called
+// ProfilerParentTracker, which has the list of living ProfilerParent instances
+// and handles shutdown.
+class ProfilerParent final : public PProfilerParent {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(ProfilerParent)
+
+ static mozilla::ipc::Endpoint<PProfilerChild> CreateForProcess(
+ base::ProcessId aOtherPid);
+
+ typedef MozPromise<Shmem, ResponseRejectReason, true>
+ SingleProcessProfilePromise;
+
+ // The following static methods can be called on any thread, but they are
+ // no-ops on anything other than the main thread.
+ // If called on the main thread, the call will be broadcast to all
+ // registered processes (all processes for which we have a ProfilerParent
+ // object).
+ // At the moment, the main process always calls these methods on the main
+ // thread, and that's the only process in which we need to forward these
+ // calls to other processes. The other processes will call these methods on
+ // the ProfilerChild background thread, but those processes don't need to
+ // forward these calls any further.
+
+ // Returns the number of profiles to expect. The gathered profiles will be
+ // provided asynchronously with a call to
+ // ProfileGatherer::ReceiveGatheredProfile.
+ static nsTArray<RefPtr<SingleProcessProfilePromise>> GatherProfiles();
+
+ static void ProfilerStarted(nsIProfilerStartParams* aParams);
+ static void ProfilerWillStopIfStarted();
+ static void ProfilerStopped();
+ static void ProfilerPaused();
+ static void ProfilerResumed();
+ static void ProfilerPausedSampling();
+ static void ProfilerResumedSampling();
+ static void ClearAllPages();
+
+ // Create a "Final" update that the Child can return to its Parent.
+ static ProfileBufferChunkManagerUpdate MakeFinalUpdate();
+
+ // True if the ProfilerParent holds a lock on this thread.
+ static bool IsLockedOnCurrentThread();
+
+ private:
+ friend class ProfileBufferGlobalController;
+ friend class ProfilerParentTracker;
+
+ explicit ProfilerParent(base::ProcessId aChildPid);
+ virtual ~ProfilerParent();
+
+ void Init();
+ void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
+ void ActorDealloc() override;
+
+ void RequestChunkManagerUpdate();
+
+ RefPtr<ProfilerParent> mSelfRef;
+ base::ProcessId mChildPid;
+ nsTArray<MozPromiseHolder<SingleProcessProfilePromise>>
+ mPendingRequestedProfiles;
+ bool mDestroyed;
+};
+
+} // namespace mozilla
+
+#endif // ProfilerParent_h
diff --git a/tools/profiler/public/shared-libraries.h b/tools/profiler/public/shared-libraries.h
new file mode 100644
index 0000000000..2397fbd84f
--- /dev/null
+++ b/tools/profiler/public/shared-libraries.h
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef SHARED_LIBRARIES_H_
+#define SHARED_LIBRARIES_H_
+
+#ifndef MOZ_GECKO_PROFILER
+# error This header does not have a useful implementation on your platform!
+#endif
+
+#include "nsNativeCharsetUtils.h"
+#include "nsString.h"
+#include <nsID.h>
+
+#include <algorithm>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string>
+#include <vector>
+
+class SharedLibrary {
+ public:
+ SharedLibrary(uintptr_t aStart, uintptr_t aEnd, uintptr_t aOffset,
+ const nsCString& aBreakpadId, const nsString& aModuleName,
+ const nsString& aModulePath, const nsString& aDebugName,
+ const nsString& aDebugPath, const nsCString& aVersion,
+ const char* aArch)
+ : mStart(aStart),
+ mEnd(aEnd),
+ mOffset(aOffset),
+ mBreakpadId(aBreakpadId),
+ mModuleName(aModuleName),
+ mModulePath(aModulePath),
+ mDebugName(aDebugName),
+ mDebugPath(aDebugPath),
+ mVersion(aVersion),
+ mArch(aArch) {}
+
+ SharedLibrary(const SharedLibrary& aEntry)
+ : mStart(aEntry.mStart),
+ mEnd(aEntry.mEnd),
+ mOffset(aEntry.mOffset),
+ mBreakpadId(aEntry.mBreakpadId),
+ mModuleName(aEntry.mModuleName),
+ mModulePath(aEntry.mModulePath),
+ mDebugName(aEntry.mDebugName),
+ mDebugPath(aEntry.mDebugPath),
+ mVersion(aEntry.mVersion),
+ mArch(aEntry.mArch) {}
+
+ SharedLibrary& operator=(const SharedLibrary& aEntry) {
+ // Gracefully handle self assignment
+ if (this == &aEntry) return *this;
+
+ mStart = aEntry.mStart;
+ mEnd = aEntry.mEnd;
+ mOffset = aEntry.mOffset;
+ mBreakpadId = aEntry.mBreakpadId;
+ mModuleName = aEntry.mModuleName;
+ mModulePath = aEntry.mModulePath;
+ mDebugName = aEntry.mDebugName;
+ mDebugPath = aEntry.mDebugPath;
+ mVersion = aEntry.mVersion;
+ mArch = aEntry.mArch;
+ return *this;
+ }
+
+ bool operator==(const SharedLibrary& other) const {
+ return (mStart == other.mStart) && (mEnd == other.mEnd) &&
+ (mOffset == other.mOffset) && (mModuleName == other.mModuleName) &&
+ (mModulePath == other.mModulePath) &&
+ (mDebugName == other.mDebugName) &&
+ (mDebugPath == other.mDebugPath) &&
+ (mBreakpadId == other.mBreakpadId) && (mVersion == other.mVersion) &&
+ (mArch == other.mArch);
+ }
+
+ uintptr_t GetStart() const { return mStart; }
+ uintptr_t GetEnd() const { return mEnd; }
+ uintptr_t GetOffset() const { return mOffset; }
+ const nsCString& GetBreakpadId() const { return mBreakpadId; }
+ const nsString& GetModuleName() const { return mModuleName; }
+ const nsString& GetModulePath() const { return mModulePath; }
+ const std::string GetNativeDebugPath() const {
+ nsAutoCString debugPathStr;
+
+ NS_CopyUnicodeToNative(mDebugPath, debugPathStr);
+
+ return debugPathStr.get();
+ }
+ const nsString& GetDebugName() const { return mDebugName; }
+ const nsString& GetDebugPath() const { return mDebugPath; }
+ const nsCString& GetVersion() const { return mVersion; }
+ const std::string& GetArch() const { return mArch; }
+
+ private:
+ SharedLibrary() : mStart{0}, mEnd{0}, mOffset{0} {}
+
+ uintptr_t mStart;
+ uintptr_t mEnd;
+ uintptr_t mOffset;
+ nsCString mBreakpadId;
+ nsString mModuleName;
+ nsString mModulePath;
+ nsString mDebugName;
+ nsString mDebugPath;
+ nsCString mVersion;
+ std::string mArch;
+};
+
+static bool CompareAddresses(const SharedLibrary& first,
+ const SharedLibrary& second) {
+ return first.GetStart() < second.GetStart();
+}
+
+class SharedLibraryInfo {
+ public:
+ static SharedLibraryInfo GetInfoForSelf();
+ static void Initialize();
+
+ SharedLibraryInfo() {}
+
+ void AddSharedLibrary(SharedLibrary entry) { mEntries.push_back(entry); }
+
+ const SharedLibrary& GetEntry(size_t i) const { return mEntries[i]; }
+
+ SharedLibrary& GetMutableEntry(size_t i) { return mEntries[i]; }
+
+ // Removes items in the range [first, last)
+ // i.e. element at the "last" index is not removed
+ void RemoveEntries(size_t first, size_t last) {
+ mEntries.erase(mEntries.begin() + first, mEntries.begin() + last);
+ }
+
+ bool Contains(const SharedLibrary& searchItem) const {
+ return (mEntries.end() !=
+ std::find(mEntries.begin(), mEntries.end(), searchItem));
+ }
+
+ size_t GetSize() const { return mEntries.size(); }
+
+ void SortByAddress() {
+ std::sort(mEntries.begin(), mEntries.end(), CompareAddresses);
+ }
+
+ void Clear() { mEntries.clear(); }
+
+ private:
+ std::vector<SharedLibrary> mEntries;
+};
+
+#endif