diff options
author | Aliaksey Kandratsenka <alkondratenko@gmail.com> | 2015-10-24 23:16:45 -0700 |
---|---|---|
committer | Aliaksey Kandratsenka <alkondratenko@gmail.com> | 2015-11-21 19:03:03 -0800 |
commit | 6fdfc5a7f40ebcff3fdaada1a2994ff54be2f9c7 (patch) | |
tree | eee24d53c01caa80b74b7a41cb4fe846aa82bfa5 | |
parent | c2a79d063c949584170b3e7dd2939a4548c16079 (diff) | |
download | gperftools-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.ac | 14 | ||||
-rw-r--r-- | src/libc_override_gcc_and_weak.h | 55 | ||||
-rwxr-xr-x | src/tests/tcmalloc_unittest.sh | 4 |
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" |