Bug 818922 - Add bidirectional method calls with replace-malloc library. r=njn
authorMike Hommey <mh+mozilla@glandium.org>
Tue, 18 Nov 2014 19:21:06 +0900
changeset 216227 bd1cbced0ccb7963fcda09ede2cd17af4bd41de0
parent 216226 53ea03a4c7462e79eb7b103849eec98c41cafcc6
child 216228 3c6bd176246d277649e7a73dd00f201cfa194fc6
push id10019
push usercbook@mozilla.com
push dateTue, 18 Nov 2014 16:35:02 +0000
treeherderfx-team@a8a9d42356c8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs818922
milestone36.0a1
Bug 818922 - Add bidirectional method calls with replace-malloc library. r=njn
b2g/installer/Makefile.in
browser/installer/Makefile.in
configure.in
memory/build/malloc_decls.h
memory/build/moz.build
memory/build/replace_malloc.c
memory/build/replace_malloc.h
memory/build/replace_malloc_bridge.h
memory/mozjemalloc/moz.build
memory/replace/logalloc/replay/moz.build
--- a/b2g/installer/Makefile.in
+++ b/b2g/installer/Makefile.in
@@ -78,19 +78,16 @@ endif
 ifneq (,$(filter rtsp,$(NECKO_PROTOCOLS)))
 DEFINES += -DMOZ_RTSP
 endif
 
 ifdef GKMEDIAS_SHARED_LIBRARY
 DEFINES += -DGKMEDIAS_SHARED_LIBRARY
 endif
 
-ifdef MOZ_REPLACE_MALLOC
-DEFINES += -DMOZ_REPLACE_MALLOC
-endif
 ifdef MOZ_JEMALLOC3
 DEFINES += -DMOZ_JEMALLOC3
 endif
 
 ifdef MOZ_WIDGET_GTK
 DEFINES += -DMOZ_GTK=1
 ifdef MOZ_ENABLE_GTK3
 DEFINES += -DMOZ_GTK3=1
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -132,19 +132,16 @@ DEFINES += -DAB=$(AB)
 
 DEFINES += -DMOZ_ICU_VERSION=$(MOZ_ICU_VERSION)
 ifdef MOZ_NATIVE_ICU
 DEFINES += -DMOZ_NATIVE_ICU
 endif
 ifdef MOZ_SHARED_ICU
 DEFINES += -DMOZ_SHARED_ICU
 endif
-ifdef MOZ_REPLACE_MALLOC
-DEFINES += -DMOZ_REPLACE_MALLOC
-endif
 ifdef MOZ_JEMALLOC3
 DEFINES += -DMOZ_JEMALLOC3
 endif
 DEFINES += -DMOZ_ICU_DBG_SUFFIX=$(MOZ_ICU_DBG_SUFFIX)
 ifdef CLANG_CXX
 DEFINES += -DCLANG_CXX
 endif
 ifdef CLANG_CL
--- a/configure.in
+++ b/configure.in
@@ -7156,16 +7156,17 @@ if test "$NS_TRACE_MALLOC" -a "$MOZ_REPL
     AC_MSG_ERROR([--enable-trace-malloc and --enable-replace-malloc are conflicting options])
 fi
 
 if test -n "$MOZ_REPLACE_MALLOC" -a -z "$MOZ_MEMORY"; then
     dnl We don't want to enable jemalloc unconditionally because it may be a
     dnl deliberate choice not to enable it (bug 702250, for instance)
     AC_MSG_ERROR([--enable-replace-malloc requires --enable-jemalloc])
 elif test -n "$MOZ_REPLACE_MALLOC"; then
