Bug 964039 - Memory used by the new cache backend is not reported, r=michal
authorHonza Bambas <honzab.moz@firemni.cz>
Thu, 27 Feb 2014 00:11:40 +0100
changeset 183964 a8cb3454d250df05a7fa57d1eb131cd3c455c547
parent 183963 c486ad9a89acd41b4261285b91762f9cc5a7abaa
child 183965 a0b80ff2d63813ee13d9f25932c508dc26693d6e
push idunknown
push userunknown
push dateunknown
reviewersmichal
bugs964039
milestone30.0a1
Bug 964039 - Memory used by the new cache backend is not reported, r=michal
netwerk/cache2/CacheEntry.cpp
netwerk/cache2/CacheEntry.h
netwerk/cache2/CacheFile.cpp
netwerk/cache2/CacheFile.h
netwerk/cache2/CacheFileChunk.cpp
netwerk/cache2/CacheFileChunk.h
netwerk/cache2/CacheFileIOManager.cpp
netwerk/cache2/CacheFileIOManager.h
netwerk/cache2/CacheFileInputStream.cpp
netwerk/cache2/CacheFileInputStream.h
netwerk/cache2/CacheFileMetadata.cpp
netwerk/cache2/CacheFileMetadata.h
netwerk/cache2/CacheFileOutputStream.cpp
netwerk/cache2/CacheFileOutputStream.h
netwerk/cache2/CacheIOThread.cpp
netwerk/cache2/CacheIOThread.h
netwerk/cache2/CacheIndex.cpp
netwerk/cache2/CacheIndex.h
netwerk/cache2/CacheStorage.h
netwerk/cache2/CacheStorageService.cpp
netwerk/cache2/CacheStorageService.h
--- a/netwerk/cache2/CacheEntry.cpp
+++ b/netwerk/cache2/CacheEntry.cpp
@@ -10,16 +10,17 @@
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
 #include "nsISeekableStream.h"
 #include "nsIURI.h"
 #include "nsICacheEntryOpenCallback.h"
 #include "nsICacheStorage.h"
 #include "nsISerializable.h"
 #include "nsIStreamTransportService.h"
+#include "nsISizeOf.h"
 
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsProxyRelease.h"
 #include "nsSerializationHelper.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Telemetry.h"
@@ -1540,10 +1541,45 @@ void CacheOutputCloseListener::OnOutputC
 }
 
 NS_IMETHODIMP CacheOutputCloseListener::Run()
 {
   mEntry->OnOutputClosed();
   return NS_OK;
 }
 
+// Memory reporting
+
+size_t CacheEntry::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  size_t n = 0;
+  nsCOMPtr<nsISizeOf> sizeOf;
+
+  n += mCallbacks.SizeOfExcludingThis(mallocSizeOf);
+  if (mFile) {
+    n += mFile->SizeOfIncludingThis(mallocSizeOf);
+  }
+
+  sizeOf = do_QueryInterface(mURI);
+  if (sizeOf) {
+    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
+  }
+
+  n += mEnhanceID.SizeOfExcludingThisIfUnshared(mallocSizeOf);
+  n += mStorageID.SizeOfExcludingThisIfUnshared(mallocSizeOf);
+
+  // mDoomCallback is an arbitrary class that is probably reported elsewhere.
+  // mOutputStream is reported in mFile.
+  // mWriter is one of many handles we create, but (intentionally) not keep
+  // any reference to, so those unfortunatelly cannot be reported.  Handles are
+  // small, though.
+  // mSecurityInfo doesn't impl nsISizeOf.
+
+  return n;
+}
+
+size_t CacheEntry::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
+}
+
 } // net
 } // mozilla
--- a/netwerk/cache2/CacheEntry.h
+++ b/netwerk/cache2/CacheEntry.h
@@ -100,16 +100,20 @@ public:
                              nsCSubstring const& aEnhanceID,
                              nsIURI* aURI,
                              nsACString &aResult);
 
   // Accessed only on the service management thread
   double mFrecency;
   uint32_t mSortingExpirationTime;
 
+  // Memory reporting
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
 private:
   virtual ~CacheEntry();
 
   // CacheFileListener
   NS_IMETHOD OnFileReady(nsresult aResult, bool aIsNew);
   NS_IMETHOD OnFileDoomed(nsresult aResult);
 
   // Keep the service alive during life-time of an entry
--- a/netwerk/cache2/CacheFile.cpp
+++ b/netwerk/cache2/CacheFile.cpp
@@ -928,17 +928,17 @@ CacheFile::Unlock()
 
   mLock.Unlock();
 
   for (uint32_t i = 0; i < objs.Length(); i++)
     objs[i]->Release();
 }
 
 void
-CacheFile::AssertOwnsLock()
+CacheFile::AssertOwnsLock() const
 {
   mLock.AssertCurrentThreadOwns();
 }
 
 void
 CacheFile::ReleaseOutsideLock(nsISupports *aObject)
 {
   AssertOwnsLock();
@@ -1573,10 +1573,62 @@ CacheFile::InitIndexEntry()
   mMetadata->GetFrecency(&frecency);
 
   rv = CacheFileIOManager::UpdateIndexEntry(mHandle, &frecency, &expTime);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
+// Memory reporting
+
+namespace { // anon
+
+size_t
+CollectChunkSize(uint32_t const & aIdx,
+                 nsRefPtr<mozilla::net::CacheFileChunk> const & aChunk,
+                 mozilla::MallocSizeOf mallocSizeOf, void* aClosure)
+{
+  return aChunk->SizeOfIncludingThis(mallocSizeOf);
+}
+
+} // anon
+
+size_t
+CacheFile::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  CacheFileAutoLock lock(const_cast<CacheFile*>(this));
+
+  size_t n = 0;
+  n += mChunks.SizeOfExcludingThis(CollectChunkSize, mallocSizeOf);
+  n += mCachedChunks.SizeOfExcludingThis(CollectChunkSize, mallocSizeOf);
+  if (mMetadata) {
+    n += mMetadata->SizeOfIncludingThis(mallocSizeOf);
+  }
+
+  // Input streams are not elsewhere reported.
+  n += mInputs.SizeOfExcludingThis(mallocSizeOf);
+  for (uint32_t i = 0; i < mInputs.Length(); ++i) {
+    n += mInputs[i]->SizeOfIncludingThis(mallocSizeOf);
+  }
+
+  // Output streams are not elsewhere reported.
+  if (mOutput) {
+    n += mOutput->SizeOfIncludingThis(mallocSizeOf);
+  }
+
+  // The listeners are usually classes reported just above.
+  n += mChunkListeners.SizeOfExcludingThis(nullptr, mallocSizeOf);
+  n += mObjsToRelease.SizeOfExcludingThis(mallocSizeOf);
+
+  // mHandle reported directly from CacheFileIOManager.
+
+  return n;
+}
+
+size_t
+CacheFile::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
+}
+
 } // net
 } // mozilla
