Bug 803684 - Add a memory reporter for in-memory file data. r=khuey,njn
authorJustin Lebar <justin.lebar@gmail.com>
Wed, 24 Oct 2012 22:06:31 -0400
changeset 111453 acbfd1eebb3bdabb5f02a83f67dfe66a49938c6a
parent 111452 05636b372638e8effb4f486fdfe87a6322e664ca
child 111454 888ac1c4683084aaea58aeba89dbb50074b4008a
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewerskhuey, njn
bugs803684
milestone19.0a1
Bug 803684 - Add a memory reporter for in-memory file data. r=khuey,njn
content/base/public/nsDOMFile.h
content/base/src/nsDOMFile.cpp
--- a/content/base/public/nsDOMFile.h
+++ b/content/base/public/nsDOMFile.h
@@ -16,17 +16,19 @@
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsIXMLHttpRequest.h"
 #include "prmem.h"
 #include "nsAutoPtr.h"
 
 #include "mozilla/GuardObjects.h"
+#include "mozilla/LinkedList.h"
 #include "mozilla/StandardInteger.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/indexedDB/FileInfo.h"
 #include "mozilla/dom/indexedDB/FileManager.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsIFile;
@@ -355,27 +357,27 @@ class nsDOMMemoryFile : public nsDOMFile
 {
 public:
   // Create as file
   nsDOMMemoryFile(void *aMemoryBuffer,
                   uint64_t aLength,
                   const nsAString& aName,
                   const nsAString& aContentType)
     : nsDOMFile(aName, aContentType, aLength, UINT64_MAX),
-      mDataOwner(new DataOwner(aMemoryBuffer))
+      mDataOwner(new DataOwner(aMemoryBuffer, aLength))
   {
     NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
   }
 
   // Create as blob
   nsDOMMemoryFile(void *aMemoryBuffer,
                   uint64_t aLength,
                   const nsAString& aContentType)
     : nsDOMFile(aContentType, aLength),
-      mDataOwner(new DataOwner(aMemoryBuffer))
+      mDataOwner(new DataOwner(aMemoryBuffer, aLength))
   {
     NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
   }
 
   NS_IMETHOD GetInternalStream(nsIInputStream**);
 
 protected:
   // Create slice
@@ -386,28 +388,50 @@ protected:
   {
     NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
     mImmutable = aOther->mImmutable;
   }
   virtual already_AddRefed<nsIDOMBlob>
   CreateSlice(uint64_t aStart, uint64_t aLength,
               const nsAString& aContentType);
 
-  friend class DataOwnerAdapter; // Needs to see DataOwner
-  class DataOwner {
+  // These classes need to see DataOwner.
+  friend class DataOwnerAdapter;
+  friend class nsDOMMemoryFileDataOwnerMemoryReporter;
+
+  class DataOwner : public mozilla::LinkedListElement<DataOwner> {
   public:
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataOwner)
-    DataOwner(void* aMemoryBuffer)
+    DataOwner(void* aMemoryBuffer, uint64_t aLength)
       : mData(aMemoryBuffer)
+      , mLength(aLength)
     {
+      if (!sDataOwners) {
+        sDataOwners = new mozilla::LinkedList<DataOwner>();
+        EnsureMemoryReporterRegistered();
+      }
+      sDataOwners->insertBack(this);
     }
+
     ~DataOwner() {
+      remove();
+      if (sDataOwners->isEmpty()) {
+        // Free the linked list if it's empty.
+        sDataOwners = nullptr;
+      }
+
       PR_Free(mData);
     }
+
+    static void EnsureMemoryReporterRegistered();
+
+    static bool sMemoryReporterRegistered;
+    static mozilla::StaticAutoPtr<mozilla::LinkedList<DataOwner> > sDataOwners;
     void* mData;
+    uint64_t mLength;
   };
 
   // Used when backed by a memory store
   nsRefPtr<DataOwner> mDataOwner;
 };
 
 class nsDOMFileList MOZ_FINAL : public nsIDOMFileList,
                                 public nsWrapperCache
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -13,27 +13,30 @@
 #include "nsICharsetDetector.h"
 #include "nsICharsetConverterManager.h"
 #include "nsIClassInfo.h"
 #include "nsIConverterInputStream.h"
 #include "nsIDocument.h"
 #include "nsIFileStreams.h"
 #include "nsIInputStream.h"
 #include "nsIIPCSerializableInputStream.h"
+#include "nsIMemoryReporter.h"
 #include "nsIMIMEService.h"
 #include "nsIPlatformCharset.h"
 #include "nsISeekableStream.h"
 #include "nsIUnicharInputStream.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsIUUIDGenerator.h"
 #include "nsBlobProtocolHandler.h"
 #include "nsStringStream.h"
 #include "nsJSUtils.h"
