Bug 1241096 - Add a better memory reporting system for AudioBuffers. r=erahm
authorPaul Adenot <paul@paul.cx>
Fri, 01 Apr 2016 13:36:41 +0200
changeset 291220 55fa7abf986320f694750d14fe67ed87722eee39
parent 291219 d04909bc6f959de45d64fdb0143df7606d443b58
child 291221 bc382208754b1ee8c255e5cbd8c984e63791231c
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerserahm
bugs1241096
milestone48.0a1
Bug 1241096 - Add a better memory reporting system for AudioBuffers. r=erahm MozReview-Commit-ID: GHiauyyD3R2
dom/media/webaudio/AudioBuffer.cpp
dom/media/webaudio/AudioBuffer.h
dom/media/webaudio/AudioBufferSourceNode.cpp
--- a/dom/media/webaudio/AudioBuffer.cpp
+++ b/dom/media/webaudio/AudioBuffer.cpp
@@ -7,16 +7,17 @@
 #include "AudioBuffer.h"
 #include "mozilla/dom/AudioBufferBinding.h"
 #include "jsfriendapi.h"
 #include "mozilla/ErrorResult.h"
 #include "AudioSegment.h"
 #include "AudioChannelFormat.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/CheckedInt.h"
+#include "mozilla/MemoryReporting.h"
 #include "AudioNodeEngine.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioBuffer)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBuffer)
@@ -34,33 +35,157 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Aud
   for (uint32_t i = 0; i < tmp->mJSChannels.Length(); ++i) {
     NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJSChannels[i])
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AudioBuffer, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioBuffer, Release)
 
