Bug 969117 - Report memory usage of MediaResources. r=njn
authorEric Rahm <erahm@mozilla.com>
Wed, 05 Mar 2014 13:31:04 -0800
changeset 173088 3788d8852f6291079111f6f02ca9ac9c6c376aed
parent 173087 77b2ebd5ffff0e7cbe98b6c058ba3fc00a0433cd
child 173089 6e71e620511d68fb8d9f42d3f5c957a06b6c6605
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersnjn
bugs969117
milestone30.0a1
Bug 969117 - Report memory usage of MediaResources. r=njn
content/media/BufferMediaResource.h
content/media/MediaCache.cpp
content/media/MediaCache.h
content/media/MediaDecoder.cpp
content/media/MediaResource.cpp
content/media/MediaResource.h
content/media/RtspMediaResource.cpp
content/media/RtspMediaResource.h
content/media/mediasource/MediaSourceDecoder.h
content/media/mediasource/SourceBufferResource.h
xpcom/glue/nsDeque.cpp
xpcom/glue/nsDeque.h
--- a/content/media/BufferMediaResource.h
+++ b/content/media/BufferMediaResource.h
@@ -137,16 +137,34 @@ public:
 
   bool IsTransportSeekable() MOZ_OVERRIDE { return true; }
 
   virtual const nsCString& GetContentType() const MOZ_OVERRIDE
   {
     return mContentType;
   }
 
+  virtual size_t SizeOfExcludingThis(
+                        MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Not owned:
+    // - mBuffer
+    // - mPrincipal
+    size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
+    size += mContentType.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+
+    return size;
+  }
+
+  virtual size_t SizeOfIncludingThis(
+                        MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   const uint8_t * mBuffer;
   uint32_t mLength;
   uint32_t mOffset;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   const nsAutoCString mContentType;
 };
 
