mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 678615 - remove ExplainLiveExpectedGarbage. r=smaug sr=peterv
This commit is contained in:
parent
02f1f01c3e
commit
a5af270e47
@ -475,3 +475,13 @@ xpc_DumpJSObject(JSObject* obj)
|
||||
|
||||
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);
|
||||
|
||||
nsCycleCollector_registerRuntime(nsIProgrammingLanguage::JAVASCRIPT, this);
|
||||
#ifdef DEBUG_CC
|
||||
mJSRoots.ops = nsnull;
|
||||
#endif
|
||||
|
||||
char* reportableEnv = PR_GetEnv("MOZ_REPORT_ALL_JS_EXCEPTIONS");
|
||||
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
|
||||
// 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
|
||||
// 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)
|
||||
// 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
|
||||
// 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.
|
||||
|
||||
MOZ_ASSERT(reason < js::gcreason::NUM_REASONS);
|
||||
@ -431,37 +428,6 @@ nsXPConnect::GarbageCollect(PRUint32 reason, PRUint32 kind)
|
||||
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
|
||||
{
|
||||
NoteWeakMapChildrenTracer(nsCycleCollectionTraversalCallback &cb)
|
||||
@ -538,8 +504,7 @@ TraceWeakMapping(js::WeakMapTracer *trc, JSObject *m,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
|
||||
bool explainLiveExpectedGarbage)
|
||||
nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
// It is important not to call GetSafeJSContext while on the
|
||||
// cycle-collector thread since this context will be destroyed
|
||||
@ -565,31 +530,6 @@ nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
|
||||
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);
|
||||
|
||||
NoteWeakMapsTracer trc(GetRuntime()->GetJSRuntime(), TraceWeakMapping, cb);
|
||||
@ -641,13 +581,6 @@ nsXPConnect::FinishTraverse()
|
||||
nsresult
|
||||
nsXPConnect::FinishCycleCollection()
|
||||
{
|
||||
#ifdef DEBUG_CC
|
||||
if (mJSRoots.ops) {
|
||||
PL_DHashTableFinish(&mJSRoots);
|
||||
mJSRoots.ops = nsnull;
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -665,19 +598,6 @@ nsXPConnect::Root(void *p)
|
||||
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
|
||||
nsXPConnect::Unlink(void *p)
|
||||
{
|
||||
@ -909,30 +829,7 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
|
||||
}
|
||||
}
|
||||
|
||||
bool isMarked;
|
||||
|
||||
#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);
|
||||
}
|
||||
bool isMarked = markJSObject || !xpc_IsGrayGCThing(p);
|
||||
|
||||
if (cb.WantDebugInfo()) {
|
||||
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
|
||||
// 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
|
||||
// to get better graphs and explanations.
|
||||
// if cb.WantAllTraces() is false, otherwise we do want to know about all JS
|
||||
// objects to get better graphs and explanations.
|
||||
if (!cb.WantAllTraces() && isMarked)
|
||||
return NS_OK;
|
||||
|
||||
|
@ -537,16 +537,12 @@ public:
|
||||
virtual void NotifyEnterCycleCollectionThread();
|
||||
virtual void NotifyLeaveCycleCollectionThread();
|
||||
virtual void NotifyEnterMainThread();
|
||||
virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
|
||||
bool explainExpectedLiveGarbage);
|
||||
virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb);
|
||||
virtual nsresult FinishTraverse();
|
||||
virtual nsresult FinishCycleCollection();
|
||||
virtual nsCycleCollectionParticipant *ToParticipant(void *p);
|
||||
virtual bool NeedCollect();
|
||||
virtual void Collect(PRUint32 reason, PRUint32 kind);
|
||||
#ifdef DEBUG_CC
|
||||
virtual void PrintAllReferencesTo(void *p);
|
||||
#endif
|
||||
|
||||
XPCCallContext *GetCycleCollectionContext()
|
||||
{
|
||||
@ -598,9 +594,6 @@ private:
|
||||
// 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.
|
||||
PRUint16 mEventDepth;
|
||||
#ifdef DEBUG_CC
|
||||
PLDHashTable mJSRoots;
|
||||
#endif
|
||||
nsAutoPtr<XPCCallContext> mCycleCollectionContext;
|
||||
|
||||
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 };
|
||||
|
||||
// This structure should be kept as small as possible; we may expect
|
||||
@ -532,18 +521,6 @@ public:
|
||||
size_t mBytes;
|
||||
char *mName;
|
||||
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
|
||||
|
||||
PtrInfo(void *aPointer, nsCycleCollectionParticipant *aParticipant
|
||||
@ -558,11 +535,7 @@ public:
|
||||
#ifdef DEBUG_CC
|
||||
, mBytes(0),
|
||||
mName(nsnull),
|
||||
mLangID(aLangID),
|
||||
mSCCIndex(0),
|
||||
mReversedEdges(nsnull),
|
||||
mShortestPathToExpectedGarbage(nsnull),
|
||||
mShortestPathToExpectedGarbageEdgeName(nsnull)
|
||||
mLangID(aLangID)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
@ -570,7 +543,6 @@ public:
|
||||
#ifdef DEBUG_CC
|
||||
void Destroy() {
|
||||
PL_strfree(mName);
|
||||
mEdgeNames.~nsTArray<nsCString>();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -766,9 +738,6 @@ struct GCGraph
|
||||
EdgePool mEdges;
|
||||
nsTArray<WeakMapping> mWeakMaps;
|
||||
PRUint32 mRootCount;
|
||||
#ifdef DEBUG_CC
|
||||
ReversedEdge *mReversedEdges;
|
||||
#endif
|
||||
|
||||
GCGraph() : mRootCount(0) {
|
||||
}
|
||||
@ -911,8 +880,6 @@ public:
|
||||
void RemoveSkippable(bool removeChildlessNodes);
|
||||
|
||||
#ifdef DEBUG_CC
|
||||
void NoteAll(GCGraphBuilder &builder);
|
||||
|
||||
bool Exists(void *p) const
|
||||
{
|
||||
return mNormalObjects.GetEntry(p) || mCompatObjects.GetEntry(p);
|
||||
@ -1086,8 +1053,7 @@ nsPurpleBuffer::SelectPointers(GCGraphBuilder &aBuilder)
|
||||
struct nsCycleCollectionXPCOMRuntime :
|
||||
public nsCycleCollectionLanguageRuntime
|
||||
{
|
||||
nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
|
||||
bool explainLiveExpectedGarbage)
|
||||
nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1103,10 +1069,6 @@ struct nsCycleCollectionXPCOMRuntime :
|
||||
}
|
||||
|
||||
inline nsCycleCollectionParticipant *ToParticipant(void *p);
|
||||
|
||||
#ifdef DEBUG_CC
|
||||
virtual void PrintAllReferencesTo(void *p) {}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct nsCycleCollector
|
||||
@ -1131,11 +1093,11 @@ struct nsCycleCollector
|
||||
PRUint32 mVisitedRefCounted;
|
||||
PRUint32 mVisitedGCed;
|
||||
|
||||
nsPurpleBuffer mPurpleBuf;
|
||||
|
||||
CC_BeforeUnlinkCallback mBeforeUnlinkCB;
|
||||
CC_ForgetSkippableCallback mForgetSkippableCB;
|
||||
|
||||
nsPurpleBuffer mPurpleBuf;
|
||||
|
||||
void RegisterRuntime(PRUint32 langID,
|
||||
nsCycleCollectionLanguageRuntime *rt);
|
||||
void ForgetRuntime(PRUint32 langID);
|
||||
@ -1187,20 +1149,16 @@ struct nsCycleCollector
|
||||
|
||||
#ifdef DEBUG_CC
|
||||
nsCycleCollectorStats mStats;
|
||||
|
||||
FILE *mPtrLog;
|
||||
PointerSet mExpectedGarbage;
|
||||
|
||||
void Allocated(void *n, size_t sz);
|
||||
void Freed(void *n);
|
||||
|
||||
void LogPurpleRemoval(void* aObject);
|
||||
|
||||
void ExplainLiveExpectedGarbage();
|
||||
bool CreateReversedEdges();
|
||||
void DestroyReversedEdges();
|
||||
void ShouldBeFreed(nsISupports *n);
|
||||
void WasFreed(nsISupports *n);
|
||||
PointerSet mExpectedGarbage;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -1780,9 +1738,6 @@ private:
|
||||
if (!childPi)
|
||||
return;
|
||||
mEdgeBuilder.Add(childPi);
|
||||
#ifdef DEBUG_CC
|
||||
mCurrPi->mEdgeNames.AppendElement(edgeName);
|
||||
#endif
|
||||
if (mListener) {
|
||||
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
|
||||
nsCycleCollector::SelectPurple(GCGraphBuilder &builder)
|
||||
{
|
||||
@ -2485,11 +2413,11 @@ nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
|
||||
// free. This stuff is disabled unless you set an environment variable.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static bool hookedMalloc = false;
|
||||
|
||||
#if defined(__GLIBC__) && !defined(__UCLIBC__)
|
||||
#include <malloc.h>
|
||||
|
||||
static bool hookedMalloc = false;
|
||||
|
||||
static void* (*old_memalign_hook)(size_t, size_t, const void *);
|
||||
static void* (*old_realloc_hook)(void *, size_t, const void *);
|
||||
static void* (*old_malloc_hook)(size_t, const void *);
|
||||
@ -2615,6 +2543,8 @@ InitMemHook(void)
|
||||
#elif defined(WIN32)
|
||||
#ifndef __MINGW32__
|
||||
|
||||
static bool hookedMalloc = false;
|
||||
|
||||
static int
|
||||
AllocHook(int allocType, void *userData, size_t size, int
|
||||
blockType, long requestNumber, const unsigned char *filename, int
|
||||
@ -2639,6 +2569,8 @@ static void InitMemHook(void)
|
||||
|
||||
#include <malloc/malloc.h>
|
||||
|
||||
static bool hookedMalloc = false;
|
||||
|
||||
static void (*old_free)(struct _malloc_zone_t *zone, void *ptr);
|
||||
|
||||
static void
|
||||
@ -2684,14 +2616,14 @@ nsCycleCollector::nsCycleCollector() :
|
||||
mWhiteNodeCount(0),
|
||||
mVisitedRefCounted(0),
|
||||
mVisitedGCed(0),
|
||||
mBeforeUnlinkCB(nsnull),
|
||||
mForgetSkippableCB(nsnull),
|
||||
#ifdef DEBUG_CC
|
||||
mPurpleBuf(mParams, mStats),
|
||||
mPtrLog(nsnull),
|
||||
mPtrLog(nsnull)
|
||||
#else
|
||||
mPurpleBuf(mParams),
|
||||
mPurpleBuf(mParams)
|
||||
#endif
|
||||
mBeforeUnlinkCB(nsnull),
|
||||
mForgetSkippableCB(nsnull)
|
||||
{
|
||||
#ifdef DEBUG_CC
|
||||
mExpectedGarbage.Init();
|
||||
@ -3125,10 +3057,6 @@ nsCycleCollector::CleanupAfterCollection()
|
||||
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_REF_COUNTED, mVisitedRefCounted);
|
||||
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_GCED, mVisitedGCed);
|
||||
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_COLLECTED, mWhiteNodeCount);
|
||||
|
||||
#ifdef DEBUG_CC
|
||||
ExplainLiveExpectedGarbage();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -3172,7 +3100,7 @@ nsCycleCollector::BeginCollection(nsICycleCollectorListener *aListener)
|
||||
|
||||
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
|
||||
if (mRuntimes[i])
|
||||
mRuntimes[i]->BeginCycleCollection(builder, false);
|
||||
mRuntimes[i]->BeginCycleCollection(builder);
|
||||
}
|
||||
|
||||
timeLog.Checkpoint("mRuntimes[*]->BeginCycleCollection()");
|
||||
@ -3348,390 +3276,6 @@ nsCycleCollector::Shutdown()
|
||||
}
|
||||
|
||||
#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
|
||||
nsCycleCollector::ShouldBeFreed(nsISupports *n)
|
||||
{
|
||||
|
@ -38,8 +38,6 @@
|
||||
#ifndef 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
|
||||
|
||||
class nsISupports;
|
||||
@ -52,14 +50,10 @@ class nsCycleCollectionTraversalCallback;
|
||||
|
||||
struct nsCycleCollectionLanguageRuntime
|
||||
{
|
||||
virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
|
||||
bool explainLiveExpectedGarbage) = 0;
|
||||
virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb) = 0;
|
||||
virtual nsresult FinishTraverse() = 0;
|
||||
virtual nsresult FinishCycleCollection() = 0;
|
||||
virtual nsCycleCollectionParticipant *ToParticipant(void *p) = 0;
|
||||
#ifdef DEBUG_CC
|
||||
virtual void PrintAllReferencesTo(void *p) = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Contains various stats about the cycle collection.
|
||||
|
@ -40,10 +40,6 @@
|
||||
|
||||
#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 \
|
||||
{ \
|
||||
0x9674489b, \
|
||||
|
Loading…
Reference in New Issue
Block a user