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
parent c7b15f876d
commit 6ac771f7ba
6 changed files with 101 additions and 100 deletions

View File

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

View File

@ -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

View File

@ -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:

View File

@ -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(in boolean aDrawGraph);
/**
* Force processing of any queued paints

View File

@ -156,6 +156,10 @@
//#define COLLECT_TIME_DEBUG
#if defined(DEBUG_CC) || defined(DEBUG)
#define DEBUG_CC_GRAPHS
#endif
#ifdef DEBUG_CC
#define IF_DEBUG_CC_PARAM(_p) , _p
#define IF_DEBUG_CC_ONLY_PARAM(_p) _p
@ -177,10 +181,10 @@
struct nsCycleCollectorParams
{
PRBool mDoNothing;
PRBool mDrawGraphs;
#ifdef DEBUG_CC
PRBool mReportStats;
PRBool mHookMalloc;
PRBool mDrawGraphs;
PRBool mFaultIsFatal;
PRBool mLogPointers;
@ -198,7 +202,8 @@ struct nsCycleCollectorParams
mShutdownCollections(DEFAULT_SHUTDOWN_COLLECTIONS)
#else
mDoNothing (PR_FALSE)
mDoNothing (PR_FALSE),
mDrawGraphs (PR_FALSE)
#endif
{
#ifdef DEBUG_CC
@ -446,9 +451,12 @@ struct PtrInfo
EdgePool::Iterator mFirstChild; // first
EdgePool::Iterator mLastChild; // one after last
#ifdef DEBUG_CC_GRAPHS
char *mName;
#endif
#ifdef DEBUG_CC
size_t mBytes;
char *mName;
PRUint32 mLangID;
// For finding roots in ExplainLiveExpectedGarbage (when there are
@ -460,9 +468,9 @@ struct PtrInfo
ReversedEdge* mReversedEdges; // linked list
PtrInfo* mShortestPathToExpectedGarbage;
nsCString* mShortestPathToExpectedGarbageEdgeName;
#endif
nsTArray<nsCString> mEdgeNames;
#endif
PtrInfo(void *aPointer, nsCycleCollectionParticipant *aParticipant
IF_DEBUG_CC_PARAM(PRUint32 aLangID)
@ -474,9 +482,11 @@ struct PtrInfo
mRefCount(0),
mFirstChild(),
mLastChild()
#ifdef DEBUG_CC_GRAPHS
, mName(nsnull)
#endif
#ifdef DEBUG_CC
, mBytes(0),
mName(nsnull),
mLangID(aLangID),
mSCCIndex(0),
mReversedEdges(nsnull),
@ -486,12 +496,12 @@ struct PtrInfo
{
}
#ifdef DEBUG_CC
void Destroy() {
#ifdef DEBUG_CC_GRAPHS
PL_strfree(mName);
#endif
mEdgeNames.~nsTArray<nsCString>();
}
#endif
// Allow NodePool::Block's constructor to compile.
PtrInfo() {
@ -651,7 +661,7 @@ struct GCGraph
// Add/Remove/Has rather than PutEntry/RemoveEntry/GetEntry.
typedef nsTHashtable<nsVoidPtrHashKey> PointerSet;
#ifdef DEBUG_CC
#ifdef DEBUG_CC_GRAPHS
static void
WriteGraph(FILE *stream, GCGraph &graph, const void *redPtr);
#endif
@ -995,7 +1005,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();
@ -1008,12 +1018,15 @@ struct nsCycleCollector
mGraph.mRootCount = 0;
}
#ifdef DEBUG_CC_GRAPHS
void MaybeDrawGraphs();
#endif
#ifdef DEBUG_CC
nsCycleCollectorStats mStats;
FILE *mPtrLog;
void MaybeDrawGraphs();
void Allocated(void *n, size_t sz);
void Freed(void *n);
@ -1301,13 +1314,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 +1354,8 @@ private:
};
GCGraphBuilder::GCGraphBuilder(GCGraph &aGraph,
nsCycleCollectionLanguageRuntime **aRuntimes)
nsCycleCollectionLanguageRuntime **aRuntimes,
PRBool aDrawGraph)
: mNodeBuilder(aGraph.mNodes),
mEdgeBuilder(aGraph.mEdges),
mRuntimes(aRuntimes)
@ -1350,11 +1363,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,7 +1473,11 @@ GCGraphBuilder::DescribeNode(CCNodeType type, nsrefcnt refCount,
{
#ifdef DEBUG_CC
mCurrPi->mBytes = objSz;
mCurrPi->mName = PL_strdup(objName);
#endif
#ifdef DEBUG_CC_GRAPHS
if (WantDebugInfo()) {
mCurrPi->mName = PL_strdup(objName);
}
#endif
if (type == RefCounted) {
@ -1478,10 +1497,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 +1517,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 +1528,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 +1542,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 +1575,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 +2098,7 @@ nsCycleCollector::ForgetRuntime(PRUint32 langID)
mRuntimes[langID] = nsnull;
}
#ifdef DEBUG_CC
#ifdef DEBUG_CC_GRAPHS
static void
WriteGraph(FILE *stream, GCGraph &graph, const void *redPtr)
{
@ -2108,62 +2126,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);
}
}
#endif
#ifdef DEBUG_CC
class Suppressor :
public nsCycleCollectionTraversalCallback
@ -2416,7 +2408,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 +2426,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 +2498,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 +2523,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,7 +2601,7 @@ nsCycleCollector::BeginCollection()
(PR_Now() - now) / PR_USEC_PER_MSEC);
#endif
#ifdef DEBUG_CC
#ifdef DEBUG_CC_GRAPHS
MaybeDrawGraphs();
#endif
@ -2692,7 +2689,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 +3160,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

View File

@ -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();