mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 04:41:11 +00:00
Bug 1900846: Emit gecko profiler markers as perfetto track events if enabled. r=profiler-reviewers,canaltinova
If perfetto is enabled, then this patch enables emitting gecko profiler markers as perfetto track events. It will always emit the marker name, category and timestamps at the very least. It also adds support to the various payload types that are currently used, and those will be emitted as perfetto debug annotations. Differential Revision: https://phabricator.services.mozilla.com/D214033
This commit is contained in:
parent
8ca09eb710
commit
069d9c4abb
@ -7,6 +7,18 @@
|
||||
#include "mozilla/Perfetto.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
const char* ProfilerCategoryNames[] = {
|
||||
#define CATEGORY_JSON_BEGIN_CATEGORY(name, labelAsString, color) #name,
|
||||
#define CATEGORY_JSON_SUBCATEGORY(supercategory, name, labelAsString)
|
||||
#define CATEGORY_JSON_END_CATEGORY
|
||||
MOZ_PROFILING_CATEGORY_LIST(CATEGORY_JSON_BEGIN_CATEGORY,
|
||||
CATEGORY_JSON_SUBCATEGORY,
|
||||
CATEGORY_JSON_END_CATEGORY)
|
||||
#undef CATEGORY_JSON_BEGIN_CATEGORY
|
||||
#undef CATEGORY_JSON_SUBCATEGORY
|
||||
#undef CATEGORY_JSON_END_CATEGORY
|
||||
};
|
||||
|
||||
PERFETTO_TRACK_EVENT_STATIC_STORAGE();
|
||||
|
||||
void InitPerfetto() {
|
||||
|
@ -8,8 +8,12 @@
|
||||
#define mozilla_Perfetto_h
|
||||
|
||||
#ifdef MOZ_PERFETTO
|
||||
# include "perfetto/perfetto.h"
|
||||
# include "mozilla/BaseProfilerMarkers.h"
|
||||
# include "mozilla/Span.h"
|
||||
# include "mozilla/TimeStamp.h"
|
||||
# include "nsString.h"
|
||||
# include "nsPrintfCString.h"
|
||||
# include "perfetto/perfetto.h"
|
||||
|
||||
// Initialization is called when a content process is created.
|
||||
// This can be called multiple times.
|
||||
@ -83,6 +87,7 @@ extern void InitPerfetto();
|
||||
# define PERFETTO_TRACE_EVENT(...) TRACE_EVENT(__VA_ARGS__)
|
||||
# define PERFETTO_TRACE_EVENT_BEGIN(...) TRACE_EVENT_BEGIN(__VA_ARGS__)
|
||||
# define PERFETTO_TRACE_EVENT_END(...) TRACE_EVENT_END(__VA_ARGS__)
|
||||
# define PERFETTO_TRACE_EVENT_INSTANT(...) TRACE_EVENT_INSTANT(__VA_ARGS__)
|
||||
|
||||
namespace perfetto {
|
||||
// Specialize custom timestamps for mozilla::TimeStamp.
|
||||
@ -101,6 +106,306 @@ struct TraceTimestampTraits<mozilla::TimeStamp> {
|
||||
PERFETTO_DEFINE_CATEGORIES(perfetto::Category("task"),
|
||||
perfetto::Category("usertiming"));
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct MarkerHasPayloadFields : std::false_type {};
|
||||
template <typename T>
|
||||
struct MarkerHasPayloadFields<T, std::void_t<decltype(T::PayloadFields)>>
|
||||
: std::true_type {};
|
||||
|
||||
using MS = mozilla::MarkerSchema;
|
||||
|
||||
// Primary template. Assert if a payload type has not been specialized so we
|
||||
// don't miss payload information.
|
||||
template <typename T, typename Enable = void>
|
||||
struct AddDebugAnnotationImpl {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const T& aValue) {
|
||||
static_assert(false,
|
||||
"Unsupported payload type for perfetto debug annotations.");
|
||||
}
|
||||
};
|
||||
|
||||
// Do nothing for these types.
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<mozilla::Nothing> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const mozilla::Nothing& aValue) {}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<std::nullptr_t> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const std::nullptr_t& aValue) {}
|
||||
};
|
||||
|
||||
// Specialize mozilla::Maybe<>
|
||||
template <typename T>
|
||||
struct AddDebugAnnotationImpl<mozilla::Maybe<T>> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const mozilla::Maybe<T>& aValue) {
|
||||
if (aValue.isNothing()) {
|
||||
return;
|
||||
}
|
||||
AddDebugAnnotationImpl<T>::call(ctx, aKey, *aValue);
|
||||
}
|
||||
};
|
||||
|
||||
// Specialize integral types.
|
||||
template <typename T>
|
||||
struct AddDebugAnnotationImpl<T, std::enable_if_t<std::is_integral_v<T>>> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const T& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
|
||||
if constexpr (std::is_same_v<T, bool>) {
|
||||
arg->set_bool_value(static_cast<uint64_t>(aValue));
|
||||
} else if constexpr (std::is_signed_v<T>) {
|
||||
arg->set_int_value(static_cast<uint64_t>(aValue));
|
||||
} else {
|
||||
static_assert(std::is_unsigned_v<T>);
|
||||
arg->set_uint_value(static_cast<uint64_t>(aValue));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Specialize time durations.
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<
|
||||
mozilla::BaseTimeDuration<mozilla::TimeDurationValueCalculator>> {
|
||||
static void call(
|
||||
perfetto::EventContext& ctx, const char* const aKey,
|
||||
const mozilla::BaseTimeDuration<mozilla::TimeDurationValueCalculator>&
|
||||
aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_uint_value(static_cast<uint64_t>(aValue.ToMilliseconds()));
|
||||
}
|
||||
};
|
||||
|
||||
// Specialize the various string representations.
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<mozilla::ProfilerString8View> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const mozilla::ProfilerString8View& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(aValue.StringView().data());
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
struct AddDebugAnnotationImpl<nsAutoCStringN<N>> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const nsAutoCStringN<N>& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(aValue.get());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<nsCString> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const nsCString& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(aValue.get());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<nsAutoCString> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const nsAutoCString& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(aValue.get());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<nsTLiteralString<char>> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const nsTLiteralString<char>& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(aValue.get());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<nsPrintfCString> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const nsPrintfCString& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(aValue.get());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<nsTDependentString<char>> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const nsTDependentString<char>& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(aValue.get());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<nsACString> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const nsACString& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(nsAutoCString(aValue).get());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<std::string> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const std::string& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(aValue);
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
struct AddDebugAnnotationImpl<char[N]> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const char* aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(aValue);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<mozilla::ProfilerString16View> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const mozilla::ProfilerString16View& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(NS_ConvertUTF16toUTF8(aValue).get());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<nsAString> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const nsAString& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(NS_ConvertUTF16toUTF8(aValue).get());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<const nsAString&> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const nsAString& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(NS_ConvertUTF16toUTF8(aValue).get());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddDebugAnnotationImpl<nsString> {
|
||||
static void call(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const nsString& aValue) {
|
||||
auto* arg = ctx.event()->add_debug_annotations();
|
||||
arg->set_name(aKey);
|
||||
arg->set_string_value(NS_ConvertUTF16toUTF8(aValue).get());
|
||||
}
|
||||
};
|
||||
|
||||
// Main helper call that dispatches to proper specialization.
|
||||
template <typename T>
|
||||
void AddDebugAnnotation(perfetto::EventContext& ctx, const char* const aKey,
|
||||
const T& aValue) {
|
||||
AddDebugAnnotationImpl<T>::call(ctx, aKey, aValue);
|
||||
}
|
||||
|
||||
extern const char* ProfilerCategoryNames[];
|
||||
|
||||
// Main entry point from the gecko profiler for each marker.
|
||||
template <typename MarkerType, typename... PayloadArguments>
|
||||
void EmitPerfettoTrackEvent(const mozilla::ProfilerString8View& aName,
|
||||
const mozilla::MarkerCategory& aCategory,
|
||||
const mozilla::MarkerOptions& aOptions,
|
||||
MarkerType aMarkerType,
|
||||
const PayloadArguments&... aPayloadArguments) {
|
||||
mozilla::TimeStamp startTime, endTime;
|
||||
mozilla::MarkerTiming::Phase phase;
|
||||
|
||||
if (aOptions.IsTimingUnspecified()) {
|
||||
startTime = mozilla::TimeStamp::Now();
|
||||
phase = mozilla::MarkerTiming::Phase::Instant;
|
||||
} else {
|
||||
startTime = aOptions.Timing().StartTime();
|
||||
endTime = aOptions.Timing().EndTime();
|
||||
phase = aOptions.Timing().MarkerPhase();
|
||||
}
|
||||
|
||||
const char* nameStr = aName.StringView().data();
|
||||
if (!nameStr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a dynamic category for the marker.
|
||||
const char* categoryName =
|
||||
ProfilerCategoryNames[static_cast<uint32_t>(aCategory.GetCategory())];
|
||||
perfetto::DynamicCategory category{categoryName};
|
||||
perfetto::DynamicString name{nameStr};
|
||||
|
||||
// If the Marker has payload fields, we can annotate them in the perfetto
|
||||
// track event. Otherwise, we define an empty lambda which does nothing.
|
||||
std::function<void(perfetto::EventContext)> annotateTrackEvent =
|
||||
[&](perfetto::EventContext ctx) {};
|
||||
if constexpr (MarkerHasPayloadFields<MarkerType>::value) {
|
||||
annotateTrackEvent = [&](perfetto::EventContext ctx) {
|
||||
size_t i = 0;
|
||||
auto processArgument = [&](const auto& payloadArg) {
|
||||
AddDebugAnnotation(ctx, MarkerType::PayloadFields[i++].Key, payloadArg);
|
||||
};
|
||||
(processArgument(aPayloadArguments), ...);
|
||||
};
|
||||
}
|
||||
|
||||
// Create a unique id for each marker so it has it's own track.
|
||||
mozilla::HashNumber hash =
|
||||
mozilla::HashStringKnownLength(nameStr, aName.StringView().length());
|
||||
|
||||
switch (phase) {
|
||||
case mozilla::MarkerTiming::Phase::Interval: {
|
||||
hash = mozilla::AddToHash(
|
||||
hash, startTime.RawClockMonotonicNanosecondsSinceBoot());
|
||||
hash = mozilla::AddToHash(
|
||||
hash, endTime.RawClockMonotonicNanosecondsSinceBoot());
|
||||
perfetto::Track track(hash);
|
||||
|
||||
PERFETTO_TRACE_EVENT_BEGIN(category, name, track, startTime);
|
||||
PERFETTO_TRACE_EVENT_END(category, track, endTime, annotateTrackEvent);
|
||||
} break;
|
||||
case mozilla::MarkerTiming::Phase::Instant: {
|
||||
PERFETTO_TRACE_EVENT_INSTANT(category, name, startTime);
|
||||
} break;
|
||||
case mozilla::MarkerTiming::Phase::IntervalStart: {
|
||||
PERFETTO_TRACE_EVENT_BEGIN(category, name, perfetto::Track(hash),
|
||||
startTime);
|
||||
} break;
|
||||
case mozilla::MarkerTiming::Phase::IntervalEnd: {
|
||||
PERFETTO_TRACE_EVENT_END(category, perfetto::Track(hash), endTime,
|
||||
annotateTrackEvent);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
#else // !defined(MOZ_PERFETTO)
|
||||
# define PERFETTO_TRACE_EVENT(...) \
|
||||
do { \
|
||||
@ -111,6 +416,9 @@ PERFETTO_DEFINE_CATEGORIES(perfetto::Category("task"),
|
||||
# define PERFETTO_TRACE_EVENT_END(...) \
|
||||
do { \
|
||||
} while (0)
|
||||
# define PERFETTO_TRACE_EVENT_INSTANT(...) \
|
||||
do { \
|
||||
} while (0)
|
||||
inline void InitPerfetto() {}
|
||||
#endif // MOZ_PERFETTO
|
||||
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/Perfetto.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsXPCOM.h"
|
||||
@ -790,6 +791,21 @@ static void* AsyncSignalControlThreadEntry(void* aArg) {
|
||||
// - mProcessStartTime, because it's immutable;
|
||||
class CorePS {
|
||||
private:
|
||||
#ifdef MOZ_PERFETTO
|
||||
class PerfettoObserver : public perfetto::TrackEventSessionObserver {
|
||||
public:
|
||||
PerfettoObserver() { perfetto::TrackEvent::AddSessionObserver(this); }
|
||||
~PerfettoObserver() { perfetto::TrackEvent::RemoveSessionObserver(this); }
|
||||
|
||||
void OnStart(const perfetto::DataSourceBase::StartArgs&) override {
|
||||
mozilla::profiler::detail::RacyFeatures::SetPerfettoTracingActive();
|
||||
}
|
||||
void OnStop(const perfetto::DataSourceBase::StopArgs&) override {
|
||||
mozilla::profiler::detail::RacyFeatures::SetPerfettoTracingInactive();
|
||||
}
|
||||
} perfettoObserver;
|
||||
#endif
|
||||
|
||||
CorePS()
|
||||
: mProcessStartTime(TimeStamp::ProcessCreation()),
|
||||
mMaybeBandwidthCounter(nullptr)
|
||||
@ -5773,6 +5789,7 @@ void profiler_init(void* aStackTop) {
|
||||
|
||||
VTUNE_INIT();
|
||||
ETW::Init();
|
||||
InitPerfetto();
|
||||
|
||||
MOZ_RELEASE_ASSERT(!CorePS::Exists());
|
||||
|
||||
|
@ -136,14 +136,15 @@ inline mozilla::ProfileBufferBlockIndex AddMarkerToBuffer(
|
||||
// return true.
|
||||
[[nodiscard]] inline bool profiler_thread_is_being_profiled_for_markers() {
|
||||
return profiler_thread_is_being_profiled(ThreadProfilingFeatures::Markers) ||
|
||||
profiler_is_etw_collecting_markers();
|
||||
profiler_is_etw_collecting_markers() || profiler_is_perfetto_tracing();
|
||||
;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool profiler_thread_is_being_profiled_for_markers(
|
||||
const ProfilerThreadId& aThreadId) {
|
||||
return profiler_thread_is_being_profiled(aThreadId,
|
||||
ThreadProfilingFeatures::Markers) ||
|
||||
profiler_is_etw_collecting_markers();
|
||||
profiler_is_etw_collecting_markers() || profiler_is_perfetto_tracing();
|
||||
}
|
||||
|
||||
// Add a marker to the Gecko Profiler buffer.
|
||||
@ -167,6 +168,14 @@ mozilla::ProfileBufferBlockIndex profiler_add_marker_impl(
|
||||
ETW::EmitETWMarker(aName, aCategory, aOptions, aMarkerType,
|
||||
aPayloadArguments...);
|
||||
# endif
|
||||
|
||||
# ifdef MOZ_PERFETTO
|
||||
if (profiler_is_perfetto_tracing()) {
|
||||
EmitPerfettoTrackEvent(aName, aCategory, aOptions, aMarkerType,
|
||||
aPayloadArguments...);
|
||||
}
|
||||
# endif
|
||||
|
||||
if (!profiler_thread_is_being_gecko_profiled_for_markers(
|
||||
aOptions.ThreadId().ThreadId())) {
|
||||
return {};
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <mozilla/DefineEnum.h>
|
||||
#include <mozilla/EnumSet.h>
|
||||
#include "mozilla/ProfilerUtils.h"
|
||||
#include "mozilla/Perfetto.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
@ -210,6 +211,7 @@ using ProfilingStateChangeCallback = std::function<void(ProfilingState)>;
|
||||
[[nodiscard]] inline bool profiler_is_active_and_unpaused() { return false; }
|
||||
[[nodiscard]] inline bool profiler_is_collecting_markers() { return false; }
|
||||
[[nodiscard]] inline bool profiler_is_etw_collecting_markers() { return false; }
|
||||
[[nodiscard]] inline bool profiler_is_perfetto_tracing() { return false; }
|
||||
[[nodiscard]] inline bool profiler_feature_active(uint32_t aFeature) {
|
||||
return false;
|
||||
}
|
||||
@ -254,6 +256,14 @@ class RacyFeatures {
|
||||
sActiveAndFeatures &= ~ETWCollectionEnabled;
|
||||
}
|
||||
|
||||
static void SetPerfettoTracingActive() {
|
||||
sActiveAndFeatures |= PerfettoTracingEnabled;
|
||||
}
|
||||
|
||||
static void SetPerfettoTracingInactive() {
|
||||
sActiveAndFeatures &= ~PerfettoTracingEnabled;
|
||||
}
|
||||
|
||||
static void SetInactive() { sActiveAndFeatures = 0; }
|
||||
|
||||
static void SetPaused() { sActiveAndFeatures |= Paused; }
|
||||
@ -315,7 +325,8 @@ class RacyFeatures {
|
||||
|
||||
[[nodiscard]] static bool IsCollectingMarkers() {
|
||||
uint32_t af = sActiveAndFeatures; // copy it first
|
||||
return ((af & Active) && !(af & Paused)) || (af & ETWCollectionEnabled);
|
||||
return ((af & Active) && !(af & Paused)) || (af & ETWCollectionEnabled) ||
|
||||
(af & PerfettoTracingEnabled);
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool IsETWCollecting() {
|
||||
@ -323,11 +334,17 @@ class RacyFeatures {
|
||||
return (af & ETWCollectionEnabled);
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool IsPerfettoTracing() {
|
||||
uint32_t af = sActiveAndFeatures; // copy it first
|
||||
return (af & PerfettoTracingEnabled);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr uint32_t Active = 1u << 31;
|
||||
static constexpr uint32_t Paused = 1u << 30;
|
||||
static constexpr uint32_t SamplingPaused = 1u << 29;
|
||||
static constexpr uint32_t ETWCollectionEnabled = 1u << 28;
|
||||
static constexpr uint32_t PerfettoTracingEnabled = 1u << 27;
|
||||
|
||||
// Ensure Active/Paused don't overlap with any of the feature bits.
|
||||
# define NO_OVERLAP(n_, str_, Name_, desc_) \
|
||||
@ -385,6 +402,10 @@ class RacyFeatures {
|
||||
return mozilla::profiler::detail::RacyFeatures::IsETWCollecting();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool profiler_is_perfetto_tracing() {
|
||||
return mozilla::profiler::detail::RacyFeatures::IsPerfettoTracing();
|
||||
}
|
||||
|
||||
// Is the profiler active and paused? Returns false if the profiler is inactive.
|
||||
[[nodiscard]] bool profiler_is_paused();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user