summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAliaksey Kandratsenka <alkondratenko@gmail.com>2015-10-24 23:16:45 -0700
committerAliaksey Kandratsenka <alkondratenko@gmail.com>2015-11-21 19:03:03 -0800
commit6fdfc5a7f40ebcff3fdaada1a2994ff54be2f9c7 (patch)
treeeee24d53c01caa80b74b7a41cb4fe846aa82bfa5
parentc2a79d063c949584170b3e7dd2939a4548c16079 (diff)
downloadgperftools-6fdfc5a7f40ebcff3fdaada1a2994ff54be2f9c7.tar.gz
implemented enabling sized-delete support at runtime
Under gcc 4.5 or greater we're using ifunc function attribute to resolve sized delete operator to either plain delete implementation (default) or to sized delete (if enabled via environment variable TCMALLOC_ENABLE_SIZED_DELETE).
-rw-r--r--configure.ac14
-rw-r--r--src/libc_override_gcc_and_weak.h55
-rwxr-xr-xsrc/tests/tcmalloc_unittest.sh4
3 files changed, 69 insertions, 4 deletions
diff --git a/configure.ac b/configure.ac
index 321a0f3..6e25cf1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -323,6 +323,16 @@ AC_CACHE_CHECK([if the compiler supports -Wno-unused-result],
AM_CONDITIONAL(HAVE_W_NO_UNUSED_RESULT,
test "$perftools_cv_w_no_unused_result" = yes)
+AC_ARG_ENABLE([dynamic-sized-delete-support],
+ [AS_HELP_STRING([--disable-dynamic-sized-delete-support],
+ [don't try to build run-time switch for sized delete operator])],
+ [enable_dyn_sized_delete="$enableval"],
+ [enable_dyn_sized_delete=yes])
+
+AS_IF([test "x$enable_dyn_sized_delete" = xyes],
+ [AC_DEFINE([ENABLE_DYNAMIC_SIZED_DELETE], 1,
+ [Build runtime detection for sized delete])])
+
AC_ARG_ENABLE([sized-delete],
[AS_HELP_STRING([--enable-sized-delete],
[build sized delete operator])],
@@ -333,14 +343,14 @@ AS_IF([test "x$enable_sized_delete" = xyes],
AC_MSG_NOTICE([Will build sized deallocation operators])],
[AC_MSG_NOTICE([Will not build sized deallocation operators])])
-AS_IF([test "x$enable_sized_delete" = xyes],
+AS_IF([test "x$enable_sized_delete" = xyes -o "x$enable_dyn_sized_delete" = xyes],
[AC_CACHE_CHECK([if C++ compiler supports -fsized-deallocation],
perftools_cv_sized_deallocation_result,
[AC_LANG_PUSH(C++)
OLD_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS -fsized-deallocation"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,)],
- perftools_cv_sized_deallocation_result="$enable_sized_delete",
+ perftools_cv_sized_deallocation_result=yes,
perftools_cv_sized_deallocation_result=no)
CXXFLAGS="$OLD_CXXFLAGS"
AC_LANG_POP(C++)])])
diff --git a/src/libc_override_gcc_and_weak.h b/src/libc_override_gcc_and_weak.h
index 5c0def2..4b7786e 100644
--- a/src/libc_override_gcc_and_weak.h
+++ b/src/libc_override_gcc_and_weak.h
@@ -44,6 +44,9 @@
#endif
#include <gperftools/tcmalloc.h>
+#include "getenv_safe.h" // TCMallocGetenvSafe
+#include "base/commandlineflags.h"
+
#ifndef __THROW // I guess we're not on a glibc-like system
# define __THROW // __THROW is just an optimization, so ok to make it ""
#endif
@@ -71,12 +74,60 @@ void operator delete(void* p, const std::nothrow_t& nt) __THROW
void operator delete[](void* p, const std::nothrow_t& nt) __THROW
ALIAS(tc_deletearray_nothrow);
-#ifdef ENABLE_SIZED_DELETE
+#if defined(ENABLE_SIZED_DELETE)
+
void operator delete(void *p, size_t size) throw()
ALIAS(tc_delete_sized);
void operator delete[](void *p, size_t size) throw()
ALIAS(tc_deletearray_sized);
-#endif
+
+#elif defined(ENABLE_DYNAMIC_SIZED_DELETE) && \
+ (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
+
+static void delegate_sized_delete(void *p, size_t s) throw() {
+ (operator delete)(p);
+}
+
+static void delegate_sized_deletearray(void *p, size_t s) throw() {
+ (operator delete[])(p);
+}
+
+extern "C" __attribute__((weak))
+int tcmalloc_sized_delete_enabled(void);
+
+static bool sized_delete_enabled(void) {
+ if (tcmalloc_sized_delete_enabled != 0) {
+ return !!tcmalloc_sized_delete_enabled();
+ }
+
+ const char *flag = TCMallocGetenvSafe("TCMALLOC_ENABLE_SIZED_DELETE");
+ return tcmalloc::commandlineflags::StringToBool(flag, false);
+}
+
+extern "C" {
+
+static void *resolve_delete_sized(void) {
+ if (sized_delete_enabled()) {
+ return reinterpret_cast<void *>(tc_delete_sized);
+ }
+ return reinterpret_cast<void *>(delegate_sized_delete);
+}
+
+static void *resolve_deletearray_sized(void) {
+ if (sized_delete_enabled()) {
+ return reinterpret_cast<void *>(tc_deletearray_sized);
+ }
+ return reinterpret_cast<void *>(delegate_sized_deletearray);
+}
+
+}
+
+void operator delete(void *p, size_t size) throw()
+ __attribute__((ifunc("resolve_delete_sized")));
+void operator delete[](void *p, size_t size) throw()
+ __attribute__((ifunc("resolve_deletearray_sized")));
+
+#endif /* !ENABLE_SIZED_DELETE && !ENABLE_DYN_SIZED_DELETE */
extern "C" {
void* malloc(size_t size) __THROW ALIAS(tc_malloc);
diff --git a/src/tests/tcmalloc_unittest.sh b/src/tests/tcmalloc_unittest.sh
index 755241e..550c8ec 100755
--- a/src/tests/tcmalloc_unittest.sh
+++ b/src/tests/tcmalloc_unittest.sh
@@ -77,4 +77,8 @@ echo -n "Testing $TCMALLOC_UNITTEST with TCMALLOC_HEAP_LIMIT_MB=512 ... "
TCMALLOC_HEAP_LIMIT_MB=512 run_unittest
+echo -n "Testing $TCMALLOC_UNITTEST with TCMALLOC_ENABLE_SIZED_DELETE=t ..."
+
+TCMALLOC_ENABLE_SIZED_DELETE=t run_unittest
+
echo "PASS"