--- a/content/media/MediaCache.cpp
+++ b/content/media/MediaCache.cpp
@@ -381,16 +381,38 @@ MediaCacheStream::MediaCacheStream(Chann
     mPlaybackBytesPerSecond(10000),
     mPinCount(0),
     mCurrentMode(MODE_PLAYBACK),
     mMetadataInPartialBlockBuffer(false),
     mPartialBlockBuffer(new int64_t[BLOCK_SIZE/sizeof(int64_t)])
 {
 }
 
+size_t MediaCacheStream::SizeOfExcludingThis(
+                                MallocSizeOf aMallocSizeOf) const
+{
+  // Looks like these are not owned:
+  // - mClient
+  // - mPrincipal
+  size_t size = mBlocks.SizeOfExcludingThis(aMallocSizeOf);
+  size += mReadaheadBlocks.SizeOfExcludingThis(aMallocSizeOf);
+  size += mMetadataBlocks.SizeOfExcludingThis(aMallocSizeOf);
+  size += mPlayedBlocks.SizeOfExcludingThis(aMallocSizeOf);
+  size += mPartialBlockBuffer.SizeOfExcludingThis(aMallocSizeOf);
+
+  return size;
+}
+
+size_t MediaCacheStream::BlockList::SizeOfExcludingThis(
+                                MallocSizeOf aMallocSizeOf) const
+{
+  return mEntries.SizeOfExcludingThis(/* sizeOfEntryExcludingThis = */ nullptr,
+                                      aMallocSizeOf);
+}
+
 void MediaCacheStream::BlockList::AddFirstBlock(int32_t aBlock)
 {
   NS_ASSERTION(!mEntries.GetEntry(aBlock), "Block already in list");
   Entry* entry = mEntries.PutEntry(aBlock);
 
   if (mFirstBlock < 0) {
     entry->mNextBlock = entry->mPrevBlock = aBlock;
   } else {
--- a/content/media/MediaCache.h
+++ b/content/media/MediaCache.h
@@ -337,16 +337,19 @@ public:
   // be less than aCount. If the first byte of data is not in the cache,
   // this will block until the data is available or the stream is
   // closed, otherwise it won't block.
   nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
   // Seeks to aOffset in the stream then performs a Read operation. See
   // 'Read' for argument and return details.
   nsresult ReadAt(int64_t aOffset, char* aBuffer,
                   uint32_t aCount, uint32_t* aBytes);
+
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
+
 private:
   friend class MediaCache;
 
   /**
    * A doubly-linked list of blocks. Add/Remove/Get methods are all
    * constant time. We declare this here so that a stream can contain a
    * BlockList of its read-ahead blocks. Blocks are referred to by index
    * into the MediaCache::mIndex array.
@@ -380,16 +383,18 @@ private:
     void NotifyBlockSwapped(int32_t aBlockIndex1, int32_t aBlockIndex2);
 #ifdef DEBUG
     // Verify linked-list invariants
     void Verify();
 #else
     void Verify() {}
 #endif
 
+    size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
   private:
     struct Entry : public nsUint32HashKey {
       Entry(KeyTypePointer aKey) : nsUint32HashKey(aKey) { }
       Entry(const Entry& toCopy) : nsUint32HashKey(&toCopy.GetKey()),
         mNextBlock(toCopy.mNextBlock), mPrevBlock(toCopy.mPrevBlock) {}
 
       int32_t mNextBlock;
       int32_t mPrevBlock;
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -54,16 +54,18 @@ PRLogModuleInfo* gMediaDecoderLog;
 #define DECODER_LOG(type, msg)
 #endif
 
 class MediaMemoryTracker : public nsIMemoryReporter
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
 
+  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
+
   MediaMemoryTracker();
   virtual ~MediaMemoryTracker();
   void InitMemoryReporter();
 
   static StaticRefPtr<MediaMemoryTracker> sUniqueInstance;
 
   static MediaMemoryTracker* UniqueInstance() {
     if (!sUniqueInstance) {
@@ -1825,37 +1827,47 @@ MediaDecoder::IsAppleMP3Enabled()
 }
 #endif
 
 NS_IMETHODIMP
 MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
                                    nsISupports* aData)
 {
   int64_t video = 0, audio = 0;
+  size_t resources = 0;
   DecodersArray& decoders = Decoders();
   for (size_t i = 0; i < decoders.Length(); ++i) {
-    video += decoders[i]->VideoQueueMemoryInUse();
-    audio += decoders[i]->SizeOfAudioQueue();
+    MediaDecoder* decoder = decoders[i];
+    video += decoder->VideoQueueMemoryInUse();
+    audio += decoder->SizeOfAudioQueue();
+
+    if (decoder->GetResource()) {
+      resources += decoder->GetResource()->SizeOfIncludingThis(MallocSizeOf);
+    }
   }
 
 #define REPORT(_path, _amount, _desc)                                         \
   do {                                                                        \
       nsresult rv;                                                            \
       rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
                                    KIND_HEAP, UNITS_BYTES, _amount,           \
                                    NS_LITERAL_CSTRING(_desc), aData);         \
       NS_ENSURE_SUCCESS(rv, rv);                                              \
   } while (0)
 
-  REPORT("explicit/media/decoded-video", video,
+  REPORT("explicit/media/decoded/video", video,
          "Memory used by decoded video frames.");
 
-  REPORT("explicit/media/decoded-audio", audio,
+  REPORT("explicit/media/decoded/audio", audio,
          "Memory used by decoded audio chunks.");
 
+  REPORT("explicit/media/resources", resources,
+         "Memory used by media resources including streaming buffers, caches, "
+         "etc.");
+
   return NS_OK;
 }
 
 MediaDecoderOwner*
 MediaDecoder::GetOwner()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mOwner;
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -1243,16 +1243,30 @@ public:
   }
   virtual bool    IsDataCachedToEndOfResource(int64_t aOffset) { return true; }
   virtual bool    IsSuspendedByCache() { return false; }
   virtual bool    IsSuspended() { return false; }
   virtual bool    IsTransportSeekable() MOZ_OVERRIDE { return true; }
 
   nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);
 
+  virtual size_t SizeOfExcludingThis(
+                        MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Might be useful to track in the future:
+    // - mInput
+    return BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  virtual size_t SizeOfIncludingThis(
+                        MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   // These Unsafe variants of Read and Seek perform their operations
   // without acquiring mLock. The caller must obtain the lock before
   // calling. The implmentation of Read, Seek and ReadAt obtains the
   // lock before calling these Unsafe variants to read or seek.
   nsresult UnsafeRead(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
   nsresult UnsafeSeek(int32_t aWhence, int64_t aOffset);
 private:
--- a/content/media/MediaResource.h
+++ b/content/media/MediaResource.h
@@ -388,28 +388,56 @@ public:
     return nullptr;
   }
 
   // Return true if the stream is a live stream
   virtual bool IsRealTime() {
     return false;
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
+    return 0;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   virtual ~MediaResource() {};
 
 private:
   void Destroy();
 };
 
 class BaseMediaResource : public MediaResource {
 public:
   virtual nsIURI* URI() const { return mURI; }
   virtual void MoveLoadsToBackground();
 
+  virtual size_t SizeOfExcludingThis(
+                  MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Might be useful to track in the future:
+    // - mChannel
+    // - mURI (possibly owned, looks like just a ref from mChannel)
+    // Not owned:
+    // - mDecoder
+    size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
+    size += mContentType.SizeOfIncludingThisIfUnshared(aMallocSizeOf);
+
+    return size;
+  }
+
+  virtual size_t SizeOfIncludingThis(
+                  MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   BaseMediaResource(MediaDecoder* aDecoder,
                     nsIChannel* aChannel,
                     nsIURI* aURI,
                     const nsACString& aContentType) :
     mDecoder(aDecoder),
     mChannel(aChannel),
     mURI(aURI),
@@ -551,16 +579,34 @@ public:
   virtual int64_t GetLength();
   virtual int64_t GetNextCachedData(int64_t aOffset);
   virtual int64_t GetCachedDataEnd(int64_t aOffset);
   virtual bool    IsDataCachedToEndOfResource(int64_t aOffset);
   virtual bool    IsSuspendedByCache();
   virtual bool    IsSuspended();
   virtual bool    IsTransportSeekable() MOZ_OVERRIDE;
 
+  virtual size_t SizeOfExcludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE {
+    // Might be useful to track in the future:
+    //   - mListener (seems minor)
+    //   - mChannelStatistics (seems minor)
+    //     owned if RecordStatisticsTo is not called
+    //   - mDataReceivedEvent (seems minor)
+    size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
+    size += mCacheStream.SizeOfExcludingThis(aMallocSizeOf);
+
+    return size;
+  }
+
+  virtual size_t SizeOfIncludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   class Listener MOZ_FINAL : public nsIStreamListener,
                              public nsIInterfaceRequestor,
                              public nsIChannelEventSink
   {
   public:
     Listener(ChannelMediaResource* aResource) : mResource(aResource) {}
     ~Listener() {}
 
--- a/content/media/RtspMediaResource.cpp
+++ b/content/media/RtspMediaResource.cpp
@@ -64,16 +64,27 @@ public:
     MOZ_ASSERT(mSlotSize < UINT32_MAX / BUFFER_SLOT_NUM);
     mRingBuffer = new uint8_t[mTotalBufferSize];
     Reset();
   };
   ~RtspTrackBuffer() {
     MOZ_COUNT_DTOR(RtspTrackBuffer);
     mRingBuffer = nullptr;
   };
+
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+    // including this
+    size_t size = aMallocSizeOf(this);
+
+    // excluding this
+    size += mRingBuffer.SizeOfExcludingThis(aMallocSizeOf);
+
+    return size;
+  }
+
   void Start() {
     MonitorAutoLock monitor(mMonitor);
     mIsStarted = true;
     mFrameType = 0;
   }
   void Stop() {
     MonitorAutoLock monitor(mMonitor);
     mIsStarted = false;
@@ -364,16 +375,33 @@ RtspMediaResource::~RtspMediaResource()
 {
   RTSPMLOG("~RtspMediaResource");
   if (mListener) {
     // Kill its reference to us since we're going away
     mListener->Revoke();
   }
 }
 
+size_t
+RtspMediaResource::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
+  size += mTrackBuffer.SizeOfExcludingThis(aMallocSizeOf);
+
+  // Include the size of each track buffer.
+  for (size_t i = 0; i < mTrackBuffer.Length(); i++) {
+    size += mTrackBuffer[i]->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  // Could add in the future:
+  // - mMediaStreamController
+
+  return size;
+}
+
 NS_IMPL_ISUPPORTS2(RtspMediaResource::Listener,
                    nsIInterfaceRequestor, nsIStreamingProtocolListener);
 
 nsresult
 RtspMediaResource::Listener::OnMediaDataAvailable(uint8_t aTrackIdx,
                                                   const nsACString &data,
                                                   uint32_t length,
                                                   uint32_t offset,
--- a/content/media/RtspMediaResource.h
+++ b/content/media/RtspMediaResource.h
@@ -180,16 +180,24 @@ public:
     return nullptr;
   }
   // dummy
   virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset,
                                  uint32_t aCount) MOZ_OVERRIDE {
     return NS_ERROR_FAILURE;
   }
 
+  virtual size_t SizeOfExcludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
+  virtual size_t SizeOfIncludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   // Listener implements nsIStreamingProtocolListener as
   // mMediaStreamController's callback function.
   // It holds RtspMediaResource reference to notify the connection status and
   // data arrival. The Revoke function releases the reference when
   // RtspMediaResource is destroyed.
   class Listener MOZ_FINAL : public nsIInterfaceRequestor,
                              public nsIStreamingProtocolListener
   {
--- a/content/media/mediasource/MediaSourceDecoder.h
+++ b/content/media/mediasource/MediaSourceDecoder.h
@@ -128,15 +128,30 @@ public:
   {
     aRanges.AppendElement(MediaByteRange(0, GetLength()));
     return NS_OK;
   }
 
   virtual bool IsTransportSeekable() MOZ_OVERRIDE { return true; }
   virtual const nsCString& GetContentType() const MOZ_OVERRIDE { return mType; }
 
+  virtual size_t SizeOfExcludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
+    size += mType.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+
+    return size;
+  }
+
+  virtual size_t SizeOfIncludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   const nsAutoCString mType;
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_MEDIASOURCEDECODER_H_ */
--- a/content/media/mediasource/SourceBufferResource.h
+++ b/content/media/mediasource/SourceBufferResource.h
@@ -47,16 +47,26 @@ private:
   // (done in SourceBuffer::AppendData) which then requests
   // all SourceBuffers to evict data up to approximately
   // the same timepoint.
   struct ResourceItem {
     ResourceItem(uint8_t const* aData, uint32_t aSize) {
       mData.AppendElements(aData, aSize);
     }
     nsTArray<uint8_t> mData;
+
+    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+      // size including this
+      size_t size = aMallocSizeOf(this);
+
+      // size excluding this
+      size += mData.SizeOfExcludingThis(aMallocSizeOf);
+
+      return size;
+    }
   };
 
   class ResourceQueueDeallocator : public nsDequeFunctor {
     virtual void* operator() (void* anObject) {
       delete static_cast<ResourceItem*>(anObject);
       return nullptr;
     }
   };
@@ -170,16 +180,30 @@ private:
           break;
         }
         mOffset += item->mData.Length();
         delete PopFront();
         evicted = true;
       }
       return evicted;
     }
