Bug 1123237 - Part 1. CountAlloc in ReplaceAlloc. r=glandium
authorTing-Yuan Huang <laszio.bugzilla@gmail.com>
Fri, 08 May 2015 11:00:42 +0800
changeset 261933 75506378ef7dbf7234dd4222dc5d30126cd76367
parent 261927 c50a3514dd867be190feb8e2cbc196781d3ecc18
child 261934 21d5ece264216605c588c707d8a1c93a118b3f68
child 261942 da4f63ddab8a8a0e393a360d6401c532fb169762
push id1942
push userkchen@mozilla.com
push dateFri, 08 May 2015 11:12:13 +0000
reviewersglandium
bugs1123237
milestone40.0a1
Bug 1123237 - Part 1. CountAlloc in ReplaceAlloc. r=glandium
memory/build/replace_malloc_bridge.h
memory/replace/countalloc/CountAlloc.cpp
memory/replace/countalloc/CountAlloc.h
memory/replace/countalloc/Makefile.in
memory/replace/countalloc/README
memory/replace/countalloc/moz.build
memory/replace/moz.build
--- a/memory/build/replace_malloc_bridge.h
+++ b/memory/build/replace_malloc_bridge.h
@@ -66,31 +66,44 @@ struct DMDFuncs;
  * See Mozilla(|Un)RegisterDebugHandle in xpcom/build/PoisonIOInterposer.h */
 struct DebugFdRegistry
 {
   virtual void RegisterHandle(intptr_t aFd);
 
   virtual void UnRegisterHandle(intptr_t aFd);
 };
 
+struct uncensored_alloc;
+
 } // namespace mozilla
 
 struct ReplaceMallocBridge
 {
-  ReplaceMallocBridge() : mVersion(2) {}
+  ReplaceMallocBridge() : mVersion(3) {}
 
   /* This method was added in version 1 of the bridge. */
   virtual mozilla::dmd::DMDFuncs* GetDMDFuncs() { return nullptr; }
 
   /* Send a DebugFdRegistry instance to the replace-malloc library so that
    * it can register/unregister file descriptors whenever needed. The
    * instance is valid until the process dies.
    * This method was added in version 2 of the bridge. */
   virtual void InitDebugFd(mozilla::DebugFdRegistry&) {}
 
+  /* It allows an user to observe how alloc & free happen.
+   * aAllocHook will be called on memory allocations.
+   * aFreeHook will be called when memory is returned.
+   * For example, a realloc() may make both hooks called.
+   * It returns the uncensored alloc & free.
+   * This method was added in version 3 of the bridge. */
+  virtual mozilla::uncensored_alloc* CountAllocRegisterHook(
+    void (*aAllocHook)(void *, int32_t),
+    void (*aFreeHook)(void *))
+  { return nullptr; }
+
 #ifndef REPLACE_MALLOC_IMPL
   /* Returns the replace-malloc bridge if its version is at least the
    * requested one. */
   static ReplaceMallocBridge* Get(int aMinimumVersion) {
     static ReplaceMallocBridge* sSingleton = get_bridge();
     return (sSingleton && sSingleton->mVersion >= aMinimumVersion)
       ? sSingleton : nullptr;
   }
@@ -119,14 +132,23 @@ struct ReplaceMalloc
 
   static void InitDebugFd(mozilla::DebugFdRegistry& aRegistry)
   {
     auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 2);
     if (singleton) {
       singleton->InitDebugFd(aRegistry);
     }
   }
+
+  static mozilla::uncensored_alloc* CountAllocRegisterHook(
+    void (*aAllocHook)(void *, int32_t),
+    void (*aFreeHook)(void *))
+  {
+    auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 3);
+    return singleton ? singleton->CountAllocRegisterHook(aAllocHook, aFreeHook)
+                     : nullptr;
+  }
 };
 #endif
 
 #endif /* __cplusplus */
 
 #endif /* replace_malloc_bridge_h */
