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 111320 acbfd1eebb3bdabb5f02a83f67dfe66a49938c6a
parent 111319 05636b372638e8effb4f486fdfe87a6322e664ca
child 111321 888ac1c4683084aaea58aeba89dbb50074b4008a
push id17008
push userjlebar@mozilla.com
push dateThu, 25 Oct 2012 02:06:51 +0000
treeherdermozilla-inbound@acbfd1eebb3b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, njn
bugs803684
milestone19.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 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)