--- a/netwerk/cache2/CacheFile.h
+++ b/netwerk/cache2/CacheFile.h
@@ -94,29 +94,33 @@ public:
   nsresult GetFrecency(uint32_t *_retval);
   nsresult GetLastFetched(uint32_t *_retval);
   nsresult GetFetchCount(uint32_t *_retval);
 
   bool DataSize(int64_t* aSize);
   void Key(nsACString& aKey) { aKey = mKey; }
   bool IsDoomed();
 
+  // Memory reporting
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
 private:
   friend class CacheFileIOManager;
   friend class CacheFileChunk;
   friend class CacheFileInputStream;
   friend class CacheFileOutputStream;
   friend class CacheFileAutoLock;
   friend class MetadataWriteTimer;
 
   virtual ~CacheFile();
 
   void     Lock();
   void     Unlock();
-  void     AssertOwnsLock();
+  void     AssertOwnsLock() const;
   void     ReleaseOutsideLock(nsISupports *aObject);
 
   nsresult GetChunk(uint32_t aIndex, bool aWriter,
                     CacheFileChunkListener *aCallback,
                     CacheFileChunk **_retval);
   nsresult GetChunkLocked(uint32_t aIndex, bool aWriter,
                           CacheFileChunkListener *aCallback,
                           CacheFileChunk **_retval);
--- a/netwerk/cache2/CacheFileChunk.cpp
+++ b/netwerk/cache2/CacheFileChunk.cpp
@@ -592,47 +592,47 @@ CacheFileChunk::OnEOFSet(CacheFileHandle
 nsresult
 CacheFileChunk::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
 {
   MOZ_CRASH("CacheFileChunk::OnFileRenamed should not be called!");
   return NS_ERROR_UNEXPECTED;
 }
 
 bool
-CacheFileChunk::IsReady()
+CacheFileChunk::IsReady() const
 {
   mFile->AssertOwnsLock();
 
   return (mState == READY || mState == WRITING);
 }
 
 bool
-CacheFileChunk::IsDirty()
+CacheFileChunk::IsDirty() const
 {
   mFile->AssertOwnsLock();
 
   return mIsDirty;
 }
 
 char *
-CacheFileChunk::BufForWriting()
+CacheFileChunk::BufForWriting() const
 {
   mFile->AssertOwnsLock();
 
   MOZ_ASSERT(mBuf); // Writer should always first call EnsureBufSize()
 
   MOZ_ASSERT((mState == READY && !mRWBuf) ||
              (mState == WRITING && mRWBuf) ||
              (mState == READING && mRWBuf));
 
   return mBuf;
 }
 
 const char *
-CacheFileChunk::BufForReading()
+CacheFileChunk::BufForReading() const
 {
   mFile->AssertOwnsLock();
 
   MOZ_ASSERT((mState == READY && mBuf && !mRWBuf) ||
              (mState == WRITING && mRWBuf));
 
   return mBuf ? mBuf : mRWBuf;
 }
@@ -672,10 +672,29 @@ CacheFileChunk::EnsureBufSize(uint32_t a
   mBufSize = aBufSize;
 
   if (copy)
     memcpy(mBuf, mRWBuf, mRWBufSize);
 
   DoMemoryReport(MemorySize());
 }
 
+// Memory reporting
+
+size_t
+CacheFileChunk::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  size_t n = 0;
+  n += mallocSizeOf(mBuf);
+  n += mallocSizeOf(mRWBuf);
+  n += mValidityMap.SizeOfExcludingThis(mallocSizeOf);
+
+  return n;
+}
+
+size_t
+CacheFileChunk::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
+}
+
 } // net
 } // mozilla
--- a/netwerk/cache2/CacheFileChunk.h
+++ b/netwerk/cache2/CacheFileChunk.h
@@ -89,23 +89,27 @@ public:
   NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult);
   NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
                            nsresult aResult);
   NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult);
   NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult);
   NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult);
   NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult);
 
-  bool   IsReady();
-  bool   IsDirty();
+  bool   IsReady() const;
+  bool   IsDirty() const;
 
-  char *       BufForWriting();
-  const char * BufForReading();
+  char *       BufForWriting() const;
+  const char * BufForReading() const;
   void         EnsureBufSize(uint32_t aBufSize);
-  uint32_t     MemorySize() { return mRWBufSize + mBufSize; }
+  uint32_t     MemorySize() const { return mRWBufSize + mBufSize; }
+
+  // Memory reporting
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
 private:
   friend class CacheFileInputStream;
   friend class CacheFileOutputStream;
   friend class CacheFile;
 
   virtual ~CacheFileChunk();
 
--- a/netwerk/cache2/CacheFileIOManager.cpp
+++ b/netwerk/cache2/CacheFileIOManager.cpp
@@ -12,16 +12,17 @@
 #include "CacheFileUtils.h"
 #include "nsThreadUtils.h"
 #include "CacheFile.h"
 #include "CacheObserver.h"
 #include "nsIFile.h"
 #include "nsITimer.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIDirectoryEnumerator.h"
+#include "nsISizeOf.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/DebugOnly.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "private/pprio.h"
 #include "mozilla/VisualEventTracer.h"
 
 // include files for ftruncate (or equivalent)
@@ -158,17 +159,17 @@ CacheFileHandle::Log()
          "isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
          "fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
          this, LOGSHA1(mHash), mIsDoomed, mPriority, mClosed,
          mInvalid, mFileExists, mFileSize, leafName.get(), mKey.get()));
   }
 }
 
 uint32_t
