b=566447; add presshell memory reporter; r=bz
authorVladimir Vukicevic <vladimir@pobox.com>
Mon, 31 May 2010 19:19:35 -0700
changeset 42982 d1fd855f7ebe4dd07f505200b4e548ff6ac4214d
parent 42981 03a73eb7f3ec75d9a1234acba77656686ad25c8f
child 42983 633756e2a580e67791449a827dbdf488025c8ec9
push idunknown
push userunknown
push dateunknown
reviewersbz
bugs566447
milestone1.9.3a5pre
b=566447; add presshell memory reporter; r=bz
layout/base/nsBidi.h
layout/base/nsBidiPresUtils.cpp
layout/base/nsBidiPresUtils.h
layout/base/nsIPresShell.h
layout/base/nsPresArena.cpp
layout/base/nsPresArena.h
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/nsPresShell.cpp
layout/build/nsLayoutStatics.cpp
memory/mozalloc/mozalloc.cpp
memory/mozalloc/mozalloc.h
--- a/layout/base/nsBidi.h
+++ b/layout/base/nsBidi.h
@@ -845,16 +845,18 @@ public:
    * @param aOptions A bit set of options for the reordering that control
    *                how the reordered text is written.
    *
    * @param aDestSize will receive the number of characters that were written to <code>aDest</code>.
    */
   nsresult WriteReverse(const PRUnichar *aSrc, PRInt32 aSrcLength, PRUnichar *aDest, PRUint16 aOptions, PRInt32 *aDestSize);
 
 protected:
+  friend class nsBidiPresUtils;
+
   /** length of the current text */
   PRInt32 mLength;
 
   /** memory sizes in bytes */
   PRSize mDirPropsSize, mLevelsSize, mRunsSize;
 
   /** allocated memory */
   DirProp* mDirPropsMemory;
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -1659,9 +1659,24 @@ nsresult nsBidiPresUtils::ProcessTextFor
                                                          PRInt32                aPosResolveCount,
                                                          nscoord*               aWidth)
 {
   nsIRenderingContextBidiProcessor processor(&aRenderingContext, nsPoint(aX, aY));
 
   return ProcessText(aText, aLength, aBaseDirection, aPresContext, processor,
                      aMode, aPosResolve, aPosResolveCount, aWidth);
 }
+
+PRUint32 nsBidiPresUtils::EstimateMemoryUsed()
+{
+  PRUint32 size = 0;
+
+  size += sizeof(nsBidiPresUtils);
+  size += mBuffer.Length() * sizeof(PRUnichar);
+  size += moz_malloc_usable_size(mBidiEngine->mDirPropsMemory);
+  size += moz_malloc_usable_size(mBidiEngine->mLevelsMemory);
+  size += moz_malloc_usable_size(mBidiEngine->mRunsMemory);
+
+  return size;
+}
+
+
 #endif // IBMBIDI
--- a/layout/base/nsBidiPresUtils.h
+++ b/layout/base/nsBidiPresUtils.h
@@ -308,16 +308,22 @@ public:
                        nsBidiDirection        aBaseDirection,
                        nsPresContext*         aPresContext,
                        BidiProcessor&         aprocessor,
                        Mode                   aMode,
                        nsBidiPositionResolve* aPosResolve,
                        PRInt32                aPosResolveCount,
                        nscoord*               aWidth);
 
