Bug 497808. Enable dumping of cycle-collector graphs in any debug build. r=peterv,a=blocking

This commit is contained in:
Robert O'Callahan 2010-08-12 12:03:23 +12:00
Родитель 24dd7c5e33
Коммит e7bf0a8c07
7 изменённых файлов: 88 добавлений и 110 удалений

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

@ -521,7 +521,7 @@ nsDOMWindowUtils::Focus(nsIDOMElement* aElement)
}
NS_IMETHODIMP
nsDOMWindowUtils::GarbageCollect()
nsDOMWindowUtils::GarbageCollect(PRBool aDrawGraphs)
{
// Always permit this in debug builds.
#ifndef DEBUG
@ -530,7 +530,7 @@ nsDOMWindowUtils::GarbageCollect()
}
#endif
nsJSContext::CC();
nsJSContext::CC(aDrawGraphs);
return NS_OK;
}

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

@ -307,7 +307,7 @@ NS_IMETHODIMP
nsCCMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic,
const PRUnichar* aData)
{
nsJSContext::CC();
nsJSContext::CC(PR_FALSE);
return NS_OK;
}
@ -973,7 +973,7 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
mem->IsLowMemory(&lowMemory);
if (lowMemory) {
// try to clean up:
nsJSContext::CC();
nsJSContext::CC(PR_FALSE);
// never prevent system scripts from running
if (!::JS_IsSystemObject(cx, ::JS_GetGlobalObject(cx))) {
@ -3616,7 +3616,7 @@ nsJSContext::ScriptExecuted()
//static
void
nsJSContext::CC()
nsJSContext::CC(PRBool aDrawGraph)
{
NS_TIME_FUNCTION_MIN(1.0);
@ -3631,7 +3631,7 @@ nsJSContext::CC()
// nsCycleCollector_collect() no longer forces a JS garbage collection,
// so we have to do it ourselves here.
nsContentUtils::XPConnect()->GarbageCollect();
sCollectedObjectsCounts = nsCycleCollector_collect();
sCollectedObjectsCounts = nsCycleCollector_collect(aDrawGraph);
sCCSuspectedCount = nsCycleCollector_suspectedCount();
sSavedGCCount = JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER);
#ifdef DEBUG_smaug
@ -3719,7 +3719,7 @@ nsJSContext::IntervalCC()
{
if ((PR_Now() - sPreviousCCTime) >=
PRTime(NS_MIN_CC_INTERVAL * PR_USEC_PER_MSEC)) {
nsJSContext::CC();
nsJSContext::CC(PR_FALSE);
return PR_TRUE;
}
#ifdef DEBUG_smaug

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

@ -188,7 +188,7 @@ public:
// CC does always call cycle collector and it also updates the counters
// that MaybeCC uses.
static void CC();
static void CC(PRBool aDrawGraph);
// MaybeCC calls cycle collector if certain conditions are fulfilled.
// The conditions are:

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

@ -53,7 +53,7 @@ interface nsIDOMEvent;
interface nsITransferable;
interface nsIQueryContentEventResult;
[scriptable, uuid(1e042706-0343-4cba-a549-6a83eefb1835)]
[scriptable, uuid(bf4df3a0-4567-47ec-be6d-9ec60eaed63c)]
interface nsIDOMWindowUtils : nsISupports {
/**
@ -258,8 +258,12 @@ interface nsIDOMWindowUtils : nsISupports {
*
* Will throw a DOM security error if called without UniversalXPConnect
* privileges in non-debug builds. Available to all callers in debug builds.
*
* @param aDrawGraph when true, a cycle collection graph will be
* dumped to a file named cycle-graph-*.dot in the current working
* directory
*/
void garbageCollect();
void garbageCollect([optional] in boolean aDrawGraph);
/**
* Force processing of any queued paints

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

@ -177,10 +177,10 @@
struct nsCycleCollectorParams
{
PRBool mDoNothing;
PRBool mDrawGraphs;
#ifdef DEBUG_CC
PRBool mReportStats;
PRBool mHookMalloc;
PRBool mDrawGraphs;
PRBool mFaultIsFatal;
PRBool mLogPointers;
@ -198,7 +198,8 @@ struct nsCycleCollectorParams
mShutdownCollections(DEFAULT_SHUTDOWN_COLLECTIONS)
#else
mDoNothing (PR_FALSE)
mDoNothing (PR_FALSE),
mDrawGraphs (PR_FALSE)
#endif
{
#ifdef DEBUG_CC
@ -445,10 +446,11 @@ struct PtrInfo
PRUint32 mRefCount;
EdgePool::Iterator mFirstChild; // first
EdgePool::Iterator mLastChild; // one after last
char *mName;
nsTArray<nsCString> mEdgeNames;
#ifdef DEBUG_CC
size_t mBytes;
char *mName;
PRUint32 mLangID;
// For finding roots in ExplainLiveExpectedGarbage (when there are
@ -460,8 +462,6 @@ struct PtrInfo
ReversedEdge* mReversedEdges; // linked list
PtrInfo* mShortestPathToExpectedGarbage;
nsCString* mShortestPathToExpectedGarbageEdgeName;
nsTArray<nsCString> mEdgeNames;
#endif
PtrInfo(void *aPointer, nsCycleCollectionParticipant *aParticipant
@ -473,10 +473,10 @@ struct PtrInfo
mInternalRefs(0),
mRefCount(0),
mFirstChild(),
mLastChild()
mLastChild(),
mName(nsnull)
#ifdef DEBUG_CC
, mBytes(0),
mName(nsnull),
mLangID(aLangID),
mSCCIndex(0),
mReversedEdges(nsnull),
@ -486,12 +486,10 @@ struct PtrInfo
{
}
#ifdef DEBUG_CC
void Destroy() {
PL_strfree(mName);
mEdgeNames.~nsTArray<nsCString>();
}
#endif
// Allow NodePool::Block's constructor to compile.
PtrInfo() {
@ -531,16 +529,15 @@ public:
NS_ASSERTION(!mBlocks, "Didn't call Clear()?");
}
void Clear()
void Clear(PRBool aClearNames)
{
#ifdef DEBUG_CC
{
if (aClearNames) {
Enumerator queue(*this);
while (!queue.IsDone()) {
queue.GetNext()->Destroy();
}
}
#endif
Block *b = mBlocks;
while (b) {
Block *n = b->mNext;
@ -651,10 +648,8 @@ struct GCGraph
// Add/Remove/Has rather than PutEntry/RemoveEntry/GetEntry.
typedef nsTHashtable<nsVoidPtrHashKey> PointerSet;
#ifdef DEBUG_CC
static void
WriteGraph(FILE *stream, GCGraph &graph, const void *redPtr);
#endif
static inline void
ToParticipant(nsISupports *s, nsXPCOMCycleCollectionParticipant **cp);
@ -995,7 +990,7 @@ struct nsCycleCollector
nsPurpleBufferEntry* Suspect2(nsISupports *n);
PRBool Forget2(nsPurpleBufferEntry *e);
PRUint32 Collect(PRUint32 aTryCollections = 1);
PRUint32 Collect(PRUint32 aTryCollections, PRBool aDrawGraph);
PRBool BeginCollection();
PRBool FinishCollection();
PRUint32 SuspectedCount();
@ -1003,17 +998,18 @@ struct nsCycleCollector
void ClearGraph()
{
mGraph.mNodes.Clear();
mGraph.mNodes.Clear(mParams.mDrawGraphs);
mGraph.mEdges.Clear();
mGraph.mRootCount = 0;
}
void MaybeDrawGraphs();
#ifdef DEBUG_CC
nsCycleCollectorStats mStats;
FILE *mPtrLog;
void MaybeDrawGraphs();
void Allocated(void *n, size_t sz);
void Freed(void *n);
@ -1301,13 +1297,12 @@ private:
PLDHashTable mPtrToNodeMap;
PtrInfo *mCurrPi;
nsCycleCollectionLanguageRuntime **mRuntimes; // weak, from nsCycleCollector
#ifdef DEBUG_CC
nsCString mNextEdgeName;
#endif
public:
GCGraphBuilder(GCGraph &aGraph,
nsCycleCollectionLanguageRuntime **aRuntimes);
nsCycleCollectionLanguageRuntime **aRuntimes,
PRBool aDrawGraph);
~GCGraphBuilder();
bool Initialized();
@ -1342,7 +1337,8 @@ private:
};
GCGraphBuilder::GCGraphBuilder(GCGraph &aGraph,
nsCycleCollectionLanguageRuntime **aRuntimes)
nsCycleCollectionLanguageRuntime **aRuntimes,
PRBool aDrawGraph)
: mNodeBuilder(aGraph.mNodes),
mEdgeBuilder(aGraph.mEdges),
mRuntimes(aRuntimes)
@ -1350,11 +1346,13 @@ GCGraphBuilder::GCGraphBuilder(GCGraph &aGraph,
if (!PL_DHashTableInit(&mPtrToNodeMap, &PtrNodeOps, nsnull,
sizeof(PtrToNodeEntry), 32768))
mPtrToNodeMap.ops = nsnull;
#ifdef DEBUG_CC
// Do we need to set these all the time?
mFlags |= nsCycleCollectionTraversalCallback::WANT_DEBUG_INFO |
nsCycleCollectionTraversalCallback::WANT_ALL_TRACES;
#ifndef DEBUG_CC
if (aDrawGraph)
#endif
{
mFlags |= nsCycleCollectionTraversalCallback::WANT_DEBUG_INFO |
nsCycleCollectionTraversalCallback::WANT_ALL_TRACES;
}
}
GCGraphBuilder::~GCGraphBuilder()
@ -1458,8 +1456,10 @@ GCGraphBuilder::DescribeNode(CCNodeType type, nsrefcnt refCount,
{
#ifdef DEBUG_CC
mCurrPi->mBytes = objSz;
mCurrPi->mName = PL_strdup(objName);
#endif
if (WantDebugInfo()) {
mCurrPi->mName = PL_strdup(objName);
}
if (type == RefCounted) {
if (refCount == 0 || refCount == PR_UINT32_MAX)
@ -1478,10 +1478,11 @@ GCGraphBuilder::DescribeNode(CCNodeType type, nsrefcnt refCount,
NS_IMETHODIMP_(void)
GCGraphBuilder::NoteXPCOMChild(nsISupports *child)
{
#ifdef DEBUG_CC
nsCString edgeName(mNextEdgeName);
mNextEdgeName.Truncate();
#endif
nsCString edgeName;
if (WantDebugInfo()) {
edgeName.Assign(mNextEdgeName);
mNextEdgeName.Truncate();
}
if (!child || !(child = canonicalize(child)))
return;
@ -1497,9 +1498,9 @@ GCGraphBuilder::NoteXPCOMChild(nsISupports *child)
if (!childPi)
return;
mEdgeBuilder.Add(childPi);
#ifdef DEBUG_CC
mCurrPi->mEdgeNames.AppendElement(edgeName);
#endif
if (WantDebugInfo()) {
mCurrPi->mEdgeNames.AppendElement(edgeName);
}
++childPi->mInternalRefs;
}
}
@ -1508,10 +1509,11 @@ NS_IMETHODIMP_(void)
GCGraphBuilder::NoteNativeChild(void *child,
nsCycleCollectionParticipant *participant)
{
#ifdef DEBUG_CC
nsCString edgeName(mNextEdgeName);
mNextEdgeName.Truncate();
#endif
nsCString edgeName;
if (WantDebugInfo()) {
edgeName.Assign(mNextEdgeName);
mNextEdgeName.Truncate();
}
if (!child)
return;
@ -1521,19 +1523,17 @@ GCGraphBuilder::NoteNativeChild(void *child,
if (!childPi)
return;
mEdgeBuilder.Add(childPi);
#ifdef DEBUG_CC
mCurrPi->mEdgeNames.AppendElement(edgeName);
#endif
if (WantDebugInfo()) {
mCurrPi->mEdgeNames.AppendElement(edgeName);
}
++childPi->mInternalRefs;
}
NS_IMETHODIMP_(void)
GCGraphBuilder::NoteScriptChild(PRUint32 langID, void *child)
{
#ifdef DEBUG_CC
nsCString edgeName(mNextEdgeName);
mNextEdgeName.Truncate();
#endif
if (!child)
return;
@ -1556,18 +1556,18 @@ GCGraphBuilder::NoteScriptChild(PRUint32 langID, void *child)
if (!childPi)
return;
mEdgeBuilder.Add(childPi);
#ifdef DEBUG_CC
mCurrPi->mEdgeNames.AppendElement(edgeName);
#endif
if (WantDebugInfo()) {
mCurrPi->mEdgeNames.AppendElement(edgeName);
}
++childPi->mInternalRefs;
}
NS_IMETHODIMP_(void)
GCGraphBuilder::NoteNextEdgeName(const char* name)
{
#ifdef DEBUG_CC
mNextEdgeName = name;
#endif
if (WantDebugInfo()) {
mNextEdgeName = name;
}
}
static PRBool
@ -2079,8 +2079,6 @@ nsCycleCollector::ForgetRuntime(PRUint32 langID)
mRuntimes[langID] = nsnull;
}
#ifdef DEBUG_CC
static void
WriteGraph(FILE *stream, GCGraph &graph, const void *redPtr)
{
@ -2108,63 +2106,36 @@ WriteGraph(FILE *stream, GCGraph &graph, const void *redPtr)
"\", fillcolor=%s, fontcolor=%s]\n",
(redPtr && redPtr == p ? "red" : (pi->mColor == black ? "black" : "white")),
(pi->mColor == black ? "white" : "black"));
PRInt32 i = 0;
for (EdgePool::Iterator child = pi->mFirstChild,
child_end = pi->mLastChild;
child != child_end; ++child) {
fprintf(stream, "n%p -> n%p\n", p, (*child)->mPointer);
fprintf(stream, "n%p -> n%p [label=\"%s\"]\n", p,
(*child)->mPointer, pi->mEdgeNames[i].get());
++i;
}
}
fprintf(stream, "\n}\n");
}
static PRUint32 gCounter;
void
nsCycleCollector::MaybeDrawGraphs()
{
if (mParams.mDrawGraphs) {
// We draw graphs only if there were any white nodes.
PRBool anyWhites = PR_FALSE;
NodePool::Enumerator fwetor(mGraph.mNodes);
while (!fwetor.IsDone())
{
PtrInfo *pinfo = fwetor.GetNext();
if (pinfo->mColor == white) {
anyWhites = PR_TRUE;
break;
}
}
if (anyWhites) {
// We can't just use _popen here because graphviz-for-windows
// doesn't set up its stdin stream properly, sigh.
FILE *stream;
#ifdef WIN32
stream = fopen("c:\\cycle-graph.dot", "w+");
#else
stream = popen("dotty -", "w");
#endif
WriteGraph(stream, mGraph, nsnull);
#ifdef WIN32
fclose(stream);
// Even dotty doesn't work terribly well on windows, since
// they execute lefty asynchronously. So we'll just run
// lefty ourselves.
_spawnlp(_P_WAIT,
"lefty",
"lefty",
"-e",
"\"load('dotty.lefty');"
"dotty.simple('c:\\cycle-graph.dot');\"",
NULL);
unlink("c:\\cycle-graph.dot");
#else
pclose(stream);
#endif
}
FILE *stream;
char name[255];
sprintf(name, "cycle-graph-%d.dot", ++gCounter);
stream = fopen(name, "w");
WriteGraph(stream, mGraph, nsnull);
fclose(stream);
}
}
#ifdef DEBUG_CC
class Suppressor :
public nsCycleCollectionTraversalCallback
{
@ -2416,7 +2387,7 @@ nsCycleCollector::Freed(void *n)
#endif
PRUint32
nsCycleCollector::Collect(PRUint32 aTryCollections)
nsCycleCollector::Collect(PRUint32 aTryCollections, PRBool aDrawGraph)
{
#if defined(DEBUG_CC) && !defined(__MINGW32__)
if (!mParams.mDoNothing && mParams.mHookMalloc)
@ -2434,6 +2405,9 @@ nsCycleCollector::Collect(PRUint32 aTryCollections)
PRTime start = PR_Now();
#endif
PRBool drawGraphs = mParams.mDrawGraphs;
mParams.mDrawGraphs |= aDrawGraph;
mCollectionInProgress = PR_TRUE;
nsCOMPtr<nsIObserverService> obs =
@ -2503,6 +2477,8 @@ nsCycleCollector::Collect(PRUint32 aTryCollections)
mCollectionInProgress = PR_FALSE;
mParams.mDrawGraphs = drawGraphs;
#ifdef XP_OS2
// Now that the cycle collector has freed some memory, we can try to
// force the C library to give back as much memory to the system as
@ -2526,7 +2502,7 @@ nsCycleCollector::BeginCollection()
if (mParams.mDoNothing)
return PR_FALSE;
GCGraphBuilder builder(mGraph, mRuntimes);
GCGraphBuilder builder(mGraph, mRuntimes, mParams.mDrawGraphs);
if (!builder.Initialized())
return PR_FALSE;
@ -2604,9 +2580,7 @@ nsCycleCollector::BeginCollection()
(PR_Now() - now) / PR_USEC_PER_MSEC);
#endif
#ifdef DEBUG_CC
MaybeDrawGraphs();
#endif
mScanInProgress = PR_FALSE;
@ -2692,7 +2666,7 @@ nsCycleCollector::Shutdown()
mRuntimes[i]->CommenceShutdown();
}
Collect(SHUTDOWN_COLLECTIONS(mParams));
Collect(SHUTDOWN_COLLECTIONS(mParams), PR_FALSE);
#ifdef DEBUG_CC
GCGraphBuilder builder(mGraph, mRuntimes);
@ -3163,9 +3137,9 @@ NS_CycleCollectorForget2(nsPurpleBufferEntry *e)
PRUint32
nsCycleCollector_collect()
nsCycleCollector_collect(PRBool aDrawGraph)
{
return sCollector ? sCollector->Collect() : 0;
return sCollector ? sCollector->Collect(1, aDrawGraph) : 0;
}
PRUint32

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

@ -63,7 +63,7 @@ struct nsCycleCollectionLanguageRuntime
nsresult nsCycleCollector_startup();
// Returns the number of collected nodes.
NS_COM PRUint32 nsCycleCollector_collect();
NS_COM PRUint32 nsCycleCollector_collect(PRBool aDrawGraph);
NS_COM PRUint32 nsCycleCollector_suspectedCount();
void nsCycleCollector_shutdown();

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

@ -276,7 +276,7 @@ void XXXNeverCalled()
}
nsXPCOMCycleCollectionParticipant();
nsCycleCollector_collect();
nsCycleCollector_collect(PR_FALSE);
#ifdef XP_WIN
sXPCOMHasLoadedNewDLLs = !sXPCOMHasLoadedNewDLLs;
NS_SetHasLoadedNewDLLs();