-CacheFileHandle::FileSizeInK()
+CacheFileHandle::FileSizeInK() const
 {
   MOZ_ASSERT(mFileSize != -1);
   uint64_t size64 = mFileSize;
 
   size64 += 0x3FF;
   size64 >>= 10;
 
   uint32_t size;
@@ -178,16 +179,40 @@ CacheFileHandle::FileSizeInK()
     size = PR_UINT32_MAX;
   } else {
     size = static_cast<uint32_t>(size64);
   }
 
   return size;
 }
 
+// Memory reporting
+
+size_t
+CacheFileHandle::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  size_t n = 0;
+  nsCOMPtr<nsISizeOf> sizeOf;
+
+  sizeOf = do_QueryInterface(mFile);
+  if (sizeOf) {
+    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
+  }
+
+  n += mallocSizeOf(mFD);
+  n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
+  return n;
+}
+
+size_t
+CacheFileHandle::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
+}
+
 /******************************************************************************
  *  CacheFileHandles::HandleHashKey
  *****************************************************************************/
 
 void
 CacheFileHandles::HandleHashKey::AddHandle(CacheFileHandle* aHandle)
 {
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
@@ -237,16 +262,30 @@ CacheFileHandles::HandleHashKey::AssertH
   for (uint32_t i = 0; i < mHandles.Length(); ++i) {
     CacheFileHandle* handle = mHandles[i];
     MOZ_ASSERT(handle->IsDoomed());
   }
 }
 
 #endif
 
+size_t
+CacheFileHandles::HandleHashKey::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+  size_t n = 0;
+  n += mallocSizeOf(mHash);
+  for (uint32_t i = 0; i < mHandles.Length(); ++i) {
+    n += mHandles[i]->SizeOfIncludingThis(mallocSizeOf);
+  }
+
+  return n;
+}
+
 /******************************************************************************
  *  CacheFileHandles
  *****************************************************************************/
 
 CacheFileHandles::CacheFileHandles()
 {
   LOG(("CacheFileHandles::CacheFileHandles() [this=%p]", this));
   MOZ_COUNT_CTOR(CacheFileHandles);
@@ -426,16 +465,38 @@ CacheFileHandles::Log(CacheFileHandlesEn
     CacheFileHandle *handle = array[i];
     handle->Log();
   }
 
   LOG(("CacheFileHandles::Log() END [entry=%p]", entry));
 }
 #endif
 
+// Memory reporting
+
+namespace { // anon
+
+size_t
+CollectHandlesMemory(CacheFileHandles::HandleHashKey* key,
+                     mozilla::MallocSizeOf mallocSizeOf,
+                     void *arg)
+{
+  return key->SizeOfExcludingThis(mallocSizeOf);
+}
+
+} // anon
+
+size_t
+CacheFileHandles::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+  return mTable.SizeOfExcludingThis(&CollectHandlesMemory, mallocSizeOf);
+}
+
 // Events
 
 class ShutdownEvent : public nsRunnable {
 public:
   ShutdownEvent(mozilla::Mutex *aLock, mozilla::CondVar *aCondVar)
     : mLock(aLock)
     , mCondVar(aCondVar)
   {
@@ -3132,10 +3193,133 @@ CacheFileIOManager::NSPRHandleUsed(Cache
 
   DebugOnly<bool> found;
   found = mHandlesByLastUsed.RemoveElement(aHandle);
   MOZ_ASSERT(found);
 
   mHandlesByLastUsed.AppendElement(aHandle);
 }
 
+// Memory reporting
+
+namespace { // anon
+
+// A helper class that dispatches and waits for an event that gets result of
+// CacheFileIOManager->mHandles.SizeOfExcludingThis() on the I/O thread
+// to safely get handles memory report.
+// We must do this, since the handle list is only accessed and managed w/o
+// locking on the I/O thread.  That is by design.
+class SizeOfHandlesRunnable : public nsRunnable
+{
+public:
+  SizeOfHandlesRunnable(mozilla::MallocSizeOf mallocSizeOf,
+                        CacheFileHandles const &handles,
+                        nsTArray<nsRefPtr<CacheFileHandle> > const &specialHandles)
+    : mMonitor("SizeOfHandlesRunnable.mMonitor")
+    , mMallocSizeOf(mallocSizeOf)
+    , mHandles(handles)
+    , mSpecialHandles(specialHandles)
+  {
+  }
+
+  size_t Get(CacheIOThread* thread)
+  {
+    nsCOMPtr<nsIEventTarget> target = thread->Target();
+    if (!target) {
+      NS_ERROR("If we have the I/O thread we also must have the I/O target");
+      return 0;
+    }
+
+    mozilla::MonitorAutoLock mon(mMonitor);
+    nsresult rv = target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
+    if (NS_FAILED(rv)) {
+      NS_ERROR("Dispatch failed, cannot do memory report of CacheFileHandles");
+      return 0;
+    }
+
+    mon.Wait();
+    return mSize;
+  }
+
+  NS_IMETHOD Run()
+  {
+    mozilla::MonitorAutoLock mon(mMonitor);
+    // Excluding this since the object itself is a member of CacheFileIOManager
+    // reported in CacheFileIOManager::SizeOfIncludingThis as part of |this|.
+    mSize = mHandles.SizeOfExcludingThis(mMallocSizeOf);
+    for (uint32_t i = 0; i < mSpecialHandles.Length(); ++i) {
+      mSize += mSpecialHandles[i]->SizeOfIncludingThis(mMallocSizeOf);
+    }
+
+    mon.Notify();
+    return NS_OK;
+  }
+
+private:
+  mozilla::Monitor mMonitor;
+  mozilla::MallocSizeOf mMallocSizeOf;
+  CacheFileHandles const &mHandles;
+  nsTArray<nsRefPtr<CacheFileHandle> > const &mSpecialHandles;
+  size_t mSize;
+};
+
+} // anon
+
+size_t
+CacheFileIOManager::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  size_t n = 0;
+  nsCOMPtr<nsISizeOf> sizeOf;
+
+  if (mIOThread) {
+    n += mIOThread->SizeOfIncludingThis(mallocSizeOf);
+
+    // mHandles and mSpecialHandles must be accessed only on the I/O thread,
+    // must sync dispatch.
+    nsRefPtr<SizeOfHandlesRunnable> sizeOfHandlesRunnable =
+      new SizeOfHandlesRunnable(mallocSizeOf, mHandles, mSpecialHandles);
+    n += sizeOfHandlesRunnable->Get(mIOThread);
+  }
+
+  // mHandlesByLastUsed just refers handles reported by mHandles.
+
+  sizeOf = do_QueryInterface(mCacheDirectory);
+  if (sizeOf)
+    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
+
+  sizeOf = do_QueryInterface(mMetadataWritesTimer);
+  if (sizeOf)
+    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
+
+  sizeOf = do_QueryInterface(mTrashTimer);
+  if (sizeOf)
+    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
+
+  sizeOf = do_QueryInterface(mTrashDir);
+  if (sizeOf)
+    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
+
+  for (uint32_t i = 0; i < mFailedTrashDirs.Length(); ++i) {
+    n += mFailedTrashDirs[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
+  }
+
+  return n;
+}
+
+// static
+size_t
+CacheFileIOManager::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
+{
+  if (!gInstance)
+    return 0;
+
+  return gInstance->SizeOfExcludingThisInternal(mallocSizeOf);
+}
+
+// static
+size_t
+CacheFileIOManager::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
+{
+  return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf);
+}
+
 } // net
 } // mozilla