+  /**
+   * Guess at how much memory is being used by this nsBidiPresUtils instance,
+   * including memory used by nsBidi.
+   */
+  PRUint32 EstimateMemoryUsed();
+
 private:
   nsresult ProcessTextForRenderingContext(const PRUnichar*       aText,
                                           PRInt32                aLength,
                                           nsBidiDirection        aBaseDirection,
                                           nsPresContext*         aPresContext,
                                           nsIRenderingContext&   aRenderingContext,
                                           Mode                   aMode,
                                           nscoord                aX, // DRAW only
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -49,16 +49,18 @@
  * 05/03/2000   IBM Corp.       Observer related defines for reflow
  */
 
 /* a presentation of a document, part 2 */
 
 #ifndef nsIPresShell_h___
 #define nsIPresShell_h___
 
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
 #include "nsISupports.h"
 #include "nsQueryFrame.h"
 #include "nsCoord.h"
 #include "nsRect.h"
 #include "nsColor.h"
 #include "nsEvent.h"
 #include "nsCompatibility.h"
 #include "nsFrameManagerBase.h"
@@ -992,16 +994,22 @@ public:
 
   /**
    * Keep track of how many times this presshell has been rendered to
    * a window.
    */
   PRUint64 GetPaintCount() { return mPaintCount; }
   void IncrementPaintCount() { ++mPaintCount; }
 
+  /**
+   * Initialize and shut down static variables.
+   */
+  static void InitializeStatics();
+  static void ReleaseStatics();
+
 protected:
   // IMPORTANT: The ownership implicit in the following member variables
   // has been explicitly checked.  If you add any members to this class,
   // please make the ownership explicit (pinkerton, scc).
 
   // these are the same Document and PresContext owned by the DocViewer.
   // we must share ownership.
   nsIDocument*              mDocument;      // [STRONG]
@@ -1044,16 +1052,20 @@ protected:
 
   PRPackedBool              mObservesMutationsForPrint;
 
   // A list of weak frames. This is a pointer to the last item in the list.
   nsWeakFrame*              mWeakFrames;
 
   // Most recent canvas background color.
   nscolor                   mCanvasBackgroundColor;
+
+  // Live pres shells, for memory and other tracking
+  typedef nsPtrHashKey<nsIPresShell> PresShellPtrKey;
+  static nsTHashtable<PresShellPtrKey> *sLiveShells;
 };
 
 /**
  * Create a new empty presentation shell. Upon success, call Init
  * before attempting to use the shell.
  */
 nsresult
 NS_NewPresShell(nsIPresShell** aInstancePtrResult);
--- a/layout/base/nsPresArena.cpp
+++ b/layout/base/nsPresArena.cpp
@@ -352,16 +352,30 @@ struct nsPresArena::State {
     for (; p < limit; p += sizeof(PRUword)) {
       *reinterpret_cast<PRUword*>(p) = ARENA_POISON;
     }
 
     list->mEntries.AppendElement(aPtr);
   }
 };
 
+PRUint32
+nsPresArena::Size()
+{
+  PLArena *arena = &mState->mPool.first;
+  PRUint32 result = 0;
+
+  while (arena) {
+    result += arena->limit - arena->base;
+    arena = arena->next;
+  }
+
+  return result;
+}
+
 #else
 // Stub implementation that forwards everything to malloc and does not
 // poison.
 
 struct nsPresArena::State
 {
   void* Allocate(PRUint32 /* unused */, size_t aSize)
   {
@@ -369,16 +383,22 @@ struct nsPresArena::State
   }
 
   void Free(PRUint32 /* unused */, void* aPtr)
   {
     PR_Free(aPtr);
   }
 };
 
+PRUint32
+nsPresArena::Size()
+{
+  return 0;
+}
+
 #endif // DEBUG_TRACEMALLOC_PRESARENA
 
 // Public interface
 nsPresArena::nsPresArena()
   : mState(new nsPresArena::State())
 {}
 
 nsPresArena::~nsPresArena()
--- a/layout/base/nsPresArena.h
+++ b/layout/base/nsPresArena.h
@@ -69,14 +69,16 @@ public:
   NS_HIDDEN_(void*) AllocateBySize(size_t aSize);
   NS_HIDDEN_(void)  FreeBySize(size_t aSize, void* aPtr);
 
   // Pool allocation with recycler lists indexed by object-type code.
   // Every type code must always be used with the same object size.
   NS_HIDDEN_(void*) AllocateByCode(nsQueryFrame::FrameIID aCode, size_t aSize);
   NS_HIDDEN_(void)  FreeByCode(nsQueryFrame::FrameIID aCode, void* aPtr);
 
