Bug 993546 - Refactor malloc-wrapping memory reporter implementations. r=njn, a=abillings, ba=noidlchanges
authorNathan Froyd <froydnj@mozilla.com>
Tue, 15 Apr 2014 13:10:26 -0400
changeset 183767 c8f1a4f5ca4d
parent 183766 e24aafe4dffd
child 183768 caf2147c04f4
push id3476
push userryanvm@gmail.com
push date2014-04-15 17:26 +0000
treeherdermozilla-beta@c8f1a4f5ca4d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn, abillings
bugs993546
milestone29.0
Bug 993546 - Refactor malloc-wrapping memory reporter implementations. r=njn, a=abillings, ba=noidlchanges
extensions/spellcheck/hunspell/src/hunspell_alloc_hooks.h
extensions/spellcheck/hunspell/src/mozHunspell.cpp
extensions/spellcheck/hunspell/src/mozHunspell.h
extensions/spellcheck/hunspell/src/mozHunspellAllocator.h
gfx/thebes/gfxAndroidPlatform.cpp
xpcom/base/nsIMemoryReporter.idl
xpcom/build/nsXPComInit.cpp
--- a/extensions/spellcheck/hunspell/src/hunspell_alloc_hooks.h
+++ b/extensions/spellcheck/hunspell/src/hunspell_alloc_hooks.h
@@ -43,68 +43,16 @@
  * during the configure step.
  *
  * Currently, the memory allocated using operator new/new[] is not being
  * tracked, but that's OK, since almost all of the memory used by Hunspell is
  * allocated using C memory allocation functions.
  */
 
 #include "mozilla/mozalloc.h"
-
-extern void HunspellReportMemoryAllocation(void*);
-extern void HunspellReportMemoryDeallocation(void*);
-
-inline void* hunspell_malloc(size_t size)
-{
-  void* result = moz_malloc(size);
-  HunspellReportMemoryAllocation(result);
-  return result;
-}
-#define malloc(size) hunspell_malloc(size)
-
-inline void* hunspell_calloc(size_t count, size_t size)
-{
-  void* result = moz_calloc(count, size);
-  HunspellReportMemoryAllocation(result);
-  return result;
-}
-#define calloc(count, size) hunspell_calloc(count, size)
-
-inline void hunspell_free(void* ptr)
-{
-  HunspellReportMemoryDeallocation(ptr);
-  moz_free(ptr);
-}
-#define free(ptr) hunspell_free(ptr)
+#include "mozHunspellAllocator.h"
 
-inline void* hunspell_realloc(void* ptr, size_t size)
-{
-  HunspellReportMemoryDeallocation(ptr);
-  void* result = moz_realloc(ptr, size);
-  if (result) {
-    HunspellReportMemoryAllocation(result);
-  } else {
-    // realloc failed;  undo the HunspellReportMemoryDeallocation from above
-    HunspellReportMemoryAllocation(ptr);
-  }
-  return result;
-}
-#define realloc(ptr, size) hunspell_realloc(ptr, size)
-
-inline char* hunspell_strdup(const char* str)
-{
-  char* result = moz_strdup(str);
-  HunspellReportMemoryAllocation(result);
-  return result;
-}
-#define strdup(str) hunspell_strdup(str)
-
-#if defined(HAVE_STRNDUP)
-inline char* hunspell_strndup(const char* str, size_t size)
-{
-  char* result = moz_strndup(str, size);
-  HunspellReportMemoryAllocation(result);
-  return result;
-}
-#define strndup(str, size) hunspell_strndup(str, size)
-#endif
+#define malloc(size) HunspellAllocator::CountingMalloc(size)
+#define calloc(count, size) HunspellAllocator::CountingCalloc(count, size)
+#define free(ptr) HunspellAllocator::CountingFree(ptr)
+#define realloc(ptr, size) HunspellAllocator::CountingRealloc(ptr, size)
 
 #endif
--- a/extensions/spellcheck/hunspell/src/mozHunspell.cpp
+++ b/extensions/spellcheck/hunspell/src/mozHunspell.cpp
@@ -91,32 +91,24 @@ NS_INTERFACE_MAP_BEGIN(mozHunspell)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozHunspell)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_3(mozHunspell,
                            mPersonalDictionary,
                            mEncoder,
                            mDecoder)
 
-int64_t mozHunspell::sAmount = 0;
-
-// WARNING: hunspell_alloc_hooks.h uses these two functions.
-void HunspellReportMemoryAllocation(void* ptr) {
-  mozHunspell::OnAlloc(ptr);
-}
-void HunspellReportMemoryDeallocation(void* ptr) {
-  mozHunspell::OnFree(ptr);
-}
+template<> mozilla::Atomic<size_t> mozilla::CountingAllocatorBase<HunspellAllocator>::sAmount(0);
 
 mozHunspell::mozHunspell()
   : mHunspell(nullptr)
 {
 #ifdef DEBUG
-  // There must be only one instance of this class, due to |sAmount|
-  // being static.
+  // There must be only one instance of this class: it reports memory based on
+  // a single static count in HunspellAllocator.
   static bool hasRun = false;
   MOZ_ASSERT(!hasRun);
   hasRun = true;
 #endif
 }
 
 nsresult
 mozHunspell::Init()