--- a/netwerk/cache2/CacheFileIOManager.h
+++ b/netwerk/cache2/CacheFileIOManager.h
@@ -40,26 +40,30 @@ class CacheFileHandle : public nsISuppor
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   bool DispatchRelease();
 
   CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority);
   CacheFileHandle(const nsACString &aKey, bool aPriority);
   CacheFileHandle(const CacheFileHandle &aOther);
   void Log();
-  bool IsDoomed() { return mIsDoomed; }
-  const SHA1Sum::Hash *Hash() { return mHash; }
-  int64_t FileSize() { return mFileSize; }
-  uint32_t FileSizeInK();
-  bool IsPriority() { return mPriority; }
-  bool FileExists() { return mFileExists; }
-  bool IsClosed() { return mClosed; }
-  bool IsSpecialFile() { return !mHash; }
+  bool IsDoomed() const { return mIsDoomed; }
+  const SHA1Sum::Hash *Hash() const { return mHash; }
+  int64_t FileSize() const { return mFileSize; }
+  uint32_t FileSizeInK() const;
+  bool IsPriority() const { return mPriority; }
+  bool FileExists() const { return mFileExists; }
+  bool IsClosed() const { return mClosed; }
+  bool IsSpecialFile() const { return !mHash; }
   nsCString & Key() { return mKey; }
 
+  // Memory reporting
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
 private:
   friend class CacheFileIOManager;
   friend class CacheFileHandles;
   friend class ReleaseNSPRHandleEvent;
 
   virtual ~CacheFileHandle();
 
   const SHA1Sum::Hash *mHash;
@@ -88,16 +92,20 @@ public:
   void     GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval);
   void     ClearAll();
   uint32_t HandleCount();
 
 #ifdef DEBUG_HANDLES
   void     Log(CacheFileHandlesEntry *entry);
 #endif
 
+  // Memory reporting
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
   class HandleHashKey : public PLDHashEntryHdr
   {
   public:
     typedef const SHA1Sum::Hash& KeyType;
     typedef const SHA1Sum::Hash* KeyTypePointer;
 
     HandleHashKey(KeyTypePointer aKey)
     {
@@ -127,25 +135,29 @@ public:
       return (reinterpret_cast<const uint32_t *>(aKey))[0];
     }
 
     void AddHandle(CacheFileHandle* aHandle);
     void RemoveHandle(CacheFileHandle* aHandle);
     already_AddRefed<CacheFileHandle> GetNewestHandle();
     void GetHandles(nsTArray<nsRefPtr<CacheFileHandle> > &aResult);
 
-    SHA1Sum::Hash *Hash() { return mHash; }
-    bool IsEmpty() { return mHandles.Length() == 0; }
+    SHA1Sum::Hash *Hash() const { return mHash; }
+    bool IsEmpty() const { return mHandles.Length() == 0; }
 
     enum { ALLOW_MEMMOVE = true };
 
 #ifdef DEBUG
     void AssertHandlesState();
 #endif
 
+    // Memory reporting
+    size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
   private:
     nsAutoArrayPtr<SHA1Sum::Hash> mHash;
     // Use weak pointers since the hash table access is on a single thread
     // only and CacheFileHandle removes itself from this table in its dtor
     // that may only be called on the same thread as we work with the hashtable
     // since we dispatch its Release() to this thread.
     nsTArray<CacheFileHandle*> mHandles;
   };
@@ -259,16 +271,20 @@ public:
     DOOMED
   };
 
   static nsresult EnumerateEntryFiles(EEnumerateMode aMode,
                                       CacheEntriesEnumerator** aEnumerator);
 
   static void GetCacheDirectory(nsIFile** result);
 
+  // Memory reporting
+  static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
+  static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
 private:
   friend class CacheFileHandle;
   friend class CacheFileChunk;
   friend class CacheFile;
   friend class ShutdownEvent;
   friend class OpenFileEvent;
   friend class CloseHandleEvent;
   friend class ReadEvent;
@@ -325,16 +341,19 @@ private:
   nsresult CreateCacheTree();
   nsresult OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate = false);
   void     NSPRHandleUsed(CacheFileHandle *aHandle);
 
   nsresult ScheduleMetadataWriteInternal(CacheFile * aFile);
   nsresult UnscheduleMetadataWriteInternal(CacheFile * aFile);
   nsresult ShutdownMetadataWriteSchedulingInternal();
 
+  // Memory reporting (private part)
+  size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
+
   static CacheFileIOManager           *gInstance;
   TimeStamp                            mStartTime;
   bool                                 mShuttingDown;
   nsRefPtr<CacheIOThread>              mIOThread;
   nsCOMPtr<nsIFile>                    mCacheDirectory;
   bool                                 mTreeCreated;
   CacheFileHandles                     mHandles;
   nsTArray<CacheFileHandle *>          mHandlesByLastUsed;
--- a/netwerk/cache2/CacheFileInputStream.cpp
+++ b/netwerk/cache2/CacheFileInputStream.cpp
@@ -625,10 +625,22 @@ CacheFileInputStream::MaybeNotifyListene
   }
   else {
     // Output have set EOF before mPos?
     MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
     NotifyListener();
   }
 }
 
