Bug 590790 part 2 - Add memory reporter for VectorImage's SVGDocumentWrapper's document. r=dholbert, r=njn, r=seth
authorJonathan Watt <jwatt@jwatt.org>
Thu, 08 May 2014 10:53:00 +0100
changeset 190928 8a968bedfccac7559e6aea17a1640c6512a68c13
parent 190927 a520a43d065c54fd65a0fd1351e56e2b148ba9a5
child 190937 b422e91ad2c9843b977867bce21c5dcb424073a9
push idunknown
push userunknown
push dateunknown
reviewersdholbert, njn, seth
bugs590790
milestone32.0a1
Bug 590790 part 2 - Add memory reporter for VectorImage's SVGDocumentWrapper's document. r=dholbert, r=njn, r=seth
image/src/Image.h
image/src/ImageWrapper.h
image/src/RasterImage.h
image/src/VectorImage.cpp
image/src/VectorImage.h
image/src/imgLoader.cpp
xpcom/base/nsIMemoryReporter.idl
--- a/image/src/Image.h
+++ b/image/src/Image.h
@@ -5,16 +5,17 @@
 
 #ifndef MOZILLA_IMAGELIB_IMAGE_H_
 #define MOZILLA_IMAGELIB_IMAGE_H_
 
 #include "mozilla/MemoryReporting.h"
 #include "imgIContainer.h"
 #include "imgStatusTracker.h"
 #include "ImageURL.h"