new file mode 100644
--- /dev/null
+++ b/memory/replace/countalloc/CountAlloc.cpp
@@ -0,0 +1,141 @@
+/* -*- 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/. */
+
+#include "replace_malloc.h"
+#include "CountAlloc.h"
+
+static const malloc_table_t* sFuncs = nullptr;
+
+#ifdef ANDROID
+/* See mozglue/android/APKOpen.cpp */
+extern "C" MOZ_EXPORT __attribute__((weak))
+void* __dso_handle;
+#endif
+
+static void (*sAllocHook)(void *, int32_t);
+static void (*sFreeHook)(void *);
+static mozilla::uncensored_alloc uncensored;
+
+static void AllocHook(void *p, size_t size)
+{
+    if (sAllocHook)
+        sAllocHook(p, size);
+}
+
+static void FreeHook(void *p)
+{
+    if (sFreeHook)
+        sFreeHook(p);
+}
+
+void
+replace_init(const malloc_table_t* aTable)
+{
+  sFuncs = aTable;
+  uncensored.malloc = aTable->malloc;
+  uncensored.free = aTable->free;
+}
+
+class CountAllocBridge : public ReplaceMallocBridge
+{
+  virtual mozilla::uncensored_alloc* CountAllocRegisterHook(
+    void (*aAllocHook)(void *, int32_t),
+    void (*aFreeHook)(void *)) override {
+
+    sAllocHook = aAllocHook;
+    sFreeHook = aFreeHook;
+    return &uncensored;
+  }
+};
+
+ReplaceMallocBridge*
+replace_get_bridge()
+{
+  static CountAllocBridge bridge;
+  return &bridge;
+}
+
+void*
+replace_malloc(size_t aSize)
+{
+  void* ptr = sFuncs->malloc(aSize);
+  if (ptr) {
+    AllocHook(ptr, aSize);
+  }
+  return ptr;
+}
+
+int
+replace_posix_memalign(void** aPtr, size_t aAlignment, size_t aSize)
+{
+  int ret = sFuncs->posix_memalign(aPtr, aAlignment, aSize);
+  if (ret == 0) {
+    AllocHook(*aPtr, aSize);
+  }
+  return ret;
+}
+
+void*
+replace_aligned_alloc(size_t aAlignment, size_t aSize)
+{
+  void* ptr = sFuncs->aligned_alloc(aAlignment, aSize);
+  if (ptr) {
+    AllocHook(ptr, aSize);
+  }
+  return ptr;
+}
+
+void*
+replace_calloc(size_t aNum, size_t aSize)
+{
+  void* ptr = sFuncs->calloc(aNum, aSize);
+  if (ptr) {
+    AllocHook(ptr, aNum * aSize);
+  }
+  return ptr;
+}
+
+void*
+replace_realloc(void* aPtr, size_t aSize)
+{
+  void* new_ptr = sFuncs->realloc(aPtr, aSize);
+  if (new_ptr || !aSize) {
+    FreeHook(aPtr);
+    if (aSize) {
+      AllocHook(new_ptr, aSize);
+    }
+  }
+  return new_ptr;
+}
+
+void
+replace_free(void* aPtr)
+{
+  if (aPtr) {
+    FreeHook(aPtr);
+  }
+  sFuncs->free(aPtr);
+}
+
+void*
+replace_memalign(size_t aAlignment, size_t aSize)
+{
+  void* ptr = sFuncs->memalign(aAlignment, aSize);
+  if (ptr) {
+    AllocHook(ptr, aSize);
+  }
+  return ptr;
+}
+
+void*
+replace_valloc(size_t aSize)
+{
+  void* ptr = sFuncs->valloc(aSize);
+  if (ptr) {
+    AllocHook(ptr, aSize);
+  }
+  return ptr;
+}
new file mode 100644
--- /dev/null
+++ b/memory/replace/countalloc/CountAlloc.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 CountAlloc_h
+#define CountAlloc_h
+
+namespace mozilla {
+struct uncensored_alloc {
+  void *(*malloc)(size_t);
+  void (*free)(void *);
+};
+}
+
+#endif /* CountAlloc_h */
new file mode 100644
--- /dev/null
+++ b/memory/replace/countalloc/Makefile.in
@@ -0,0 +1,6 @@
+# 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/.
+
+# Avoid Lock_impl code depending on mozilla::Logger
+MOZ_DEBUG_ENABLE_DEFS=
new file mode 100644
--- /dev/null
+++ b/memory/replace/countalloc/README
@@ -0,0 +1,17 @@
+CountAlloc is a replace-malloc library for Firefox
+(see memory/build/replace_malloc.h) that counts allocations in native heaps
+for the memory profiler.
+
+To enable counting allocations in native heaps the following environment
+variables need to be set when starting Firefox:
+- on Linux:
+  LD_PRELOAD=/path/to/libcountalloc.so
+- on Mac OSX:
+  DYLD_INSERT_LIBRARIES=/path/to/libcountalloc.dylib
+- on Windows:
+  MOZ_REPLACE_MALLOC_LIB=/path/to/countalloc.dll
+- on Android:
+  MOZ_REPLACE_MALLOC_LIB=/path/to/libcountalloc.so
+  (see https://wiki.mozilla.org/Mobile/Fennec/Android#Arguments_and_Environment_Variables
+  for how to pass environment variables to Firefox for Android)
+
new file mode 100644
--- /dev/null
+++ b/memory/replace/countalloc/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SharedLibrary('countalloc')
+
+EXPORTS += [
+    'CountAlloc.h',
+]
+
+SOURCES += [
+    'CountAlloc.cpp',
+]
+
+DISABLE_STL_WRAPPING = True
+USE_STATIC_LIBS = True
+DEFINES['MOZ_NO_MOZALLOC'] = True
+# Avoid Lock_impl code depending on mozilla::Logger.
+DEFINES['NDEBUG'] = True
+
--- a/memory/replace/moz.build
+++ b/memory/replace/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 DIRS += ['logalloc']
+DIRS += ['countalloc']
 
 # Build jemalloc3 as a replace-malloc lib when building with mozjemalloc
 if not CONFIG['MOZ_JEMALLOC3']:
     DIRS += ['jemalloc']
 
 if CONFIG['MOZ_REPLACE_MALLOC_LINKAGE'] == 'dummy library':
     DIRS += ['dummy']