+  PRUint32 Size();
+
 private:
   struct State;
   State* mState;
 };
 
 #endif
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1411,16 +1411,26 @@ nsPresContext::SetBidi(PRUint32 aSource,
   }
 }
 
 PRUint32
 nsPresContext::GetBidi() const
 {
   return Document()->GetBidiOptions();
 }
+
+PRUint32
+nsPresContext::GetBidiMemoryUsed()
+{
+  if (!mBidiUtils)
+    return 0;
+
+  return mBidiUtils->EstimateMemoryUsed();
+}
+
 #endif //IBMBIDI
 
 PRBool
 nsPresContext::IsTopLevelWindowInactive()
 {
   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryReferent(mContainer));
   if (!treeItem)
     return PR_FALSE;
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -741,16 +741,20 @@ public:
                            PRBool aForceRestyle = PR_FALSE);
 
   /**
    * Get the Bidi options for the presentation context
    * Not inline so consumers of nsPresContext are not forced to
    * include nsIDocument.
    */  
   NS_HIDDEN_(PRUint32) GetBidi() const;
+
+  PRUint32 GetBidiMemoryUsed();
+#else
+  PRUint32 GetBidiMemoryUsed() { return 0; }
 #endif // IBMBIDI
 
   /**
    * Render only Selection
    */
   void SetIsRenderingOnlySelection(PRBool aResult)
   {
     NS_ASSERTION(!(aResult & ~1), "Value must be true or false");
@@ -951,16 +955,25 @@ public:
     NS_PRECONDITION(aContent, "Don't do that");
     if (GetPresShell() &&
         GetPresShell()->GetDocument() == aContent->GetCurrentDoc()) {
       return aContent->GetPrimaryFrame();
     }
     return nsnull;
   }
 