+// Memory reporting
+
+size_t
+CacheFileInputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  // Everything the stream keeps a reference to is already reported somewhere else.
+  // mFile reports itself.
+  // mChunk reported as part of CacheFile.
+  // mCallback is usually CacheFile or a class that is reported elsewhere.
+  return mallocSizeOf(this);
+}
+
 } // net
 } // mozilla
--- a/netwerk/cache2/CacheFileInputStream.h
+++ b/netwerk/cache2/CacheFileInputStream.h
@@ -30,16 +30,19 @@ public:
   CacheFileInputStream(CacheFile *aFile);
 
   NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk);
   NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk);
   NS_IMETHOD OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
                               CacheFileChunk *aChunk);
   NS_IMETHOD OnChunkUpdated(CacheFileChunk *aChunk);
 
+  // Memory reporting
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
 private:
   virtual ~CacheFileInputStream();
 
   void ReleaseChunk();
   void EnsureCorrectChunk(bool aReleaseOnly);
   void CanRead(int64_t *aCanRead, const char **aBuf);
   void NotifyListener();
   void MaybeNotifyListener();
--- a/netwerk/cache2/CacheFileMetadata.cpp
+++ b/netwerk/cache2/CacheFileMetadata.cpp
@@ -856,10 +856,32 @@ CacheFileMetadata::ParseKey(const nsACSt
 
   mAnonymous =  info->IsAnonymous();
   mAppId = info->AppId();
   mInBrowser = info->IsInBrowserElement();
 
   return NS_OK;
 }
 
+// Memory reporting
+
+size_t
+CacheFileMetadata::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  size_t n = 0;
+  // mHandle reported via CacheFileIOManager.
+  n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
+  n += mallocSizeOf(mHashArray);
+  n += mallocSizeOf(mBuf);
+  n += mallocSizeOf(mWriteBuf);
+  // mListener is usually the owning CacheFile.
+
+  return n;
+}
+
+size_t
+CacheFileMetadata::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
+}
+
 } // net
 } // mozilla
--- a/netwerk/cache2/CacheFileMetadata.h
+++ b/netwerk/cache2/CacheFileMetadata.h
@@ -103,16 +103,20 @@ public:
   NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult);
   NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
                            nsresult aResult);
   NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult);
   NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult);
   NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult);
   NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult);
 
+  // Memory reporting
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
 private:
   virtual ~CacheFileMetadata();
 
   void     InitEmptyMetadata();
   nsresult ParseMetadata(uint32_t aMetaOffset, uint32_t aBufOffset, bool aHaveKey);
   nsresult CheckElements(const char *aBuf, uint32_t aSize);
   void     EnsureBuffer(uint32_t aSize);
   nsresult ParseKey(const nsACString &aKey);
--- a/netwerk/cache2/CacheFileOutputStream.cpp
+++ b/netwerk/cache2/CacheFileOutputStream.cpp
@@ -386,10 +386,23 @@ CacheFileOutputStream::NotifyListener()
     NS_NewOutputStreamReadyEvent(mCallback, mCallbackTarget);
 
   mCallback = nullptr;
   mCallbackTarget = nullptr;
 
   asyncCallback->OnOutputStreamReady(this);
 }
 
+// Memory reporting
+
+size_t
+CacheFileOutputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  // Everything the stream keeps a reference to is already reported somewhere else.
+  // mFile reports itself.
+  // mChunk reported as part of CacheFile.
+  // mCloseListener is CacheEntry, already reported.
+  // mCallback is usually CacheFile or a class that is reported elsewhere.
+  return mallocSizeOf(this);
+}
+
 } // net
 } // mozilla
--- a/netwerk/cache2/CacheFileOutputStream.h
+++ b/netwerk/cache2/CacheFileOutputStream.h
@@ -33,16 +33,19 @@ public:
   NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk);
   NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk);
   NS_IMETHOD OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
                               CacheFileChunk *aChunk);
   NS_IMETHOD OnChunkUpdated(CacheFileChunk *aChunk);
 
   void NotifyCloseListener();
 
+  // Memory reporting
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
 private:
   virtual ~CacheFileOutputStream();
 
   void ReleaseChunk();
   void EnsureCorrectChunk(bool aReleaseOnly);
   void FillHole();
   void NotifyListener();
 
--- a/netwerk/cache2/CacheIOThread.cpp
+++ b/netwerk/cache2/CacheIOThread.cpp
@@ -246,10 +246,33 @@ NS_IMETHODIMP CacheIOThread::OnProcessNe
 }
 
 NS_IMETHODIMP CacheIOThread::AfterProcessNextEvent(nsIThreadInternal *thread, uint32_t recursionDepth,
                                                    bool eventWasProcessed)
 {
   return NS_OK;
 }
 
+// Memory reporting
+
+size_t CacheIOThread::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  MonitorAutoLock lock(const_cast<CacheIOThread*>(this)->mMonitor);
+
+  size_t n = 0;
+  n += mallocSizeOf(mThread);
+  for (uint32_t level = 0; level < LAST_LEVEL; ++level) {
+    n += mEventQueue[level].SizeOfExcludingThis(mallocSizeOf);
+    // Events referenced by the queues are arbitrary objects we cannot be sure
+    // are reported elsewhere as well as probably not implementing nsISizeOf
+    // interface.  Deliberatly omitting them from reporting here.
+  }
+
+  return n;
+}
+
+size_t CacheIOThread::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
+}
+
 } // net
 } // mozilla
--- a/netwerk/cache2/CacheIOThread.h
+++ b/netwerk/cache2/CacheIOThread.h
@@ -40,16 +40,20 @@ public:
   };
 
   nsresult Init();
   nsresult Dispatch(nsIRunnable* aRunnable, uint32_t aLevel);
   bool IsCurrentThread();
   nsresult Shutdown();
   already_AddRefed<nsIEventTarget> Target();
 
+  // Memory reporting
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
 private:
   static void ThreadFunc(void* aClosure);
   void ThreadFunc();
   void LoopOneLevel(uint32_t aLevel);
   bool EventsPending(uint32_t aLastLevel = LAST_LEVEL);
 
   mozilla::Monitor mMonitor;
   PRThread* mThread;
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -5,16 +5,17 @@
 #include "CacheIndex.h"
 
 #include "CacheLog.h"
 #include "CacheFileIOManager.h"
 #include "CacheFileMetadata.h"
 #include "nsThreadUtils.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIDirectoryEnumerator.h"
