Bug 1431755 - Part 2: Teach GeckoProfiler to profile responsiveness on nsIThreads. r=mstange

MozReview-Commit-ID: AqpNf9pDzrg

--HG--
extra : rebase_source : d7c67ec8564345f6b851691f4e3dee666dd025b1
This commit is contained in:
Byron Campen [:bwc] 2018-01-19 09:42:47 -06:00
Родитель c3c54d2281
Коммит 8d9cde7d0d
6 изменённых файлов: 87 добавлений и 44 удалений

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

@ -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<RacyThreadInfo*>(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

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

@ -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<nsIEventTarget> 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<char[]> mSavedStreamedMarkers;
mozilla::Maybe<UniqueStacks> mUniqueStacks;
// This is only used for the main thread.
// This is used only for nsIThreads.
mozilla::Maybe<ThreadResponsiveness> mResponsiveness;
public:

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

@ -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)) {

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

@ -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<nsIThread> 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<nsIThread> 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<nsITimer> 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<double> is not available.
Atomic<uint64_t> 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<bool> 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<nsIEventTarget> 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());
}

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

@ -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();

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

@ -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<nsIThread> 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<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
ThreadInfo info("testThread", tid, true, mainThread, nullptr);
auto pb = MakeUnique<ProfileBuffer>(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<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
ThreadInfo info("testThread", tid, true, mainThread, nullptr);
auto pb = MakeUnique<ProfileBuffer>(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<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
ThreadInfo info("testThread", tid, true, mainThread, nullptr);
auto pb = MakeUnique<ProfileBuffer>(buffer_size);
int test_size = 43;
for (int i = 0; i < test_size; i++) {