Bug 678615 - remove ExplainLiveExpectedGarbage. r=smaug sr=peterv

This commit is contained in:
Andrew McCreight 2012-04-25 08:10:09 -07:00
Родитель d15ab65754
Коммит 033fbde830
6 изменённых файлов: 34 добавлений и 600 удалений

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

@ -475,3 +475,13 @@ xpc_DumpJSObject(JSObject* obj)
return true; return true;
} }
#ifdef DEBUG
void
xpc_PrintAllReferencesTo(void *p)
{
/* p must be a JS object */
XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
JS_DumpHeap(rt->GetJSRuntime(), stdout, nsnull, JSTRACE_OBJECT, p, 0x7fffffff, nsnull);
}
#endif

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

@ -119,9 +119,6 @@ nsXPConnect::nsXPConnect()
mRuntime = XPCJSRuntime::newXPCJSRuntime(this); mRuntime = XPCJSRuntime::newXPCJSRuntime(this);
nsCycleCollector_registerRuntime(nsIProgrammingLanguage::JAVASCRIPT, this); nsCycleCollector_registerRuntime(nsIProgrammingLanguage::JAVASCRIPT, this);
#ifdef DEBUG_CC
mJSRoots.ops = nsnull;
#endif
char* reportableEnv = PR_GetEnv("MOZ_REPORT_ALL_JS_EXCEPTIONS"); char* reportableEnv = PR_GetEnv("MOZ_REPORT_ALL_JS_EXCEPTIONS");
if (reportableEnv && *reportableEnv) if (reportableEnv && *reportableEnv)
@ -399,14 +396,14 @@ nsXPConnect::Collect(PRUint32 reason, PRUint32 kind)
// JS objects that are part of cycles the cycle collector breaks will be // JS objects that are part of cycles the cycle collector breaks will be
// collected by the next JS. // collected by the next JS.
// //
// If DEBUG_CC is not defined the cycle collector will not traverse roots // If WantAllTraces() is false the cycle collector will not traverse roots
// from category 1 or any JS objects held by them. Any JS objects they hold // from category 1 or any JS objects held by them. Any JS objects they hold
// will already be marked by the JS GC and will thus be colored black // will already be marked by the JS GC and will thus be colored black
// themselves. Any C++ objects they hold will have a missing (untraversed) // themselves. Any C++ objects they hold will have a missing (untraversed)
// edge from the JS object to the C++ object and so it will be marked black // edge from the JS object to the C++ object and so it will be marked black
// too. This decreases the number of objects that the cycle collector has to // too. This decreases the number of objects that the cycle collector has to
// deal with. // deal with.
// To improve debugging, if DEBUG_CC is defined all JS objects are // To improve debugging, if WantAllTraces() is true all JS objects are
// traversed. // traversed.
MOZ_ASSERT(reason < js::gcreason::NUM_REASONS); MOZ_ASSERT(reason < js::gcreason::NUM_REASONS);
@ -431,37 +428,6 @@ nsXPConnect::GarbageCollect(PRUint32 reason, PRUint32 kind)
return NS_OK; return NS_OK;
} }
#ifdef DEBUG_CC
struct NoteJSRootTracer : public JSTracer
{
NoteJSRootTracer(PLDHashTable *aObjects,
nsCycleCollectionTraversalCallback& cb)
: mObjects(aObjects),
mCb(cb)
{
}
PLDHashTable* mObjects;
nsCycleCollectionTraversalCallback& mCb;
};
static void
NoteJSRoot(JSTracer *trc, void *thing, JSGCTraceKind kind)
{
if (AddToCCKind(kind)) {
NoteJSRootTracer *tracer = static_cast<NoteJSRootTracer*>(trc);
PLDHashEntryHdr *entry = PL_DHashTableOperate(tracer->mObjects, thing,
PL_DHASH_ADD);
if (entry && !reinterpret_cast<PLDHashEntryStub*>(entry)->key) {
reinterpret_cast<PLDHashEntryStub*>(entry)->key = thing;
tracer->mCb.NoteRoot(nsIProgrammingLanguage::JAVASCRIPT, thing,
nsXPConnect::GetXPConnect());
}
} else if (kind != JSTRACE_STRING) {
JS_TraceChildren(trc, thing, kind);
}
}
#endif
struct NoteWeakMapChildrenTracer : public JSTracer struct NoteWeakMapChildrenTracer : public JSTracer
{ {
NoteWeakMapChildrenTracer(nsCycleCollectionTraversalCallback &cb) NoteWeakMapChildrenTracer(nsCycleCollectionTraversalCallback &cb)
@ -538,8 +504,7 @@ TraceWeakMapping(js::WeakMapTracer *trc, JSObject *m,
} }
nsresult nsresult
nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb, nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb)
bool explainLiveExpectedGarbage)
{ {
// It is important not to call GetSafeJSContext while on the // It is important not to call GetSafeJSContext while on the
// cycle-collector thread since this context will be destroyed // cycle-collector thread since this context will be destroyed
@ -565,31 +530,6 @@ nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
gcHasRun = true; gcHasRun = true;
} }
#ifdef DEBUG_CC
NS_ASSERTION(!mJSRoots.ops, "Didn't call FinishCycleCollection?");
if (explainLiveExpectedGarbage) {
// Being called from nsCycleCollector::ExplainLiveExpectedGarbage.
// Record all objects held by the JS runtime. This avoids doing a
// complete GC if we're just tracing to explain (from
// ExplainLiveExpectedGarbage), which makes the results of cycle
// collection identical for DEBUG_CC and non-DEBUG_CC builds.
if (!PL_DHashTableInit(&mJSRoots, PL_DHashGetStubOps(), nsnull,
sizeof(PLDHashEntryStub), PL_DHASH_MIN_SIZE)) {
mJSRoots.ops = nsnull;
return NS_ERROR_OUT_OF_MEMORY;
}
NoteJSRootTracer trc(&mJSRoots, cb);
JS_TracerInit(&trc, mCycleCollectionContext->GetJSContext(), NoteJSRoot);
JS_TraceRuntime(&trc);
}
#else
NS_ASSERTION(!explainLiveExpectedGarbage, "Didn't call nsXPConnect::Collect()?");
#endif
GetRuntime()->AddXPConnectRoots(cb); GetRuntime()->AddXPConnectRoots(cb);
NoteWeakMapsTracer trc(GetRuntime()->GetJSRuntime(), TraceWeakMapping, cb); NoteWeakMapsTracer trc(GetRuntime()->GetJSRuntime(), TraceWeakMapping, cb);
@ -641,13 +581,6 @@ nsXPConnect::FinishTraverse()
nsresult nsresult
nsXPConnect::FinishCycleCollection() nsXPConnect::FinishCycleCollection()
{ {
#ifdef DEBUG_CC
if (mJSRoots.ops) {
PL_DHashTableFinish(&mJSRoots);
mJSRoots.ops = nsnull;
}
#endif
return NS_OK; return NS_OK;
} }
@ -665,19 +598,6 @@ nsXPConnect::Root(void *p)
return NS_OK; return NS_OK;
} }
#ifdef DEBUG_CC
void
nsXPConnect::PrintAllReferencesTo(void *p)
{
#ifdef DEBUG
XPCCallContext ccx(NATIVE_CALLER);
if (ccx.IsValid())
JS_DumpHeap(ccx.GetJSContext(), stdout, nsnull, 0, p,
0x7fffffff, nsnull);
#endif
}
#endif
NS_IMETHODIMP NS_IMETHODIMP
nsXPConnect::Unlink(void *p) nsXPConnect::Unlink(void *p)
{ {
@ -909,30 +829,7 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
} }
} }
bool isMarked; bool isMarked = markJSObject || !xpc_IsGrayGCThing(p);
#ifdef DEBUG_CC
// Note that the conditions under which we specify GCMarked vs.
// GCUnmarked are different between ExplainLiveExpectedGarbage and
// the normal case. In the normal case, we're saying that anything
// reachable from a JS runtime root is itself such a root. This
// doesn't actually break anything; it really just does some of the
// cycle collector's work for it. However, when debugging, we
// (1) actually need to know what the root is and (2) don't want to
// do an extra GC, so we use mJSRoots, built from JS_TraceRuntime,
// which produces a different result because we didn't call
// JS_TraceChildren to trace everything that was reachable.
if (mJSRoots.ops) {
// ExplainLiveExpectedGarbage codepath
PLDHashEntryHdr* entry =
PL_DHashTableOperate(&mJSRoots, p, PL_DHASH_LOOKUP);
isMarked = markJSObject || PL_DHASH_ENTRY_IS_BUSY(entry);
} else
#endif
{
// Normal codepath (matches non-DEBUG_CC codepath).
isMarked = markJSObject || !xpc_IsGrayGCThing(p);
}
if (cb.WantDebugInfo()) { if (cb.WantDebugInfo()) {
char name[72]; char name[72];
@ -982,8 +879,8 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
// There's no need to trace objects that have already been marked by the JS // There's no need to trace objects that have already been marked by the JS
// GC. Any JS objects hanging from them will already be marked. Only do this // GC. Any JS objects hanging from them will already be marked. Only do this
// if DEBUG_CC is not defined, else we do want to know about all JS objects // if cb.WantAllTraces() is false, otherwise we do want to know about all JS
// to get better graphs and explanations. // objects to get better graphs and explanations.
if (!cb.WantAllTraces() && isMarked) if (!cb.WantAllTraces() && isMarked)
return NS_OK; return NS_OK;

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

@ -537,16 +537,12 @@ public:
virtual void NotifyEnterCycleCollectionThread(); virtual void NotifyEnterCycleCollectionThread();
virtual void NotifyLeaveCycleCollectionThread(); virtual void NotifyLeaveCycleCollectionThread();
virtual void NotifyEnterMainThread(); virtual void NotifyEnterMainThread();
virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb, virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb);
bool explainExpectedLiveGarbage);
virtual nsresult FinishTraverse(); virtual nsresult FinishTraverse();
virtual nsresult FinishCycleCollection(); virtual nsresult FinishCycleCollection();
virtual nsCycleCollectionParticipant *ToParticipant(void *p); virtual nsCycleCollectionParticipant *ToParticipant(void *p);
virtual bool NeedCollect(); virtual bool NeedCollect();
virtual void Collect(PRUint32 reason, PRUint32 kind); virtual void Collect(PRUint32 reason, PRUint32 kind);
#ifdef DEBUG_CC
virtual void PrintAllReferencesTo(void *p);
#endif
XPCCallContext *GetCycleCollectionContext() XPCCallContext *GetCycleCollectionContext()
{ {
@ -598,9 +594,6 @@ private:
// an 'after' notification without getting an 'on' notification. If we don't // an 'after' notification without getting an 'on' notification. If we don't
// watch out for this, we'll do an unmatched |pop| on the context stack. // watch out for this, we'll do an unmatched |pop| on the context stack.
PRUint16 mEventDepth; PRUint16 mEventDepth;
#ifdef DEBUG_CC
PLDHashTable mJSRoots;
#endif
nsAutoPtr<XPCCallContext> mCycleCollectionContext; nsAutoPtr<XPCCallContext> mCycleCollectionContext;
typedef nsBaseHashtable<nsPtrHashKey<void>, nsISupports*, nsISupports*> ScopeSet; typedef nsBaseHashtable<nsPtrHashKey<void>, nsISupports*, nsISupports*> ScopeSet;

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

@ -500,17 +500,6 @@ public:
}; };
#ifdef DEBUG_CC
struct ReversedEdge {
PtrInfo *mTarget;
nsCString *mEdgeName;
ReversedEdge *mNext;
};
#endif
enum NodeColor { black, white, grey }; enum NodeColor { black, white, grey };
// This structure should be kept as small as possible; we may expect // This structure should be kept as small as possible; we may expect
@ -532,18 +521,6 @@ public:
size_t mBytes; size_t mBytes;
char *mName; char *mName;
PRUint32 mLangID; PRUint32 mLangID;
// For finding roots in ExplainLiveExpectedGarbage (when there are
// missing calls to suspect or failures to unlink).
PRUint32 mSCCIndex; // strongly connected component
// For finding roots in ExplainLiveExpectedGarbage (when nodes
// expected to be garbage are black).
ReversedEdge* mReversedEdges; // linked list
PtrInfo* mShortestPathToExpectedGarbage;
nsCString* mShortestPathToExpectedGarbageEdgeName;
nsTArray<nsCString> mEdgeNames;
#endif #endif
PtrInfo(void *aPointer, nsCycleCollectionParticipant *aParticipant PtrInfo(void *aPointer, nsCycleCollectionParticipant *aParticipant
@ -558,11 +535,7 @@ public:
#ifdef DEBUG_CC #ifdef DEBUG_CC
, mBytes(0), , mBytes(0),
mName(nsnull), mName(nsnull),
mLangID(aLangID), mLangID(aLangID)
mSCCIndex(0),
mReversedEdges(nsnull),
mShortestPathToExpectedGarbage(nsnull),
mShortestPathToExpectedGarbageEdgeName(nsnull)
#endif #endif
{ {
} }
@ -570,7 +543,6 @@ public:
#ifdef DEBUG_CC #ifdef DEBUG_CC
void Destroy() { void Destroy() {
PL_strfree(mName); PL_strfree(mName);
mEdgeNames.~nsTArray<nsCString>();
} }
#endif #endif
@ -766,9 +738,6 @@ struct GCGraph
EdgePool mEdges; EdgePool mEdges;
nsTArray<WeakMapping> mWeakMaps; nsTArray<WeakMapping> mWeakMaps;
PRUint32 mRootCount; PRUint32 mRootCount;
#ifdef DEBUG_CC
ReversedEdge *mReversedEdges;
#endif
GCGraph() : mRootCount(0) { GCGraph() : mRootCount(0) {
} }
@ -911,8 +880,6 @@ public:
void RemoveSkippable(bool removeChildlessNodes); void RemoveSkippable(bool removeChildlessNodes);
#ifdef DEBUG_CC #ifdef DEBUG_CC
void NoteAll(GCGraphBuilder &builder);
bool Exists(void *p) const bool Exists(void *p) const
{ {
return mNormalObjects.GetEntry(p) || mCompatObjects.GetEntry(p); return mNormalObjects.GetEntry(p) || mCompatObjects.GetEntry(p);
@ -1086,8 +1053,7 @@ nsPurpleBuffer::SelectPointers(GCGraphBuilder &aBuilder)
struct nsCycleCollectionXPCOMRuntime : struct nsCycleCollectionXPCOMRuntime :
public nsCycleCollectionLanguageRuntime public nsCycleCollectionLanguageRuntime
{ {
nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb, nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb)
bool explainLiveExpectedGarbage)
{ {
return NS_OK; return NS_OK;
} }
@ -1103,10 +1069,6 @@ struct nsCycleCollectionXPCOMRuntime :
} }
inline nsCycleCollectionParticipant *ToParticipant(void *p); inline nsCycleCollectionParticipant *ToParticipant(void *p);
#ifdef DEBUG_CC
virtual void PrintAllReferencesTo(void *p) {}
#endif
}; };
struct nsCycleCollector struct nsCycleCollector
@ -1131,11 +1093,11 @@ struct nsCycleCollector
PRUint32 mVisitedRefCounted; PRUint32 mVisitedRefCounted;
PRUint32 mVisitedGCed; PRUint32 mVisitedGCed;
nsPurpleBuffer mPurpleBuf;
CC_BeforeUnlinkCallback mBeforeUnlinkCB; CC_BeforeUnlinkCallback mBeforeUnlinkCB;
CC_ForgetSkippableCallback mForgetSkippableCB; CC_ForgetSkippableCallback mForgetSkippableCB;
nsPurpleBuffer mPurpleBuf;
void RegisterRuntime(PRUint32 langID, void RegisterRuntime(PRUint32 langID,
nsCycleCollectionLanguageRuntime *rt); nsCycleCollectionLanguageRuntime *rt);
void ForgetRuntime(PRUint32 langID); void ForgetRuntime(PRUint32 langID);
@ -1187,20 +1149,16 @@ struct nsCycleCollector
#ifdef DEBUG_CC #ifdef DEBUG_CC
nsCycleCollectorStats mStats; nsCycleCollectorStats mStats;
FILE *mPtrLog; FILE *mPtrLog;
PointerSet mExpectedGarbage;
void Allocated(void *n, size_t sz); void Allocated(void *n, size_t sz);
void Freed(void *n); void Freed(void *n);
void LogPurpleRemoval(void* aObject); void LogPurpleRemoval(void* aObject);
void ExplainLiveExpectedGarbage();
bool CreateReversedEdges();
void DestroyReversedEdges();
void ShouldBeFreed(nsISupports *n); void ShouldBeFreed(nsISupports *n);
void WasFreed(nsISupports *n); void WasFreed(nsISupports *n);
PointerSet mExpectedGarbage;
#endif #endif
}; };
@ -1780,9 +1738,6 @@ private:
if (!childPi) if (!childPi)
return; return;
mEdgeBuilder.Add(childPi); mEdgeBuilder.Add(childPi);
#ifdef DEBUG_CC
mCurrPi->mEdgeNames.AppendElement(edgeName);
#endif
if (mListener) { if (mListener) {
mListener->NoteEdge((PRUint64)child, edgeName.get()); mListener->NoteEdge((PRUint64)child, edgeName.get());
} }
@ -2183,33 +2138,6 @@ nsPurpleBuffer::RemoveSkippable(bool removeChildlessNodes)
} }
} }
#ifdef DEBUG_CC
static PLDHashOperator
noteAllCallback(nsPtrHashKey<const void>* key, void* userArg)
{
GCGraphBuilder *builder = static_cast<GCGraphBuilder*>(userArg);
builder->NoteXPCOMRoot(
static_cast<nsISupports *>(const_cast<void*>(key->GetKey())));
return PL_DHASH_NEXT;
}
void
nsPurpleBuffer::NoteAll(GCGraphBuilder &builder)
{
mCompatObjects.EnumerateEntries(noteAllCallback, &builder);
for (Block *b = &mFirstBlock; b; b = b->mNext) {
for (nsPurpleBufferEntry *e = b->mEntries,
*eEnd = ArrayEnd(b->mEntries);
e != eEnd; ++e) {
if (!(uintptr_t(e->mObject) & uintptr_t(1)) && e->mObject) {
builder.NoteXPCOMRoot(e->mObject);
}
}
}
}
#endif
void void
nsCycleCollector::SelectPurple(GCGraphBuilder &builder) nsCycleCollector::SelectPurple(GCGraphBuilder &builder)
{ {
@ -2485,11 +2413,11 @@ nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
// free. This stuff is disabled unless you set an environment variable. // free. This stuff is disabled unless you set an environment variable.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
static bool hookedMalloc = false;
#if defined(__GLIBC__) && !defined(__UCLIBC__) #if defined(__GLIBC__) && !defined(__UCLIBC__)
#include <malloc.h> #include <malloc.h>
static bool hookedMalloc = false;
static void* (*old_memalign_hook)(size_t, size_t, const void *); static void* (*old_memalign_hook)(size_t, size_t, const void *);
static void* (*old_realloc_hook)(void *, size_t, const void *); static void* (*old_realloc_hook)(void *, size_t, const void *);
static void* (*old_malloc_hook)(size_t, const void *); static void* (*old_malloc_hook)(size_t, const void *);
@ -2615,6 +2543,8 @@ InitMemHook(void)
#elif defined(WIN32) #elif defined(WIN32)
#ifndef __MINGW32__ #ifndef __MINGW32__
static bool hookedMalloc = false;
static int static int
AllocHook(int allocType, void *userData, size_t size, int AllocHook(int allocType, void *userData, size_t size, int
blockType, long requestNumber, const unsigned char *filename, int blockType, long requestNumber, const unsigned char *filename, int
@ -2639,6 +2569,8 @@ static void InitMemHook(void)
#include <malloc/malloc.h> #include <malloc/malloc.h>
static bool hookedMalloc = false;
static void (*old_free)(struct _malloc_zone_t *zone, void *ptr); static void (*old_free)(struct _malloc_zone_t *zone, void *ptr);
static void static void
@ -2684,14 +2616,14 @@ nsCycleCollector::nsCycleCollector() :
mWhiteNodeCount(0), mWhiteNodeCount(0),
mVisitedRefCounted(0), mVisitedRefCounted(0),
mVisitedGCed(0), mVisitedGCed(0),
mBeforeUnlinkCB(nsnull),
mForgetSkippableCB(nsnull),
#ifdef DEBUG_CC #ifdef DEBUG_CC
mPurpleBuf(mParams, mStats), mPurpleBuf(mParams, mStats),
mPtrLog(nsnull), mPtrLog(nsnull)
#else #else
mPurpleBuf(mParams), mPurpleBuf(mParams)
#endif #endif
mBeforeUnlinkCB(nsnull),
mForgetSkippableCB(nsnull)
{ {
#ifdef DEBUG_CC #ifdef DEBUG_CC
mExpectedGarbage.Init(); mExpectedGarbage.Init();
@ -3125,10 +3057,6 @@ nsCycleCollector::CleanupAfterCollection()
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_REF_COUNTED, mVisitedRefCounted); Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_REF_COUNTED, mVisitedRefCounted);
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_GCED, mVisitedGCed); Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_GCED, mVisitedGCed);
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_COLLECTED, mWhiteNodeCount); Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_COLLECTED, mWhiteNodeCount);
#ifdef DEBUG_CC
ExplainLiveExpectedGarbage();
#endif
} }
void void
@ -3172,7 +3100,7 @@ nsCycleCollector::BeginCollection(nsICycleCollectorListener *aListener)
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) { for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
if (mRuntimes[i]) if (mRuntimes[i])
mRuntimes[i]->BeginCycleCollection(builder, false); mRuntimes[i]->BeginCycleCollection(builder);
} }
timeLog.Checkpoint("mRuntimes[*]->BeginCycleCollection()"); timeLog.Checkpoint("mRuntimes[*]->BeginCycleCollection()");
@ -3348,390 +3276,6 @@ nsCycleCollector::Shutdown()
} }
#ifdef DEBUG_CC #ifdef DEBUG_CC
static PLDHashOperator
AddExpectedGarbage(nsPtrHashKey<const void> *p, void *arg)
{
GCGraphBuilder *builder = static_cast<GCGraphBuilder*>(arg);
nsISupports *root =
static_cast<nsISupports*>(const_cast<void*>(p->GetKey()));
builder->NoteXPCOMRoot(root);
return PL_DHASH_NEXT;
}
struct SetSCCVisitor
{
SetSCCVisitor(PRUint32 aIndex) : mIndex(aIndex) {}
bool ShouldVisitNode(PtrInfo const *pi) { return pi->mSCCIndex == 0; }
void VisitNode(PtrInfo *pi) { pi->mSCCIndex = mIndex; }
private:
PRUint32 mIndex;
};
struct SetNonRootGreyVisitor
{
bool ShouldVisitNode(PtrInfo const *pi) { return pi->mColor == white; }
void VisitNode(PtrInfo *pi) { pi->mColor = grey; }
};
static void
PrintPathToExpectedGarbage(PtrInfo *pi)
{
printf(" An object expected to be garbage could be "
"reached from it by the path:\n");
for (PtrInfo *path = pi, *prev = nsnull; prev != path;
prev = path,
path = path->mShortestPathToExpectedGarbage) {
if (prev) {
nsCString *edgeName = prev
->mShortestPathToExpectedGarbageEdgeName;
printf(" via %s\n",
edgeName->IsEmpty() ? "<unknown edge>"
: edgeName->get());
}
printf(" %s %p\n", path->mName, path->mPointer);
}
}
void
nsCycleCollector::ExplainLiveExpectedGarbage()
{
if (mScanInProgress || mCollectionInProgress)
Fault("can't explain expected garbage during collection itself");
if (mParams.mDoNothing) {
printf("nsCycleCollector: not explaining expected garbage since\n"
" cycle collection disabled\n");
return;
}
mCollectionInProgress = true;
mScanInProgress = true;
{
GCGraphBuilder builder(mGraph, mRuntimes, nsnull);
// Instead of adding roots from the purple buffer, we add them
// from the list of nodes we were expected to collect.
// Put the expected garbage in *before* calling
// BeginCycleCollection so that we can separate the expected
// garbage from the NoteRoot calls in such a way that something
// that's in both is considered expected garbage.
mExpectedGarbage.EnumerateEntries(&AddExpectedGarbage, &builder);
PRUint32 expectedGarbageCount = builder.Count();
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
if (mRuntimes[i])
mRuntimes[i]->BeginCycleCollection(builder, true);
}
// But just for extra information, add entries from the purple
// buffer too, since it may give us extra information about
// traversal deficiencies.
mPurpleBuf.NoteAll(builder);
MarkRoots(builder);
ScanRoots();
mScanInProgress = false;
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
if (mRuntimes[i]) {
mRuntimes[i]->FinishTraverse();
}
}
bool describeExtraRefcounts = false;
bool findCycleRoots = false;
{
NodePool::Enumerator queue(mGraph.mNodes);
PRUint32 i = 0;
while (!queue.IsDone()) {
PtrInfo *pi = queue.GetNext();
if (pi->mColor == white) {
findCycleRoots = true;
}
if (pi->mInternalRefs != pi->mRefCount &&
(i < expectedGarbageCount || i >= mGraph.mRootCount)) {
// This check isn't particularly useful anymore
// given that we need to enter this part for i >=
// mGraph.mRootCount and there are plenty of
// NoteRoot roots.
describeExtraRefcounts = true;
}
++i;
}
}
if ((describeExtraRefcounts || findCycleRoots) &&
CreateReversedEdges()) {
// Note that the external references may have been external
// to a different node in the cycle collection that just
// happened, if that different node was purple and then
// black.
// Use mSCCIndex temporarily to track whether we've reached
// nodes in the breadth-first search.
const PRUint32 INDEX_UNREACHED = 0;
const PRUint32 INDEX_REACHED = 1;
NodePool::Enumerator etor_clear(mGraph.mNodes);
while (!etor_clear.IsDone()) {
PtrInfo *pi = etor_clear.GetNext();
pi->mSCCIndex = INDEX_UNREACHED;
}
nsDeque queue; // for breadth-first search
NodePool::Enumerator etor_roots(mGraph.mNodes);
for (PRUint32 i = 0; i < mGraph.mRootCount; ++i) {
PtrInfo *root_pi = etor_roots.GetNext();
if (i < expectedGarbageCount) {
root_pi->mSCCIndex = INDEX_REACHED;
root_pi->mShortestPathToExpectedGarbage = root_pi;
queue.Push(root_pi);
}
}
while (queue.GetSize() > 0) {
PtrInfo *pi = (PtrInfo*)queue.PopFront();
for (ReversedEdge *e = pi->mReversedEdges; e; e = e->mNext) {
if (e->mTarget->mSCCIndex == INDEX_UNREACHED) {
e->mTarget->mSCCIndex = INDEX_REACHED;
PtrInfo *target = e->mTarget;
if (!target->mShortestPathToExpectedGarbage) {
target->mShortestPathToExpectedGarbage = pi;
target->mShortestPathToExpectedGarbageEdgeName =
e->mEdgeName;
}
queue.Push(target);
}
}
if (pi->mRefCount == PR_UINT32_MAX ||
(pi->mInternalRefs != pi->mRefCount && pi->mRefCount > 0)) {
if (pi->mRefCount == PR_UINT32_MAX) {
printf("nsCycleCollector: %s %p was not collected due "
"to \n"
" external references\n",
pi->mName, pi->mPointer);
}
else {
printf("nsCycleCollector: %s %p was not collected due "
"to %d\n"
" external references (%d total - %d known)\n",
pi->mName, pi->mPointer,
pi->mRefCount - pi->mInternalRefs,
pi->mRefCount, pi->mInternalRefs);
}
PrintPathToExpectedGarbage(pi);
if (pi->mRefCount == PR_UINT32_MAX) {
printf(" The known references to it were from:\n");
}
else {
printf(" The %d known references to it were from:\n",
pi->mInternalRefs);
}
for (ReversedEdge *e = pi->mReversedEdges;
e; e = e->mNext) {
printf(" %s %p",
e->mTarget->mName, e->mTarget->mPointer);
if (!e->mEdgeName->IsEmpty()) {
printf(" via %s", e->mEdgeName->get());
}
printf("\n");
}
mRuntimes[pi->mLangID]->PrintAllReferencesTo(pi->mPointer);
}
}
if (findCycleRoots) {
// NOTE: This code changes the white nodes that are not
// roots to gray.
// Put the nodes in post-order traversal order from a
// depth-first search.
nsDeque DFSPostOrder;
{
// Use mSCCIndex temporarily to track the DFS numbering:
const PRUint32 INDEX_UNREACHED = 0;
const PRUint32 INDEX_TRAVERSING = 1;
const PRUint32 INDEX_NUMBERED = 2;
NodePool::Enumerator etor_clear(mGraph.mNodes);
while (!etor_clear.IsDone()) {
PtrInfo *pi = etor_clear.GetNext();
pi->mSCCIndex = INDEX_UNREACHED;
}
nsDeque stack;
NodePool::Enumerator etor_roots(mGraph.mNodes);
for (PRUint32 i = 0; i < mGraph.mRootCount; ++i) {
PtrInfo *root_pi = etor_roots.GetNext();
stack.Push(root_pi);
}
while (stack.GetSize() > 0) {
PtrInfo *pi = (PtrInfo*)stack.Peek();
if (pi->mSCCIndex == INDEX_UNREACHED) {
pi->mSCCIndex = INDEX_TRAVERSING;
for (EdgePool::Iterator child = pi->FirstChild(),
child_end = pi->LastChild();
child != child_end; ++child) {
stack.Push(*child);
}
} else {
stack.Pop();
// Somebody else might have numbered it already
// (since this is depth-first, not breadth-first).
// This happens if a node is pushed on the stack
// a second time while it is on the stack in
// UNREACHED state.
if (pi->mSCCIndex == INDEX_TRAVERSING) {
pi->mSCCIndex = INDEX_NUMBERED;
DFSPostOrder.Push(pi);
}
}
}
}
// Put the nodes into strongly-connected components.
{
NodePool::Enumerator etor_clear(mGraph.mNodes);
while (!etor_clear.IsDone()) {
PtrInfo *pi = etor_clear.GetNext();
pi->mSCCIndex = 0;
}
PRUint32 currentSCC = 1;
while (DFSPostOrder.GetSize() > 0) {
GraphWalker<SetSCCVisitor>(SetSCCVisitor(currentSCC)).Walk((PtrInfo*)DFSPostOrder.PopFront());
++currentSCC;
}
}
// Mark any white nodes reachable from other components as
// grey.
{
NodePool::Enumerator queue(mGraph.mNodes);
while (!queue.IsDone()) {
PtrInfo *pi = queue.GetNext();
if (pi->mColor != white)
continue;
for (EdgePool::Iterator child = pi->FirstChild(),
child_end = pi->LastChild();
child != child_end; ++child) {
if ((*child)->mSCCIndex != pi->mSCCIndex) {
GraphWalker<SetNonRootGreyVisitor>(SetNonRootGreyVisitor()).Walk(*child);
}
}
}
}
{
NodePool::Enumerator queue(mGraph.mNodes);
while (!queue.IsDone()) {
PtrInfo *pi = queue.GetNext();
if (pi->mColor == white) {
if (pi->mLangID ==
nsIProgrammingLanguage::CPLUSPLUS &&
mPurpleBuf.Exists(pi->mPointer)) {
printf(
"nsCycleCollector: %s %p in component %d\n"
" which was reference counted during the root/unlink/unroot phase of the\n"
" last collection was not collected due to failure to unlink (see other\n"
" warnings) or deficiency in traverse that causes cycles referenced only\n"
" from other cycles to require multiple rounds of cycle collection in which\n"
" this object was likely the reachable object\n",
pi->mName, pi->mPointer, pi->mSCCIndex);
} else {
printf(
"nsCycleCollector: %s %p in component %d\n"
" was not collected due to missing call to suspect, failure to unlink (see\n"
" other warnings), or deficiency in traverse that causes cycles referenced\n"
" only from other cycles to require multiple rounds of cycle collection\n",
pi->mName, pi->mPointer, pi->mSCCIndex);
}
if (pi->mShortestPathToExpectedGarbage)
PrintPathToExpectedGarbage(pi);
}
}
}
}
DestroyReversedEdges();
}
}
ClearGraph();
mCollectionInProgress = false;
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
if (mRuntimes[i])
mRuntimes[i]->FinishCycleCollection();
}
}
bool
nsCycleCollector::CreateReversedEdges()
{
// Count the edges in the graph.
PRUint32 edgeCount = 0;
NodePool::Enumerator countQueue(mGraph.mNodes);
while (!countQueue.IsDone()) {
PtrInfo *pi = countQueue.GetNext();
for (EdgePool::Iterator e = pi->FirstChild(), e_end = pi->LastChild();
e != e_end; ++e, ++edgeCount) {
}
}
// Allocate a pool to hold all of the edges.
mGraph.mReversedEdges = new ReversedEdge[edgeCount];
if (mGraph.mReversedEdges == nsnull) {
NS_NOTREACHED("allocation failure creating reversed edges");
return false;
}
// Fill in the reversed edges by scanning all forward edges.
ReversedEdge *current = mGraph.mReversedEdges;
NodePool::Enumerator buildQueue(mGraph.mNodes);
while (!buildQueue.IsDone()) {
PtrInfo *pi = buildQueue.GetNext();
PRInt32 i = 0;
for (EdgePool::Iterator e = pi->FirstChild(), e_end = pi->LastChild();
e != e_end; ++e) {
current->mTarget = pi;
current->mEdgeName = &pi->mEdgeNames[i];
current->mNext = (*e)->mReversedEdges;
(*e)->mReversedEdges = current;
++current;
++i;
}
}
NS_ASSERTION(current - mGraph.mReversedEdges == ptrdiff_t(edgeCount),
"misallocation");
return true;
}
void
nsCycleCollector::DestroyReversedEdges()
{
NodePool::Enumerator queue(mGraph.mNodes);
while (!queue.IsDone()) {
PtrInfo *pi = queue.GetNext();
pi->mReversedEdges = nsnull;
}
delete mGraph.mReversedEdges;
mGraph.mReversedEdges = nsnull;
}
void void
nsCycleCollector::ShouldBeFreed(nsISupports *n) nsCycleCollector::ShouldBeFreed(nsISupports *n)
{ {

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

@ -38,8 +38,6 @@
#ifndef nsCycleCollector_h__ #ifndef nsCycleCollector_h__
#define nsCycleCollector_h__ #define nsCycleCollector_h__
// NOTE: If you use header files to define DEBUG_CC, you must do so here
// *and* in nsCycleCollectionParticipant.h
//#define DEBUG_CC //#define DEBUG_CC
class nsISupports; class nsISupports;
@ -52,14 +50,10 @@ class nsCycleCollectionTraversalCallback;
struct nsCycleCollectionLanguageRuntime struct nsCycleCollectionLanguageRuntime
{ {
virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb, virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb) = 0;
bool explainLiveExpectedGarbage) = 0;
virtual nsresult FinishTraverse() = 0; virtual nsresult FinishTraverse() = 0;
virtual nsresult FinishCycleCollection() = 0; virtual nsresult FinishCycleCollection() = 0;
virtual nsCycleCollectionParticipant *ToParticipant(void *p) = 0; virtual nsCycleCollectionParticipant *ToParticipant(void *p) = 0;
#ifdef DEBUG_CC
virtual void PrintAllReferencesTo(void *p) = 0;
#endif
}; };
// Contains various stats about the cycle collection. // Contains various stats about the cycle collection.

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

@ -40,10 +40,6 @@
#include "nsISupports.h" #include "nsISupports.h"
// NOTE: If you use header files to define DEBUG_CC, you must do so here
// *and* in nsCycleCollector.h
//#define DEBUG_CC
#define NS_CYCLECOLLECTIONPARTICIPANT_IID \ #define NS_CYCLECOLLECTIONPARTICIPANT_IID \
{ \ { \
0x9674489b, \ 0x9674489b, \