+/**
+ * AudioBuffers can be shared between AudioContexts, so we need a separate
+ * mechanism to track their memory usage. This thread-safe class keeps track of
+ * all the AudioBuffers, and gets called back by the memory reporting system
+ * when a memory report is needed, reporting how much memory is used by the
+ * buffers backing AudioBuffer objects. */
+class AudioBufferMemoryTracker : public nsIMemoryReporter
+{
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIMEMORYREPORTER
+
+private:
+  AudioBufferMemoryTracker();
+  virtual ~AudioBufferMemoryTracker();
+
+public:
+  /* Those methods can be called on any thread. */
+  static void RegisterAudioBuffer(const AudioBuffer* aAudioBuffer);
+  static void UnregisterAudioBuffer(const AudioBuffer* aAudioBuffer);
+private:
+  static AudioBufferMemoryTracker* GetInstance();
+  /* Those methods must be called with the lock held. */
+  void RegisterAudioBufferInternal(const AudioBuffer* aAudioBuffer);
+  /* Returns the number of buffers still present in the hash table. */
+  uint32_t UnregisterAudioBufferInternal(const AudioBuffer* aAudioBuffer);
+  void Init();
+
+  /* This protects all members of this class. */
+  static StaticMutex sMutex;
+  static StaticRefPtr<AudioBufferMemoryTracker> sSingleton;
+  nsTHashtable<nsPtrHashKey<const AudioBuffer>> mBuffers;
+};
+
+StaticRefPtr<AudioBufferMemoryTracker> AudioBufferMemoryTracker::sSingleton;
+StaticMutex AudioBufferMemoryTracker::sMutex;
+
+NS_IMPL_ISUPPORTS(AudioBufferMemoryTracker, nsIMemoryReporter);
+
+AudioBufferMemoryTracker* AudioBufferMemoryTracker::GetInstance()
+{
+  sMutex.AssertCurrentThreadOwns();
+  if (!sSingleton) {
+    sSingleton = new AudioBufferMemoryTracker();
+    sSingleton->Init();
+  }
+  return sSingleton;
+}
+
+AudioBufferMemoryTracker::AudioBufferMemoryTracker()
+{
+}
+
+void
+AudioBufferMemoryTracker::Init()
+{
+  RegisterWeakMemoryReporter(this);
+}
+
+AudioBufferMemoryTracker::~AudioBufferMemoryTracker()
+{
+  UnregisterWeakMemoryReporter(this);
+}
+
+void
+AudioBufferMemoryTracker::RegisterAudioBuffer(const AudioBuffer* aAudioBuffer)
+{
+  StaticMutexAutoLock lock(sMutex);
+  AudioBufferMemoryTracker* tracker = AudioBufferMemoryTracker::GetInstance();
+  tracker->RegisterAudioBufferInternal(aAudioBuffer);
+}
+
+void
+AudioBufferMemoryTracker::UnregisterAudioBuffer(const AudioBuffer* aAudioBuffer)
+{
+  StaticMutexAutoLock lock(sMutex);
+  AudioBufferMemoryTracker* tracker = AudioBufferMemoryTracker::GetInstance();
+  uint32_t count;
+  count = tracker->UnregisterAudioBufferInternal(aAudioBuffer);
+  if (count == 0) {
+    sSingleton = nullptr;
+  }
+}
+
+void
+AudioBufferMemoryTracker::RegisterAudioBufferInternal(const AudioBuffer* aAudioBuffer)
+{
+  sMutex.AssertCurrentThreadOwns();
+  mBuffers.PutEntry(aAudioBuffer);
+}
+
+uint32_t
+AudioBufferMemoryTracker::UnregisterAudioBufferInternal(const AudioBuffer* aAudioBuffer)
+{
+  sMutex.AssertCurrentThreadOwns();
+  mBuffers.RemoveEntry(aAudioBuffer);
+  return mBuffers.Count();
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(AudioBufferMemoryTrackerMallocSizeOf)
+
+NS_IMETHODIMP
+AudioBufferMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
+                                         nsISupports* data, bool)
+{
+ size_t amount = 0;
+ nsresult rv;
+
+ for (auto iter = mBuffers.Iter(); !iter.Done(); iter.Next()) {
+   amount += iter.Get()->GetKey()->SizeOfIncludingThis(AudioBufferMemoryTrackerMallocSizeOf);
+ }
+
+ rv = handleReport->Callback(EmptyCString(),
+                             NS_LITERAL_CSTRING("explicit/webaudio/audiobuffer"),
+                             KIND_HEAP, UNITS_BYTES, amount,
+                             NS_LITERAL_CSTRING("Memory used by AudioBuffer"
+                                                " objects (Web Audio)"),
+                             data);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
 AudioBuffer::AudioBuffer(AudioContext* aContext, uint32_t aNumberOfChannels,
                          uint32_t aLength, float aSampleRate,
                          already_AddRefed<ThreadSharedFloatArrayBufferList>
                            aInitialContents)
   : mOwnerWindow(do_GetWeakReference(aContext->GetOwner())),
     mSharedChannels(aInitialContents),
     mLength(aLength),
     mSampleRate(aSampleRate)
 {
   MOZ_ASSERT(!mSharedChannels ||
              mSharedChannels->GetChannels() == aNumberOfChannels);
   mJSChannels.SetLength(aNumberOfChannels);
   mozilla::HoldJSObjects(this);
+  AudioBufferMemoryTracker::RegisterAudioBuffer(this);
 }
 
 AudioBuffer::~AudioBuffer()
 {
+  AudioBufferMemoryTracker::UnregisterAudioBuffer(this);
   ClearJSChannels();
 }
 
 void
 AudioBuffer::ClearJSChannels()
 {
   mJSChannels.Clear();
   mozilla::DropJSObjects(this);
--- a/dom/media/webaudio/AudioBuffer.h
+++ b/dom/media/webaudio/AudioBuffer.h
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef AudioBuffer_h_
 #define AudioBuffer_h_
 
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/StaticMutex.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "AudioContext.h"
 #include "js/TypeDecls.h"
 #include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -610,19 +610,18 @@ AudioBufferSourceNode::DestroyMediaStrea
     Context()->UnregisterAudioBufferSourceNode(this);
   }
 }
 
 size_t
 AudioBufferSourceNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
-  if (mBuffer) {
-    amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
-  }
+
+  /* mBuffer can be shared and is accounted for separately. */
 
   amount += mPlaybackRate->SizeOfIncludingThis(aMallocSizeOf);
   amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
   return amount;
 }
 
 size_t
 AudioBufferSourceNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const