Bug 649532 - make cycle collector logging output complete CC graph. r=peterv
authorAndrew McCreight <amccreight@mozilla.com>
Wed, 22 Jun 2011 10:41:17 -0700
changeset 77657 220b6e404f1bfb626b7821ba6354c4cd2b502fae
parent 77656 e5072a22fe9510ee98e46811374f23e6e37cd854
child 77658 e43df9a4b36e65c09b30d853bc645207bff7ed35
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs649532
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 649532 - make cycle collector logging output complete CC graph. r=peterv
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsICycleCollectorListener.idl
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -1069,17 +1069,19 @@ struct nsCycleCollector
     void RegisterRuntime(PRUint32 langID, 
                          nsCycleCollectionLanguageRuntime *rt);
     nsCycleCollectionLanguageRuntime * GetRuntime(PRUint32 langID);
     void ForgetRuntime(PRUint32 langID);
 
     void SelectPurple(GCGraphBuilder &builder);
     void MarkRoots(GCGraphBuilder &builder);
     void ScanRoots();
-    PRBool CollectWhite(); // returns whether anything was collected
+
+    // returns whether anything was collected
+    PRBool CollectWhite(nsICycleCollectorListener *aListener);
 
     nsCycleCollector();
     ~nsCycleCollector();
 
     // The first pair of Suspect and Forget functions are only used by
     // old XPCOM binary components.
     PRBool Suspect(nsISupports *n);
     PRBool Forget(nsISupports *n);
@@ -1091,17 +1093,17 @@ struct nsCycleCollector
 
     // Prepare for and cleanup after one or more collection(s).
     PRBool PrepareForCollection(nsTArray<PtrInfo*> *aWhiteNodes);
     void GCIfNeeded(PRBool aForceGC);
     void CleanupAfterCollection();
 
     // Start and finish an individual collection.
     PRBool BeginCollection(nsICycleCollectorListener *aListener);