+#include "nsStringFwd.h"
 
 class nsIRequest;
 class nsIInputStream;
 
 namespace mozilla {
 namespace image {
 
 class Image : public imgIContainer
@@ -79,16 +80,23 @@ public:
   /**
    * The components that make up SizeOfData().
    */
   virtual size_t HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
   virtual size_t HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
   virtual size_t NonHeapSizeOfDecoded() const = 0;
   virtual size_t OutOfProcessSizeOfDecoded() const = 0;
 
+  /**
+   * Gets the size of the memory taken up for the parsed vector image's
+   * document (e.g. SVGDocument), and returns the document's URL via the
+   * aDocURL outparam.
+   */
+  virtual size_t HeapSizeOfVectorImageDocument(nsACString* aDocURL = nullptr) const = 0;
+
   virtual void IncrementAnimationConsumers() = 0;
   virtual void DecrementAnimationConsumers() = 0;
 #ifdef DEBUG
   virtual uint32_t GetAnimationConsumers() = 0;
 #endif
 
   /**
    * Called from OnDataAvailable when the stream associated with the image has
--- a/image/src/ImageWrapper.h
+++ b/image/src/ImageWrapper.h
@@ -30,16 +30,20 @@ public:
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
 
   virtual uint32_t SizeOfData() MOZ_OVERRIDE;
   virtual size_t HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
   virtual size_t HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
   virtual size_t NonHeapSizeOfDecoded() const MOZ_OVERRIDE;
   virtual size_t OutOfProcessSizeOfDecoded() const MOZ_OVERRIDE;
 
+  virtual size_t HeapSizeOfVectorImageDocument(nsACString* aDocURL = nullptr) const MOZ_OVERRIDE {
+    return mInnerImage->HeapSizeOfVectorImageDocument(aDocURL);
+  }
+
   virtual void IncrementAnimationConsumers() MOZ_OVERRIDE;
   virtual void DecrementAnimationConsumers() MOZ_OVERRIDE;
 #ifdef DEBUG
   virtual uint32_t GetAnimationConsumers() MOZ_OVERRIDE;
 #endif
 
   virtual nsresult OnImageDataAvailable(nsIRequest* aRequest,
                                         nsISupports* aContext,
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -176,16 +176,20 @@ public:
   /* The total number of frames in this image. */
   uint32_t GetNumFrames() const;
 
   virtual size_t HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const;
   virtual size_t HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const;
   virtual size_t NonHeapSizeOfDecoded() const;
   virtual size_t OutOfProcessSizeOfDecoded() const;
 
+  virtual size_t HeapSizeOfVectorImageDocument(nsACString* aDocURL = nullptr) const MOZ_OVERRIDE {
+    return 0;
+  }
+
   /* Triggers discarding. */
   void Discard(bool force = false);
   void ForceDiscard() { Discard(/* force = */ true); }
 
   /* Callbacks for decoders */
   nsresult SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult);
 
   /** Sets the size and inherent orientation of the container. This should only
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -17,18 +17,20 @@
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "nsIDOMEvent.h"
 #include "nsIPresShell.h"
 #include "nsIStreamListener.h"
 #include "nsMimeTypes.h"
 #include "nsPresContext.h"
 #include "nsRect.h"
+#include "nsString.h"
 #include "nsStubDocumentObserver.h"
 #include "nsSVGEffects.h" // for nsSVGRenderingObserver
+#include "nsWindowMemoryReporter.h"
 #include "Orientation.h"
 #include "SVGDocumentWrapper.h"
 #include "nsIDOMEventListener.h"
 #include "SurfaceCache.h"
 
 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
 #undef GetCurrentTime
 
@@ -354,38 +356,71 @@ VectorImage::FrameRect(uint32_t aWhichFr
 }
 
 size_t
 VectorImage::HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   // We're not storing the source data -- we just feed that directly to
   // our helper SVG document as we receive it, for it to parse.
   // So 0 is an appropriate return value here.
+  // If implementing this, we'll need to restructure our callers to make sure
+  // any amount we return is attributed to the vector images measure (i.e.
+  // "explicit/images/{content,chrome}/vector/{used,unused}/...")
   return 0;
 }
 
 size_t
 VectorImage::HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
 {
-  // XXXdholbert TODO: return num bytes used by helper SVG doc. (bug 590790)
+  // If implementing this, we'll need to restructure our callers to make sure
+  // any amount we return is attributed to the vector images measure (i.e.
+  // "explicit/images/{content,chrome}/vector/{used,unused}/...")
   return 0;
 }
 
 size_t
 VectorImage::NonHeapSizeOfDecoded() const
 {
+  // If implementing this, we'll need to restructure our callers to make sure
+  // any amount we return is attributed to the vector images measure (i.e.
+  // "explicit/images/{content,chrome}/vector/{used,unused}/...")
   return 0;
 }
 
 size_t
 VectorImage::OutOfProcessSizeOfDecoded() const
 {
+  // If implementing this, we'll need to restructure our callers to make sure
+  // any amount we return is attributed to the vector images measure (i.e.
+  // "explicit/images/{content,chrome}/vector/{used,unused}/...")
   return 0;
 }
 
+MOZ_DEFINE_MALLOC_SIZE_OF(WindowsMallocSizeOf);
+
+size_t
+VectorImage::HeapSizeOfVectorImageDocument(nsACString* aDocURL) const
+{
+  nsIDocument* doc = mSVGDocumentWrapper->GetDocument();
+  if (!doc) {
+    if (aDocURL) {
+      mURI->GetSpec(*aDocURL);
+    }
+    return 0; // No document, so no memory used for the document
+  }
+
+  if (aDocURL) {
+    doc->GetDocumentURI()->GetSpec(*aDocURL);
+  }
+
+  nsWindowSizes windowSizes(WindowsMallocSizeOf);
+  doc->DocAddSizeOfExcludingThis(&windowSizes);
+  return windowSizes.getTotalSize();
+}
+
 nsresult
 VectorImage::OnImageDataComplete(nsIRequest* aRequest,
                                  nsISupports* aContext,
                                  nsresult aStatus,
                                  bool aLastPart)
 {
   // Call our internal OnStopRequest method, which only talks to our embedded
   // SVG document. This won't have any effect on our imgStatusTracker.
--- a/image/src/VectorImage.h
+++ b/image/src/VectorImage.h
@@ -43,16 +43,18 @@ public:
                 uint32_t aFlags);
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
 
   virtual size_t HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const;
   virtual size_t HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const;
   virtual size_t NonHeapSizeOfDecoded() const;
   virtual size_t OutOfProcessSizeOfDecoded() const;
 
+  virtual size_t HeapSizeOfVectorImageDocument(nsACString* aDocURL = nullptr) const MOZ_OVERRIDE;
+
   virtual nsresult OnImageDataAvailable(nsIRequest* aRequest,
                                         nsISupports* aContext,
                                         nsIInputStream* aInStr,
                                         uint64_t aSourceOffset,
                                         uint32_t aCount) MOZ_OVERRIDE;
   virtual nsresult OnImageDataComplete(nsIRequest* aRequest,
                                        nsISupports* aContext,
                                        nsresult aResult,
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -70,66 +70,111 @@ public:
     do {                                                                      \
       nsresult rv;                                                            \
       rv = callback->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path),      \
                               _kind, UNITS_BYTES, _amount,                    \
                               NS_LITERAL_CSTRING(_desc), closure);            \
       NS_ENSURE_SUCCESS(rv, rv);                                              \
     } while (0)
 
-    REPORT("explicit/images/chrome/used/raw",
+    REPORT("explicit/images/chrome/raster/used/raw",
            KIND_HEAP, chrome.mUsedRaw,
            "Memory used by in-use chrome images (compressed data).");
 
-    REPORT("explicit/images/chrome/used/uncompressed-heap",
+    REPORT("explicit/images/chrome/raster/used/uncompressed-heap",
            KIND_HEAP, chrome.mUsedUncompressedHeap,
            "Memory used by in-use chrome images (uncompressed data).");
 
-    REPORT("explicit/images/chrome/used/uncompressed-nonheap",
+    REPORT("explicit/images/chrome/raster/used/uncompressed-nonheap",
            KIND_NONHEAP, chrome.mUsedUncompressedNonheap,
            "Memory used by in-use chrome images (uncompressed data).");
 
-    REPORT("explicit/images/chrome/unused/raw",
+    REPORT("explicit/images/chrome/raster/unused/raw",
            KIND_HEAP, chrome.mUnusedRaw,
            "Memory used by not in-use chrome images (compressed data).");
 
-    REPORT("explicit/images/chrome/unused/uncompressed-heap",
+    REPORT("explicit/images/chrome/raster/unused/uncompressed-heap",
            KIND_HEAP, chrome.mUnusedUncompressedHeap,
            "Memory used by not in-use chrome images (uncompressed data).");
 
-    REPORT("explicit/images/chrome/unused/uncompressed-nonheap",
+    REPORT("explicit/images/chrome/raster/unused/uncompressed-nonheap",
            KIND_NONHEAP, chrome.mUnusedUncompressedNonheap,
            "Memory used by not in-use chrome images (uncompressed data).");
 
-    REPORT("explicit/images/content/used/raw",
+    REPORT("explicit/images/content/raster/used/raw",
            KIND_HEAP, content.mUsedRaw,
            "Memory used by in-use content images (compressed data).");
 
-    REPORT("explicit/images/content/used/uncompressed-heap",
+    REPORT("explicit/images/content/raster/used/uncompressed-heap",
            KIND_HEAP, content.mUsedUncompressedHeap,
            "Memory used by in-use content images (uncompressed data).");
 
-    REPORT("explicit/images/content/used/uncompressed-nonheap",
+    REPORT("explicit/images/content/raster/used/uncompressed-nonheap",
            KIND_NONHEAP, content.mUsedUncompressedNonheap,
            "Memory used by in-use content images (uncompressed data).");
 
-    REPORT("explicit/images/content/unused/raw",
+    REPORT("explicit/images/content/raster/unused/raw",
            KIND_HEAP, content.mUnusedRaw,
            "Memory used by not in-use content images (compressed data).");
 
-    REPORT("explicit/images/content/unused/uncompressed-heap",
+    REPORT("explicit/images/content/raster/unused/uncompressed-heap",
            KIND_HEAP, content.mUnusedUncompressedHeap,
            "Memory used by not in-use content images (uncompressed data).");
 
-    REPORT("explicit/images/content/unused/uncompressed-nonheap",
+    REPORT("explicit/images/content/raster/unused/uncompressed-nonheap",
            KIND_NONHEAP, content.mUnusedUncompressedNonheap,
            "Memory used by not in-use content images (uncompressed data).");
 
 #undef REPORT
 
+#define REPORT_VECTOR(_path, _uri, _amount, _desc)                            \
+    do {                                                                      \
+      nsAutoCString path(NS_LITERAL_CSTRING(_path));                          \
+      path.Append("/(");                                                      \
+      path.Append(_uri);                                                      \
+      path.Append(")");                                                       \
+      nsresult rv;                                                            \
+      rv = callback->Callback(EmptyCString(), path,                           \
+                              KIND_HEAP, UNITS_BYTES, _amount,                \
+                              NS_LITERAL_CSTRING(_desc), closure);            \
+      NS_ENSURE_SUCCESS(rv, rv);                                              \
+    } while (0)
+
+    for (uint32_t i = 0; i < chrome.mVectorImageDocInfo.Length(); i++) {
+      chrome.mVectorImageDocInfo[i].mURI.ReplaceChar('/', '\\');
+      if (chrome.mVectorImageDocInfo[i].mUsed) {
+        REPORT_VECTOR("explicit/images/chrome/vector/used/documents",
+                      chrome.mVectorImageDocInfo[i].mURI,
+                      chrome.mVectorImageDocInfo[i].mSize,
+                      "Memory used by in-use chrome vector images for their parsed vector documents.");
+      } else {
+        REPORT_VECTOR("explicit/images/chrome/vector/unused/documents",
+                      chrome.mVectorImageDocInfo[i].mURI,
+                      chrome.mVectorImageDocInfo[i].mSize,
+                      "Memory used by not in-use chrome vector images for their parsed vector documents.");
+      }
+    }
+
+    for (uint32_t i = 0; i < content.mVectorImageDocInfo.Length(); i++) {
+      content.mVectorImageDocInfo[i].mURI.ReplaceChar('/', '\\');
+      if (content.mVectorImageDocInfo[i].mUsed) {
+        REPORT_VECTOR("explicit/images/content/vector/used/documents",
+                      content.mVectorImageDocInfo[i].mURI,
+                      content.mVectorImageDocInfo[i].mSize,
+                      "Memory used by in-use content vector images for their parsed vector documents.");
+      } else {
+        REPORT_VECTOR("explicit/images/content/vector/unused/documents",
+                      content.mVectorImageDocInfo[i].mURI,
+                      content.mVectorImageDocInfo[i].mSize,
+                      "Memory used by not in-use content vector images for their parsed vector documents.");
+      }
+    }
+
+#undef REPORT_VECTOR
+
     return NS_OK;
   }
 
   static int64_t ImagesContentUsedUncompressedDistinguishedAmount()
   {
     size_t n = 0;
     for (uint32_t i = 0; i < imgLoader::sMemReporter->mKnownLoaders.Length(); i++) {
       imgLoader::sMemReporter->mKnownLoaders[i]->mCache.EnumerateRead(EntryUsedUncompressedSize, &n);
@@ -145,27 +190,41 @@ public:
   void UnregisterLoader(imgLoader* aLoader)
   {
     mKnownLoaders.RemoveElement(aLoader);
   }
 
 private:
   nsTArray<imgLoader*> mKnownLoaders;
 
+  struct VectorImageDocInfo {
+    size_t mSize;
+    bool mUsed;
+    nsAutoCString mURI;
+  };
+
   struct AllSizes {
     size_t mUsedRaw;
     size_t mUsedUncompressedHeap;
     size_t mUsedUncompressedNonheap;
     size_t mUnusedRaw;
     size_t mUnusedUncompressedHeap;
     size_t mUnusedUncompressedNonheap;
-
-    AllSizes() {
-      memset(this, 0, sizeof(*this));
-    }
+    // The size of VectorImages' documents are recorded individually so that we
+    // can report on each SVG-as-an-image individually.
+    nsTArray<VectorImageDocInfo> mVectorImageDocInfo;
+
+    AllSizes()
+      : mUsedRaw(0)
+      , mUsedUncompressedHeap(0)
+      , mUsedUncompressedNonheap(0)
+      , mUnusedRaw(0)
+      , mUnusedUncompressedHeap(0)
+      , mUnusedUncompressedNonheap(0)
+    {}
   };
 
   static PLDHashOperator EntryAllSizes(const nsACString&,
                                        imgCacheEntry *entry,
                                        void *userArg)
   {
     nsRefPtr<imgRequest> req = entry->GetRequest();
     Image *image = static_cast<Image*>(req->mImage.get());
@@ -179,16 +238,22 @@ private:
         sizes->mUnusedUncompressedNonheap += image->NonHeapSizeOfDecoded();
       } else {
         sizes->mUsedRaw +=
           image->HeapSizeOfSourceWithComputedFallback(ImagesMallocSizeOf);
         sizes->mUsedUncompressedHeap +=
           image->HeapSizeOfDecodedWithComputedFallback(ImagesMallocSizeOf);
         sizes->mUsedUncompressedNonheap += image->NonHeapSizeOfDecoded();
       }
+      VectorImageDocInfo vectInfo;
+      vectInfo.mSize = image->HeapSizeOfVectorImageDocument(&vectInfo.mURI);
+      if (!vectInfo.mURI.IsEmpty()) {
+        vectInfo.mUsed = !entry->HasNoProxies();
+        sizes->mVectorImageDocInfo.AppendElement(vectInfo);
+      }
     }
 
     return PL_DHASH_NEXT;
   }
 
   static PLDHashOperator EntryUsedUncompressedSize(const nsACString&,
                                                    imgCacheEntry *entry,
                                                    void *userArg)
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -323,17 +323,17 @@ interface nsIMemoryReporterManager : nsI
    *
    * |JSMainRuntimeTemporaryPeak| (UNITS_BYTES)  Peak size of the transient
    * storage in the main JSRuntime.
    *
    * |JSMainRuntimeCompartments{System,User}| (UNITS_COUNT)  The number of
    * {system,user} compartments in the main JS runtime.
    *
    * |imagesContentUsedUncompressed| (UNITS_BYTES)  Memory used for decoded
-   * images in content.
+   * raster images in content.
    *
    * |storageSQLite| (UNITS_BYTES)  Memory used by SQLite.
    *
    * |lowMemoryEvents{Virtual,Physical}| (UNITS_COUNT_CUMULATIVE)  The number
    * of low-{virtual,physical}-memory events that have occurred since the
    * process started.
    *
    * |ghostWindows| (UNITS_COUNT)  The number of ghost windows.