aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAliaksey Kandratsenka <alkondratenko@gmail.com>2016-01-31 23:17:50 -0800
committerAliaksey Kandratsenka <alkondratenko@gmail.com>2016-02-21 10:53:45 -0800
commit7f12051dbe1f402771a747c1192a66e7571d94f9 (patch)
tree3700851818d5a9577db42e32b1b54b1cf171990b
parent3ee2360250c639f02b354820bc50efc6e104b754 (diff)
downloadgperftools-7f12051dbe1f402771a747c1192a66e7571d94f9.tar.gz
implemented emergency malloc
Emergency malloc is enabled for cases when backtrace capturing needs to call malloc. In this case, we enable emergency malloc just prior to calling such code and disable it after it is done.
-rwxr-xr-xMakefile.am24
-rw-r--r--configure.ac10
-rw-r--r--src/debugallocation.cc15
-rw-r--r--src/emergency_malloc.cc169
-rw-r--r--src/emergency_malloc.h60
-rw-r--r--src/emergency_malloc_for_stacktrace.cc48
-rw-r--r--src/fake_stacktrace_scope.cc39
-rw-r--r--src/malloc_hook.cc7
-rw-r--r--src/maybe_emergency_malloc.h55
-rw-r--r--src/stacktrace.cc55
-rw-r--r--src/tcmalloc.cc29
-rw-r--r--src/thread_cache.h30
12 files changed, 531 insertions, 10 deletions
diff --git a/Makefile.am b/Makefile.am
index b2a9f9c..efd38fc 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -376,6 +376,9 @@ libstacktrace_la_LIBADD = $(UNWIND_LIBS) $(LIBSPINLOCK)
STACKTRACE_SYMBOLS = '(GetStackTrace|GetStackFrames|GetStackTraceWithContext|GetStackFramesWithContext)'
libstacktrace_la_LDFLAGS = -export-symbols-regex $(STACKTRACE_SYMBOLS) $(AM_LDFLAGS)
+noinst_LTLIBRARIES += libfake_stacktrace_scope.la
+libfake_stacktrace_scope_la_SOURCES = src/fake_stacktrace_scope.cc
+
### Unittests
TESTS += stacktrace_unittest
STACKTRACE_UNITTEST_INCLUDES = src/config_for_unittests.h \
@@ -384,7 +387,7 @@ STACKTRACE_UNITTEST_INCLUDES = src/config_for_unittests.h \
$(LOGGING_INCLUDES)
stacktrace_unittest_SOURCES = src/tests/stacktrace_unittest.cc \
$(STACKTRACE_UNITTEST_INCLUDES)
-stacktrace_unittest_LDADD = libstacktrace.la liblogging.la
+stacktrace_unittest_LDADD = libstacktrace.la liblogging.la libfake_stacktrace_scope.la
### Documentation
dist_doc_DATA +=
@@ -900,13 +903,23 @@ S_TCMALLOC_INCLUDES = $(S_TCMALLOC_MINIMAL_INCLUDES) \
src/base/sysinfo.h \
src/base/thread_lister.h \
src/heap-profile-table.h \
- src/heap-profile-stats.h
+ src/heap-profile-stats.h \
+ src/emergency_malloc.h
+
SG_TCMALLOC_INCLUDES = src/gperftools/heap-profiler.h \
src/gperftools/heap-checker.h
TCMALLOC_INCLUDES = $(S_TCMALLOC_INCLUDES) $(SG_TCMALLOC_MINIMAL_INCLUDES) \
$(SG_TCMALLOC_INCLUDES) $(SG_STACKTRACE_INCLUDES)
perftoolsinclude_HEADERS += $(SG_TCMALLOC_INCLUDES)
+if BUILD_EMERGENCY_MALLOC
+EMERGENCY_MALLOC_CC = src/emergency_malloc.cc src/emergency_malloc_for_stacktrace.cc
+EMERGENCY_MALLOC_DEFINE = -DENABLE_EMERGENCY_MALLOC
+else !BUILD_EMERGENCY_MALLOC
+EMERGENCY_MALLOC_CC = src/fake_stacktrace_scope.cc
+EMERGENCY_MALLOC_DEFINE =
+endif !BUILD_EMERGENCY_MALLOC
+
### Making the library
# As we describe at the top of this file, we want to turn off exceptions
@@ -920,15 +933,16 @@ libtcmalloc_internal_la_SOURCES = $(libtcmalloc_minimal_internal_la_SOURCES) \
src/heap-profile-table.cc \
src/heap-profiler.cc \
src/raw_printer.cc \
+ $(EMERGENCY_MALLOC_CC) \
src/memory_region_map.cc
libtcmalloc_internal_la_CXXFLAGS = $(PTHREAD_CFLAGS) -DNDEBUG \
- $(AM_CXXFLAGS) $(NO_EXCEPTIONS)
+ $(AM_CXXFLAGS) $(NO_EXCEPTIONS) $(EMERGENCY_MALLOC_DEFINE)
libtcmalloc_internal_la_LDFLAGS = $(PTHREAD_CFLAGS)
libtcmalloc_internal_la_LIBADD = libstacktrace.la $(PTHREAD_LIBS)
lib_LTLIBRARIES += libtcmalloc.la
libtcmalloc_la_SOURCES = $(TCMALLOC_CC) $(TCMALLOC_INCLUDES)
-libtcmalloc_la_CXXFLAGS = $(PTHREAD_CFLAGS) -DNDEBUG $(AM_CXXFLAGS)
+libtcmalloc_la_CXXFLAGS = $(PTHREAD_CFLAGS) -DNDEBUG $(AM_CXXFLAGS) $(EMERGENCY_MALLOC_DEFINE)
libtcmalloc_la_LDFLAGS = $(PTHREAD_CFLAGS) -version-info @TCMALLOC_SO_VERSION@
libtcmalloc_la_LIBADD = libtcmalloc_internal.la libmaybe_threads.la $(PTHREAD_LIBS)
@@ -1270,7 +1284,7 @@ libprofiler_la_SOURCES = src/profiler.cc \
src/profile-handler.cc \
src/profiledata.cc \
$(CPU_PROFILER_INCLUDES)
-libprofiler_la_LIBADD = libstacktrace.la libmaybe_threads.la
+libprofiler_la_LIBADD = libstacktrace.la libmaybe_threads.la libfake_stacktrace_scope.la
# We have to include ProfileData for profiledata_unittest
CPU_PROFILER_SYMBOLS = '(ProfilerStart|ProfilerStartWithOptions|ProfilerStop|ProfilerFlush|ProfilerEnable|ProfilerDisable|ProfilingIsEnabledForAllThreads|ProfilerRegisterThread|ProfilerGetCurrentState|ProfilerState|ProfileData|ProfileHandler)'
libprofiler_la_LDFLAGS = -export-symbols-regex $(CPU_PROFILER_SYMBOLS) \
diff --git a/configure.ac b/configure.ac
index 12848cf..02dab0b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -390,6 +390,14 @@ AC_CACHE_CHECK([if target has _Unwind_Backtrace],
AS_IF([test "x$perftools_cv_have_unwind_backtrace" = xyes],
[AC_DEFINE(HAVE_UNWIND_BACKTRACE, 1, [Whether <unwind.h> contains _Unwind_Backtrace])])
+AC_ARG_ENABLE([emergency-malloc],
+ [AS_HELP_STRING([--enable-emergency-malloc],
+ [build emergency malloc feature])],
+ [enable_emergency_malloc="$enableval"],
+ [enable_emergency_malloc="$enable_backtrace"])
+
+AM_CONDITIONAL(BUILD_EMERGENCY_MALLOC, [test "x$enable_emergency_malloc" = xyes])
+
# Defines PRIuS
AC_COMPILER_CHARACTERISTICS
@@ -608,5 +616,5 @@ AS_IF([test "$x86_no_fp_by_default" = yes && test "x$enable_frame_pointers" != x
[AS_IF([test "x$perftools_cv_have_unwind_backtrace" = xyes],
[AC_MSG_WARN([No frame pointers and no libunwind. Using experimental backtrace capturing via libgcc])],
[AS_IF([test "x$enable_backtrace" = xyes],
- [AC_MSG_WARN([No frame pointers and no libunwind. Expect backtrace capturing and unittests to fail])],
+ [AC_MSG_WARN([No frame pointers and no libunwind. Using experimental backtrace()])],
[AC_MSG_FAILURE([No frame pointers and no libunwind. The compilation will fail])])])])
diff --git a/src/debugallocation.cc b/src/debugallocation.cc
index 7b97250..0e650b6 100644
--- a/src/debugallocation.cc
+++ b/src/debugallocation.cc
@@ -1213,12 +1213,18 @@ inline void* do_debug_malloc_or_debug_cpp_alloc(size_t size) {
// Exported routines
extern "C" PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) PERFTOOLS_THROW {
+ if (ThreadCache::IsUseEmergencyMalloc()) {
+ return tcmalloc::EmergencyMalloc(size);
+ }
void* ptr = do_debug_malloc_or_debug_cpp_alloc(size);
MallocHook::InvokeNewHook(ptr, size);
return ptr;
}
extern "C" PERFTOOLS_DLL_DECL void tc_free(void* ptr) PERFTOOLS_THROW {
+ if (tcmalloc::IsEmergencyPtr(ptr)) {
+ return tcmalloc::EmergencyFree(ptr);
+ }
MallocHook::InvokeDeleteHook(ptr);
DebugDeallocate(ptr, MallocBlock::kMallocType, 0);
}
@@ -1229,6 +1235,9 @@ extern "C" PERFTOOLS_DLL_DECL void tc_free_sized(void *ptr, size_t size) PERFTOO
}
extern "C" PERFTOOLS_DLL_DECL void* tc_calloc(size_t count, size_t size) PERFTOOLS_THROW {
+ if (ThreadCache::IsUseEmergencyMalloc()) {
+ return tcmalloc::EmergencyCalloc(count, size);
+ }
// Overflow check
const size_t total_size = count * size;
if (size != 0 && total_size / size != count) return NULL;
@@ -1240,11 +1249,17 @@ extern "C" PERFTOOLS_DLL_DECL void* tc_calloc(size_t count, size_t size) PERFTOO
}
extern "C" PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) PERFTOOLS_THROW {
+ if (tcmalloc::IsEmergencyPtr(ptr)) {
+ return tcmalloc::EmergencyFree(ptr);
+ }
MallocHook::InvokeDeleteHook(ptr);
DebugDeallocate(ptr, MallocBlock::kMallocType, 0);
}
extern "C" PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) PERFTOOLS_THROW {
+ if (tcmalloc::IsEmergencyPtr(ptr)) {
+ return tcmalloc::EmergencyRealloc(ptr, size);
+ }
if (ptr == NULL) {
ptr = do_debug_malloc_or_debug_cpp_alloc(size);
MallocHook::InvokeNewHook(ptr, size);
diff --git a/src/emergency_malloc.cc b/src/emergency_malloc.cc
new file mode 100644
index 0000000..81c5554
--- /dev/null
+++ b/src/emergency_malloc.cc
@@ -0,0 +1,169 @@
+// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
+// Copyright (c) 2014, gperftools Contributors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "config.h"
+
+#include "emergency_malloc.h"
+
+#include <errno.h> // for ENOMEM, errno
+#include <string.h> // for memset
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/low_level_alloc.h"
+#include "base/spinlock.h"
+#include "internal_logging.h"
+
+
+namespace tcmalloc {
+ __attribute__ ((visibility("internal"))) char *emergency_arena_start;
+ __attribute__ ((visibility("internal"))) uintptr_t emergency_arena_start_shifted;
+
+ static CACHELINE_ALIGNED SpinLock emergency_malloc_lock(base::LINKER_INITIALIZED);
+ static char *emergency_arena_end;
+ static LowLevelAlloc::Arena *emergency_arena;
+
+ class EmergencyArenaPagesAllocator : public LowLevelAlloc::PagesAllocator {
+ ~EmergencyArenaPagesAllocator() {}
+ void *MapPages(int32 flags, size_t size) {
+ char *new_end = emergency_arena_end + size;
+ if (new_end > emergency_arena_start + kEmergencyArenaSize) {
+ RAW_LOG(FATAL, "Unable to allocate %" PRIuS " bytes in emergency zone.", size);
+ }
+ char *rv = emergency_arena_end;
+ emergency_arena_end = new_end;
+ return static_cast<void *>(rv);
+ }
+ void UnMapPages(int32 flags, void *addr, size_t size) {
+ RAW_LOG(FATAL, "UnMapPages is not implemented for emergency arena");
+ }
+ };
+
+ static union {
+ char bytes[sizeof(EmergencyArenaPagesAllocator)];
+ void *ptr;
+ } pages_allocator_place;
+
+ static void InitEmergencyMalloc(void) {
+ const int32 flags = LowLevelAlloc::kAsyncSignalSafe;
+
+ void *arena = LowLevelAlloc::GetDefaultPagesAllocator()->MapPages(flags, kEmergencyArenaSize * 2);
+
+ uintptr_t arena_ptr = reinterpret_cast<uintptr_t>(arena);
+ uintptr_t ptr = (arena_ptr + kEmergencyArenaSize - 1) & ~(kEmergencyArenaSize-1);
+
+ emergency_arena_end = emergency_arena_start = reinterpret_cast<char *>(ptr);
+ EmergencyArenaPagesAllocator *allocator = new (pages_allocator_place.bytes) EmergencyArenaPagesAllocator();
+ emergency_arena = LowLevelAlloc::NewArenaWithCustomAlloc(0, LowLevelAlloc::DefaultArena(), allocator);
+
+ emergency_arena_start_shifted = reinterpret_cast<uintptr_t>(emergency_arena_start) >> kEmergencyArenaShift;
+
+ uintptr_t head_unmap_size = ptr - arena_ptr;
+ CHECK_CONDITION(head_unmap_size < kEmergencyArenaSize);
+ if (head_unmap_size != 0) {
+ LowLevelAlloc::GetDefaultPagesAllocator()->UnMapPages(flags, arena, ptr - arena_ptr);
+ }
+
+ uintptr_t tail_unmap_size = kEmergencyArenaSize - head_unmap_size;
+ void *tail_start = reinterpret_cast<void *>(arena_ptr + head_unmap_size + kEmergencyArenaSize);
+ LowLevelAlloc::GetDefaultPagesAllocator()->UnMapPages(flags, tail_start, tail_unmap_size);
+ }
+
+ PERFTOOLS_DLL_DECL void *EmergencyMalloc(size_t size) {
+ SpinLockHolder l(&emergency_malloc_lock);
+
+ if (emergency_arena_start == NULL) {
+ InitEmergencyMalloc();
+ CHECK_CONDITION(emergency_arena_start != NULL);
+ }
+
+ void *rv = LowLevelAlloc::AllocWithArena(size, emergency_arena);
+ if (rv == NULL) {
+ errno = ENOMEM;
+ }
+ return rv;
+ }
+
+ PERFTOOLS_DLL_DECL void EmergencyFree(void *p) {
+ SpinLockHolder l(&emergency_malloc_lock);
+ if (emergency_arena_start == NULL) {
+ InitEmergencyMalloc();
+ CHECK_CONDITION(emergency_arena_start != NULL);
+ free(p);
+ return;
+ }
+ CHECK_CONDITION(emergency_arena_start);
+ LowLevelAlloc::Free(p);
+ }
+
+ PERFTOOLS_DLL_DECL void *EmergencyRealloc(void *_old_ptr, size_t new_size) {
+ if (_old_ptr == NULL) {
+ return EmergencyMalloc(new_size);
+ }
+ if (new_size == 0) {
+ EmergencyFree(_old_ptr);
+ return NULL;
+ }
+ SpinLockHolder l(&emergency_malloc_lock);
+ CHECK_CONDITION(emergency_arena_start);
+
+ char *old_ptr = static_cast<char *>(_old_ptr);
+ CHECK_CONDITION(old_ptr <= emergency_arena_end);
+ CHECK_CONDITION(emergency_arena_start <= old_ptr);
+
+ // NOTE: we don't know previous size of old_ptr chunk. So instead
+ // of trying to figure out right size of copied memory, we just
+ // copy largest possible size. We don't care about being slow.
+ size_t old_ptr_size = emergency_arena_end - old_ptr;
+ size_t copy_size = (new_size < old_ptr_size) ? new_size : old_ptr_size;
+
+ void *new_ptr = LowLevelAlloc::AllocWithArena(new_size, emergency_arena);
+ if (new_ptr == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ memcpy(new_ptr, old_ptr, copy_size);
+
+ LowLevelAlloc::Free(old_ptr);
+ return new_ptr;
+ }
+
+ PERFTOOLS_DLL_DECL void *EmergencyCalloc(size_t n, size_t elem_size) {
+ // Overflow check
+ const size_t size = n * elem_size;
+ if (elem_size != 0 && size / elem_size != n) return NULL;
+ void *rv = EmergencyMalloc(size);
+ if (rv != NULL) {
+ memset(rv, 0, size);
+ }
+ return rv;
+ }
+};
diff --git a/src/emergency_malloc.h b/src/emergency_malloc.h
new file mode 100644
index 0000000..8ec53d2
--- /dev/null
+++ b/src/emergency_malloc.h
@@ -0,0 +1,60 @@
+// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
+// Copyright (c) 2014, gperftools Contributors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef EMERGENCY_MALLOC_H
+#define EMERGENCY_MALLOC_H
+#include "config.h"
+
+#include <stddef.h>
+
+#include "base/basictypes.h"
+#include "common.h"
+
+namespace tcmalloc {
+ static const uintptr_t kEmergencyArenaShift = 20+4; // 16 megs
+ static const uintptr_t kEmergencyArenaSize = 1 << kEmergencyArenaShift;
+
+ extern __attribute__ ((visibility("internal"))) char *emergency_arena_start;
+ extern __attribute__ ((visibility("internal"))) uintptr_t emergency_arena_start_shifted;;
+
+ PERFTOOLS_DLL_DECL void *EmergencyMalloc(size_t size);
+ PERFTOOLS_DLL_DECL void EmergencyFree(void *p);
+ PERFTOOLS_DLL_DECL void *EmergencyCalloc(size_t n, size_t elem_size);
+ PERFTOOLS_DLL_DECL void *EmergencyRealloc(void *old_ptr, size_t new_size);
+
+ static inline bool IsEmergencyPtr(const void *_ptr) {
+ uintptr_t ptr = reinterpret_cast<uintptr_t>(_ptr);
+ return UNLIKELY((ptr >> kEmergencyArenaShift) == emergency_arena_start_shifted)
+ && emergency_arena_start_shifted;
+ }
+
+} // namespace tcmalloc
+
+#endif
diff --git a/src/emergency_malloc_for_stacktrace.cc b/src/emergency_malloc_for_stacktrace.cc
new file mode 100644
index 0000000..f1dc35e
--- /dev/null
+++ b/src/emergency_malloc_for_stacktrace.cc
@@ -0,0 +1,48 @@
+// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
+// Copyright (c) 2014, gperftools Contributors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include "emergency_malloc.h"
+#include "thread_cache.h"
+
+namespace tcmalloc {
+ bool EnterStacktraceScope(void);
+ void LeaveStacktraceScope(void);
+}
+
+bool tcmalloc::EnterStacktraceScope(void) {
+ if (ThreadCache::IsUseEmergencyMalloc()) {
+ return false;
+ }
+ ThreadCache::SetUseEmergencyMalloc();
+ return true;
+}
+
+void tcmalloc::LeaveStacktraceScope(void) {
+ ThreadCache::ResetUseEmergencyMalloc();
+}
diff --git a/src/fake_stacktrace_scope.cc b/src/fake_stacktrace_scope.cc
new file mode 100644
index 0000000..ee35a04
--- /dev/null
+++ b/src/fake_stacktrace_scope.cc
@@ -0,0 +1,39 @@
+// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
+// Copyright (c) 2014, gperftools Contributors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/basictypes.h"
+
+namespace tcmalloc {
+ ATTRIBUTE_WEAK bool EnterStacktraceScope(void) {
+ return true;
+ }
+ ATTRIBUTE_WEAK void LeaveStacktraceScope(void) {
+ }
+}
diff --git a/src/malloc_hook.cc b/src/malloc_hook.cc
index 678eab7..57b516d 100644
--- a/src/malloc_hook.cc
+++ b/src/malloc_hook.cc
@@ -49,6 +49,7 @@
#include <algorithm>
#include "base/logging.h"
#include "base/spinlock.h"
+#include "maybe_emergency_malloc.h"
#include "maybe_threads.h"
#include "malloc_hook-inl.h"
#include <gperftools/malloc_hook.h>
@@ -491,10 +492,16 @@ MallocHook_SbrkHook MallocHook_SetSbrkHook(MallocHook_SbrkHook hook) {
void MallocHook::InvokeNewHookSlow(const void* p, size_t s) {
+ if (tcmalloc::IsEmergencyPtr(p)) {
+ return;
+ }
INVOKE_HOOKS(NewHook, new_hooks_, (p, s));
}
void MallocHook::InvokeDeleteHookSlow(const void* p) {
+ if (tcmalloc::IsEmergencyPtr(p)) {
+ return;
+ }
INVOKE_HOOKS(DeleteHook, delete_hooks_, (p));
}
diff --git a/src/maybe_emergency_malloc.h b/src/maybe_emergency_malloc.h
new file mode 100644
index 0000000..250ecf0
--- /dev/null
+++ b/src/maybe_emergency_malloc.h
@@ -0,0 +1,55 @@
+// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
+// Copyright (c) 2014, gperftools Contributors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef MAYBE_EMERGENCY_MALLOC_H
+#define MAYBE_EMERGENCY_MALLOC_H
+
+#include "config.h"
+
+#ifdef ENABLE_EMERGENCY_MALLOC
+
+#include "emergency_malloc.h"
+
+#else
+
+namespace tcmalloc {
+ static inline void *EmergencyMalloc(size_t size) {return NULL;}
+ static inline void EmergencyFree(void *p) {}
+ static inline void *EmergencyCalloc(size_t n, size_t elem_size) {return NULL;}
+ static inline void *EmergencyRealloc(void *old_ptr, size_t new_size) {return NULL;}
+
+ static inline bool IsEmergencyPtr(const void *_ptr) {
+ return false;
+ }
+}
+
+#endif // ENABLE_EMERGENCY_MALLOC
+
+#endif
diff --git a/src/stacktrace.cc b/src/stacktrace.cc
index 41b9113..395d569 100644
--- a/src/stacktrace.cc
+++ b/src/stacktrace.cc
@@ -197,6 +197,8 @@ static GetStackImplementation *all_impls[] = {
#endif
#endif
+static bool get_stack_impl_inited;
+
#if defined(HAVE_GST_instrument)
static GetStackImplementation *get_stack_impl = &impl__instrument;
#elif defined(HAVE_GST_win32)
@@ -231,13 +233,52 @@ static int ATTRIBUTE_NOINLINE frame_forcer(int rv) {
return rv;
}
+static void init_default_stack_impl_inner(void);
+
+namespace tcmalloc {
+ bool EnterStacktraceScope(void);
+ void LeaveStacktraceScope(void);
+}
+
+namespace {
+ using tcmalloc::EnterStacktraceScope;
+ using tcmalloc::LeaveStacktraceScope;
+
+ class StacktraceScope {
+ bool stacktrace_allowed;
+ public:
+ StacktraceScope() {
+ stacktrace_allowed = true;
+ stacktrace_allowed = EnterStacktraceScope();
+ }
+ bool IsStacktraceAllowed() {
+ return stacktrace_allowed;
+ }
+ ~StacktraceScope() {
+ if (stacktrace_allowed) {
+ LeaveStacktraceScope();
+ }
+ }
+ };
+}
+
PERFTOOLS_DLL_DECL int GetStackFrames(void** result, int* sizes, int max_depth,
int skip_count) {
+ StacktraceScope scope;
+ if (!scope.IsStacktraceAllowed()) {
+ return 0;
+ }
+ init_default_stack_impl_inner();
return frame_forcer(get_stack_impl->GetStackFramesPtr(result, sizes, max_depth, skip_count));
}
PERFTOOLS_DLL_DECL int GetStackFramesWithContext(void** result, int* sizes, int max_depth,
int skip_count, const void *uc) {
+ StacktraceScope scope;
+ if (!scope.IsStacktraceAllowed()) {
+ return 0;
+ }
+ init_default_stack_impl_inner();
return frame_forcer(get_stack_impl->GetStackFramesWithContextPtr(
result, sizes, max_depth,
skip_count, uc));
@@ -245,16 +286,30 @@ PERFTOOLS_DLL_DECL int GetStackFramesWithContext(void** result, int* sizes, int
PERFTOOLS_DLL_DECL int GetStackTrace(void** result, int max_depth,
int skip_count) {
+ StacktraceScope scope;
+ if (!scope.IsStacktraceAllowed()) {
+ return 0;
+ }
+ init_default_stack_impl_inner();
return frame_forcer(get_stack_impl->GetStackTracePtr(result, max_depth, skip_count));
}
PERFTOOLS_DLL_DECL int GetStackTraceWithContext(void** result, int max_depth,
int skip_count, const void *uc) {
+ StacktraceScope scope;
+ if (!scope.IsStacktraceAllowed()) {
+ return 0;
+ }
+ init_default_stack_impl_inner();
return frame_forcer(get_stack_impl->GetStackTraceWithContextPtr(
result, max_depth, skip_count, uc));
}
static void init_default_stack_impl_inner(void) {
+ if (get_stack_impl_inited) {
+ return;
+ }
+ get_stack_impl_inited = true;
char *val = getenv("TCMALLOC_STACKTRACE_METHOD");
if (!val || !*val) {
return;
diff --git a/src/tcmalloc.cc b/src/tcmalloc.cc
index 319350b..2f74fa5 100644
--- a/src/tcmalloc.cc
+++ b/src/tcmalloc.cc
@@ -140,6 +140,8 @@
#define ALWAYS_INLINE inline
#endif
+#include "maybe_emergency_malloc.h"
+
#if (defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)) && !defined(WIN32_OVERRIDE_ALLOCATORS)
# define WIN32_DO_PATCHING 1
#endif
@@ -274,6 +276,10 @@ static int tc_new_mode = 0; // See tc_set_new_mode().
// required) kind of exception handling for these routines.
namespace {
void InvalidFree(void* ptr) {
+ if (tcmalloc::IsEmergencyPtr(ptr)) {
+ tcmalloc::EmergencyFree(ptr);
+ return;
+ }
Log(kCrash, __FILE__, __LINE__, "Attempt to free invalid pointer", ptr);
}
@@ -1184,10 +1190,16 @@ ALWAYS_INLINE void* do_malloc_small(ThreadCache* heap, size_t size) {
}
ALWAYS_INLINE void* do_malloc(size_t size) {
- if (ThreadCache::have_tls &&
- LIKELY(size < ThreadCache::MinSizeForSlowPath())) {
- return do_malloc_small(ThreadCache::GetCacheWhichMustBePresent(), size);
- } else if (size <= kMaxSize) {
+ if (ThreadCache::have_tls) {
+ if (LIKELY(size < ThreadCache::MinSizeForSlowPath())) {
+ return do_malloc_small(ThreadCache::GetCacheWhichMustBePresent(), size);
+ }
+ if (UNLIKELY(ThreadCache::IsUseEmergencyMalloc())) {
+ return tcmalloc::EmergencyMalloc(size);
+ }
+ }
+
+ if (size <= kMaxSize) {
return do_malloc_small(ThreadCache::GetCache(), size);
} else {
return do_malloc_pages(ThreadCache::GetCache(), size);
@@ -1618,12 +1630,18 @@ extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_sized(void *p, size_t size) th
extern "C" PERFTOOLS_DLL_DECL void* tc_calloc(size_t n,
size_t elem_size) PERFTOOLS_THROW {
+ if (ThreadCache::IsUseEmergencyMalloc()) {
+ return tcmalloc::EmergencyCalloc(n, elem_size);
+ }
void* result = do_calloc(n, elem_size);
MallocHook::InvokeNewHook(result, n * elem_size);
return result;
}
extern "C" PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) PERFTOOLS_THROW {
+ if (tcmalloc::IsEmergencyPtr(ptr)) {
+ return tcmalloc::EmergencyFree(ptr);
+ }
MallocHook::InvokeDeleteHook(ptr);
do_free(ptr);
}
@@ -1640,6 +1658,9 @@ extern "C" PERFTOOLS_DLL_DECL void* tc_realloc(void* old_ptr,
do_free(old_ptr);
return NULL;
}
+ if (UNLIKELY(tcmalloc::IsEmergencyPtr(old_ptr))) {
+ return tcmalloc::EmergencyRealloc(old_ptr, new_size);
+ }
return do_realloc(old_ptr, new_size);
}
diff --git a/src/thread_cache.h b/src/thread_cache.h
index e3f4d67..445a0b5 100644
--- a/src/thread_cache.h
+++ b/src/thread_cache.h
@@ -111,6 +111,9 @@ class ThreadCache {
static void BecomeTemporarilyIdle();
static size_t MinSizeForSlowPath();
static void SetMinSizeForSlowPath(size_t size);
+ static void SetUseEmergencyMalloc();
+ static void ResetUseEmergencyMalloc();
+ static bool IsUseEmergencyMalloc();
static bool IsFastPathAllowed() { return MinSizeForSlowPath() != 0; }
@@ -272,6 +275,9 @@ class ThreadCache {
// and we can then proceed, knowing that global and thread-local tcmalloc
// state is initialized.
size_t min_size_for_slow_path;
+
+ bool use_emergency_malloc;
+ size_t old_min_size_for_slow_path;
};
static __thread ThreadLocalData threadlocal_data_ ATTR_INITIAL_EXEC;
#endif
@@ -445,6 +451,30 @@ inline void ThreadCache::SetMinSizeForSlowPath(size_t size) {
#endif
}
+inline void ThreadCache::SetUseEmergencyMalloc() {
+#ifdef HAVE_TLS
+ threadlocal_data_.old_min_size_for_slow_path = threadlocal_data_.min_size_for_slow_path;
+ threadlocal_data_.min_size_for_slow_path = 0;
+ threadlocal_data_.use_emergency_malloc = true;
+#endif
+}
+
+inline void ThreadCache::ResetUseEmergencyMalloc() {
+#ifdef HAVE_TLS
+ threadlocal_data_.min_size_for_slow_path = threadlocal_data_.old_min_size_for_slow_path;
+ threadlocal_data_.use_emergency_malloc = false;
+#endif
+}
+
+inline bool ThreadCache::IsUseEmergencyMalloc() {
+#if defined(HAVE_TLS) && defined(ENABLE_EMERGENCY_MALLOC)
+ return UNLIKELY(threadlocal_data_.use_emergency_malloc);
+#else
+ return false;
+#endif
+}
+
+
} // namespace tcmalloc
#endif // TCMALLOC_THREAD_CACHE_H_