+#include "nsPrintfCString.h"
+#include "mozilla/SHA1.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Attributes.h"
 
 #include "plbase64.h"
 #include "prmem.h"
 #include "mozilla/dom/FileListBinding.h"
 
@@ -682,16 +685,131 @@ NS_IMETHODIMP
 nsDOMMemoryFile::GetInternalStream(nsIInputStream **aStream)
 {
   if (mLength > INT32_MAX)
     return NS_ERROR_FAILURE;
 
   return DataOwnerAdapter::Create(mDataOwner, mStart, mLength, aStream);
 }
 
+/* static */ StaticAutoPtr<LinkedList<nsDOMMemoryFile::DataOwner> >
+nsDOMMemoryFile::DataOwner::sDataOwners;
+
+/* static */ bool
+nsDOMMemoryFile::DataOwner::sMemoryReporterRegistered;
+
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(DOMMemoryFileDataOwnerSizeOf,
+                                     "memory-file-data");
+
+class nsDOMMemoryFileDataOwnerMemoryReporter
+  : public nsIMemoryMultiReporter
+{
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD GetName(nsACString& aName)
+  {
+    aName.AssignASCII("dom-memory-file-data-owner");
+    return NS_OK;
+  }
+
+  NS_IMETHOD GetExplicitNonHeap(int64_t *aResult)
+  {
+    // All of this reporter's memory is on the heap.
+    *aResult = 0;
+    return NS_OK;
+  }
+
+  NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *aCallback,
+                            nsISupports *aClosure)
+  {
+    typedef nsDOMMemoryFile::DataOwner DataOwner;
+
+    if (!DataOwner::sDataOwners) {
+      return NS_OK;
+    }
+
+    const size_t LARGE_OBJECT_MIN_SIZE = 8 * 1024;
+    size_t smallObjectsTotal = 0;
+
+    for (DataOwner *owner = DataOwner::sDataOwners->getFirst();
+         owner; owner = owner->getNext()) {
+
+      size_t size = DOMMemoryFileDataOwnerSizeOf(owner->mData);
+
+      if (size < LARGE_OBJECT_MIN_SIZE) {
+        smallObjectsTotal += size;
+      }
+      else {
+        SHA1Sum sha1;
+        sha1.update(owner->mData, owner->mLength);
+        uint8_t digest[SHA1Sum::HashSize]; // SHA1 digests are 20 bytes long.
+        sha1.finish(digest);
+
+        nsAutoCString digestString;
+        for (uint8_t i = 0; i < sizeof(digest); i++) {
+          digestString.AppendPrintf("%02x", digest[i]);
+        }
+
+        nsresult rv = aCallback->Callback(
+          /* process */ NS_LITERAL_CSTRING(""),
+          nsPrintfCString(
+            "explicit/dom/memory-file-data/large/file(length=%d, sha1=%s)",
+            owner->mLength, digestString.get()),
+          nsIMemoryReporter::KIND_HEAP,
+          nsIMemoryReporter::UNITS_BYTES,
+          size,
+          nsPrintfCString(
+            "Memory used to back a memory file of length %d.  The file has a "
+            "sha1 of %s.\n\n"
+            "Note that the allocator may round up a memory file's length -- "
+            "that is, an N-byte memory file may take up more than N bytes of "
+            "memory.",
+            owner->mLength, digestString.get()),
+          aClosure);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+
+    if (smallObjectsTotal > 0) {
+      nsresult rv = aCallback->Callback(
+        /* process */ NS_LITERAL_CSTRING(""),
+        NS_LITERAL_CSTRING("explicit/dom/memory-file-data/small"),
+        nsIMemoryReporter::KIND_HEAP,
+        nsIMemoryReporter::UNITS_BYTES,
+        smallObjectsTotal,
+        nsPrintfCString(
+          "Memory used to back small memory files (less than %d bytes each).\n\n"
+          "Note that the allocator may round up a memory file's length -- "
+          "that is, an N-byte memory file may take up more than N bytes of "
+          "memory."),
+        aClosure);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    return NS_OK;
+  }
+};
+
+NS_IMPL_ISUPPORTS1(nsDOMMemoryFileDataOwnerMemoryReporter,
+                   nsIMemoryMultiReporter);
+
+/* static */ void
+nsDOMMemoryFile::DataOwner::EnsureMemoryReporterRegistered()
+{
+  if (sMemoryReporterRegistered) {
+    return;
+  }
+
+  nsRefPtr<nsDOMMemoryFileDataOwnerMemoryReporter> reporter = new
+    nsDOMMemoryFileDataOwnerMemoryReporter();
+  NS_RegisterMemoryMultiReporter(reporter);
+
+  sMemoryReporterRegistered = true;
+}
+
 ////////////////////////////////////////////////////////////////////////////
 // nsDOMFileList implementation
 
 DOMCI_DATA(FileList, nsDOMFileList)
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsDOMFileList)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMFileList)