зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
c3c54d2281
Коммит
8d9cde7d0d
|
@ -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++) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче