Bug 1738627 - Store distinct profiling features of individual threads - r=canaltinova

This replaces the simple boolean ThreadRegistrationData::mIsBeingProfiled and its directly-dependent functions.

profiler_thread_is_being_profiled now takes an extra (currently optional) argument, to check if any of the given ThreadProfilingFeatures is currently live.

This is used to control:
- Periodic sampling of CPU utilization.
- Periodic sampling of stacks.
- Markers.

This patch doesn't change the observed behavior yet (i.e., instead of IsBeingProfiled being true or false, all thread profiling features are either all or nothing), but will be used for finer-grained control in later patches.

Differential Revision: https://phabricator.services.mozilla.com/D130008
This commit is contained in:
Gerald Squelart 2021-11-05 05:52:28 +00:00
Родитель 508562cc85
Коммит 5448bc6c6e
7 изменённых файлов: 454 добавлений и 208 удалений

Просмотреть файл

@ -94,11 +94,11 @@ static void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info) {
info.size, info.inNursery);
}
void ThreadRegistrationLockedRWFromAnyThread::
SetIsBeingProfiledWithProfiledThreadData(
ProfiledThreadData* aProfiledThreadData, const PSAutoLock&) {
MOZ_ASSERT(!mIsBeingProfiled);
mIsBeingProfiled = true;
void ThreadRegistrationLockedRWFromAnyThread::SetProfilingFeaturesAndData(
ThreadProfilingFeatures aProfilingFeatures,
ProfiledThreadData* aProfiledThreadData, const PSAutoLock&) {
MOZ_ASSERT(mProfilingFeatures == ThreadProfilingFeatures::NotProfiled);
mProfilingFeatures = aProfilingFeatures;
MOZ_ASSERT(!mProfiledThreadData);
MOZ_ASSERT(aProfiledThreadData);
@ -112,13 +112,16 @@ void ThreadRegistrationLockedRWFromAnyThread::
}
// Check invariants.
MOZ_ASSERT(mIsBeingProfiled == !!mProfiledThreadData);
MOZ_ASSERT((mJSContext && mIsBeingProfiled) == !!mJsFrameBuffer);
MOZ_ASSERT((mProfilingFeatures != ThreadProfilingFeatures::NotProfiled) ==
!!mProfiledThreadData);
MOZ_ASSERT((mJSContext &&
(mProfilingFeatures != ThreadProfilingFeatures::NotProfiled)) ==
!!mJsFrameBuffer);
}
void ThreadRegistrationLockedRWFromAnyThread::
ClearIsBeingProfiledAndProfiledThreadData(const PSAutoLock&) {
mIsBeingProfiled = false;
void ThreadRegistrationLockedRWFromAnyThread::ClearProfilingFeaturesAndData(
const PSAutoLock&) {
mProfilingFeatures = ThreadProfilingFeatures::NotProfiled;
mProfiledThreadData = nullptr;
if (mJsFrameBuffer) {
@ -127,8 +130,11 @@ void ThreadRegistrationLockedRWFromAnyThread::
}
// Check invariants.
MOZ_ASSERT(mIsBeingProfiled == !!mProfiledThreadData);
MOZ_ASSERT((mJSContext && mIsBeingProfiled) == !!mJsFrameBuffer);
MOZ_ASSERT((mProfilingFeatures != ThreadProfilingFeatures::NotProfiled) ==
!!mProfiledThreadData);
MOZ_ASSERT((mJSContext &&
(mProfilingFeatures != ThreadProfilingFeatures::NotProfiled)) ==
!!mJsFrameBuffer);
}
void ThreadRegistrationLockedRWOnThread::SetJSContext(JSContext* aJSContext) {
@ -137,7 +143,8 @@ void ThreadRegistrationLockedRWOnThread::SetJSContext(JSContext* aJSContext) {
mJSContext = aJSContext;
if (mProfiledThreadData) {
MOZ_ASSERT(mIsBeingProfiled == !!mProfiledThreadData);
MOZ_ASSERT((mProfilingFeatures != ThreadProfilingFeatures::NotProfiled) ==
!!mProfiledThreadData);
// We now have a JSContext, and the thread is already being profiled,
// allocate a JsFramesBuffer to allow profiler-unlocked on-thread sampling.
MOZ_ASSERT(!mJsFrameBuffer);
@ -149,7 +156,9 @@ void ThreadRegistrationLockedRWOnThread::SetJSContext(JSContext* aJSContext) {
js::SetContextProfilingStack(aJSContext, &ProfilingStackRef());
// Check invariants.
MOZ_ASSERT((mJSContext && mIsBeingProfiled) == !!mJsFrameBuffer);
MOZ_ASSERT((mJSContext &&
(mProfilingFeatures != ThreadProfilingFeatures::NotProfiled)) ==
!!mJsFrameBuffer);
}
void ThreadRegistrationLockedRWOnThread::ClearJSContext() {
@ -161,7 +170,9 @@ void ThreadRegistrationLockedRWOnThread::ClearJSContext() {
}
// Check invariants.
MOZ_ASSERT((mJSContext && mIsBeingProfiled) == !!mJsFrameBuffer);
MOZ_ASSERT((mJSContext &&
(mProfilingFeatures != ThreadProfilingFeatures::NotProfiled)) ==
!!mJsFrameBuffer);
}
void ThreadRegistrationLockedRWOnThread::PollJSSampling() {

Просмотреть файл

@ -840,11 +840,15 @@ class ActivePS {
return n;
}
static bool ShouldProfileThread(PSLockRef aLock,
const ThreadRegistrationInfo& aInfo) {
static ThreadProfilingFeatures ProfilingFeaturesForThread(
PSLockRef aLock, const ThreadRegistrationInfo& aInfo) {
MOZ_ASSERT(sInstance);
return ((aInfo.IsMainThread() || FeatureThreads(aLock)) &&
sInstance->ThreadSelected(aInfo.Name()));
if ((aInfo.IsMainThread() || FeatureThreads(aLock)) &&
sInstance->ThreadSelected(aInfo.Name())) {
// This thread was selected by the user, record everything.
return ThreadProfilingFeatures::Any;
}
return ThreadProfilingFeatures::NotProfiled;
}
[[nodiscard]] static bool AppendPostSamplingCallback(
@ -3645,11 +3649,27 @@ void SamplerThread::Run() {
// This thread is not being profiled, continue with the next one.
continue;
}
const ThreadProfilingFeatures whatToProfile =
unlockedThreadData.ProfilingFeatures();
const bool threadCPUUtilization =
cpuUtilization &&
DoFeaturesIntersect(whatToProfile,
ThreadProfilingFeatures::CPUUtilization);
const bool threadStackSampling =
stackSampling &&
DoFeaturesIntersect(whatToProfile,
ThreadProfilingFeatures::Sampling);
if (!threadCPUUtilization && !threadStackSampling) {
// Nothing to profile on this thread, continue with the next one.
continue;
}
const ProfilerThreadId threadId =
unlockedThreadData.Info().ThreadId();
const RunningTimes runningTimesDiff = [&]() {
if (!cpuUtilization) {
if (!threadCPUUtilization) {
// If we don't need CPU measurements, we only need a timestamp.
return RunningTimes(TimeStamp::Now());
}
@ -3672,8 +3692,9 @@ void SamplerThread::Run() {
// could result in an unneeded sample.
// However we're using current running times (instead of copying the
// old ones) because some work could have happened.
if (unlockedThreadData.CanDuplicateLastSampleDueToSleep() ||
runningTimesDiff.GetThreadCPUDelta() == Some(uint64_t(0))) {
if (threadStackSampling &&
(unlockedThreadData.CanDuplicateLastSampleDueToSleep() ||
runningTimesDiff.GetThreadCPUDelta() == Some(uint64_t(0)))) {
const bool dup_ok = ActivePS::Buffer(lock).DuplicateLastSample(
threadId, threadSampleDeltaMs,
profiledThreadData->LastSample(), runningTimesDiff);
@ -3709,7 +3730,7 @@ void SamplerThread::Run() {
ProfileBufferEntry::Kind::RunningTimes, runningTimesDiff);
}
if (stackSampling) {
if (threadStackSampling) {
ThreadRegistry::OffThreadRef::RWFromAnyThreadWithLock
lockedThreadData = offThreadRef.LockedRWFromAnyThread();
// Suspend the thread and collect its stack data in the local
@ -4122,33 +4143,36 @@ static ProfilingStack* locked_register_thread(
VTUNE_REGISTER_THREAD(aOffThreadRef.UnlockedConstReaderCRef().Info().Name());
if (ActivePS::Exists(aLock) &&
ActivePS::ShouldProfileThread(
aLock, aOffThreadRef.UnlockedConstReaderCRef().Info())) {
ThreadRegistry::OffThreadRef::RWFromAnyThreadWithLock
lockedRWFromAnyThread = aOffThreadRef.LockedRWFromAnyThread();
if (ActivePS::Exists(aLock)) {
ThreadProfilingFeatures threadProfilingFeatures =
ActivePS::ProfilingFeaturesForThread(
aLock, aOffThreadRef.UnlockedConstReaderCRef().Info());
if (threadProfilingFeatures != ThreadProfilingFeatures::NotProfiled) {
ThreadRegistry::OffThreadRef::RWFromAnyThreadWithLock
lockedRWFromAnyThread = aOffThreadRef.LockedRWFromAnyThread();
nsCOMPtr<nsIEventTarget> eventTarget =
lockedRWFromAnyThread->GetEventTarget();
ProfiledThreadData* profiledThreadData = ActivePS::AddLiveProfiledThread(
aLock,
MakeUnique<ProfiledThreadData>(
aOffThreadRef.UnlockedConstReaderCRef().Info(), eventTarget));
lockedRWFromAnyThread->SetIsBeingProfiledWithProfiledThreadData(
profiledThreadData, aLock);
nsCOMPtr<nsIEventTarget> eventTarget =
lockedRWFromAnyThread->GetEventTarget();
ProfiledThreadData* profiledThreadData = ActivePS::AddLiveProfiledThread(
aLock,
MakeUnique<ProfiledThreadData>(
aOffThreadRef.UnlockedConstReaderCRef().Info(), eventTarget));
lockedRWFromAnyThread->SetProfilingFeaturesAndData(
threadProfilingFeatures, profiledThreadData, aLock);
if (ActivePS::FeatureJS(aLock)) {
lockedRWFromAnyThread->StartJSSampling(ActivePS::JSFlags(aLock));
if (ThreadRegistration::LockedRWOnThread* lockedRWOnThread =
lockedRWFromAnyThread.GetLockedRWOnThread();
lockedRWOnThread) {
// We can manually poll the current thread so it starts sampling
// immediately.
lockedRWOnThread->PollJSSampling();
}
if (lockedRWFromAnyThread->GetJSContext()) {
profiledThreadData->NotifyReceivedJSContext(
ActivePS::Buffer(aLock).BufferRangeEnd());
if (ActivePS::FeatureJS(aLock)) {
lockedRWFromAnyThread->StartJSSampling(ActivePS::JSFlags(aLock));
if (ThreadRegistration::LockedRWOnThread* lockedRWOnThread =
lockedRWFromAnyThread.GetLockedRWOnThread();
lockedRWOnThread) {
// We can manually poll the current thread so it starts sampling
// immediately.
lockedRWOnThread->PollJSSampling();
}
if (lockedRWFromAnyThread->GetJSContext()) {
profiledThreadData->NotifyReceivedJSContext(
ActivePS::Buffer(aLock).BufferRangeEnd());
}
}
}
}
@ -4912,14 +4936,16 @@ static void locked_profiler_start(PSLockRef aLock, PowerOfTwo32 aCapacity,
const ThreadRegistrationInfo& info =
offThreadRef.UnlockedConstReaderCRef().Info();
if (ActivePS::ShouldProfileThread(aLock, info)) {
ThreadProfilingFeatures threadProfilingFeatures =
ActivePS::ProfilingFeaturesForThread(aLock, info);
if (threadProfilingFeatures != ThreadProfilingFeatures::NotProfiled) {
ThreadRegistry::OffThreadRef::RWFromAnyThreadWithLock lockedThreadData =
offThreadRef.LockedRWFromAnyThread();
nsCOMPtr<nsIEventTarget> eventTarget = lockedThreadData->GetEventTarget();
ProfiledThreadData* profiledThreadData = ActivePS::AddLiveProfiledThread(
aLock, MakeUnique<ProfiledThreadData>(info, eventTarget));
lockedThreadData->SetIsBeingProfiledWithProfiledThreadData(
profiledThreadData, aLock);
lockedThreadData->SetProfilingFeaturesAndData(threadProfilingFeatures,
profiledThreadData, aLock);
if (ActivePS::FeatureJS(aLock)) {
lockedThreadData->StartJSSampling(ActivePS::JSFlags(aLock));
if (ThreadRegistration::LockedRWOnThread* lockedRWOnThread =
@ -5116,14 +5142,15 @@ void profiler_ensure_started(PowerOfTwo32 aCapacity, double aInterval,
// Stop sampling live threads.
ThreadRegistry::LockedRegistry lockedRegistry;
for (ThreadRegistry::OffThreadRef offThreadRef : lockedRegistry) {
if (!offThreadRef.UnlockedRWForLockedProfilerRef().IsBeingProfiled(aLock)) {
if (offThreadRef.UnlockedRWForLockedProfilerRef().ProfilingFeatures() ==
ThreadProfilingFeatures::NotProfiled) {
continue;
}
ThreadRegistry::OffThreadRef::RWFromAnyThreadWithLock lockedThreadData =
offThreadRef.LockedRWFromAnyThread();
lockedThreadData->ClearIsBeingProfiledAndProfiledThreadData(aLock);
lockedThreadData->ClearProfilingFeaturesAndData(aLock);
if (ActivePS::FeatureJS(aLock)) {
lockedThreadData->StopJSSampling();
@ -5448,7 +5475,7 @@ static void locked_unregister_thread(
ProfiledThreadData* profiledThreadData =
lockedThreadData->GetProfiledThreadData(lock);
lockedThreadData->ClearIsBeingProfiledAndProfiledThreadData(lock);
lockedThreadData->ClearProfilingFeaturesAndData(lock);
MOZ_RELEASE_ASSERT(
lockedThreadData->Info().ThreadId() == profiler_current_thread_id(),

Просмотреть файл

@ -136,7 +136,8 @@ mozilla::ProfileBufferBlockIndex profiler_add_marker(
#ifndef MOZ_GECKO_PROFILER
return {};
#else
if (!profiler_thread_is_being_profiled(aOptions.ThreadId().ThreadId())) {
if (!profiler_thread_is_being_profiled(aOptions.ThreadId().ThreadId(),
ThreadProfilingFeatures::Markers)) {
return {};
}
return ::AddMarkerToBuffer(profiler_markers_detail::CachedCoreBuffer(), aName,

Просмотреть файл

@ -50,6 +50,55 @@ class ProfiledThreadData;
class PSAutoLock;
struct JSContext;
// Enum listing which profiling features are active for a single thread.
enum class ThreadProfilingFeatures : uint32_t {
// The thread is not being profiled at all (either the profiler is not
// running, or this thread is not examined during profiling.)
NotProfiled = 0u,
// Single features, binary exclusive. May be `Combine()`d.
CPUUtilization = 1u << 0,
Sampling = 1u << 1,
Markers = 1u << 2,
// All possible features. Usually used as a mask to see if any feature is
// active at a given time.
Any = CPUUtilization | Sampling | Markers
};
// Binary OR of one of more ThreadProfilingFeatures, to mix all arguments.
template <typename... Ts>
[[nodiscard]] constexpr ThreadProfilingFeatures Combine(
ThreadProfilingFeatures a1, Ts... as) {
static_assert((true && ... &&
std::is_same_v<std::remove_cv_t<std::remove_reference_t<Ts>>,
ThreadProfilingFeatures>));
return static_cast<ThreadProfilingFeatures>(
(static_cast<std::underlying_type_t<ThreadProfilingFeatures>>(a1) | ... |
static_cast<std::underlying_type_t<ThreadProfilingFeatures>>(as)));
}
// Binary AND of one of more ThreadProfilingFeatures, to find features common to
// all arguments.
template <typename... Ts>
[[nodiscard]] constexpr ThreadProfilingFeatures Intersect(
ThreadProfilingFeatures a1, Ts... as) {
static_assert((true && ... &&
std::is_same_v<std::remove_cv_t<std::remove_reference_t<Ts>>,
ThreadProfilingFeatures>));
return static_cast<ThreadProfilingFeatures>(
(static_cast<std::underlying_type_t<ThreadProfilingFeatures>>(a1) & ... &
static_cast<std::underlying_type_t<ThreadProfilingFeatures>>(as)));
}
// Are there features in common between the two given sets?
// Mostly useful to test if any of a set of features is present in another set.
template <typename... Ts>
[[nodiscard]] constexpr bool DoFeaturesIntersect(ThreadProfilingFeatures a1,
ThreadProfilingFeatures a2) {
return Intersect(a1, a2) != ThreadProfilingFeatures::NotProfiled;
}
namespace mozilla::profiler {
// All data members related to thread profiling are stored here.
@ -59,7 +108,7 @@ namespace mozilla::profiler {
class ThreadRegistrationData {
public:
// No public accessors here. See derived classes for accessors, and
// Get.../With... functions for who can uses these accessors.
// Get.../With... functions for who can use these accessors.
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
// Not including data that is not fully owned here.
@ -81,7 +130,8 @@ class ThreadRegistrationData {
#ifdef DEBUG
// Destructor only used to check invariants.
~ThreadRegistrationData() {
MOZ_ASSERT(mIsBeingProfiled == !!mProfiledThreadData);
MOZ_ASSERT((mProfilingFeatures != ThreadProfilingFeatures::NotProfiled) ==
!!mProfiledThreadData);
MOZ_ASSERT(!mProfiledThreadData,
"mProfiledThreadData pointer should have been reset before "
"~ThreadRegistrationData");
@ -182,11 +232,6 @@ class ThreadRegistrationData {
// They need to be atomic, because LockData() does not prevent reads from
// the owning thread.
// Is this thread being profiled? (e.g., should markers be recorded?)
// Written from profiler, read from thread.
// Invariant: `mIsBeingProfiled == !!mProfiledThreadData` (set together.)
Atomic<bool, MemoryOrdering::Relaxed> mIsBeingProfiled{false};
// mSleep tracks whether the thread is sleeping, and if so, whether it has
// been previously observed. This is used for an optimization: in some
// cases, when a thread is asleep, we duplicate the previous sample, which
@ -226,10 +271,16 @@ class ThreadRegistrationData {
// Read&written from thread and suspended thread.
Atomic<int> mSleep{AWAKE};
// Is this thread currently being profiled, and with which features?
// Written from profiler, read from any thread.
// Invariant: `!!mProfilingFeatures == !!mProfiledThreadData` (set together.)
Atomic<ThreadProfilingFeatures, MemoryOrdering::Relaxed> mProfilingFeatures{
ThreadProfilingFeatures::NotProfiled};
// If the profiler is active and this thread is selected for profiling, this
// points at the relevant ProfiledThreadData.
// Fully controlled by the profiler.
// Invariant: `mIsBeingProfiled == !!mProfiledThreadData` (set together.)
// Invariant: `!!mProfilingFeatures == !!mProfiledThreadData` (set together).
ProfiledThreadData* mProfiledThreadData = nullptr;
};
@ -261,17 +312,19 @@ class ThreadRegistrationUnlockedConstReaderAndAtomicRW
// Similar to `profiler_is_active()`, this atomic flag may become out-of-date.
// It should only be used as an indication to know whether this thread is
// probably being profiled, to avoid doing expensive operations otherwise.
// Edge cases:
// - This thread could get `false`, but the profiler has just started, so some
// very early data may be missing. No real impact on profiling.
// - This thread could get `true`, but the profiled has just stopped, so some
// some work will be done and then discarded when finally attempting to
// write to the buffer. No impact on profiling.
// - This thread could see `true`, but the profiler will quickly stop and
// restart, so this thread will write information relevant to the previous
// profiling session. Very rare, and little impact on profiling.
[[nodiscard]] bool IsBeingProfiled() const { return mIsBeingProfiled; }
// probably being profiled (with some specific features), to avoid doing
// expensive operations otherwise. Edge cases:
// - This thread could get `NotProfiled`, but the profiler has just started,
// so some very early data may be missing. No real impact on profiling.
// - This thread could see profiled features, but the profiled has just
// stopped, so some some work will be done and then discarded when finally
// attempting to write to the buffer. No impact on profiling.
// - This thread could see profiled features, but the profiler will quickly
// stop and restart, so this thread will write information relevant to the
// previous profiling session. Very rare, and little impact on profiling.
[[nodiscard]] ThreadProfilingFeatures ProfilingFeatures() const {
return mProfilingFeatures;
}
// Call this whenever the current thread sleeps. Calling it twice in a row
// without an intervening setAwake() call is an error.
@ -326,15 +379,6 @@ class ThreadRegistrationUnlockedRWForLockedProfiler
// Only add functions that take a `const PSAutoLock&` proof-of-lock.
// (Because there is no other lock.)
// This is like IsBeingProfiled, but guaranteed to be stable while the lock is
// held (unless modified under the same lock, of course), and is a bit faster
// by not doing an atomic read.
[[nodiscard]] bool IsBeingProfiled(const PSAutoLock&) const {
// Invariant: `mIsBeingProfiled == !!mProfiledThreadData` (set together),
// but mProfiledThreadData is not atomic and therefore faster to read.
return mProfiledThreadData;
}
[[nodiscard]] const ProfiledThreadData* GetProfiledThreadData(
const PSAutoLock&) const {
return mProfiledThreadData;
@ -375,9 +419,10 @@ class ThreadRegistrationUnlockedReaderAndAtomicRWOnThread
class ThreadRegistrationLockedRWFromAnyThread
: public ThreadRegistrationUnlockedReaderAndAtomicRWOnThread {
public:
void SetIsBeingProfiledWithProfiledThreadData(
ProfiledThreadData* aProfiledThreadData, const PSAutoLock&);
void ClearIsBeingProfiledAndProfiledThreadData(const PSAutoLock&);
void SetProfilingFeaturesAndData(ThreadProfilingFeatures aProfilingFeatures,
ProfiledThreadData* aProfiledThreadData,
const PSAutoLock&);
void ClearProfilingFeaturesAndData(const PSAutoLock&);
// Not null when JSContext is not null AND this thread is being profiled.
// Points at the start of JsFrameBuffer.

Просмотреть файл

@ -16,30 +16,37 @@
// 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().)
// (Same caveats and recommended usage as profiler_is_active().)
[[nodiscard]] inline bool profiler_is_active_and_thread_is_registered() {
return profiler_is_active() &&
mozilla::profiler::ThreadRegistration::IsRegistered();
}
// Is the profiler active and unpaused, and is the current thread being
// profiled? (Same caveats and recommented usage as profiler_is_active().)
[[nodiscard]] inline bool profiler_thread_is_being_profiled() {
// profiled for any of the given features? (Same caveats and recommended usage
// as profiler_is_active().)
[[nodiscard]] inline bool profiler_thread_is_being_profiled(
ThreadProfilingFeatures aThreadProfilingFeatures =
ThreadProfilingFeatures::Any) {
return profiler_is_active_and_unpaused() &&
mozilla::profiler::ThreadRegistration::WithOnThreadRefOr(
[](mozilla::profiler::ThreadRegistration::OnThreadRef aTR) {
return aTR.UnlockedConstReaderAndAtomicRWCRef()
.IsBeingProfiled();
[aThreadProfilingFeatures](
mozilla::profiler::ThreadRegistration::OnThreadRef aTR) {
return DoFeaturesIntersect(
aTR.UnlockedConstReaderAndAtomicRWCRef().ProfilingFeatures(),
aThreadProfilingFeatures);
},
false);
}
// Is the profiler active and unpaused, and is the given thread being profiled?
// (Same caveats and recommented usage as profiler_is_active().)
// (Same caveats and recommended usage as profiler_is_active().)
// Safe to use with the current thread id, or unspecified ProfilerThreadId (same
// as current thread id).
[[nodiscard]] inline bool profiler_thread_is_being_profiled(
const ProfilerThreadId& aThreadId) {
const ProfilerThreadId& aThreadId,
ThreadProfilingFeatures aThreadProfilingFeatures =
ThreadProfilingFeatures::Any) {
if (!profiler_is_active_and_unpaused()) {
return false;
}
@ -48,8 +55,11 @@
// For the current thread id, use the ThreadRegistration directly, it is
// more efficient.
return mozilla::profiler::ThreadRegistration::WithOnThreadRefOr(
[](mozilla::profiler::ThreadRegistration::OnThreadRef aTR) {
return aTR.UnlockedConstReaderAndAtomicRWCRef().IsBeingProfiled();
[aThreadProfilingFeatures](
mozilla::profiler::ThreadRegistration::OnThreadRef aTR) {
return DoFeaturesIntersect(
aTR.UnlockedConstReaderAndAtomicRWCRef().ProfilingFeatures(),
aThreadProfilingFeatures);
},
false);
}
@ -57,8 +67,11 @@
// For other threads, go through the ThreadRegistry.
return mozilla::profiler::ThreadRegistry::WithOffThreadRefOr(
aThreadId,
[](mozilla::profiler::ThreadRegistry::OffThreadRef aTR) {
return aTR.UnlockedConstReaderAndAtomicRWCRef().IsBeingProfiled();
[aThreadProfilingFeatures](
mozilla::profiler::ThreadRegistry::OffThreadRef aTR) {
return DoFeaturesIntersect(
aTR.UnlockedConstReaderAndAtomicRWCRef().ProfilingFeatures(),
aThreadProfilingFeatures);
},
false);
}

Просмотреть файл

@ -175,6 +175,68 @@ TEST(GeckoProfiler, ThreadRegistrationInfo)
}
}
static constexpr ThreadProfilingFeatures scEachAndAnyThreadProfilingFeatures[] =
{ThreadProfilingFeatures::CPUUtilization, ThreadProfilingFeatures::Sampling,
ThreadProfilingFeatures::Markers, ThreadProfilingFeatures::Any};
TEST(GeckoProfiler, ThreadProfilingFeaturesType)
{
ASSERT_EQ(static_cast<uint32_t>(ThreadProfilingFeatures::Any), 1u + 2u + 4u)
<< "This test assumes that there are 3 binary choices 1+2+4; "
"Is this test up to date?";
EXPECT_EQ(Combine(ThreadProfilingFeatures::CPUUtilization,
ThreadProfilingFeatures::Sampling,
ThreadProfilingFeatures::Markers),
ThreadProfilingFeatures::Any);
constexpr ThreadProfilingFeatures allThreadProfilingFeatures[] = {
ThreadProfilingFeatures::NotProfiled,
ThreadProfilingFeatures::CPUUtilization,
ThreadProfilingFeatures::Sampling, ThreadProfilingFeatures::Markers,
ThreadProfilingFeatures::Any};
for (ThreadProfilingFeatures f1 : allThreadProfilingFeatures) {
// Combine and Intersect are commutative.
for (ThreadProfilingFeatures f2 : allThreadProfilingFeatures) {
EXPECT_EQ(Combine(f1, f2), Combine(f2, f1));
EXPECT_EQ(Intersect(f1, f2), Intersect(f2, f1));
}
// Combine works like OR.
EXPECT_EQ(Combine(f1, f1), f1);
EXPECT_EQ(Combine(f1, f1, f1), f1);
// 'OR NotProfiled' doesn't change anything.
EXPECT_EQ(Combine(f1, ThreadProfilingFeatures::NotProfiled), f1);
// 'OR Any' makes Any.
EXPECT_EQ(Combine(f1, ThreadProfilingFeatures::Any),
ThreadProfilingFeatures::Any);
// Intersect works like AND.
EXPECT_EQ(Intersect(f1, f1), f1);
EXPECT_EQ(Intersect(f1, f1, f1), f1);
// 'AND NotProfiled' erases anything.
EXPECT_EQ(Intersect(f1, ThreadProfilingFeatures::NotProfiled),
ThreadProfilingFeatures::NotProfiled);
// 'AND Any' doesn't change anything.
EXPECT_EQ(Intersect(f1, ThreadProfilingFeatures::Any), f1);
}
for (ThreadProfilingFeatures f1 : scEachAndAnyThreadProfilingFeatures) {
EXPECT_TRUE(DoFeaturesIntersect(f1, f1));
// NotProfiled doesn't intersect with any feature.
EXPECT_FALSE(DoFeaturesIntersect(f1, ThreadProfilingFeatures::NotProfiled));
// Any intersects with any feature.
EXPECT_TRUE(DoFeaturesIntersect(f1, ThreadProfilingFeatures::Any));
}
}
static void TestConstUnlockedConstReader(
const profiler::ThreadRegistration::UnlockedConstReader& aData,
const TimeStamp& aBeforeRegistration, const TimeStamp& aAfterRegistration,
@ -233,7 +295,7 @@ static void TestConstUnlockedConstReaderAndAtomicRW(
(void)aData.ProfilingStackCRef();
EXPECT_FALSE(aData.IsBeingProfiled());
EXPECT_EQ(aData.ProfilingFeatures(), ThreadProfilingFeatures::NotProfiled);
EXPECT_FALSE(aData.IsSleeping());
};
@ -285,9 +347,9 @@ static void TestConstUnlockedRWForLockedProfiler(
// We can't create a PSAutoLock here, so just verify that the call would
// compile and return the expected type.
static_assert(
std::is_same_v<
decltype(aData.IsBeingProfiled(std::declval<PSAutoLock>())), bool>);
static_assert(std::is_same_v<decltype(aData.GetProfiledThreadData(
std::declval<PSAutoLock>())),
const ProfiledThreadData*>);
};
static void TestConstUnlockedReaderAndAtomicRWOnThread(
@ -359,11 +421,11 @@ static void TestLockedRWFromAnyThread(
// We can't create a ProfiledThreadData nor PSAutoLock here, so just verify
// that the call would compile and return the expected type.
static_assert(
std::is_same_v<decltype(aData.SetIsBeingProfiledWithProfiledThreadData(
std::declval<ProfiledThreadData*>(),
std::declval<PSAutoLock>())),
void>);
static_assert(std::is_same_v<decltype(aData.SetProfilingFeaturesAndData(
std::declval<ThreadProfilingFeatures>(),
std::declval<ProfiledThreadData*>(),
std::declval<PSAutoLock>())),
void>);
aData.ResetMainThread(nullptr);
@ -1817,39 +1879,56 @@ TEST(GeckoProfiler, Pause)
const char* filters[] = {"GeckoMain", "Profiled GeckoProfiler.Pause"};
ASSERT_TRUE(!profiler_is_paused());
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(!profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features : scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(profiler_current_thread_id(),
features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(profiler_main_thread_id(),
features));
}
std::thread{[&]() {
{
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_main_thread_id(), features));
}
}
{
AUTO_PROFILER_REGISTER_THREAD(
"Ignored GeckoProfiler.Pause - before start");
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_main_thread_id(), features));
}
}
{
AUTO_PROFILER_REGISTER_THREAD(
"Profiled GeckoProfiler.Pause - before start");
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_main_thread_id(), features));
}
}
}}.join();
@ -1857,35 +1936,54 @@ TEST(GeckoProfiler, Pause)
filters, MOZ_ARRAY_LENGTH(filters), 0);
ASSERT_TRUE(!profiler_is_paused());
ASSERT_TRUE(profiler_thread_is_being_profiled());
ASSERT_TRUE(profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_current_thread_id()));
for (ThreadProfilingFeatures features : scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_current_thread_id(),
features));
}
std::thread{[&]() {
{
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_main_thread_id(),
features));
}
}
{
AUTO_PROFILER_REGISTER_THREAD(
"Ignored GeckoProfiler.Pause - after start");
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_main_thread_id(),
features));
}
}
{
AUTO_PROFILER_REGISTER_THREAD(
"Profiled GeckoProfiler.Pause - after start");
ASSERT_TRUE(profiler_thread_is_being_profiled());
ASSERT_TRUE(profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_main_thread_id(),
features));
}
}
}}.join();
@ -1904,38 +2002,54 @@ TEST(GeckoProfiler, Pause)
profiler_pause();
ASSERT_TRUE(profiler_is_paused());
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(!profiler_thread_is_being_profiled(profiler_current_thread_id()));
for (ThreadProfilingFeatures features : scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(profiler_current_thread_id(),
features));
}
std::thread{[&]() {
{
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_main_thread_id(), features));
}
}
{
AUTO_PROFILER_REGISTER_THREAD(
"Ignored GeckoProfiler.Pause - after pause");
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_main_thread_id(), features));
}
}
{
AUTO_PROFILER_REGISTER_THREAD(
"Profiled GeckoProfiler.Pause - after pause");
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_main_thread_id(), features));
}
}
}}.join();
@ -1957,72 +2071,107 @@ TEST(GeckoProfiler, Pause)
profiler_resume();
ASSERT_TRUE(!profiler_is_paused());
ASSERT_TRUE(profiler_thread_is_being_profiled());
ASSERT_TRUE(profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_current_thread_id()));
for (ThreadProfilingFeatures features : scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_current_thread_id(),
features));
}
std::thread{[&]() {
{
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_main_thread_id(),
features));
}
}
{
AUTO_PROFILER_REGISTER_THREAD(
"Ignored GeckoProfiler.Pause - after resume");
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_main_thread_id(),
features));
}
}
{
AUTO_PROFILER_REGISTER_THREAD(
"Profiled GeckoProfiler.Pause - after resume");
ASSERT_TRUE(profiler_thread_is_being_profiled());
ASSERT_TRUE(profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(profiler_thread_is_being_profiled(profiler_main_thread_id(),
features));
}
}
}}.join();
profiler_stop();
ASSERT_TRUE(!profiler_is_paused());
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(!profiler_thread_is_being_profiled(profiler_current_thread_id()));
for (ThreadProfilingFeatures features : scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(profiler_current_thread_id(),
features));
}
std::thread{[&]() {
{
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_main_thread_id(), features));
}
}
{
AUTO_PROFILER_REGISTER_THREAD("Ignored GeckoProfiler.Pause - after stop");
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_main_thread_id(), features));
}
}
{
AUTO_PROFILER_REGISTER_THREAD(
"Profiled GeckoProfiler.Pause - after stop");
ASSERT_TRUE(!profiler_thread_is_being_profiled());
ASSERT_TRUE(!profiler_thread_is_being_profiled(ProfilerThreadId{}));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_current_thread_id()));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(profiler_main_thread_id()));
for (ThreadProfilingFeatures features :
scEachAndAnyThreadProfilingFeatures) {
ASSERT_TRUE(!profiler_thread_is_being_profiled(features));
ASSERT_TRUE(
!profiler_thread_is_being_profiled(ProfilerThreadId{}, features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_current_thread_id(), features));
ASSERT_TRUE(!profiler_thread_is_being_profiled(
profiler_main_thread_id(), features));
}
}
}}.join();
}

Просмотреть файл

@ -57,7 +57,7 @@ void EventQueueInternal<ItemsPerPage>::PutEvent(
return;
}
if (profiler_thread_is_being_profiled()) {
if (profiler_thread_is_being_profiled(ThreadProfilingFeatures::Sampling)) {
// check to see if the profiler has been enabled since the last PutEvent
while (mDispatchTimes.Count() < mQueue.Count()) {
mDispatchTimes.Push(TimeStamp());