+  PRUint32 EstimateMemoryUsed() {
+    PRUint32 result = 0;
+
+    result += sizeof(nsPresContext);
+    result += GetBidiMemoryUsed();
+
+    return result;
+  }
+
 protected:
   friend class nsRunnableMethod<nsPresContext>;
   NS_HIDDEN_(void) ThemeChangedInternal();
   NS_HIDDEN_(void) SysColorChangedInternal();
 
   NS_HIDDEN_(void) SetImgAnimations(nsIContent *aParent, PRUint16 aMode);
 #ifdef MOZ_SMIL
   NS_HIDDEN_(void) SetSMILAnimations(nsIDocument *aDoc, PRUint16 aNewMode,
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -494,16 +494,26 @@ public:
 
   nsresult Init() { return mBlocks ? NS_OK : NS_ERROR_OUT_OF_MEMORY; }
 
   // Memory management functions
   void* Allocate(size_t aSize);
   void Push();
   void Pop();
 
+  PRUint32 Size() {
+    PRUint32 result = 0;
+    StackBlock *block = mBlocks;
+    while (block) {
+      result += sizeof(StackBlock);
+      block = block->mNext;
+    }
+    return result;
+  }
+
 private:
   // our current position in memory
   size_t mPos;
 
   // a list of memory block. Usually there is only one
   // but if we overrun our stack size we can get more memory.
   StackBlock* mBlocks;
 
@@ -1291,16 +1301,64 @@ private:
    * only visible if the contents of the view as a whole are translucent.
    */
   nscolor ComputeBackstopColor(nsIView* aDisplayRoot);
 
 #ifdef DEBUG
   // Ensure that every allocation from the PresArena is eventually freed.
   PRUint32 mPresArenaAllocCount;
 #endif
+
+public:
+
+  PRUint32 EstimateMemoryUsed() {
+    PRUint32 result = 0;
+
+    result += sizeof(PresShell);
+    result += mStackArena.Size();
+    result += mFrameArena.Size();
+
+    return result;
+  }
+
+  static PLDHashOperator LiveShellSizeEnumerator(PresShellPtrKey *aEntry,
+                                                 void *userArg)
+  {
+    PresShell *aShell = static_cast<PresShell*>(aEntry->GetKey());
+    PRUint32 *val = (PRUint32*)userArg;
+    *val += aShell->EstimateMemoryUsed();
+    *val += aShell->mPresContext->EstimateMemoryUsed();
+    return PL_DHASH_NEXT;
+  }
+
+  static PLDHashOperator LiveShellBidiSizeEnumerator(PresShellPtrKey *aEntry,
+                                                     void *userArg)
+  {
+    PresShell *aShell = static_cast<PresShell*>(aEntry->GetKey());
+    PRUint32 *val = (PRUint32*)userArg;
+    *val += aShell->mPresContext->GetBidiMemoryUsed();
+    return PL_DHASH_NEXT;
+  }
+
+  static PRUint32
+  EstimateShellsMemory(nsTHashtable<PresShellPtrKey>::Enumerator aEnumerator)
+  {
+    PRUint32 result = 0;
+    sLiveShells->EnumerateEntries(aEnumerator, &result);
+    return result;
+  }
+                  
+                                  
+  static PRInt64 SizeOfLayoutMemoryReporter(void *) {
+    return EstimateShellsMemory(LiveShellSizeEnumerator);
+  }
+
+  static PRInt64 SizeOfBidiMemoryReporter(void *) {
+    return EstimateShellsMemory(LiveShellBidiSizeEnumerator);
+  }
 };
 
 class nsAutoCauseReflowNotifier
 {
 public:
   nsAutoCauseReflowNotifier(PresShell* aShell)
     : mShell(aShell)
   {
@@ -1496,16 +1554,30 @@ NS_NewPresShell(nsIPresShell** aInstance
   *aInstancePtrResult = new PresShell();
   if (!*aInstancePtrResult)
     return NS_ERROR_OUT_OF_MEMORY;
 
   NS_ADDREF(*aInstancePtrResult);
   return NS_OK;
 }
 
+nsTHashtable<PresShell::PresShellPtrKey> *nsIPresShell::sLiveShells = 0;
+
+NS_MEMORY_REPORTER_IMPLEMENT(LayoutPresShell,
+                             "layout/all",
+                             "Memory in use by layout PresShell, PresContext, and other related areas.",
+                             PresShell::SizeOfLayoutMemoryReporter,
+                             nsnull);
+
+NS_MEMORY_REPORTER_IMPLEMENT(LayoutBidi,
+                             "layout/bidi",
+                             "Memory in use by layout Bidi processor.",
+                             PresShell::SizeOfBidiMemoryReporter,
+                             nsnull);
+
 PresShell::PresShell()
 {
   mSelection = nsnull;
 #ifdef MOZ_REFLOW_PERF
   mReflowCountMgr = new ReflowCountMgr();
   mReflowCountMgr->SetPresContext(mPresContext);
   mReflowCountMgr->SetPresShell(this);
 #endif
@@ -1514,26 +1586,37 @@ PresShell::PresShell()
     gLog = PR_NewLogModule("PresShell");
 #endif
   mSelectionFlags = nsISelectionDisplay::DISPLAY_TEXT | nsISelectionDisplay::DISPLAY_IMAGES;
   mIsThemeSupportDisabled = PR_FALSE;
 #ifdef DEBUG
   mPresArenaAllocCount = 0;
 #endif
 
+  static bool registeredReporter = false;
+  if (!registeredReporter) {
+    NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(LayoutPresShell));
+    NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(LayoutBidi));
+    registeredReporter = true;
+  }
+
   new (this) nsFrameManager();
+
+  sLiveShells->PutEntry(this);
 }
 
 NS_IMPL_ISUPPORTS8(PresShell, nsIPresShell, nsIDocumentObserver,
                    nsIViewObserver, nsISelectionController,
                    nsISelectionDisplay, nsIObserver, nsISupportsWeakReference,
                    nsIMutationObserver)
 
 PresShell::~PresShell()
 {
+  sLiveShells->RemoveEntry(this);
+
   if (!mHaveShutDown) {
     NS_NOTREACHED("Someone did not call nsIPresShell::destroy");
     Destroy();
   }
 
   NS_ASSERTION(mCurrentEventContentStack.Count() == 0,
                "Huh, event content left on the stack in pres shell dtor!");
   NS_ASSERTION(mFirstCallbackEventRequest == nsnull &&
@@ -8689,8 +8772,22 @@ void ColorToString(nscolor aColor, nsAut
               NS_GET_R(aColor), NS_GET_G(aColor), NS_GET_B(aColor));
   CopyASCIItoUTF16(buf, aString);
 }
 
 nsIFrame* nsIPresShell::GetAbsoluteContainingBlock(nsIFrame *aFrame)
 {
   return FrameConstructor()->GetAbsoluteContainingBlock(aFrame);
 }
+
+void nsIPresShell::InitializeStatics()
+{
+  NS_ASSERTION(sLiveShells == nsnull, "InitializeStatics called multiple times!");
+  sLiveShells = new nsTHashtable<PresShellPtrKey>();
+  sLiveShells->Init();
+}
+
+void nsIPresShell::ReleaseStatics()
+{
+  NS_ASSERTION(sLiveShells, "ReleaseStatics called without Initialize!");
+  delete sLiveShells;
+  sLiveShells = nsnull;
+}
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -279,17 +279,18 @@ nsLayoutStatics::Initialize()
 #endif
 
 #ifdef MOZ_SYDNEYAUDIO
   nsAudioStream::InitLibrary();
 #endif
 
   nsContentSink::InitializeStatics();
   nsHtml5Module::InitializeStatics();
-  
+  nsIPresShell::InitializeStatics();
+
   nsCrossSiteListenerProxy::Startup();
 
   rv = nsFrameList::Init();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize nsFrameList");
     return rv;
   }
 