--- a/extensions/spellcheck/hunspell/src/mozHunspell.h
+++ b/extensions/spellcheck/hunspell/src/mozHunspell.h
@@ -68,16 +68,17 @@
 #include "nsCOMArray.h"
 #include "nsIMemoryReporter.h"
 #include "nsIObserver.h"
 #include "nsIUnicodeEncoder.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsInterfaceHashtable.h"
 #include "nsWeakReference.h"
 #include "nsCycleCollectionParticipant.h"
+#include "mozHunspellAllocator.h"
 
 #define MOZ_HUNSPELL_CONTRACTID "@mozilla.org/spellchecker/engine;1"
 #define MOZ_HUNSPELL_CID         \
 /* 56c778e4-1bee-45f3-a689-886692a97fe7 */   \
 { 0x56c778e4, 0x1bee, 0x45f3, \
   { 0xa6, 0x89, 0x88, 0x66, 0x92, 0xa9, 0x7f, 0xe7 } }
 
 class mozHunspell : public mozISpellCheckingEngine,
@@ -96,27 +97,21 @@ public:
 
   nsresult Init();
 
   void LoadDictionaryList();
 
   // helper method for converting a word to the charset of the dictionary
   nsresult ConvertCharset(const char16_t* aStr, char ** aDst);
 
-  MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
-  MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
-
-  static void OnAlloc(void* ptr) { sAmount += MallocSizeOfOnAlloc(ptr); }
-  static void OnFree (void* ptr) { sAmount -= MallocSizeOfOnFree (ptr); }
-
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                             nsISupports* aData)
   {
     return MOZ_COLLECT_REPORT(
-      "explicit/spell-check", KIND_HEAP, UNITS_BYTES, sAmount,
+      "explicit/spell-check", KIND_HEAP, UNITS_BYTES, HunspellAllocator::MemoryAllocated(),
       "Memory used by the spell-checking engine.");
   }
 
 protected:
 
   nsCOMPtr<mozIPersonalDictionary> mPersonalDictionary;
   nsCOMPtr<nsIUnicodeEncoder>      mEncoder;
   nsCOMPtr<nsIUnicodeDecoder>      mDecoder;
@@ -126,13 +121,11 @@ protected:
   nsString  mDictionary;
   nsString  mLanguage;
   nsCString mAffixFileName;
 
   // dynamic dirs used to search for dictionaries
   nsCOMArray<nsIFile> mDynamicDirectories;
 
   Hunspell  *mHunspell;
-
-  static int64_t sAmount;
 };
 
 #endif
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/src/mozHunspellAllocator.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 mozHunspellAllocator_h__
+#define mozHunspellAllocator_h__
+
+#include "nsIMemoryReporter.h"
+
+class HunspellAllocator : public mozilla::CountingAllocatorBase<HunspellAllocator>
+{
+};
+
+#endif
--- a/gfx/thebes/gfxAndroidPlatform.cpp
+++ b/gfx/thebes/gfxAndroidPlatform.cpp
@@ -26,87 +26,60 @@
 #include FT_MODULE_H
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 static FT_Library gPlatformFTLibrary = nullptr;
 
-class FreetypeReporter MOZ_FINAL : public nsIMemoryReporter
+class FreetypeReporter MOZ_FINAL : public nsIMemoryReporter,
+                                   public CountingAllocatorBase<FreetypeReporter>
 {
 public:
     NS_DECL_ISUPPORTS
 
-    FreetypeReporter()
+    static void* Malloc(FT_Memory, long size)
     {
-#ifdef DEBUG
-        // There must be only one instance of this class, due to |sAmount|
-        // being static.
-        static bool hasRun = false;
-        MOZ_ASSERT(!hasRun);
-        hasRun = true;
-#endif
+        return CountingMalloc(size);
     }
 
-    MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
-    MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
-
-    static void* CountingAlloc(FT_Memory, long size)
+    static void Free(FT_Memory, void* p)
     {
-        void *p = malloc(size);
-        sAmount += MallocSizeOfOnAlloc(p);
-        return p;
-    }
-
-    static void CountingFree(FT_Memory, void* p)
-    {
-        sAmount -= MallocSizeOfOnFree(p);
-        free(p);
+        return CountingFree(p);
     }
 
     static void*
-    CountingRealloc(FT_Memory, long cur_size, long new_size, void* p)
+    Realloc(FT_Memory, long cur_size, long new_size, void* p)
     {
-        sAmount -= MallocSizeOfOnFree(p);
-        void *pnew = realloc(p, new_size);
-        if (pnew) {
-            sAmount += MallocSizeOfOnAlloc(pnew);
-        } else {
-            // realloc failed;  undo the decrement from above
-            sAmount += MallocSizeOfOnAlloc(p);
-        }
-        return pnew;
+        return CountingRealloc(p, new_size);
     }
 
     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                               nsISupports* aData)
     {
         return MOZ_COLLECT_REPORT(
-            "explicit/freetype", KIND_HEAP, UNITS_BYTES, sAmount,
+            "explicit/freetype", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
             "Memory used by Freetype.");
     }