+
+    size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
+      // Calculate the size of the internal deque.
+      size_t size = nsDeque::SizeOfExcludingThis(aMallocSizeOf);
+
+      // Sum the ResourceItems.
+      for (int32_t i = 0; i < nsDeque::GetSize(); ++i) {
+        const ResourceItem* item =
+            static_cast<const ResourceItem*>(nsDeque::ObjectAt(i));
+        size += item->SizeOfIncludingThis(aMallocSizeOf);
+      }
+
+      return size;
+    }
   };
 
 public:
   SourceBufferResource(nsIPrincipal* aPrincipal,
                        const nsACString& aType);
   ~SourceBufferResource();
 
   virtual nsresult Close() MOZ_OVERRIDE;
@@ -221,16 +245,36 @@ public:
   {
     aRanges.AppendElement(MediaByteRange(mInputBuffer.GetOffset(),
                                          mInputBuffer.GetLength()));
     return NS_OK;
   }
 
   virtual const nsCString& GetContentType() const MOZ_OVERRIDE { return mType; }
 
+  virtual size_t SizeOfExcludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    ReentrantMonitorAutoEnter mon(mMonitor);
+
+    // Not owned:
+    // - mPrincipal
+    size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
+    size += mType.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+    size += mInputBuffer.SizeOfExcludingThis(aMallocSizeOf);
+
+    return size;
+  }
+
+  virtual size_t SizeOfIncludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   // Used by SourceBuffer.
   void AppendData(const uint8_t* aData, uint32_t aLength);
   void Ended();
   // Remove data from resource if it holds more than the threshold
   // number of bytes. Returns true if some data was evicted.
   bool EvictData(uint32_t aThreshold);
 
   // Remove data from resource before the given offset.
