Bug 1233831 - Part 3: Install a callback to trace nursery collections; r=mccr8

This commit makes CycleCollectedJSRuntime install a nursery collection callback
which traces nursery collections with a new `mozilla::TimelineMarker` subclass.
This commit is contained in:
Nick Fitzgerald 2016-01-19 12:48:22 -08:00
parent 12fb783bb6
commit 7cc189e394
2 changed files with 92 additions and 1 deletions

View File

@ -58,11 +58,15 @@
#include <algorithm>
#include "mozilla/ArrayUtils.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/Move.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Snprintf.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimelineConsumers.h"
#include "mozilla/TimelineMarker.h"
#include "mozilla/DebuggerOnGCRunnable.h"
#include "mozilla/dom/DOMJSClass.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ScriptSettings.h"
#include "jsprf.h"
@ -411,6 +415,7 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
, mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal)
, mJSRuntime(nullptr)
, mPrevGCSliceCallback(nullptr)
, mPrevGCNurseryCollectionCallback(nullptr)
, mJSHolders(256)
, mDoingStableStates(false)
, mOutOfMemoryState(OOMState::OK)
@ -437,6 +442,19 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
JS_SetGCCallback(mJSRuntime, GCCallback, this);
mPrevGCSliceCallback = JS::SetGCSliceCallback(mJSRuntime, GCSliceCallback);
if (NS_IsMainThread()) {
// We would like to support all threads here, but the way timeline consumers
// are set up currently, you can either add a marker for one specific
// docshell, or for every consumer globally. We would like to add a marker
// for every consumer observing anything on this thread, but that is not
// currently possible. For now, add global markers only when we are on the
// main thread, since the UI for this tracing data only displays data
// relevant to the main-thread.
mPrevGCNurseryCollectionCallback = JS::SetGCNurseryCollectionCallback(
mJSRuntime, GCNurseryCollectionCallback);
}
JS_SetObjectsTenuredCallback(mJSRuntime, JSObjectsTenuredCb, this);
JS::SetOutOfMemoryCallback(mJSRuntime, OutOfMemoryCallback, this);
JS::SetLargeAllocationFailureCallback(mJSRuntime,
@ -745,6 +763,76 @@ CycleCollectedJSRuntime::GCSliceCallback(JSRuntime* aRuntime,
}
}
class MinorGCMarker : public TimelineMarker
{
private:
JS::gcreason::Reason mReason;
public:
MinorGCMarker(MarkerTracingType aTracingType,
JS::gcreason::Reason aReason)
: TimelineMarker("MinorGC",
aTracingType,
MarkerStackRequest::NO_STACK)
, mReason(aReason)
{
MOZ_ASSERT(aTracingType == MarkerTracingType::START ||
aTracingType == MarkerTracingType::END);
}
MinorGCMarker(JS::GCNurseryProgress aProgress,
JS::gcreason::Reason aReason)
: TimelineMarker("MinorGC",
aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START
? MarkerTracingType::START
: MarkerTracingType::END,
MarkerStackRequest::NO_STACK)
, mReason(aReason)
{ }
virtual void
AddDetails(JSContext* aCx,
dom::ProfileTimelineMarker& aMarker) override
{
TimelineMarker::AddDetails(aCx, aMarker);
if (GetTracingType() == MarkerTracingType::START) {
auto reason = JS::gcreason::ExplainReason(mReason);
aMarker.mCauseName.Construct(NS_ConvertUTF8toUTF16(reason));
}
}
virtual UniquePtr<AbstractTimelineMarker>
Clone() override
{
auto clone = MakeUnique<MinorGCMarker>(GetTracingType(), mReason);
clone->SetCustomTime(GetTime());
return UniquePtr<AbstractTimelineMarker>(Move(clone));
}
};
/* static */ void
CycleCollectedJSRuntime::GCNurseryCollectionCallback(JSRuntime* aRuntime,
JS::GCNurseryProgress aProgress,
JS::gcreason::Reason aReason)
{
CycleCollectedJSRuntime* self = CycleCollectedJSRuntime::Get();
MOZ_ASSERT(self->Runtime() == aRuntime);
MOZ_ASSERT(NS_IsMainThread());
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (timelines && !timelines->IsEmpty()) {
UniquePtr<AbstractTimelineMarker> abstractMarker(
MakeUnique<MinorGCMarker>(aProgress, aReason));
timelines->AddMarkerForAllObservedDocShells(abstractMarker);
}
if (self->mPrevGCNurseryCollectionCallback) {
self->mPrevGCNurseryCollectionCallback(aRuntime, aProgress, aReason);
}
}
/* static */ void
CycleCollectedJSRuntime::OutOfMemoryCallback(JSContext* aContext,
void* aData)
@ -1403,4 +1491,3 @@ CycleCollectedJSRuntime::OnLargeAllocationFailure()
CustomLargeAllocationFailureCallback();
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reported);
}

View File

@ -203,6 +203,9 @@ private:
static void GCCallback(JSRuntime* aRuntime, JSGCStatus aStatus, void* aData);
static void GCSliceCallback(JSRuntime* aRuntime, JS::GCProgress aProgress,
const JS::GCDescription& aDesc);
static void GCNurseryCollectionCallback(JSRuntime* aRuntime,
JS::GCNurseryProgress aProgress,
JS::gcreason::Reason aReason);
static void OutOfMemoryCallback(JSContext* aContext, void* aData);
static void LargeAllocationFailureCallback(void* aData);
static bool ContextCallback(JSContext* aCx, unsigned aOperation,
@ -341,6 +344,7 @@ private:
JSRuntime* mJSRuntime;
JS::GCSliceCallback mPrevGCSliceCallback;
JS::GCNurseryCollectionCallback mPrevGCNurseryCollectionCallback;
nsDataHashtable<nsPtrHashKey<void>, nsScriptObjectTracer*> mJSHolders;