Bug 1241096 - Add a better memory reporting system for AudioBuffers. r=erahm
☠☠ backed out by 2579ddeadeb1 ☠ ☠
authorPaul Adenot <paul@paul.cx>
Fri, 01 Apr 2016 08:27:08 +0200
changeset 291136 5d5b5862ab90e00fcf4c9737dbe4f810f9a7dae3
parent 291135 35053f4c889b0c2bec7812a6614d2b3eba2f349a
child 291137 f71f7068c9f0de5d9a006c67c4d1e397dc3b0b0b
push id74482
push userpaul@paul.cx
push dateFri, 01 Apr 2016 06:27:46 +0000
treeherdermozilla-inbound@5d5b5862ab90 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerserahm
bugs1241096
milestone48.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 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);
+  static AudioBufferMemoryTracker* GetInstance();
+private:
+  /* 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()
+{
+  StaticMutexAutoLock lock(sMutex);
+  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::GetInstance()->RegisterAudioBuffer(this);
 }
 
 AudioBuffer::~AudioBuffer()
 {
+  AudioBufferMemoryTracker::GetInstance()->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