@@ -239,17 +283,17 @@ public:
 private:
   nsCOMPtr<nsIPrincipal> mPrincipal;
   const nsAutoCString mType;
 
   // Provides synchronization between SourceBuffers and InputAdapters.
   // Protects all of the member variables below.  Read() will await a
   // Notify() (from Seek, AppendData, Ended, or Close) when insufficient
   // data is available in mData.
-  ReentrantMonitor mMonitor;
+  mutable ReentrantMonitor mMonitor;
 
   // The buffer holding resource data is a queue of ResourceItem's.
   ResourceQueue mInputBuffer;
 
   uint64_t mOffset;
   bool mClosed;
   bool mEnded;
 };
--- a/xpcom/glue/nsDeque.cpp
+++ b/xpcom/glue/nsDeque.cpp
@@ -86,16 +86,33 @@ nsDeque::~nsDeque() {
   Erase();
   if (mData && (mData!=mBuffer)) {
     free(mData);
   }
   mData=0;
   SetDeallocator(0);
 }
 
+size_t nsDeque::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+  size_t size = 0;
+  if (mData != mBuffer) {
+    size += aMallocSizeOf(mData);
+  }
+
+  if (mDeallocator) {
+    size += aMallocSizeOf(mDeallocator);
+  }
+
+  return size;
+}
+
+size_t nsDeque::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 /**
  * Set the functor to be called by Erase()
  * The deque owns the functor.
  *
  * @param   aDeallocator functor object for use by Erase()
  */
 void nsDeque::SetDeallocator(nsDequeFunctor* aDeallocator){
   delete mDeallocator;
--- a/xpcom/glue/nsDeque.h
+++ b/xpcom/glue/nsDeque.h
@@ -23,16 +23,17 @@
 
 #ifndef _NSDEQUE
 #define _NSDEQUE
 
 #include "nscore.h"
 #include "nsDebug.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/fallible.h"
+#include "mozilla/MemoryReporting.h"
 
 /**
  * The nsDequeFunctor class is used when you want to create
  * callbacks between the deque and your generic code.
  * Use these objects in a call to ForEach();
  *
  */
 
@@ -190,16 +191,19 @@ class NS_COM_GLUE nsDeque {
    *
    * @param   aFunctor object to call for each member
    * @return  first nonzero result of aFunctor or 0.
    */
   const void* FirstThat(nsDequeFunctor& aFunctor) const;
 
   void SetDeallocator(nsDequeFunctor* aDeallocator);
 
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 protected:
   int32_t         mSize;
   int32_t         mCapacity;
   int32_t         mOrigin;
   nsDequeFunctor* mDeallocator;
   void*           mBuffer[8];
   void**          mData;