+#include "nsISizeOf.h"
 #include "nsPrintfCString.h"
 #include "mozilla/DebugOnly.h"
 #include "prinrval.h"
 #include "nsIFile.h"
 #include "nsITimer.h"
 #include <algorithm>
 
 
@@ -3263,10 +3264,78 @@ CacheIndex::OnFileRenamed(CacheFileHandl
       break;
     default:
       MOZ_ASSERT(false, "Unexpected state!");
   }
 
   return NS_OK;
 }
 
+// Memory reporting
+
+namespace { // anon
+
+size_t
+CollectIndexEntryMemory(CacheIndexEntry* aEntry,
+                        mozilla::MallocSizeOf mallocSizeOf,
+                        void *arg)
+{
+  return aEntry->SizeOfExcludingThis(mallocSizeOf);
+}
+
+} // anon
+
+size_t
+CacheIndex::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  CacheIndexAutoLock lock(const_cast<CacheIndex*>(this));
+
+  size_t n = 0;
+  nsCOMPtr<nsISizeOf> sizeOf;
+
+  // mIndexHandle and mJournalHandle are reported via SizeOfHandlesRunnable
+  // in CacheFileIOManager::SizeOfExcludingThisInternal as part of special
+  // handles array.
+
+  sizeOf = do_QueryInterface(mCacheDirectory);
+  if (sizeOf) {
+    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
+  }
+
+  sizeOf = do_QueryInterface(mTimer);
+  if (sizeOf) {
+    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
+  }
+
+  n += mallocSizeOf(mRWBuf);
+  n += mallocSizeOf(mRWHash);
+
+  n += mIndex.SizeOfExcludingThis(&CollectIndexEntryMemory, mallocSizeOf);
+  n += mPendingUpdates.SizeOfExcludingThis(&CollectIndexEntryMemory, mallocSizeOf);
+  n += mTmpJournal.SizeOfExcludingThis(&CollectIndexEntryMemory, mallocSizeOf);
+
+  // mFrecencyArray and mExpirationArray items are reported by
+  // mIndex/mPendingUpdates
+  n += mFrecencyArray.SizeOfExcludingThis(mallocSizeOf);
+  n += mExpirationArray.SizeOfExcludingThis(mallocSizeOf);
+
+  return n;
+}
+
+// static
+size_t
+CacheIndex::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
+{
+  if (!gInstance)
+    return 0;
+
+  return gInstance->SizeOfExcludingThisInternal(mallocSizeOf);
+}
+
+// static
+size_t
+CacheIndex::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
+{
+  return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf);
+}
+
 } // net
 } // mozilla
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -81,29 +81,28 @@ class CacheIndexEntry : public PLDHashEn
 public:
   typedef const SHA1Sum::Hash& KeyType;
   typedef const SHA1Sum::Hash* KeyTypePointer;
 
   CacheIndexEntry(KeyTypePointer aKey)
   {
     MOZ_COUNT_CTOR(CacheIndexEntry);
     mRec = new CacheIndexRecord();
-    LOG(("CacheIndexEntry::CacheIndexEntry() - Created record [rec=%p]", mRec));
+    LOG(("CacheIndexEntry::CacheIndexEntry() - Created record [rec=%p]", mRec.get()));
     memcpy(&mRec->mHash, aKey, sizeof(SHA1Sum::Hash));
   }
   CacheIndexEntry(const CacheIndexEntry& aOther)
   {
     NS_NOTREACHED("CacheIndexEntry copy constructor is forbidden!");
   }
   ~CacheIndexEntry()
   {
     MOZ_COUNT_DTOR(CacheIndexEntry);
     LOG(("CacheIndexEntry::~CacheIndexEntry() - Deleting record [rec=%p]",
-         mRec));
-    delete mRec;
+         mRec.get()));
   }
 
   // KeyEquals(): does this entry match this key?
   bool KeyEquals(KeyTypePointer aKey) const
   {
     return memcmp(&mRec->mHash, aKey, sizeof(SHA1Sum::Hash)) == 0;
   }
 
@@ -242,16 +241,27 @@ public:
     LOG(("CacheIndexEntry::Log() [this=%p, hash=%08x%08x%08x%08x%08x, fresh=%u,"
          " initialized=%u, removed=%u, dirty=%u, anonymous=%u, inBrowser=%u, "
          "appId=%u, frecency=%u, expirationTime=%u, size=%u]",
          this, LOGSHA1(mRec->mHash), IsFresh(), IsInitialized(), IsRemoved(),
          IsDirty(), Anonymous(), InBrowser(), AppId(), GetFrecency(),
          GetExpirationTime(), GetFileSize()));
   }
 
+  // Memory reporting
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+  {
+    return mallocSizeOf(mRec.get());
+  }
+
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+  {
+    return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
+  }
+
 private:
   friend class CacheIndex;
   friend class CacheIndexEntryAutoManage;
 
   static const uint32_t kInitializedMask = 0x80000000;
   static const uint32_t kAnonymousMask   = 0x40000000;
   static const uint32_t kInBrowserMask   = 0x20000000;
 
@@ -268,17 +278,17 @@ private:
   // this entry during update or build process.
   static const uint32_t kFreshMask       = 0x04000000;
 
   static const uint32_t kReservedMask    = 0x03000000;
 
   // FileSize in kilobytes
   static const uint32_t kFileSizeMask    = 0x00FFFFFF;
 