@@ -375,16 +376,18 @@ nsLayoutStatics::Shutdown()
   nsHTMLMediaElement::ShutdownMediaTypes();
 #endif
 #ifdef MOZ_SYDNEYAUDIO
   nsAudioStream::ShutdownLibrary();
 #endif
 
   nsXMLHttpRequest::ShutdownACCache();
   
+  nsIPresShell::ReleaseStatics();
+
   nsHtml5Module::ReleaseStatics();
 
   nsRegion::ShutdownStatic();
 
   NS_ShutdownChainItemPool();
 
   nsFrameList::Shutdown();
 
--- a/memory/mozalloc/mozalloc.cpp
+++ b/memory/mozalloc/mozalloc.cpp
@@ -224,14 +224,30 @@ moz_xvalloc(size_t size)
 }
 void*
 moz_valloc(size_t size)
 {
     return valloc(size);
 }
 #endif // if defined(HAVE_VALLOC)
 
+size_t
+moz_malloc_usable_size(void *ptr)
+{
+    if (!ptr)
+        return 0;
+
+#if defined(MOZ_MEMORY)
+    return malloc_usable_size(ptr);
+#elif defined(XP_MACOSX)
+    return malloc_size(ptr);
+#elif defined(XP_WIN)
+    return _msize(ptr);
+#else
+    return 0;
+#endif
+}
 
 namespace mozilla {
 
 const fallible_t fallible = fallible_t();
 
 } // namespace mozilla
--- a/memory/mozalloc/mozalloc.h
+++ b/memory/mozalloc/mozalloc.h
@@ -126,16 +126,17 @@ MOZALLOC_EXPORT void* moz_realloc(void* 
 
 
 MOZALLOC_EXPORT char* moz_xstrdup(const char* str)
     NS_ATTR_MALLOC NS_WARN_UNUSED_RESULT;
 
 MOZALLOC_EXPORT char* moz_strdup(const char* str)
     NS_ATTR_MALLOC NS_WARN_UNUSED_RESULT;
 
+MOZALLOC_EXPORT size_t moz_malloc_usable_size(void *ptr);
 
 #if defined(HAVE_STRNDUP)
 MOZALLOC_EXPORT char* moz_xstrndup(const char* str, size_t strsize)
     NS_ATTR_MALLOC NS_WARN_UNUSED_RESULT;
 
 MOZALLOC_EXPORT char* moz_strndup(const char* str, size_t strsize)
     NS_ATTR_MALLOC NS_WARN_UNUSED_RESULT;
 #endif /* if defined(HAVE_STRNDUP) */