-    PRBool FinishCollection();
+    PRBool FinishCollection(nsICycleCollectorListener *aListener);
 
     PRUint32 SuspectedCount();
     void Shutdown();
 
     void ClearGraph()
     {
         mGraph.mNodes.Clear();
         mGraph.mEdges.Clear();
@@ -1366,50 +1368,53 @@ public:
     NS_IMETHOD Begin()
     {
         char name[255];
         sprintf(name, "cc-edges-%d.log", ++gLogCounter);
         mStream = fopen(name, "w");
 
         return mStream ? NS_OK : NS_ERROR_FAILURE;
     }
-    NS_IMETHOD NoteObject(PRUint64 aAddress, const char *aObjectDescription)
+    NS_IMETHOD NoteRefCountedObject(PRUint64 aAddress, PRUint32 refCount,
+                                    const char *aObjectDescription)
     {
-        fprintf(mStream, "%p %s\n", (void*)aAddress, aObjectDescription);
+        fprintf(mStream, "%p [rc=%u] %s\n", (void*)aAddress, refCount,
+                aObjectDescription);
 
         return NS_OK;
     }
-    NS_IMETHOD NoteEdge(PRUint64 aFromAddress, PRUint64 aToAddress,
-                        const char *aEdgeName)
+    NS_IMETHOD NoteGCedObject(PRUint64 aAddress, PRBool aMarked,
+                              const char *aObjectDescription)
+    {
+        fprintf(mStream, "%p [gc%s] %s\n", (void*)aAddress,
+                aMarked ? ".marked" : "", aObjectDescription);
+
+        return NS_OK;
+    }
+    NS_IMETHOD NoteEdge(PRUint64 aToAddress, const char *aEdgeName)
     {
         fprintf(mStream, "> %p %s\n", (void*)aToAddress, aEdgeName);
 
         return NS_OK;
     }
-    NS_IMETHOD BeginDescriptions()
+    NS_IMETHOD BeginResults()
     {
         fputs("==========\n", mStream);
 
         return NS_OK;
     }
-    NS_IMETHOD DescribeRefcountedObject(PRUint64 aAddress, PRUint32 aKnownEdges,
-                                        PRUint32 aTotalEdges)
+    NS_IMETHOD DescribeRoot(PRUint64 aAddress, PRUint32 aKnownEdges)
     {
-        PRBool root = aKnownEdges != aTotalEdges;
-        fprintf(mStream, "%p", (void*)aAddress);
-        if (root) {
-            fprintf(mStream, " [root] [%u/%u]", aKnownEdges, aTotalEdges);
-        }
-        fputc('\n', mStream);
+        fprintf(mStream, "%p [known=%u]\n", (void*)aAddress, aKnownEdges);
 
         return NS_OK;
     }
-    NS_IMETHOD DescribeGCedObject(PRUint64 aAddress, PRBool aMarked)
+    NS_IMETHOD DescribeGarbage(PRUint64 aAddress)
     {
-        fprintf(mStream, "%p%s\n", (void*)aAddress, aMarked ? " [root]" : "");
+        fprintf(mStream, "%p [garbage]\n", (void*)aAddress);
 
         return NS_OK;
     }
     NS_IMETHOD End()
     {
         fclose(mStream);
         mStream = nsnull;
 
@@ -1505,28 +1510,20 @@ public:
     // nsCycleCollectionTraversalCallback methods.
     NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports *root);
 
 private:
     void DescribeNode(PRUint32 refCount,
                       size_t objSz,
                       const char *objName)
     {
+        mCurrPi->mRefCount = refCount;
 #ifdef DEBUG_CC
         mCurrPi->mBytes = objSz;
         mCurrPi->mName = PL_strdup(objName);
-#endif
-
-        if (mListener) {
-            mListener->NoteObject((PRUint64)mCurrPi->mPointer, objName);
-        }
-
-        mCurrPi->mRefCount = refCount;
-
-#ifdef DEBUG_CC
         sCollector->mStats.mVisitedNode++;
 #endif
     }
 
     NS_IMETHOD_(void) DescribeRefCountedNode(nsrefcnt refCount, size_t objSz,
                                              const char *objName);
     NS_IMETHOD_(void) DescribeGCedNode(PRBool isMarked, size_t objSz,
                                        const char *objName);
@@ -1664,25 +1661,37 @@ NS_IMETHODIMP_(void)
 GCGraphBuilder::DescribeRefCountedNode(nsrefcnt refCount, size_t objSz,
                                        const char *objName)
 {
     if (refCount == 0)
         Fault("zero refcount", mCurrPi);
     if (refCount == PR_UINT32_MAX)
         Fault("overflowing refcount", mCurrPi);
     sCollector->mVisitedRefCounted++;
+
+    if (mListener) {
+        mListener->NoteRefCountedObject((PRUint64)mCurrPi->mPointer, refCount,
+                                        objName);
+    }
+
     DescribeNode(refCount, objSz, objName);
 }
 
 NS_IMETHODIMP_(void)
 GCGraphBuilder::DescribeGCedNode(PRBool isMarked, size_t objSz,
                                  const char *objName)
 {
     PRUint32 refCount = isMarked ? PR_UINT32_MAX : 0;
     sCollector->mVisitedGCed++;
+
+    if (mListener) {
+        mListener->NoteGCedObject((PRUint64)mCurrPi->mPointer, isMarked,
+                                  objName);
+    }
+
     DescribeNode(refCount, objSz, objName);
 }
 
 NS_IMETHODIMP_(void)
 GCGraphBuilder::NoteXPCOMChild(nsISupports *child) 
 {
     nsCString edgeName;
     if (WantDebugInfo()) {
@@ -1703,18 +1712,17 @@ GCGraphBuilder::NoteXPCOMChild(nsISuppor
         PtrInfo *childPi = AddNode(child, cp, nsIProgrammingLanguage::CPLUSPLUS);
         if (!childPi)
             return;
         mEdgeBuilder.Add(childPi);
 #ifdef DEBUG_CC
         mCurrPi->mEdgeNames.AppendElement(edgeName);
 #endif
         if (mListener) {
-            mListener->NoteEdge((PRUint64)mCurrPi->mPointer, (PRUint64)child,
-                                edgeName.get());
+            mListener->NoteEdge((PRUint64)child, edgeName.get());
         }
         ++childPi->mInternalRefs;
     }
 }
 
 NS_IMETHODIMP_(void)
 GCGraphBuilder::NoteNativeChild(void *child,
                                 nsCycleCollectionParticipant *participant)
@@ -1732,18 +1740,17 @@ GCGraphBuilder::NoteNativeChild(void *ch
     PtrInfo *childPi = AddNode(child, participant, nsIProgrammingLanguage::CPLUSPLUS);
     if (!childPi)
         return;
     mEdgeBuilder.Add(childPi);
 #ifdef DEBUG_CC
     mCurrPi->mEdgeNames.AppendElement(edgeName);
 #endif
     if (mListener) {
-        mListener->NoteEdge((PRUint64)mCurrPi->mPointer, (PRUint64)child,
-                            edgeName.get());
+        mListener->NoteEdge((PRUint64)child, edgeName.get());
     }
     ++childPi->mInternalRefs;
 }
 
 NS_IMETHODIMP_(void)
 GCGraphBuilder::NoteScriptChild(PRUint32 langID, void *child) 
 {
     nsCString edgeName;
@@ -1778,18 +1785,17 @@ GCGraphBuilder::NoteScriptChild(PRUint32
     PtrInfo *childPi = AddNode(child, cp, langID);
     if (!childPi)
         return;
     mEdgeBuilder.Add(childPi);
 #ifdef DEBUG_CC
     mCurrPi->mEdgeNames.AppendElement(edgeName);
 #endif
     if (mListener) {
-        mListener->NoteEdge((PRUint64)mCurrPi->mPointer, (PRUint64)child,
-                            edgeName.get());
+        mListener->NoteEdge((PRUint64)child, edgeName.get());
     }
     ++childPi->mInternalRefs;
 }
 
 NS_IMETHODIMP_(void)
 GCGraphBuilder::NoteNextEdgeName(const char* name)
 {
     if (WantDebugInfo()) {
@@ -1957,17 +1963,17 @@ nsCycleCollector::ScanRoots()
 }
 
 
 ////////////////////////////////////////////////////////////////////////
 // Bacon & Rajan's |CollectWhite| routine, somewhat modified.
 ////////////////////////////////////////////////////////////////////////
 
 PRBool
-nsCycleCollector::CollectWhite()
+nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
 {
     // Explanation of "somewhat modified": we have no way to collect the
     // set of whites "all at once", we have to ask each of them to drop
     // their outgoing links and assume this will cause the garbage cycle
     // to *mostly* self-destruct (except for the reference we continue
     // to hold). 
     // 
     // To do this "safely" we must make sure that the white nodes we're
@@ -1999,16 +2005,25 @@ nsCycleCollector::CollectWhite()
     }
 
 #if defined(DEBUG_CC) && !defined(__MINGW32__) && defined(WIN32)
     struct _CrtMemState ms1, ms2;
     _CrtMemCheckpoint(&ms1);
 #endif
 
     PRUint32 i, count = mWhiteNodes->Length();
+
+    if (aListener) {
+        for (i = 0; i < count; ++i) {
+            PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
+            aListener->DescribeGarbage((PRUint64)pinfo->mPointer);
+        }
+        aListener->End();
+    }
+
     for (i = 0; i < count; ++i) {
         PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
         rv = pinfo->mParticipant->Unlink(pinfo->mPointer);
         if (NS_FAILED(rv)) {
             Fault("Failed unlink call while unlinking", pinfo);
 #ifdef DEBUG_CC
             mStats.mFailedUnlink++;
 #endif
@@ -2670,17 +2685,18 @@ nsCycleCollector::Collect(PRUint32 aTryC
 
     if (!PrepareForCollection(&whiteNodes))
         return 0;
 
     PRUint32 totalCollections = 0;
     while (aTryCollections > totalCollections) {
         // Synchronous cycle collection. Always force a JS GC beforehand.
         GCIfNeeded(PR_TRUE);
-        if (!(BeginCollection(aListener) && FinishCollection()))
+        if (!(BeginCollection(aListener) &&
+              FinishCollection(aListener)))
             break;
 
         ++totalCollections;
     }
 
     CleanupAfterCollection();
 
     return mCollectedObjects;
@@ -2772,34 +2788,28 @@ nsCycleCollector::BeginCollection(nsICyc
 #ifdef COLLECT_TIME_DEBUG
         printf("cc: ScanRoots() took %lldms\n",
                (PR_Now() - now) / PR_USEC_PER_MSEC);
 #endif
 
         mScanInProgress = PR_FALSE;
 
         if (aListener) {
-            aListener->BeginDescriptions();
+            aListener->BeginResults();
 
             NodePool::Enumerator etor(mGraph.mNodes);
             while (!etor.IsDone()) {
                 PtrInfo *pi = etor.GetNext();
-                if (pi->mColor == black) {
-                    PRUint64 p = (PRUint64)pi->mPointer;
-                    if (pi->mRefCount > 0 && pi->mRefCount < PR_UINT32_MAX) {
-                        aListener->DescribeRefcountedObject(p, pi->mInternalRefs,
-                                                           pi->mRefCount);
-                    }
-                    else {
-                        aListener->DescribeGCedObject(p, pi->mRefCount != 0);
-                    }
+                if (pi->mColor == black &&
+                    pi->mRefCount > 0 && pi->mRefCount < PR_UINT32_MAX &&
+                    pi->mInternalRefs != pi->mRefCount) {
+                    aListener->DescribeRoot((PRUint64)pi->mPointer,
+                                            pi->mInternalRefs);
                 }
             }
-
-            aListener->End();
         }
 
 #ifdef DEBUG_CC
         if (mFollowupCollection && purpleStart != purpleEnd) {
             PRUint32 i = 0;
             NodePool::Enumerator queue(mGraph.mNodes);
             while (i++ < purpleStart) {
                 queue.GetNext();
@@ -2824,23 +2834,23 @@ nsCycleCollector::BeginCollection(nsICyc
     else {
         mScanInProgress = PR_FALSE;
     }
 
     return PR_TRUE;
 }
 
 PRBool
-nsCycleCollector::FinishCollection()
+nsCycleCollector::FinishCollection(nsICycleCollectorListener *aListener)
 {
 #ifdef COLLECT_TIME_DEBUG
     PRTime now = PR_Now();
 #endif
 
-    PRBool collected = CollectWhite();
+    PRBool collected = CollectWhite(aListener);
 
 #ifdef COLLECT_TIME_DEBUG
     printf("cc: CollectWhite() took %lldms\n",
            (PR_Now() - now) / PR_USEC_PER_MSEC);
 #endif
 
 #ifdef DEBUG_CC
     mStats.mCollection++;
@@ -3518,17 +3528,17 @@ public:
         mListener = aListener;
 
         mRequest.Notify();
         mReply.Wait();
 
         mListener = nsnull;
 
         if (mCollected) {
-            mCollected = mCollector->FinishCollection();
+            mCollected = mCollector->FinishCollection(aListener);
 
             mCollector->CleanupAfterCollection();
 
             return mCollected ? mCollector->mCollectedObjects : 0;
         }
 
         return 0;
     }
--- a/xpcom/base/nsICycleCollectorListener.idl
+++ b/xpcom/base/nsICycleCollectorListener.idl
@@ -31,34 +31,39 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
-/**
- * Interface to pass to the cycle collector to get information about the CC
- * graph while it's being built. The order of calls will be call to begin();
- * then for every node in the graph a call to noteObject() and calls to
+/** Interface to pass to the cycle collector to get information about
+ * the CC graph while it's being built. The order of calls will be a
+ * call to begin(); then for every node in the graph a call to either
+ * noteRefCountedObject() or noteGCedObject(), followed by calls to
  * noteEdge() for every edge starting at that node; then a call to
- * beginDescriptions(); then for every black node in the CC graph a call to
- * either describeRefcountedObject() or to describeGCedObject(); and then a
- * call to end(). If begin() returns an error none of the other functions will
- * be called.
+ * beginResults(); then a mixture of describeRoot() for ref counted
+ * nodes the CC has identified as roots and describeGarbage() for
+ * nodes the CC has identified as garbage.  Ref counted nodes that are
+ * not identified as either roots or garbage are neither, and have a
+ * known edges count equal to their ref count.  Finally, there will be
+ * a call to end().  If begin() returns an error none of the other
+ * functions will be called.
  */
-[scriptable, uuid(194b749a-4ceb-4dd1-928d-d30b5f14c23e)]
+[scriptable, uuid(3f3901bb-6a1c-4998-b32e-6f10a51db470)]
 interface nsICycleCollectorListener : nsISupports
 {
     void begin();
-    void noteObject(in unsigned long long aAddress,
-                    in string aObjectDescription);
-    void noteEdge(in unsigned long long aFromAddress,
-                  in unsigned long long aToAddress,
+    void noteRefCountedObject (in unsigned long long aAddress,
+			       in unsigned long aRefCount,
+			       in string aObjectDescription);
+    void noteGCedObject (in unsigned long long aAddress,
+			 in boolean aMarked,
+			 in string aObjectDescription);
+    void noteEdge(in unsigned long long aToAddress,
                   in string aEdgeName);
-    void beginDescriptions();
-    void describeRefcountedObject(in unsigned long long aAddress,
-                                  in unsigned long aKnownEdges,
-                                  in unsigned long aTotalEdges);
-    void describeGCedObject(in unsigned long long aAddress, in boolean aMarked);
+    void beginResults();
+    void describeRoot(in unsigned long long aAddress,
+		      in unsigned long aKnownEdges);
+    void describeGarbage(in unsigned long long aAddress);
     void end();
 };