-  CacheIndexRecord *mRec;
+  nsAutoPtr<CacheIndexRecord> mRec;
 };
 
 class CacheIndexStats
 {
 public:
   CacheIndexStats()
     : mCount(0)
     , mNotInitialized(0)
@@ -515,16 +525,19 @@ public:
   // Returns a hash of the least important entry that should be evicted if the
   // cache size is over limit and also returns a total number of all entries in
   // the index.
   static nsresult GetEntryForEviction(SHA1Sum::Hash *aHash, uint32_t *aCnt);
 
   // Returns cache size in kB.
   static nsresult GetCacheSize(uint32_t *_retval);
 
+  // Memory reporting
+  static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
+  static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
 private:
   friend class CacheIndexEntryAutoManage;
   friend class CacheIndexAutoLock;
   friend class CacheIndexAutoUnlock;
 
   virtual ~CacheIndex();
 
@@ -757,16 +770,19 @@ private:
   void ReleaseBuffer();
 
   // Methods used by CacheIndexEntryAutoManage to keep the arrays up to date.
   void InsertRecordToFrecencyArray(CacheIndexRecord *aRecord);
   void InsertRecordToExpirationArray(CacheIndexRecord *aRecord);
   void RemoveRecordFromFrecencyArray(CacheIndexRecord *aRecord);
   void RemoveRecordFromExpirationArray(CacheIndexRecord *aRecord);
 
+  // Memory reporting (private part)
+  size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
+
   static CacheIndex *gInstance;
 
   nsCOMPtr<nsIFile> mCacheDirectory;
 
   mozilla::Mutex mLock;
   EState         mState;
   // Timestamp of time when the index was initialized. We use it to delay
   // initial update or build of index.
--- a/netwerk/cache2/CacheStorage.h
+++ b/netwerk/cache2/CacheStorage.h
@@ -20,17 +20,34 @@ class nsIURI;
 class nsIApplicationCache;
 
 namespace mozilla {
 namespace net {
 
 // This dance is needed to make CacheEntryTable declarable-only in headers
 // w/o exporting CacheEntry.h file to make nsNetModule.cpp compilable.
 typedef nsRefPtrHashtable<nsCStringHashKey, CacheEntry> TCacheEntryTable;
-class CacheEntryTable : public TCacheEntryTable { };
+class CacheEntryTable : public TCacheEntryTable
+{
+public:
+  enum EType
+  {
+    MEMORY_ONLY,
+    ALL_ENTRIES
+  };
+
+  CacheEntryTable(EType aType) : mType(aType) { }
+  EType Type() const
+  {
+    return mType;
+  }
+private:
+  EType const mType;
+  CacheEntryTable() MOZ_DELETE;
+};
 
 class CacheStorage : public nsICacheStorage
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSICACHESTORAGE
 
 public:
   CacheStorage(nsILoadContextInfo* aInfo,
--- a/netwerk/cache2/CacheStorageService.cpp
+++ b/netwerk/cache2/CacheStorageService.cpp
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CacheLog.h"
 #include "CacheStorageService.h"
 #include "CacheFileIOManager.h"
 #include "CacheObserver.h"
+#include "CacheIndex.h"
 
 #include "nsICacheStorageVisitor.h"
 #include "nsIObserverService.h"
 #include "CacheStorage.h"
 #include "AppCacheStorage.h"
 #include "CacheEntry.h"
 #include "CacheFileUtils.h"
 
@@ -63,32 +64,34 @@ CacheMemoryConsumer::CacheMemoryConsumer
 
 void
 CacheMemoryConsumer::DoMemoryReport(uint32_t aCurrentSize)
 {
   if (CacheStorageService::Self())
     CacheStorageService::Self()->OnMemoryConsumptionChange(this, aCurrentSize);
 }
 
-NS_IMPL_ISUPPORTS1(CacheStorageService, nsICacheStorageService)
+NS_IMPL_ISUPPORTS2(CacheStorageService, nsICacheStorageService, nsIMemoryReporter)
 
 CacheStorageService* CacheStorageService::sSelf = nullptr;
 
 CacheStorageService::CacheStorageService()
 : mLock("CacheStorageService")
 , mShutdown(false)
 , mMemorySize(0)
 , mPurging(false)
 {
   CacheFileIOManager::Init();
 
   MOZ_ASSERT(!sSelf);
 
   sSelf = this;
   sGlobalEntryTables = new GlobalEntryTables();
+
+  RegisterStrongMemoryReporter(this);
 }
 
 CacheStorageService::~CacheStorageService()
 {
   LOG(("CacheStorageService::~CacheStorageService"));
   sSelf = nullptr;
 
   if (mMemorySize != 0)
@@ -579,17 +582,17 @@ nsresult CacheFilesDeletor::Init(CacheFi
 }
 
 void CacheFilesDeletor::Callback()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
   if (obsSvc) {
-    obsSvc->NotifyObservers(CacheStorageService::Self(),
+    obsSvc->NotifyObservers(CacheStorageService::SelfISupports(),
                             "cacheservice:empty-cache",
                             nullptr);
   }
 
   if (!mCallback)
     return;
 
   nsCOMPtr<nsICacheEntryDoomCallback> callback;
@@ -968,17 +971,17 @@ CacheStorageService::RecordMemoryOnlyEnt
   AppendMemoryStorageID(memoryStorageID);
 
   if (!sGlobalEntryTables->Get(memoryStorageID, &entries)) {
     if (!aOnlyInMemory) {
       LOG(("  not recorded as memory only"));
       return;
     }
 
-    entries = new CacheEntryTable();
+    entries = new CacheEntryTable(CacheEntryTable::MEMORY_ONLY);
     sGlobalEntryTables->Put(memoryStorageID, entries);
     LOG(("  new memory-only storage table for %s", memoryStorageID.get()));
   }
 
   if (aOnlyInMemory) {
     AddExactEntry(entries, entryKey, aEntry, aOverwrite);
   }
   else {
@@ -1186,17 +1189,17 @@ CacheStorageService::AddStorageEntry(nsC
   {
     mozilla::MutexAutoLock lock(mLock);
 
     NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
 
     // Ensure storage table
     CacheEntryTable* entries;
     if (!sGlobalEntryTables->Get(aContextKey, &entries)) {
-      entries = new CacheEntryTable();
+      entries = new CacheEntryTable(CacheEntryTable::ALL_ENTRIES);
       sGlobalEntryTables->Put(aContextKey, entries);
       LOG(("  new storage entries table for context %s", aContextKey.BeginReading()));
     }
 
     bool entryExists = entries->Get(entryKey, getter_AddRefs(entry));
 
     // check whether the file is already doomed
     if (entryExists && entry->IsFileDoomed() && !aReplace) {
@@ -1464,11 +1467,141 @@ CacheStorageService::CacheFileDoomed(nsI
 
   if (entry && entry->IsFileDoomed()) {
     entry->PurgeAndDoom();
   }
 
   return NS_OK;
 }
 
+// nsIMemoryReporter
+
+size_t
+CacheStorageService::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
+
+  size_t n = 0;
+  // The elemets are referenced by sGlobalEntryTables and are reported from there
+  n += mFrecencyArray.SizeOfExcludingThis(mallocSizeOf);
+  // The elemets are referenced by sGlobalEntryTables and are reported from there
+  n += mExpirationArray.SizeOfExcludingThis(mallocSizeOf);
+  // Entries reported manually in CacheStorageService::CollectReports callback
+  if (sGlobalEntryTables) {
+    n += sGlobalEntryTables->SizeOfIncludingThis(nullptr, mallocSizeOf);
+  }
+
+  return n;
+}
+
+size_t
+CacheStorageService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
+}
+
+namespace { // anon
+
+class ReportStorageMemoryData
+{
+public:
+  nsIMemoryReporterCallback *mHandleReport;
+  nsISupports *mData;
+};
+
+size_t CollectEntryMemory(nsACString const & aKey,
+                          nsRefPtr<mozilla::net::CacheEntry> const & aEntry,
+                          mozilla::MallocSizeOf mallocSizeOf,
+                          void * aClosure)
+{
+  CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
+
+  CacheEntryTable* aTable = static_cast<CacheEntryTable*>(aClosure);
+
+  size_t n = 0;
+  n += aKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
+
+  // Bypass memory-only entries, those will be reported when iterating
+  // the memory only table. Memory-only entries are stored in both ALL_ENTRIES
+  // and MEMORY_ONLY hashtables.
+  if (aTable->Type() == CacheEntryTable::MEMORY_ONLY || aEntry->UsingDisk())
+    n += aEntry->SizeOfIncludingThis(mallocSizeOf);
+
+  return n;
+}
+
+PLDHashOperator ReportStorageMemory(const nsACString& aKey,
+                                    CacheEntryTable* aTable,
+                                    void* aClosure)
+{
+  CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
+
+  size_t size = aTable->SizeOfIncludingThis(&CollectEntryMemory,
+                                            CacheStorageService::MallocSizeOf,
+                                            aTable);
+
+  ReportStorageMemoryData& data = *static_cast<ReportStorageMemoryData*>(aClosure);
+  data.mHandleReport->Callback(
+    EmptyCString(),
+    nsPrintfCString("explicit/network/cache2/%s-storage(%s)",
+      aTable->Type() == CacheEntryTable::MEMORY_ONLY ? "memory" : "disk",
+      aKey.BeginReading()),
+    nsIMemoryReporter::KIND_HEAP,
+    nsIMemoryReporter::UNITS_BYTES,
+    size,
+    NS_LITERAL_CSTRING("Memory used by the cache storage."),
+    data.mData);
+
+  return PL_DHASH_NEXT;
+}
+
+} // anon
+
+NS_IMETHODIMP
+CacheStorageService::CollectReports(nsIMemoryReporterCallback* aHandleReport, nsISupports* aData)
+{
+  nsresult rv;
+
+  rv = MOZ_COLLECT_REPORT(
+    "explicit/network/cache2/io", KIND_HEAP, UNITS_BYTES,
+    CacheFileIOManager::SizeOfIncludingThis(MallocSizeOf),
+    "Memory used by the cache IO manager.");
+  if (NS_WARN_IF(NS_FAILED(rv)))
+    return rv;
+
+  rv = MOZ_COLLECT_REPORT(
+    "explicit/network/cache2/index", KIND_HEAP, UNITS_BYTES,
+    CacheIndex::SizeOfIncludingThis(MallocSizeOf),
+    "Memory used by the cache index.");
+  if (NS_WARN_IF(NS_FAILED(rv)))
+    return rv;
+
+  MutexAutoLock lock(mLock);
+
+  // Report the service instance, this doesn't report entries, done lower
+  rv = MOZ_COLLECT_REPORT(
+    "explicit/network/cache2/service", KIND_HEAP, UNITS_BYTES,
+    SizeOfIncludingThis(MallocSizeOf),
+    "Memory used by the cache storage service.");
+  if (NS_WARN_IF(NS_FAILED(rv)))
+    return rv;
+
+  // Report all entries, each storage separately (by the context key)
+  //
+  // References are:
+  // sGlobalEntryTables to N CacheEntryTable
+  // CacheEntryTable to N CacheEntry
+  // CacheEntry to 1 CacheFile
+  // CacheFile to
+  //   N CacheFileChunk (keeping the actual data)
+  //   1 CacheFileMetadata (keeping http headers etc.)
+  //   1 CacheFileOutputStream
+  //   N CacheFileInputStream
+  ReportStorageMemoryData data;
+  data.mHandleReport = aHandleReport;
+  data.mData = aData;
+  sGlobalEntryTables->EnumerateRead(&ReportStorageMemory, &data);
+
+  return NS_OK;
+}
 
 } // net
 } // mozilla
--- a/netwerk/cache2/CacheStorageService.h
+++ b/netwerk/cache2/CacheStorageService.h
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef CacheStorageService__h__
 #define CacheStorageService__h__
 
 #include "nsICacheStorageService.h"
+#include "nsIMemoryReporter.h"
 
 #include "nsClassHashtable.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Atomics.h"
 #include "nsTArray.h"
@@ -38,36 +39,44 @@ private:
   friend class CacheStorageService;
   uint32_t mReportedMemoryConsumption;
 protected:
   CacheMemoryConsumer();
   void DoMemoryReport(uint32_t aCurrentSize);
 };
 
 class CacheStorageService : public nsICacheStorageService
+                          , public nsIMemoryReporter
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSICACHESTORAGESERVICE
+  NS_DECL_NSIMEMORYREPORTER
 
   CacheStorageService();
 
   void Shutdown();
   void DropPrivateBrowsingEntries();
 
   // Wipes out the new or the old cache directory completely.
   static void WipeCacheDirectory(uint32_t aVersion);
 
   static CacheStorageService* Self() { return sSelf; }
+  static nsISupports* SelfISupports() { return static_cast<nsICacheStorageService*>(Self()); }
   nsresult Dispatch(nsIRunnable* aEvent);
   static bool IsRunning() { return sSelf && !sSelf->mShutdown; }
   static bool IsOnManagementThread();
   already_AddRefed<nsIEventTarget> Thread() const;
   mozilla::Mutex& Lock() { return mLock; }
 
+  // Memory reporting
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
 private:
   virtual ~CacheStorageService();
   void ShutdownBackground();
 
 private:
   // The following methods may only be called on the management
   // thread.
   friend class CacheEntry;