-
-private:
-    static int64_t sAmount;
 };
 
 NS_IMPL_ISUPPORTS1(FreetypeReporter, nsIMemoryReporter)
 
-int64_t FreetypeReporter::sAmount = 0;
+template<> Atomic<size_t> CountingAllocatorBase<FreetypeReporter>::sAmount(0);
 
 static FT_MemoryRec_ sFreetypeMemoryRecord;
 
 gfxAndroidPlatform::gfxAndroidPlatform()
 {
     // A custom allocator.  It counts allocations, enabling memory reporting.
     sFreetypeMemoryRecord.user    = nullptr;
-    sFreetypeMemoryRecord.alloc   = FreetypeReporter::CountingAlloc;
-    sFreetypeMemoryRecord.free    = FreetypeReporter::CountingFree;
-    sFreetypeMemoryRecord.realloc = FreetypeReporter::CountingRealloc;
+    sFreetypeMemoryRecord.alloc   = FreetypeReporter::Malloc;
+    sFreetypeMemoryRecord.free    = FreetypeReporter::Free;
+    sFreetypeMemoryRecord.realloc = FreetypeReporter::Realloc;
 
     // These two calls are equivalent to FT_Init_FreeType(), but allow us to
     // provide a custom memory allocator.
     FT_New_Library(&sFreetypeMemoryRecord, &gPlatformFTLibrary);
     FT_Add_Default_Modules(gPlatformFTLibrary);
 
     RegisterStrongMemoryReporter(new FreetypeReporter());
 
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -372,16 +372,17 @@ interface nsIMemoryReporterManager : nsI
                  out double jsMilliseconds, out double nonJSMilliseconds);
 };
 
 %{C++
 
 #include "js/TypeDecls.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
+#include "mozilla/Atomics.h"
 
 class nsPIDOMWindow;
 
 // nsIHandleReportCallback is a better name, but keep nsIMemoryReporterCallback
 // around for backwards compatibility.
 typedef nsIMemoryReporterCallback nsIHandleReportCallback;
 
 namespace mozilla {
@@ -501,16 +502,126 @@ void RunReportersForThisProcess();
       return moz_malloc_size_of(aPtr);                                        \
   }
 #define MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(fn)                                 \
   static size_t fn(const void* aPtr)                                          \
   {                                                                           \
       return moz_malloc_size_of(aPtr);                                        \
   }
 
+namespace mozilla {
+
+// This CRTP class handles several details of wrapping allocators and should
+// be preferred to manually counting with MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC
+// and MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE.  The typical use is in a memory
+// reporter for a particular third party library:
+//
+//   class MyMemoryReporter : public CountingAllocatorBase<MyMemoryReporter>
+//   {
+//     ...
+//     NS_IMETHODIMP
+//     CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+//     {
+//        return MOZ_COLLECT_REPORTER(
+//          "explicit/path/to/somewhere", KIND_HEAP, UNITS_BYTES,
+//          MemoryAllocated(),
+//          "A description of what we are reporting."
+//     }
+//   };
+//
+//   ...somewhere later in the code...
+//   SetThirdPartyMemoryFunctions(MyMemoryReporter::CountingAlloc,
+//                                MyMemoryReporter::CountingFree);
+template<typename T>
+class CountingAllocatorBase
+{
+public:
+  CountingAllocatorBase()
+  {
+#ifdef DEBUG
+    // There must be only one instance of this class, due to |sAmount| being
+    // static.
+    static bool hasRun = false;
+    MOZ_ASSERT(!hasRun);
+    hasRun = true;
+#endif
+  }
+
+  static size_t
+  MemoryAllocated()
+  {
+    return sAmount;
+  }
+
+  static void*
+  CountingMalloc(size_t size)
+  {
+    void* p = malloc(size);
+    sAmount += MallocSizeOfOnAlloc(p);
+    return p;
+  }
+
+  static void*
+  CountingCalloc(size_t nmemb, size_t size)
+  {
+    void* p = calloc(nmemb, size);
+    sAmount += MallocSizeOfOnAlloc(p);
+    return p;
+  }
+
+  static void*
+  CountingRealloc(void* p, size_t size)
+  {
+    size_t oldsize = MallocSizeOfOnFree(p);
+    void *pnew = realloc(p, size);
+    if (pnew) {
+      size_t newsize = MallocSizeOfOnAlloc(pnew);
+      sAmount += newsize - oldsize;
+    } else if (size == 0) {
+      // We asked for a 0-sized (re)allocation of some existing pointer
+      // and received NULL in return.  0-sized allocations are permitted
+      // to either return NULL or to allocate a unique object per call (!).
+      // For a malloc implementation that chooses the second strategy,
+      // that allocation may fail (unlikely, but possible).
+      //
+      // Given a NULL return value and an allocation size of 0, then, we
+      // don't know if that means the original pointer was freed or if
+      // the allocation of the unique object failed.  If the original
+      // pointer was freed, then we have nothing to do here.  If the
+      // allocation of the unique object failed, the original pointer is
+      // still valid and we ought to undo the decrement from above.
+      // However, we have no way of knowing how the underlying realloc
+      // implementation is behaving.  Assuming that the original pointer
+      // was freed is the safest course of action.  We do, however, need
+      // to note that we freed memory.
+      sAmount -= oldsize;
+    } else {
+      // realloc failed.  The amount allocated hasn't changed.
+    }
+    return pnew;
+  }
+
+  static void
+  CountingFree(void* p)
+  {
+    sAmount -= MallocSizeOfOnFree(p);
+    free(p);
+  }
+
+private:
+  // |sAmount| can be (implicitly) accessed by multiple threads, so it
+  // must be thread-safe.
+  static Atomic<size_t> sAmount;
+
+  MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
+  MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
+};
+
+}
+
 // This macro assumes the presence of appropriate |aHandleReport| and |aData|
 // variables.
 #define MOZ_COLLECT_REPORT(path, kind, units, amount, description)            \
   aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(path),           \
                           kind, units, amount,                                \
                           NS_LITERAL_CSTRING(description), aData)
 
 %}
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -333,80 +333,50 @@ NS_GetTraceRefcnt(nsITraceRefcnt** resul
 
 EXPORT_XPCOM_API(nsresult)
 NS_InitXPCOM(nsIServiceManager* *result,
                              nsIFile* binDirectory)
 {
     return NS_InitXPCOM2(result, binDirectory, nullptr);
 }
 
-class ICUReporter MOZ_FINAL : public nsIMemoryReporter
+class ICUReporter MOZ_FINAL : public nsIMemoryReporter,
+                              public CountingAllocatorBase<ICUReporter>
 {
 public:
     NS_DECL_ISUPPORTS
 
-    ICUReporter()
-    {
-#ifdef DEBUG
-        // There must be only one instance of this class, due to |sAmount|
-        // being static.
-        static bool hasRun = false;
-        MOZ_ASSERT(!hasRun);
-        hasRun = true;
-#endif
-        sAmount = 0;
-    }
-
     static void* Alloc(const void*, size_t size)
     {
-        void* p = malloc(size);
-        sAmount += MallocSizeOfOnAlloc(p);
-        return p;
+        return CountingMalloc(size);
     }
 
     static void* Realloc(const void*, void* p, size_t size)
     {
-        sAmount -= MallocSizeOfOnFree(p);
-        void *pnew = realloc(p, size);
-        if (pnew) {
-            sAmount += MallocSizeOfOnAlloc(pnew);
-        } else {
-            // realloc failed;  undo the decrement from above
-            sAmount += MallocSizeOfOnAlloc(p);
-        }
-        return pnew;
+        return CountingRealloc(p, size);
     }
 
     static void Free(const void*, void* p)
     {
-        sAmount -= MallocSizeOfOnFree(p);
-        free(p);
+        return CountingFree(p);
     }
 
 private:
-    // |sAmount| can be (implicitly) accessed by multiple JSRuntimes, so it
-    // must be thread-safe.
-    static Atomic<size_t> sAmount;
-
-    MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
-    MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
-    MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
-
     NS_IMETHODIMP
     CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
     {
         return MOZ_COLLECT_REPORT(
-            "explicit/icu", KIND_HEAP, UNITS_BYTES, sAmount,
+            "explicit/icu", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
             "Memory used by ICU, a Unicode and globalization support library.");
     }
 };
 
 NS_IMPL_ISUPPORTS1(ICUReporter, nsIMemoryReporter)
 
-/* static */ Atomic<size_t> ICUReporter::sAmount;
+/* static */ template<> Atomic<size_t> CountingAllocatorBase<ICUReporter>::sAmount(0);
 
 EXPORT_XPCOM_API(nsresult)
 NS_InitXPCOM2(nsIServiceManager* *result,
               nsIFile* binDirectory,
               nsIDirectoryServiceProvider* appFileLocationProvider)
 {
     mozPoisonValueInit();