Bug 1429904 - Tell the ProfiledThreadData what the buffer position was when the thread received its JSContext. r?njn
This also renames FlushSamplesAndMarkers to NotifyAboutToLoseJSContext.
MozReview-Commit-ID: FWinMi85yDZ
--- a/tools/profiler/core/ProfiledThreadData.cpp
+++ b/tools/profiler/core/ProfiledThreadData.cpp
@@ -187,19 +187,19 @@ StreamSamplesAndMarkers(const char* aNam
aSinceTime, aUniqueStacks);
}
aWriter.EndArray();
}
aWriter.EndObject();
}
void
-ProfiledThreadData::FlushSamplesAndMarkers(JSContext* aCx,
- const TimeStamp& aProcessStartTime,
- ProfileBuffer& aBuffer)
+ProfiledThreadData::NotifyAboutToLoseJSContext(JSContext* aCx,
+ const TimeStamp& aProcessStartTime,
+ ProfileBuffer& aBuffer)
{
// This function is used to serialize the current buffer just before
// JSContext destruction.
MOZ_ASSERT(aCx);
// Unlike StreamJSObject, do not surround the samples in brackets by calling
// aWriter.{Start,End}BareList. The result string will be a comma-separated
// list of JSON object literals that will prepended by StreamJSObject into
@@ -274,12 +274,14 @@ ProfiledThreadData::FlushSamplesAndMarke
if (haveMarkers) {
markersJSON = b.WriteFunc()->CopyData();
}
}
mPartialProfile = MakeUnique<PartialThreadProfile>(
Move(samplesJSON), Move(markersJSON), Move(uniqueStacks));
+ mBufferPositionWhenReceivedJSContext = Nothing();
+
// Reset the buffer. Attempting to symbolicate JS samples after mContext has
// gone away will crash.
aBuffer.Reset();
}
--- a/tools/profiler/core/ProfiledThreadData.h
+++ b/tools/profiler/core/ProfiledThreadData.h
@@ -61,44 +61,51 @@ class ProfiledThreadData final
public:
ProfiledThreadData(ThreadInfo* aThreadInfo, nsIEventTarget* aEventTarget);
~ProfiledThreadData();
void NotifyUnregistered(uint64_t aBufferPosition)
{
mResponsiveness.reset();
mLastSample = mozilla::Nothing();
+ MOZ_ASSERT(!mBufferPositionWhenReceivedJSContext,
+ "JSContext should have been cleared before the thread was unregistered");
mUnregisterTime = TimeStamp::Now();
mBufferPositionWhenUnregistered = mozilla::Some(aBufferPosition);
}
mozilla::Maybe<uint64_t> BufferPositionWhenUnregistered() { return mBufferPositionWhenUnregistered; }
mozilla::Maybe<uint64_t>& LastSample() { return mLastSample; }
void StreamJSON(const ProfileBuffer& aBuffer, JSContext* aCx,
SpliceableJSONWriter& aWriter,
const mozilla::TimeStamp& aProcessStartTime,
double aSinceTime);
- // Call this method when the JS entries inside the buffer are about to
- // become invalid, i.e., just before JS shutdown.
- void FlushSamplesAndMarkers(JSContext* aCx,
- const mozilla::TimeStamp& aProcessStartTime,
- ProfileBuffer& aBuffer);
-
// Returns nullptr if this is not the main thread or if this thread is not
// being profiled.
ThreadResponsiveness* GetThreadResponsiveness()
{
ThreadResponsiveness* responsiveness = mResponsiveness.ptrOr(nullptr);
return responsiveness;
}
const RefPtr<ThreadInfo> Info() const { return mThreadInfo; }
+ void NotifyReceivedJSContext(uint64_t aCurrentBufferPosition)
+ {
+ mBufferPositionWhenReceivedJSContext = mozilla::Some(aCurrentBufferPosition);
+ }
+
+ // Call this method when the JS entries inside the buffer are about to
+ // become invalid, i.e., just before JS shutdown.
+ void NotifyAboutToLoseJSContext(JSContext* aCx,
+ const TimeStamp& aProcessStartTime,
+ ProfileBuffer& aBuffer);
+
private:
// Group A:
// The following fields are interesting for the entire lifetime of a
// ProfiledThreadData object.
// This thread's thread info.
const RefPtr<ThreadInfo> mThreadInfo;
@@ -116,16 +123,19 @@ private:
// information about their event loop.
mozilla::Maybe<ThreadResponsiveness> mResponsiveness;
// When sampling, this holds the position in ActivePS::mBuffer of the most
// recent sample for this thread, or Nothing() if there is no sample for this
// thread in the buffer.
mozilla::Maybe<uint64_t> mLastSample;
+ // Only non-Nothing() if the thread currently has a JSContext.
+ mozilla::Maybe<uint64_t> mBufferPositionWhenReceivedJSContext;
+
// Group C:
// The following fields are only used once this thread has been unregistered.
mozilla::Maybe<uint64_t> mBufferPositionWhenUnregistered;
mozilla::TimeStamp mUnregisterTime;
};
void
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -565,21 +565,25 @@ public:
LiveProfiledThreadData& thread = sInstance->mLiveProfiledThreads[i];
if (thread.mRegisteredThread == aRegisteredThread) {
return thread.mProfiledThreadData.get();
}
}
return nullptr;
}
- static void AddLiveProfiledThread(PSLockRef, RegisteredThread* aRegisteredThread,
- UniquePtr<ProfiledThreadData>&& aProfiledThreadData)
+ static ProfiledThreadData*
+ AddLiveProfiledThread(PSLockRef, RegisteredThread* aRegisteredThread,
+ UniquePtr<ProfiledThreadData>&& aProfiledThreadData)
{
sInstance->mLiveProfiledThreads.AppendElement(
LiveProfiledThreadData{ aRegisteredThread, Move(aProfiledThreadData) });
+
+ // Return a weak pointer to the ProfiledThreadData object.
+ return sInstance->mLiveProfiledThreads.LastElement().mProfiledThreadData.get();
}
static void UnregisterThread(PSLockRef aLockRef, RegisteredThread* aRegisteredThread)
{
DiscardExpiredDeadProfiledThreads(aLockRef);
// Find the right entry in the mLiveProfiledThreads array and remove the
// element, moving the ProfiledThreadData object for the thread into the
@@ -2237,24 +2241,28 @@ locked_register_thread(PSLockRef aLock,
MakeUnique<RegisteredThread>(info, NS_GetCurrentThreadNoCreate(),
aStackTop);
TLSRegisteredThread::SetRegisteredThread(aLock, registeredThread.get());
if (ActivePS::Exists(aLock) &&
ActivePS::ShouldProfileThread(aLock, info)) {
nsCOMPtr<nsIEventTarget> eventTarget = registeredThread->GetEventTarget();
- ActivePS::AddLiveProfiledThread(aLock, registeredThread.get(),
- MakeUnique<ProfiledThreadData>(info, eventTarget));
+ ProfiledThreadData* profiledThreadData =
+ ActivePS::AddLiveProfiledThread(aLock, registeredThread.get(),
+ MakeUnique<ProfiledThreadData>(info, eventTarget));
if (ActivePS::FeatureJS(aLock)) {
// This StartJSSampling() call is on-thread, so we can poll manually to
// start JS sampling immediately.
registeredThread->StartJSSampling();
registeredThread->PollJSSampling();
+ if (registeredThread->GetJSContext()) {
+ profiledThreadData->NotifyReceivedJSContext(ActivePS::Buffer(aLock).mRangeEnd);
+ }
}
}
CorePS::AppendRegisteredThread(aLock, Move(registeredThread));
}
static void
NotifyObservers(const char* aTopic, nsISupports* aSubject = nullptr)
@@ -2835,32 +2843,36 @@ locked_profiler_start(PSLockRef aLock, u
int tid = Thread::GetCurrentId();
const nsTArray<UniquePtr<RegisteredThread>>& registeredThreads =
CorePS::RegisteredThreads(aLock);
for (auto& registeredThread : registeredThreads) {
RefPtr<ThreadInfo> info = registeredThread->Info();
if (ActivePS::ShouldProfileThread(aLock, info)) {
nsCOMPtr<nsIEventTarget> eventTarget = registeredThread->GetEventTarget();
- ActivePS::AddLiveProfiledThread(aLock, registeredThread.get(),
- MakeUnique<ProfiledThreadData>(info, eventTarget));
+ ProfiledThreadData* profiledThreadData =
+ ActivePS::AddLiveProfiledThread(aLock, registeredThread.get(),
+ MakeUnique<ProfiledThreadData>(info, eventTarget));
if (ActivePS::FeatureJS(aLock)) {
registeredThread->StartJSSampling();
if (info->ThreadId() == tid) {
// We can manually poll the current thread so it starts sampling
// immediately.
registeredThread->PollJSSampling();
} else if (info->IsMainThread()) {
// Dispatch a runnable to the main thread to call PollJSSampling(),
// so that we don't have wait for the next JS interrupt callback in
// order to start profiling JS.
TriggerPollJSSamplingOnMainThread();
}
}
registeredThread->RacyRegisteredThread().ReinitializeOnResume();
+ if (registeredThread->GetJSContext()) {
+ profiledThreadData->NotifyReceivedJSContext(0);
+ }
}
}
#ifdef MOZ_TASK_TRACER
if (ActivePS::FeatureTaskTracer(aLock)) {
tasktracer::StartLogging();
}
#endif
@@ -3417,16 +3429,24 @@ profiler_set_js_context(JSContext* aCx)
return;
}
registeredThread->SetJSContext(aCx);
// This call is on-thread, so we can call PollJSSampling() to start JS
// sampling immediately.
registeredThread->PollJSSampling();
+
+ if (ActivePS::Exists(lock)) {
+ ProfiledThreadData* profiledThreadData =
+ ActivePS::GetProfiledThreadData(lock, registeredThread);
+ if (profiledThreadData) {
+ profiledThreadData->NotifyReceivedJSContext(ActivePS::Buffer(lock).mRangeEnd);
+ }
+ }
}
void
profiler_clear_js_context()
{
MOZ_RELEASE_ASSERT(CorePS::Exists());
PSAutoLock lock(gPSMutex);
@@ -3437,42 +3457,36 @@ profiler_clear_js_context()
return;
}
JSContext* cx = registeredThread->GetJSContext();
if (!cx) {
return;
}
- // On JS shut down, flush the current buffer as stringifying JIT samples
- // requires a live JSContext.
-
- if (ActivePS::Exists(lock)) {
- // Flush this thread's profile data, if it is being profiled.
+ if (ActivePS::Exists(lock) && ActivePS::FeatureJS(lock)) {
ProfiledThreadData* profiledThreadData =
ActivePS::GetProfiledThreadData(lock, registeredThread);
if (profiledThreadData) {
- profiledThreadData->FlushSamplesAndMarkers(cx,
- CorePS::ProcessStartTime(),
- ActivePS::Buffer(lock));
-
- if (ActivePS::FeatureJS(lock)) {
- // Notify the JS context that profiling for this context has stopped.
- // Do this by calling StopJSSampling and PollJSSampling before
- // nulling out the JSContext.
- registeredThread->StopJSSampling();
- registeredThread->PollJSSampling();
-
- registeredThread->ClearJSContext();
-
- // Tell the thread that we'd like to have JS sampling on this
- // thread again, once it gets a new JSContext (if ever).
- registeredThread->StartJSSampling();
- return;
- }
+ profiledThreadData->NotifyAboutToLoseJSContext(cx,
+ CorePS::ProcessStartTime(),
+ ActivePS::Buffer(lock));
+
+ // Notify the JS context that profiling for this context has stopped.
+ // Do this by calling StopJSSampling and PollJSSampling before
+ // nulling out the JSContext.
+ registeredThread->StopJSSampling();
+ registeredThread->PollJSSampling();
+
+ registeredThread->ClearJSContext();
+
+ // Tell the thread that we'd like to have JS sampling on this
+ // thread again, once it gets a new JSContext (if ever).
+ registeredThread->StartJSSampling();
+ return;
}
}
registeredThread->ClearJSContext();
}
int
profiler_current_thread_id()