diff --git a/tools/profiler/core/ThreadInfo.cpp b/tools/profiler/core/ThreadInfo.cpp index a4d462a3dbc4..aabd2c01a36a 100644 --- a/tools/profiler/core/ThreadInfo.cpp +++ b/tools/profiler/core/ThreadInfo.cpp @@ -22,10 +22,12 @@ ThreadInfo::ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, + nsIEventTarget* aThread, void* aStackTop) : mName(strdup(aName)) , mRegisterTime(TimeStamp::Now()) , mIsMainThread(aIsMainThread) + , mThread(aThread) , mRacyInfo(mozilla::MakeNotNull(aThreadId)) , mPlatformData(AllocPlatformData(aThreadId)) , mStackTop(aStackTop) @@ -60,9 +62,7 @@ ThreadInfo::StartProfiling() { mIsBeingProfiled = true; mRacyInfo->ReinitializeOnResume(); - if (mIsMainThread) { - mResponsiveness.emplace(); - } + mResponsiveness.emplace(mThread, mIsMainThread); } void diff --git a/tools/profiler/core/ThreadInfo.h b/tools/profiler/core/ThreadInfo.h index b16418380c8f..4fef7ad13f55 100644 --- a/tools/profiler/core/ThreadInfo.h +++ b/tools/profiler/core/ThreadInfo.h @@ -175,7 +175,7 @@ class ThreadInfo final { public: ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, - void* aStackTop); + nsIEventTarget* aThread, void* aStackTop); ~ThreadInfo(); @@ -207,6 +207,7 @@ private: mozilla::TimeStamp mRegisterTime; mozilla::TimeStamp mUnregisterTime; const bool mIsMainThread; + nsCOMPtr mThread; // The thread's RacyThreadInfo. This is an owning pointer. It could be an // inline member, but we don't do that because RacyThreadInfo is quite large @@ -238,7 +239,7 @@ public: ThreadResponsiveness* GetThreadResponsiveness() { ThreadResponsiveness* responsiveness = mResponsiveness.ptrOr(nullptr); - MOZ_ASSERT(!!responsiveness == (mIsMainThread && mIsBeingProfiled)); + MOZ_ASSERT(!responsiveness || mIsBeingProfiled); return responsiveness; } @@ -323,7 +324,7 @@ private: mozilla::UniquePtr mSavedStreamedMarkers; mozilla::Maybe mUniqueStacks; - // This is only used for the main thread. + // This is used only for nsIThreads. mozilla::Maybe mResponsiveness; public: diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp index 58213cda0f4f..530703c5f8a8 100644 --- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -2003,10 +2003,7 @@ SamplerThread::Run() } } - // We only track responsiveness for the main thread. - if (info->IsMainThread()) { - info->GetThreadResponsiveness()->Update(); - } + info->GetThreadResponsiveness()->Update(); // We only get the memory measurements once for all live threads. int64_t rssMemory = 0; @@ -2189,7 +2186,9 @@ locked_register_thread(PSLockRef aLock, const char* aName, void* aStackTop) } ThreadInfo* info = new ThreadInfo(aName, Thread::GetCurrentId(), - NS_IsMainThread(), aStackTop); + NS_IsMainThread(), + NS_GetCurrentThreadNoCreate(), + aStackTop); TLSInfo::SetInfo(aLock, info); if (ActivePS::Exists(aLock) && ActivePS::ShouldProfileThread(aLock, info)) { diff --git a/tools/profiler/gecko/ThreadResponsiveness.cpp b/tools/profiler/gecko/ThreadResponsiveness.cpp index c7d36655a9ee..4ad52666c932 100644 --- a/tools/profiler/gecko/ThreadResponsiveness.cpp +++ b/tools/profiler/gecko/ThreadResponsiveness.cpp @@ -13,14 +13,16 @@ using namespace mozilla; -class CheckResponsivenessTask : public Runnable, +class CheckResponsivenessTask : public CancelableRunnable, public nsITimerCallback { public: - CheckResponsivenessTask() - : Runnable("CheckResponsivenessTask") + explicit CheckResponsivenessTask(nsIEventTarget* aThread, bool aIsMainThread) + : CancelableRunnable("CheckResponsivenessTask") , mStartToPrevTracer_us(uint64_t(profiler_time() * 1000.0)) , mStop(false) , mHasEverBeenSuccessfullyDispatched(false) + , mThread(aThread) + , mIsMainThread(aIsMainThread) { } @@ -34,36 +36,61 @@ public: // Must be called from the same thread every time. Call that the update // thread, because it's the thread that ThreadResponsiveness::Update() is // called on. In reality it's the profiler's sampler thread. - void DoFirstDispatchIfNeeded() + bool DoFirstDispatchIfNeeded() { if (mHasEverBeenSuccessfullyDispatched) { - return; + return true; } - // We can hit this code very early during startup, at a time where the - // thread manager hasn't been initialized with the main thread yet. - // In that case, calling SystemGroup::Dispatch would assert, so we make - // sure that NS_GetMainThread succeeds before attempting to dispatch this - // runnable. - nsCOMPtr mainThread; - nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread)); - if (NS_SUCCEEDED(rv) && mainThread) { - rv = SystemGroup::Dispatch(TaskCategory::Other, do_AddRef(this)); + // The profiler for the main thread is set up before the thread manager is, + // meaning we can't get the nsIThread when the CheckResponsivenessTask is + // constructed. We _do_ know whether it is the main thread at that time, + // however, so here's the workaround. We can still hit this code before the + // thread manager is initted, in which case we won't try to record + // responsiveness, which is fine because there's no event queue to check + // responsiveness on anyway. + if (mIsMainThread) { + if (!mThread) { + nsCOMPtr temp; + NS_GetMainThread(getter_AddRefs(temp)); + mThread = temp.forget(); + } + + if (mThread) { + nsresult rv = SystemGroup::Dispatch(TaskCategory::Other, do_AddRef(this)); + if (NS_SUCCEEDED(rv)) { + mHasEverBeenSuccessfullyDispatched = true; + } + } + } else if (mThread) { + nsresult rv = mThread->Dispatch(this, nsIThread::NS_DISPATCH_NORMAL); if (NS_SUCCEEDED(rv)) { mHasEverBeenSuccessfullyDispatched = true; } } + + return mHasEverBeenSuccessfullyDispatched; } - // Can only run on the main thread. + nsresult Cancel() override + { + // No special work needed. + return NS_OK; + } + + // Only runs on the thread being profiled NS_IMETHOD Run() override { mStartToPrevTracer_us = uint64_t(profiler_time() * 1000.0); if (!mStop) { if (!mTimer) { - mTimer = NS_NewTimer( - SystemGroup::EventTargetFor(TaskCategory::Other)); + if (mIsMainThread) { + mTimer = NS_NewTimer( + SystemGroup::EventTargetFor(TaskCategory::Other)); + } else { + mTimer = NS_NewTimer(); + } } mTimer->InitWithCallback(this, 16, nsITimer::TYPE_ONE_SHOT); } @@ -71,11 +98,10 @@ public: return NS_OK; } - // Main thread only + // Should always fire on the thread being profiled NS_IMETHOD Notify(nsITimer* aTimer) final override { - SystemGroup::Dispatch(TaskCategory::Other, - do_AddRef(this)); + mThread->Dispatch(this, nsIThread::NS_DISPATCH_NORMAL); return NS_OK; } @@ -92,31 +118,37 @@ public: NS_DECL_ISUPPORTS_INHERITED private: - // The timer that's responsible for redispatching this event to the main - // thread. This field is only accessed on the main thread. + // The timer that's responsible for redispatching this event to the thread we + // are profiling (ie; mThread). Only touched on mThread. nsCOMPtr mTimer; // The time (in integer microseconds since process startup) at which this // event was last processed (Run() was last called). - // This field is written on the main thread and read on the update thread. + // This field is written on mThread and read on the update thread. // This is stored as integer microseconds instead of double milliseconds // because Atomic is not available. Atomic mStartToPrevTracer_us; // Whether we should stop redispatching this event once the timer fires the // next time. Set to true by any thread when the profiler is stopped; read on - // the main thread. + // mThread. Atomic mStop; // Only accessed on the update thread. bool mHasEverBeenSuccessfullyDispatched; + + // The thread that we're profiling. Use nsIEventTarget to allow for checking + // responsiveness on non-nsIThreads someday. + nsCOMPtr mThread; + bool mIsMainThread; }; -NS_IMPL_ISUPPORTS_INHERITED(CheckResponsivenessTask, mozilla::Runnable, +NS_IMPL_ISUPPORTS_INHERITED(CheckResponsivenessTask, CancelableRunnable, nsITimerCallback) -ThreadResponsiveness::ThreadResponsiveness() - : mActiveTracerEvent(new CheckResponsivenessTask()) +ThreadResponsiveness::ThreadResponsiveness(nsIEventTarget* aThread, + bool aIsMainThread) + : mActiveTracerEvent(new CheckResponsivenessTask(aThread, aIsMainThread)) { MOZ_COUNT_CTOR(ThreadResponsiveness); } @@ -130,7 +162,9 @@ ThreadResponsiveness::~ThreadResponsiveness() void ThreadResponsiveness::Update() { - mActiveTracerEvent->DoFirstDispatchIfNeeded(); + if (!mActiveTracerEvent->DoFirstDispatchIfNeeded()) { + return; + } mStartToPrevTracer_ms = Some(mActiveTracerEvent->GetStartToPrevTracer_ms()); } diff --git a/tools/profiler/gecko/ThreadResponsiveness.h b/tools/profiler/gecko/ThreadResponsiveness.h index c0dfcd38335b..c07e7c5f5da2 100644 --- a/tools/profiler/gecko/ThreadResponsiveness.h +++ b/tools/profiler/gecko/ThreadResponsiveness.h @@ -12,11 +12,12 @@ #include "mozilla/TimeStamp.h" class CheckResponsivenessTask; +class nsIEventTarget; // This class should only be used for the main thread. class ThreadResponsiveness { public: - explicit ThreadResponsiveness(); + explicit ThreadResponsiveness(nsIEventTarget* aThread, bool aIsMainThread); ~ThreadResponsiveness(); diff --git a/tools/profiler/tests/gtest/ThreadProfileTest.cpp b/tools/profiler/tests/gtest/ThreadProfileTest.cpp index 598c85e32323..f76b7aca8143 100644 --- a/tools/profiler/tests/gtest/ThreadProfileTest.cpp +++ b/tools/profiler/tests/gtest/ThreadProfileTest.cpp @@ -12,14 +12,18 @@ // Make sure we can initialize our thread profile TEST(ThreadProfile, Initialization) { int tid = 1000; - ThreadInfo info("testThread", tid, true, nullptr); + nsCOMPtr mainThread; + NS_GetMainThread(getter_AddRefs(mainThread)); + ThreadInfo info("testThread", tid, true, mainThread, nullptr); info.StartProfiling(); } // Make sure we can record one entry and read it TEST(ThreadProfile, InsertOneEntry) { int tid = 1000; - ThreadInfo info("testThread", tid, true, nullptr); + nsCOMPtr mainThread; + NS_GetMainThread(getter_AddRefs(mainThread)); + ThreadInfo info("testThread", tid, true, mainThread, nullptr); auto pb = MakeUnique(10); pb->AddEntry(ProfileBufferEntry::Time(123.1)); ASSERT_TRUE(pb->mEntries != nullptr); @@ -30,7 +34,9 @@ TEST(ThreadProfile, InsertOneEntry) { // See if we can insert some entries TEST(ThreadProfile, InsertEntriesNoWrap) { int tid = 1000; - ThreadInfo info("testThread", tid, true, nullptr); + nsCOMPtr mainThread; + NS_GetMainThread(getter_AddRefs(mainThread)); + ThreadInfo info("testThread", tid, true, mainThread, nullptr); auto pb = MakeUnique(100); int test_size = 50; for (int i = 0; i < test_size; i++) { @@ -51,7 +57,9 @@ TEST(ThreadProfile, InsertEntriesWrap) { // we can fit only 24 entries in this buffer because of the empty slot int entries = 24; int buffer_size = entries + 1; - ThreadInfo info("testThread", tid, true, nullptr); + nsCOMPtr mainThread; + NS_GetMainThread(getter_AddRefs(mainThread)); + ThreadInfo info("testThread", tid, true, mainThread, nullptr); auto pb = MakeUnique(buffer_size); int test_size = 43; for (int i = 0; i < test_size; i++) {