+    AC_DEFINE(MOZ_REPLACE_MALLOC)
     MOZ_NATIVE_JEMALLOC=
 
     dnl Replace-malloc Mac linkage quirks
     if test -n "$MACOSX_DEPLOYMENT_TARGET"; then
         AC_CACHE_CHECK([how to do weak dynamic linking],
             ac_cv_weak_dynamic_linking,
             [echo 'extern void foo() __attribute__((weak_import));int bar() { if (foo) foo(); return 0; }' > conftest.c
              if AC_TRY_COMMAND([${CC-cc} -o conftest${DLL_SUFFIX} $CFLAGS -dynamiclib $LDFLAGS -Wl,-U,_foo conftest.c $LIBS 1>&5]) &&
--- a/memory/build/malloc_decls.h
+++ b/memory/build/malloc_decls.h
@@ -19,28 +19,33 @@
 typedef void * usable_ptr_t;
 #  else
 typedef const void * usable_ptr_t;
 #  endif
 
 #  define MALLOC_FUNCS_MALLOC 1
 #  define MALLOC_FUNCS_JEMALLOC 2
 #  define MALLOC_FUNCS_INIT 4
-#  define MALLOC_FUNCS_ALL (MALLOC_FUNCS_INIT | MALLOC_FUNCS_MALLOC | MALLOC_FUNCS_JEMALLOC)
+#  define MALLOC_FUNCS_BRIDGE 8
+#  define MALLOC_FUNCS_ALL (MALLOC_FUNCS_INIT | MALLOC_FUNCS_BRIDGE | \
+                            MALLOC_FUNCS_MALLOC | MALLOC_FUNCS_JEMALLOC)
 
 #endif /* malloc_decls_h */
 
 #ifndef MALLOC_FUNCS
 #  define MALLOC_FUNCS (MALLOC_FUNCS_MALLOC | MALLOC_FUNCS_JEMALLOC)
 #endif
 
 #ifdef MALLOC_DECL
 #  if MALLOC_FUNCS & MALLOC_FUNCS_INIT
 MALLOC_DECL(init, void, const malloc_table_t *)
 #  endif
+#  if MALLOC_FUNCS & MALLOC_FUNCS_BRIDGE
+MALLOC_DECL(get_bridge, struct ReplaceMallocBridge*, void)
+#  endif
 #  if MALLOC_FUNCS & MALLOC_FUNCS_MALLOC
 MALLOC_DECL(malloc, void *, size_t)
 MALLOC_DECL(posix_memalign, int, void **, size_t, size_t)
 MALLOC_DECL(aligned_alloc, void *, size_t, size_t)
 MALLOC_DECL(calloc, void *, size_t, size_t)
 MALLOC_DECL(realloc, void *, void *, size_t)
 MALLOC_DECL(free, void, void *)
 MALLOC_DECL(memalign, void *, size_t, size_t)
--- a/memory/build/moz.build
+++ b/memory/build/moz.build
@@ -10,18 +10,18 @@ EXPORTS += [
 ]
 
 DEFINES['MOZ_MEMORY_IMPL'] = True
 
 if CONFIG['MOZ_REPLACE_MALLOC']:
     EXPORTS += [
         'malloc_decls.h',
         'replace_malloc.h',
+        'replace_malloc_bridge.h',
     ]
-    DEFINES['MOZ_REPLACE_MALLOC'] = True
 
 SOURCES += [
     'jemalloc_config.c',
     'mozmemory_wrap.c',
 ]
 
 if CONFIG['MOZ_JEMALLOC3']:
     SOURCES += [
--- a/memory/build/replace_malloc.c
+++ b/memory/build/replace_malloc.c
@@ -17,16 +17,17 @@
 #include "mozmemory_wrap.h"
 
 /* Declare all je_* functions */
 #define MALLOC_DECL(name, return_type, ...) \
   return_type je_ ## name(__VA_ARGS__);
 #include "malloc_decls.h"
 
 #include "mozilla/Likely.h"
+
 /*
  * Windows doesn't come with weak imports as they are possible with
  * LD_PRELOAD or DYLD_INSERT_LIBRARIES on Linux/OSX. On this platform,
  * the replacement functions are defined as variable pointers to the
  * function resolved with GetProcAddress() instead of weak definitions
  * of functions. On Android, the same needs to happen as well, because
  * the Android linker doesn't handle weak linking with non LD_PRELOADed
  * libraries, but LD_PRELOADing is not very convenient on Android, with
@@ -126,16 +127,26 @@ init()
 #endif
   // Set this *before* calling replace_init, otherwise if replace_init calls
   // malloc() we'll get an infinite loop.
   replace_malloc_initialized = 1;
   if (replace_init)
     replace_init(&malloc_table);
 }
 
+MFBT_API struct ReplaceMallocBridge*
+get_bridge(void)
+{
+  if (MOZ_UNLIKELY(!replace_malloc_initialized))
+    init();
+  if (MOZ_LIKELY(!replace_get_bridge))
+    return NULL;
+  return replace_get_bridge();
+}
+
 void*
 malloc_impl(size_t size)
 {
   if (MOZ_UNLIKELY(!replace_malloc_initialized))
     init();
   if (MOZ_LIKELY(!replace_malloc))
     return je_malloc(size);
   return replace_malloc(size);
--- a/memory/build/replace_malloc.h
+++ b/memory/build/replace_malloc.h
@@ -53,16 +53,25 @@
  * It is recommended to add the following to a replace-malloc implementation's
  * moz.build:
  *   DISABLE_STL_WRAPPING = True # Avoid STL wrapping
  *
  * If your replace-malloc implementation lives under memory/replace, these
  * are taken care of by memory/replace/defs.mk.
  */
 
+#ifdef replace_malloc_bridge_h
+#error Do not include replace_malloc_bridge.h before replace_malloc.h. \
+  In fact, you only need the latter.
+#endif
+
+#define REPLACE_MALLOC_IMPL
+
+#include "replace_malloc_bridge.h"
+
 /* Implementing a replace-malloc library is incompatible with using mozalloc. */
 #define MOZ_NO_MOZALLOC 1
 
 #include "mozilla/Types.h"
 
 MOZ_BEGIN_EXTERN_C
 
 #define MALLOC_DECL(name, return_type, ...) \
new file mode 100644
--- /dev/null
+++ b/memory/build/replace_malloc_bridge.h
@@ -0,0 +1,93 @@
+/* -*- 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 replace_malloc_bridge_h
+#define replace_malloc_bridge_h
+
+/*
+ * The replace-malloc bridge allows bidirectional method calls between
+ * a program and the replace-malloc library that has been loaded for it.
+ * In Firefox, this is used to allow method calls between code in libxul
+ * and code in the replace-malloc library, without libxul needing to link
+ * against that library or vice-versa.
+ *
+ * Subsystems can add methods for their own need. Replace-malloc libraries
+ * can decide to implement those methods or not.
+ *
+ * Replace-malloc libraries can provide such a bridge by implementing
+ * a ReplaceMallocBridge-derived class, and a replace_get_bridge function
+ * returning an instance of that class. The default methods in
+ * ReplaceMallocBridge are expected to return values that callers would
+ * understand as "the bridge doesn't implement this method", so that a
+ * replace-malloc library doesn't have to implement all methods.
+ *
+ * The ReplaceMallocBridge class contains definitions for methods for
+ * all replace-malloc libraries. Each library picks the methods it wants
+ * to reply to in its ReplaceMallocBridge-derived class instance.
+ * All methods of ReplaceMallocBridge must be virtual. Similarly,
+ * anything passed as an argument to those methods must be plain data, or
+ * an instance of a class with only virtual methods.
+ *
+ * Binary compatibility is expected to be maintained, such that a newer
+ * Firefox can be used with an old replace-malloc library, or an old
+ * Firefox can be used with a newer replace-malloc library. As such, only
+ * new virtual methods should be added to ReplaceMallocBridge, and
+ * each change should have a corresponding bump of the mVersion value.
+ * At the same time, each virtual method should have a corresponding
+ * wrapper calling the virtual method on the instance from
+ * ReplaceMallocBridge::Get(), giving it the version the virtual method
+ * was added.
+ *
+ * Parts that are not relevant to the replace-malloc library end of the
+ * bridge are hidden when REPLACE_MALLOC_IMPL is not defined, which is
+ * the case when including replace_malloc.h.
+ */
+
+struct ReplaceMallocBridge;
+
+#ifdef __cplusplus
+
+#include "mozilla/NullPtr.h"
+#include "mozilla/Types.h"
+
+#ifndef REPLACE_MALLOC_IMPL
+/* Returns the replace-malloc bridge if there is one to be returned. */
+extern "C" MFBT_API ReplaceMallocBridge* get_bridge();
+#endif
+
+struct ReplaceMallocBridge
+{
+  ReplaceMallocBridge() : mVersion(0) {}
+
+#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;
+  }
+#endif
+
+protected:
+  const int mVersion;
+};
+
+#ifndef REPLACE_MALLOC_IMPL
+/* Class containing wrappers for calls to ReplaceMallocBridge methods.
+ * Those wrappers need to be static methods in a class because compilers
+ * complain about unused static global functions, and linkers complain
+ * about multiple definitions of non-static global functions.
+ * Using a separate class from ReplaceMallocBridge allows the function
+ * names to be identical. */
+struct ReplaceMalloc
+{
+};
+#endif
+
+#endif /* __cplusplus */
+
+#endif /* replace_malloc_bridge_h */
--- a/memory/mozjemalloc/moz.build
+++ b/memory/mozjemalloc/moz.build
@@ -18,19 +18,16 @@ if not CONFIG['MOZ_JEMALLOC3']:
 
 # For non release/esr builds, enable (some) fatal jemalloc assertions.  This
 # helps us catch memory errors.
 if CONFIG['MOZ_UPDATE_CHANNEL'] not in ('release', 'esr'):
     DEFINES['MOZ_JEMALLOC_HARD_ASSERTS'] = True
 
 DEFINES['abort'] = 'moz_abort'
 
-if CONFIG['MOZ_REPLACE_MALLOC']:
-    DEFINES['MOZ_REPLACE_MALLOC'] = True
-
 DEFINES['MOZ_JEMALLOC_IMPL'] = True
 
 #XXX: PGO on Linux causes problems here
 # See bug 419470
 if CONFIG['OS_TARGET'] == 'Linux':
     NO_PGO = True
 
 if CONFIG['MOZ_NUWA_PROCESS']:
--- a/memory/replace/logalloc/replay/moz.build
+++ b/memory/replace/logalloc/replay/moz.build
@@ -16,10 +16,8 @@ LOCAL_INCLUDES += [
 ]
 
 # Link replace-malloc and the default allocator.
 USE_LIBS += [
     'memory',
 ]
 
 DISABLE_STL_WRAPPING = True
-
-DEFINES['MOZ_REPLACE_MALLOC'] = True