Index: third_party/tcmalloc/chromium/src/debugallocation.cc |
=================================================================== |
--- third_party/tcmalloc/chromium/src/debugallocation.cc (revision 88335) |
+++ third_party/tcmalloc/chromium/src/debugallocation.cc (working copy) |
@@ -31,8 +31,16 @@ |
// Author: Urs Holzle <opensource@google.com> |
#include "config.h" |
-#ifdef HAVE_MALLOC_H |
-#include <malloc.h> |
+// We only need malloc.h for struct mallinfo. |
+#ifdef HAVE_STRUCT_MALLINFO |
+// Malloc can be in several places on older versions of OS X. |
+# if defined(HAVE_MALLOC_H) |
+# include <malloc.h> |
+# elif defined(HAVE_MALLOC_MALLOC_H) |
+# include <malloc/malloc.h> |
+# elif defined(HAVE_SYS_MALLOC_H) |
+# include <sys/malloc.h> |
+# endif |
#endif |
#include <pthread.h> |
#include <stdio.h> |
@@ -54,22 +62,19 @@ |
#include <errno.h> |
#include <string.h> |
+#include <google/malloc_extension.h> |
+#include <google/malloc_hook.h> |
+#include <google/stacktrace.h> |
#include "base/commandlineflags.h" |
#include "base/googleinit.h" |
#include "base/logging.h" |
-#include "google/malloc_extension.h" |
-#include "google/malloc_hook.h" |
-#include "google/stacktrace.h" |
+#include "base/spinlock.h" |
#include "addressmap-inl.h" |
#include "malloc_hook-inl.h" |
#include "symbolize.h" |
-#ifdef TCMALLOC_FOR_DEBUGALLOCATION |
+#define TCMALLOC_USING_DEBUGALLOCATION |
#include "tcmalloc.cc" |
-#else |
-#include "base/spinlock.h" |
-// Else we already have a SpinLock defined in tcmalloc/internal_spinlock.h |
-#endif |
// __THROW is defined in glibc systems. It means, counter-intuitively, |
// "This function will never throw an exception." It's an optional |
@@ -126,49 +131,17 @@ |
static void TracePrintf(int fd, const char *fmt, ...) |
__attribute__ ((__format__ (__printf__, 2, 3))); |
-// |
-// GNU has some weird "weak aliasing" thing that permits us to define our |
-// own malloc(), free(), and realloc() which can use the normal versions of |
-// of themselves by calling __libc_malloc(), __libc_free(), and |
-// __libc_realloc(). |
-// |
-extern "C" { |
- extern void* __libc_malloc(size_t size); |
- extern void __libc_free(void* ptr); |
- extern void* __libc_realloc(void* ptr, size_t size); |
- extern void* __libc_calloc(size_t nmemb, size_t size); |
- extern int __libc_mallopt(int cmd, int value); |
-#ifdef HAVE_STRUCT_MALLINFO |
- extern struct mallinfo __libc_mallinfo(void); |
-#endif |
-} |
- |
-// Define the malloc/free/mallopt/mallinfo implementations |
-// we will be working on top of. |
-// TODO(csilvers): provide debugallocation on top of libc alloc, |
-// so this #ifdef might sometimes be false. |
-#ifdef TCMALLOC_FOR_DEBUGALLOCATION |
- |
-// The do_* functions are defined in tcmalloc.cc, |
+// The do_* functions are defined in tcmalloc/tcmalloc.cc, |
// which is included before this file |
-// when TCMALLOC_FOR_DEBUGALLOCATION is defined. |
-#define BASE_MALLOC_NEW(size) cpp_alloc(size, false) |
-#define BASE_MALLOC do_malloc_or_cpp_alloc |
-#define BASE_FREE do_free |
-#define BASE_MALLOPT do_mallopt |
-#define BASE_MALLINFO do_mallinfo |
+// when TCMALLOC_FOR_DEBUGALLOCATION is defined |
+#define BASE_MALLOC_NEW(size) cpp_alloc(size, false) |
+#define BASE_MALLOC do_malloc |
+#define BASE_FREE do_free |
+#define BASE_MALLOC_STATS do_malloc_stats |
+#define BASE_MALLOPT do_mallopt |
+#define BASE_MALLINFO do_mallinfo |
+#define BASE_MALLOC_SIZE(ptr) GetSizeWithCallback(ptr, &InvalidGetAllocatedSize) |
-#else |
- |
-// We are working on top of standard libc's malloc library |
-#define BASE_MALLOC_NEW __libc_malloc |
-#define BASE_MALLOC __libc_malloc |
-#define BASE_FREE __libc_free |
-#define BASE_MALLOPT __libc_mallopt |
-#define BASE_MALLINFO __libc_mallinfo |
- |
-#endif |
- |
// ========================================================================= // |
class MallocBlock; |
@@ -190,7 +163,7 @@ |
return (q_front_ + 1) % kFreeQueueSize == q_back_; |
} |
- void Push(QueueEntry block) { |
+ void Push(const QueueEntry& block) { |
q_[q_front_] = block; |
q_front_ = (q_front_ + 1) % kFreeQueueSize; |
} |
@@ -273,12 +246,13 @@ |
// NOTE: tcmalloc.cc depends on the value of kMagicDeletedByte |
// to work around a bug in the pthread library. |
static const int kMagicDeletedByte = 0xCD; |
- // An int (type of alloc_type_ below) in a deallocated storage |
+ // A size_t (type of alloc_type_ below) in a deallocated storage |
// filled with kMagicDeletedByte. |
- static const int kMagicDeletedInt = 0xCDCDCDCD | ((0xCDCDCDCD << 16) << 16); |
- // Initializer works for 32 and 64 bit ints; |
+ static const size_t kMagicDeletedSizeT = |
+ 0xCDCDCDCD | (((size_t)0xCDCDCDCD << 16) << 16); |
+ // Initializer works for 32 and 64 bit size_ts; |
// "<< 16 << 16" is to fool gcc from issuing a warning |
- // when ints are 32 bits. |
+ // when size_ts are 32 bits. |
// NOTE: on Linux, you can enable malloc debugging support in libc by |
// setting the environment variable MALLOC_CHECK_ to 1 before you |
@@ -297,12 +271,17 @@ |
private: // data layout |
// The four fields size1_,offset_,magic1_,alloc_type_ |
- // should together occupy a multiple of 8 bytes. |
+ // should together occupy a multiple of 16 bytes. (At the |
+ // moment, sizeof(size_t) == 4 or 8 depending on piii vs |
+ // k8, and 4 of those sum to 16 or 32 bytes). |
+ // This, combined with BASE_MALLOC's alignment guarantees, |
+ // ensures that SSE types can be stored into the returned |
+ // block, at &size2_. |
size_t size1_; |
size_t offset_; // normally 0 unless memaligned memory |
// see comments in memalign() and FromRawPointer(). |
- int magic1_; |
- int alloc_type_; |
+ size_t magic1_; |
+ size_t alloc_type_; |
// here comes the actual data (variable length) |
// ... |
// then come the size2_ and magic2_, or a full page of mprotect-ed memory |
@@ -435,7 +414,7 @@ |
"has been already deallocated (it was allocated with %s)", |
data_addr(), AllocName(map_type & ~kDeallocatedTypeBit)); |
} |
- if (alloc_type_ == kMagicDeletedInt) { |
+ if (alloc_type_ == kMagicDeletedSizeT) { |
RAW_LOG(FATAL, "memory stomping bug: a word before object at %p " |
"has been corrupted; or else the object has been already " |
"deallocated and our memory map has been corrupted", |
@@ -497,7 +476,7 @@ |
// practical effect is that allocations are limited to 4Gb or so, even if |
// the address space could take more. |
static size_t max_size_t = ~0; |
- if (size < 0 || size > max_size_t - sizeof(MallocBlock)) { |
+ if (size > max_size_t - sizeof(MallocBlock)) { |
RAW_LOG(ERROR, "Massive size passed to malloc: %"PRIuS"", size); |
return NULL; |
} |
@@ -662,24 +641,24 @@ |
reinterpret_cast<void*>( |
PRINTABLE_PTHREAD(queue_entry.deleter_threadid))); |
- SymbolTable symbolization_table; |
- const int num_symbols = queue_entry.num_deleter_pcs; // short alias name |
- for (int i = 0; i < num_symbols; i++) { |
+ // We don't want to allocate or deallocate memory here, so we use |
+ // placement-new. It's ok that we don't destroy this, since we're |
+ // just going to error-exit below anyway. Union is for alignment. |
+ union { void* alignment; char buf[sizeof(SymbolTable)]; } tablebuf; |
+ SymbolTable* symbolization_table = new (tablebuf.buf) SymbolTable; |
+ for (int i = 0; i < queue_entry.num_deleter_pcs; i++) { |
// Symbolizes the previous address of pc because pc may be in the |
// next function. This may happen when the function ends with |
// a call to a function annotated noreturn (e.g. CHECK). |
- char* pc = |
- reinterpret_cast<char*>(queue_entry.deleter_pcs[i]) - 1; |
- symbolization_table.Add(pc); |
+ char *pc = reinterpret_cast<char*>(queue_entry.deleter_pcs[i]); |
+ symbolization_table->Add(pc - 1); |
} |
if (FLAGS_symbolize_stacktrace) |
- symbolization_table.Symbolize(); |
- for (int i = 0; i < num_symbols; i++) { |
- char *pc = |
- reinterpret_cast<char*>(queue_entry.deleter_pcs[i]) - 1; |
- TracePrintf(STDERR_FILENO, " @ %"PRIxPTR" %s\n", |
- reinterpret_cast<uintptr_t>(pc), |
- symbolization_table.GetSymbol(pc)); |
+ symbolization_table->Symbolize(); |
+ for (int i = 0; i < queue_entry.num_deleter_pcs; i++) { |
+ char *pc = reinterpret_cast<char*>(queue_entry.deleter_pcs[i]); |
+ TracePrintf(STDERR_FILENO, " @ %p %s\n", |
+ pc, symbolization_table->GetSymbol(pc - 1)); |
} |
} else { |
RAW_LOG(ERROR, |
@@ -701,8 +680,8 @@ |
// Find the header just before client's memory. |
MallocBlock *mb = reinterpret_cast<MallocBlock *>( |
reinterpret_cast<char *>(p) - data_offset); |
- // If mb->alloc_type_ is kMagicDeletedInt, we're not an ok pointer. |
- if (mb->alloc_type_ == kMagicDeletedInt) { |
+ // If mb->alloc_type_ is kMagicDeletedSizeT, we're not an ok pointer. |
+ if (mb->alloc_type_ == kMagicDeletedSizeT) { |
RAW_LOG(FATAL, "memory allocation bug: object at %p has been already" |
" deallocated; or else a word before the object has been" |
" corrupted (memory stomping bug)", p); |
@@ -976,71 +955,176 @@ |
// ========================================================================= // |
-// Alloc/free stuff for debug hooks for malloc & friends |
+// The following functions may be called via MallocExtension::instance() |
+// for memory verification and statistics. |
+class DebugMallocImplementation : public TCMallocImplementation { |
+ public: |
+ virtual bool GetNumericProperty(const char* name, size_t* value) { |
+ bool result = TCMallocImplementation::GetNumericProperty(name, value); |
+ if (result && (strcmp(name, "generic.current_allocated_bytes") == 0)) { |
+ // Subtract bytes kept in the free queue |
+ size_t qsize = MallocBlock::FreeQueueSize(); |
+ if (*value >= qsize) { |
+ *value -= qsize; |
+ } |
+ } |
+ return result; |
+ } |
-// CAVEAT: The code structure below ensures that MallocHook methods are always |
-// called from the stack frame of the invoked allocation function. |
-// heap-checker.cc depends on this to start a stack trace from |
-// the call to the (de)allocation function. |
+ virtual bool VerifyNewMemory(void* p) { |
+ if (p) MallocBlock::FromRawPointer(p)->Check(MallocBlock::kNewType); |
+ return true; |
+ } |
-// Put all callers of MallocHook::Invoke* in this module into |
-// ATTRIBUTE_SECTION(google_malloc) section, |
-// so that MallocHook::GetCallerStackTrace can function accurately: |
+ virtual bool VerifyArrayNewMemory(void* p) { |
+ if (p) MallocBlock::FromRawPointer(p)->Check(MallocBlock::kArrayNewType); |
+ return true; |
+ } |
-extern "C" { |
- void* malloc(size_t size) __THROW ATTRIBUTE_SECTION(google_malloc); |
- void free(void* ptr) __THROW ATTRIBUTE_SECTION(google_malloc); |
- void* realloc(void* ptr, size_t size) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
- void* calloc(size_t nmemb, size_t size) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
- void cfree(void* ptr) __THROW ATTRIBUTE_SECTION(google_malloc); |
+ virtual bool VerifyMallocMemory(void* p) { |
+ if (p) MallocBlock::FromRawPointer(p)->Check(MallocBlock::kMallocType); |
+ return true; |
+ } |
- void* memalign(size_t __alignment, size_t __size) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
- int posix_memalign(void** ptr, size_t align, size_t size) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
- void* valloc(size_t __size) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
- void* pvalloc(size_t __size) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
+ virtual bool VerifyAllMemory() { |
+ return MallocBlock::CheckEverything(); |
+ } |
+ |
+ virtual bool MallocMemoryStats(int* blocks, size_t* total, |
+ int histogram[kMallocHistogramSize]) { |
+ return MallocBlock::MemoryStats(blocks, total, histogram); |
+ } |
+ |
+ virtual size_t GetAllocatedSize(void* p) { |
+ if (p) { |
+ return MallocBlock::FromRawPointer(p)->data_size(); |
+ } |
+ return 0; |
+ } |
+ virtual size_t GetEstimatedAllocatedSize(size_t size) { |
+ return size; |
+ } |
+ |
+ virtual void GetFreeListSizes(vector<MallocExtension::FreeListInfo>* v) { |
+ static const char* kDebugFreeQueue = "debug.free_queue"; |
+ |
+ TCMallocImplementation::GetFreeListSizes(v); |
+ |
+ MallocExtension::FreeListInfo i; |
+ i.type = kDebugFreeQueue; |
+ i.min_object_size = 0; |
+ i.max_object_size = numeric_limits<size_t>::max(); |
+ i.total_bytes_free = MallocBlock::FreeQueueSize(); |
+ v->push_back(i); |
+ } |
+ |
+ }; |
+ |
+static DebugMallocImplementation debug_malloc_implementation; |
+ |
+REGISTER_MODULE_INITIALIZER(debugallocation, { |
+ // Either we or valgrind will control memory management. We |
+ // register our extension if we're the winner. |
+ if (RunningOnValgrind()) { |
+ // Let Valgrind uses its own malloc (so don't register our extension). |
+ } else { |
+ MallocExtension::Register(&debug_malloc_implementation); |
+ // When the program exits, check all blocks still in the free |
+ // queue for corruption. |
+ atexit(DanglingWriteChecker); |
+ } |
+}); |
+ |
+// ========================================================================= // |
+ |
+// This is mostly the same a cpp_alloc in tcmalloc.cc. |
+// TODO(csilvers): write a wrapper for new-handler so we don't have to |
+// copy this code so much. |
+inline void* debug_cpp_alloc(size_t size, int new_type, bool nothrow) { |
+ for (;;) { |
+ void* p = DebugAllocate(size, new_type); |
+#ifdef PREANSINEW |
+ return p; |
+#else |
+ if (p == NULL) { // allocation failed |
+ // Get the current new handler. NB: this function is not |
+ // thread-safe. We make a feeble stab at making it so here, but |
+ // this lock only protects against tcmalloc interfering with |
+ // itself, not with other libraries calling set_new_handler. |
+ std::new_handler nh; |
+ { |
+ SpinLockHolder h(&set_new_handler_lock); |
+ nh = std::set_new_handler(0); |
+ (void) std::set_new_handler(nh); |
+ } |
+#if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS) |
+ if (nh) { |
+ // Since exceptions are disabled, we don't really know if new_handler |
+ // failed. Assume it will abort if it fails. |
+ (*nh)(); |
+ continue; |
+ } |
+ return 0; |
+#else |
+ // If no new_handler is established, the allocation failed. |
+ if (!nh) { |
+ if (nothrow) return 0; |
+ throw std::bad_alloc(); |
+ } |
+ // Otherwise, try the new_handler. If it returns, retry the |
+ // allocation. If it throws std::bad_alloc, fail the allocation. |
+ // if it throws something else, don't interfere. |
+ try { |
+ (*nh)(); |
+ } catch (const std::bad_alloc&) { |
+ if (!nothrow) throw; |
+ return p; |
+ } |
+#endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS) |
+ } else { // allocation success |
+ return p; |
+ } |
+#endif // PREANSINEW |
+ } |
} |
-static void *MemalignOverride(size_t align, size_t size, |
- const void *caller) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
+inline void* do_debug_malloc_or_debug_cpp_alloc(size_t size) { |
+ return tc_new_mode ? debug_cpp_alloc(size, MallocBlock::kMallocType, true) |
+ : DebugAllocate(size, MallocBlock::kMallocType); |
+} |
-void* operator new(size_t size) throw (std::bad_alloc) |
- ATTRIBUTE_SECTION(google_malloc); |
-void* operator new(size_t size, const std::nothrow_t&) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
-void operator delete(void* p) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
-void operator delete(void* p, const std::nothrow_t&) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
-void* operator new[](size_t size) throw (std::bad_alloc) |
- ATTRIBUTE_SECTION(google_malloc); |
-void* operator new[](size_t size, const std::nothrow_t&) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
-void operator delete[](void* p) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
-void operator delete[](void* p, const std::nothrow_t&) __THROW |
- ATTRIBUTE_SECTION(google_malloc); |
+// Exported routines |
-extern "C" void* malloc(size_t size) __THROW { |
- void* ptr = DebugAllocate(size, MallocBlock::kMallocType); |
+extern "C" PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) __THROW { |
+ void* ptr = do_debug_malloc_or_debug_cpp_alloc(size); |
MallocHook::InvokeNewHook(ptr, size); |
return ptr; |
} |
-extern "C" void free(void* ptr) __THROW { |
+extern "C" PERFTOOLS_DLL_DECL void tc_free(void* ptr) __THROW { |
MallocHook::InvokeDeleteHook(ptr); |
DebugDeallocate(ptr, MallocBlock::kMallocType); |
} |
-extern "C" void* realloc(void* ptr, size_t size) __THROW { |
+extern "C" PERFTOOLS_DLL_DECL void* tc_calloc(size_t count, size_t size) __THROW { |
+ // Overflow check |
+ const size_t total_size = count * size; |
+ if (size != 0 && total_size / size != count) return NULL; |
+ |
+ void* block = do_debug_malloc_or_debug_cpp_alloc(total_size); |
+ MallocHook::InvokeNewHook(block, total_size); |
+ if (block) memset(block, 0, total_size); |
+ return block; |
+} |
+ |
+extern "C" PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) __THROW { |
+ MallocHook::InvokeDeleteHook(ptr); |
+ DebugDeallocate(ptr, MallocBlock::kMallocType); |
+} |
+ |
+extern "C" PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) __THROW { |
if (ptr == NULL) { |
- ptr = DebugAllocate(size, MallocBlock::kMallocType); |
+ ptr = do_debug_malloc_or_debug_cpp_alloc(size); |
MallocHook::InvokeNewHook(ptr, size); |
return ptr; |
} |
@@ -1066,28 +1150,68 @@ |
return p->data_addr(); |
} |
-extern "C" void* calloc(size_t count, size_t size) __THROW { |
- // Overflow check |
- const size_t total_size = count * size; |
- if (size != 0 && total_size / size != count) return NULL; |
+extern "C" PERFTOOLS_DLL_DECL void* tc_new(size_t size) { |
+ void* ptr = debug_cpp_alloc(size, MallocBlock::kNewType, false); |
+ MallocHook::InvokeNewHook(ptr, size); |
+ if (ptr == NULL) { |
+ RAW_LOG(FATAL, "Unable to allocate %"PRIuS" bytes: new failed.", size); |
+ } |
+ return ptr; |
+} |
- void* block = DebugAllocate(total_size, MallocBlock::kMallocType); |
- MallocHook::InvokeNewHook(block, total_size); |
- if (block) memset(block, 0, total_size); |
- return block; |
+extern "C" PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size, const std::nothrow_t&) __THROW { |
+ void* ptr = debug_cpp_alloc(size, MallocBlock::kNewType, true); |
+ MallocHook::InvokeNewHook(ptr, size); |
+ return ptr; |
} |
-extern "C" void cfree(void* ptr) __THROW { |
- MallocHook::InvokeDeleteHook(ptr); |
- DebugDeallocate(ptr, MallocBlock::kMallocType); |
+extern "C" PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW { |
+ MallocHook::InvokeDeleteHook(p); |
+ DebugDeallocate(p, MallocBlock::kNewType); |
} |
+// Some STL implementations explicitly invoke this. |
+// It is completely equivalent to a normal delete (delete never throws). |
+extern "C" PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p, const std::nothrow_t&) __THROW { |
+ MallocHook::InvokeDeleteHook(p); |
+ DebugDeallocate(p, MallocBlock::kNewType); |
+} |
+ |
+extern "C" PERFTOOLS_DLL_DECL void* tc_newarray(size_t size) { |
+ void* ptr = debug_cpp_alloc(size, MallocBlock::kArrayNewType, false); |
+ MallocHook::InvokeNewHook(ptr, size); |
+ if (ptr == NULL) { |
+ RAW_LOG(FATAL, "Unable to allocate %"PRIuS" bytes: new[] failed.", size); |
+ } |
+ return ptr; |
+} |
+ |
+extern "C" PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size, const std::nothrow_t&) |
+ __THROW { |
+ void* ptr = debug_cpp_alloc(size, MallocBlock::kArrayNewType, true); |
+ MallocHook::InvokeNewHook(ptr, size); |
+ return ptr; |
+} |
+ |
+extern "C" PERFTOOLS_DLL_DECL void tc_deletearray(void* p) __THROW { |
+ MallocHook::InvokeDeleteHook(p); |
+ DebugDeallocate(p, MallocBlock::kArrayNewType); |
+} |
+ |
+// Some STL implementations explicitly invoke this. |
+// It is completely equivalent to a normal delete (delete never throws). |
+extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p, const std::nothrow_t&) __THROW { |
+ MallocHook::InvokeDeleteHook(p); |
+ DebugDeallocate(p, MallocBlock::kArrayNewType); |
+} |
+ |
// Round "value" up to next "alignment" boundary. |
// Requires that "alignment" be a power of two. |
static intptr_t RoundUp(intptr_t value, intptr_t alignment) { |
return (value + alignment - 1) & ~(alignment - 1); |
} |
+// This is mostly the same as do_memalign in tcmalloc.cc. |
static void *do_debug_memalign(size_t alignment, size_t size) { |
// Allocate >= size bytes aligned on "alignment" boundary |
// "alignment" is a power of two. |
@@ -1117,83 +1241,10 @@ |
return p; |
} |
-// Override __libc_memalign in libc on linux boxes. |
-// They have a bug in libc that causes them (very rarely) to allocate |
-// with __libc_memalign() yet deallocate with free(). |
-// This function is an exception to the rule of calling MallocHook method |
-// from the stack frame of the allocation function; |
-// heap-checker handles this special case explicitly. |
-static void *MemalignOverride(size_t align, size_t size, |
- const void *caller) __THROW { |
- void *p = do_debug_memalign(align, size); |
- MallocHook::InvokeNewHook(p, size); |
- return p; |
-} |
-void *(*__memalign_hook)(size_t, size_t, const void *) = MemalignOverride; |
- |
-extern "C" void* memalign(size_t align, size_t size) __THROW { |
- void *p = do_debug_memalign(align, size); |
- MallocHook::InvokeNewHook(p, size); |
- return p; |
-} |
- |
-// Implementation taken from tcmalloc/tcmalloc.cc |
-extern "C" int posix_memalign(void** result_ptr, |
- size_t align, size_t size) __THROW { |
- if (((align % sizeof(void*)) != 0) || |
- ((align & (align - 1)) != 0) || |
- (align == 0)) { |
- return EINVAL; |
- } |
- |
- void* result = do_debug_memalign(align, size); |
- MallocHook::InvokeNewHook(result, size); |
- if (result == NULL) { |
- return ENOMEM; |
- } else { |
- *result_ptr = result; |
- return 0; |
- } |
-} |
- |
-extern "C" void* valloc(size_t size) __THROW { |
- // Allocate >= size bytes starting on a page boundary |
- void *p = do_debug_memalign(getpagesize(), size); |
- MallocHook::InvokeNewHook(p, size); |
- return p; |
-} |
- |
-extern "C" void* pvalloc(size_t size) __THROW { |
- // Round size up to a multiple of pages |
- // then allocate memory on a page boundary |
- int pagesize = getpagesize(); |
- size = RoundUp(size, pagesize); |
- if (size == 0) { // pvalloc(0) should allocate one page, according to |
- size = pagesize; // http://man.free4web.biz/man3/libmpatrol.3.html |
- } |
- void *p = do_debug_memalign(pagesize, size); |
- MallocHook::InvokeNewHook(p, size); |
- return p; |
-} |
- |
-extern "C" int mallopt(int cmd, int value) __THROW { |
- return BASE_MALLOPT(cmd, value); |
-} |
- |
-#ifdef HAVE_STRUCT_MALLINFO |
-extern "C" struct mallinfo mallinfo(void) __THROW { |
- return BASE_MALLINFO(); |
-} |
-#endif |
- |
-// ========================================================================= // |
- |
-// Alloc/free stuff for debug operator new & friends |
- |
-// This is mostly the same a cpp_alloc in tcmalloc.cc. |
-inline void* cpp_debug_alloc(size_t size, int new_type, bool nothrow) { |
+// This is mostly the same as cpp_memalign in tcmalloc.cc. |
+static void* debug_cpp_memalign(size_t align, size_t size) { |
for (;;) { |
- void* p = DebugAllocate(size, new_type); |
+ void* p = do_debug_memalign(align, size); |
#ifdef PREANSINEW |
return p; |
#else |
@@ -1218,17 +1269,15 @@ |
return 0; |
#else |
// If no new_handler is established, the allocation failed. |
- if (!nh) { |
- if (nothrow) return 0; |
- throw std::bad_alloc(); |
- } |
+ if (!nh) |
+ return 0; |
+ |
// Otherwise, try the new_handler. If it returns, retry the |
// allocation. If it throws std::bad_alloc, fail the allocation. |
// if it throws something else, don't interfere. |
try { |
(*nh)(); |
} catch (const std::bad_alloc&) { |
- if (!nothrow) throw; |
return p; |
} |
#endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS) |
@@ -1239,171 +1288,96 @@ |
} |
} |
-void* operator new(size_t size) throw (std::bad_alloc) { |
- void* ptr = cpp_debug_alloc(size, MallocBlock::kNewType, false); |
- MallocHook::InvokeNewHook(ptr, size); |
- if (ptr == NULL) { |
- RAW_LOG(FATAL, "Unable to allocate %"PRIuS" bytes: new failed.", size); |
- } |
- return ptr; |
+inline void* do_debug_memalign_or_debug_cpp_memalign(size_t align, |
+ size_t size) { |
+ return tc_new_mode ? debug_cpp_memalign(align, size) |
+ : do_debug_memalign(align, size); |
} |
-void* operator new(size_t size, const std::nothrow_t&) __THROW { |
- void* ptr = cpp_debug_alloc(size, MallocBlock::kNewType, true); |
- MallocHook::InvokeNewHook(ptr, size); |
- return ptr; |
+extern "C" PERFTOOLS_DLL_DECL void* tc_memalign(size_t align, size_t size) __THROW { |
+ void *p = do_debug_memalign_or_debug_cpp_memalign(align, size); |
+ MallocHook::InvokeNewHook(p, size); |
+ return p; |
} |
-void operator delete(void* ptr) __THROW { |
- MallocHook::InvokeDeleteHook(ptr); |
- DebugDeallocate(ptr, MallocBlock::kNewType); |
+// Implementation taken from tcmalloc/tcmalloc.cc |
+extern "C" PERFTOOLS_DLL_DECL int tc_posix_memalign(void** result_ptr, size_t align, size_t size) |
+ __THROW { |
+ if (((align % sizeof(void*)) != 0) || |
+ ((align & (align - 1)) != 0) || |
+ (align == 0)) { |
+ return EINVAL; |
+ } |
+ |
+ void* result = do_debug_memalign_or_debug_cpp_memalign(align, size); |
+ MallocHook::InvokeNewHook(result, size); |
+ if (result == NULL) { |
+ return ENOMEM; |
+ } else { |
+ *result_ptr = result; |
+ return 0; |
+ } |
} |
-// Some STL implementations explicitly invoke this. |
-// It is completely equivalent to a normal delete (delete never throws). |
-void operator delete(void* ptr, const std::nothrow_t&) __THROW { |
- MallocHook::InvokeDeleteHook(ptr); |
- DebugDeallocate(ptr, MallocBlock::kNewType); |
+extern "C" PERFTOOLS_DLL_DECL void* tc_valloc(size_t size) __THROW { |
+ // Allocate >= size bytes starting on a page boundary |
+ void *p = do_debug_memalign_or_debug_cpp_memalign(getpagesize(), size); |
+ MallocHook::InvokeNewHook(p, size); |
+ return p; |
} |
-// ========================================================================= // |
- |
-// Alloc/free stuff for debug operator new[] & friends |
- |
-void* operator new[](size_t size) throw (std::bad_alloc) { |
- void* ptr = cpp_debug_alloc(size, MallocBlock::kArrayNewType, false); |
- MallocHook::InvokeNewHook(ptr, size); |
- if (ptr == NULL) { |
- RAW_LOG(FATAL, "Unable to allocate %"PRIuS" bytes: new[] failed.", size); |
+extern "C" PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t size) __THROW { |
+ // Round size up to a multiple of pages |
+ // then allocate memory on a page boundary |
+ int pagesize = getpagesize(); |
+ size = RoundUp(size, pagesize); |
+ if (size == 0) { // pvalloc(0) should allocate one page, according to |
+ size = pagesize; // http://man.free4web.biz/man3/libmpatrol.3.html |
} |
- return ptr; |
+ void *p = do_debug_memalign_or_debug_cpp_memalign(pagesize, size); |
+ MallocHook::InvokeNewHook(p, size); |
+ return p; |
} |
-void* operator new[](size_t size, const std::nothrow_t&) __THROW { |
- void* ptr = cpp_debug_alloc(size, MallocBlock::kArrayNewType, true); |
- MallocHook::InvokeNewHook(ptr, size); |
- return ptr; |
+// malloc_stats just falls through to the base implementation. |
+extern "C" PERFTOOLS_DLL_DECL void tc_malloc_stats(void) __THROW { |
+ BASE_MALLOC_STATS(); |
} |
-void operator delete[](void* ptr) __THROW { |
- MallocHook::InvokeDeleteHook(ptr); |
- DebugDeallocate(ptr, MallocBlock::kArrayNewType); |
+extern "C" PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) __THROW { |
+ return BASE_MALLOPT(cmd, value); |
} |
-// Some STL implementations explicitly invoke this. |
-// It is completely equivalent to a normal delete (delete never throws). |
-void operator delete[](void* ptr, const std::nothrow_t&) __THROW { |
- MallocHook::InvokeDeleteHook(ptr); |
- DebugDeallocate(ptr, MallocBlock::kArrayNewType); |
+#ifdef HAVE_STRUCT_MALLINFO |
+extern "C" PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) __THROW { |
+ return BASE_MALLINFO(); |
} |
- |
-// ========================================================================= // |
- |
-// The following functions may be called via MallocExtension::instance() |
-// for memory verification and statistics. |
-#ifdef TCMALLOC_FOR_DEBUGALLOCATION |
-// Inherit from tcmalloc's version |
-typedef TCMallocImplementation ParentImplementation; |
-#else |
-// Inherit from default version |
-typedef MallocExtension ParentImplementation; |
#endif |
-class DebugMallocImplementation : public ParentImplementation { |
- public: |
- virtual bool GetNumericProperty(const char* name, size_t* value) { |
- bool result = ParentImplementation::GetNumericProperty(name, value); |
- if (result && (strcmp(name, "generic.current_allocated_bytes") == 0)) { |
- // Subtract bytes kept in the free queue |
- size_t qsize = MallocBlock::FreeQueueSize(); |
- if (*value >= qsize) { |
- *value -= qsize; |
- } |
- } |
- return result; |
- } |
- |
- virtual bool VerifyNewMemory(void* p) { |
- if (p) MallocBlock::FromRawPointer(p)->Check(MallocBlock::kNewType); |
- return true; |
- } |
- |
- virtual bool VerifyArrayNewMemory(void* p) { |
- if (p) MallocBlock::FromRawPointer(p)->Check(MallocBlock::kArrayNewType); |
- return true; |
- } |
- |
- virtual bool VerifyMallocMemory(void* p) { |
- if (p) MallocBlock::FromRawPointer(p)->Check(MallocBlock::kMallocType); |
- return true; |
- } |
- |
- virtual bool VerifyAllMemory() { |
- return MallocBlock::CheckEverything(); |
- } |
- |
- virtual bool MallocMemoryStats(int* blocks, size_t* total, |
- int histogram[kMallocHistogramSize]) { |
- return MallocBlock::MemoryStats(blocks, total, histogram); |
- } |
- |
- virtual size_t GetAllocatedSize(void* p) { |
- if (p) { |
- return MallocBlock::FromRawPointer(p)->data_size(); |
- } |
+extern "C" PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) __THROW { |
+ if (!ptr) { |
return 0; |
} |
- virtual size_t GetEstimatedAllocatedSize(size_t size) { |
- return size; |
- } |
- }; |
- |
-static DebugMallocImplementation debug_malloc_implementation; |
- |
-REGISTER_MODULE_INITIALIZER(debugallocation, { |
- // Either we or valgrind will control memory management. We |
- // register our extension if we're the winner. |
- if (RunningOnValgrind()) { |
- // Let Valgrind uses its own malloc (so don't register our extension). |
- } else { |
- MallocExtension::Register(&debug_malloc_implementation); |
- // When the program exits, check all blocks still in the free |
- // queue for corruption. |
- atexit(DanglingWriteChecker); |
- } |
-}); |
- |
-#ifdef TCMALLOC_FOR_DEBUGALLOCATION |
- |
-// Redefine malloc_stats to use tcmalloc's implementation: |
-extern "C" void malloc_stats(void) __THROW { |
- do_malloc_stats(); |
+ MallocBlock* mb = MallocBlock::FromRawPointer(ptr); |
+ // This is just to make sure we actually own mb (and ptr). We don't |
+ // use the actual value, just the 'exception' it raises on error. |
+ (void)BASE_MALLOC_SIZE(mb); |
+ return mb->data_size(); |
} |
-// Some library routines on RedHat 9 allocate memory using malloc() |
-// and free it using __libc_free() (or vice-versa). Since we provide |
-// our own implementations of malloc/free using tcmalloc.cc, |
-// we need to make sure that the __libc_XXX variants |
-// also point to the same implementations. |
-// |
-// Note: this might not override __libc_XXX calls withing libc itself, |
-// but it can be important for other libraries that mention these functions |
-// or when this code is LD_PRELOAD-ed. |
-// TODO: In case these __libc_* definitions do not actually matter, |
-// they should go away from here and from tcmalloc/tcmalloc.cc. |
-// |
-extern "C" { |
- void* __libc_malloc(size_t size) { return malloc(size); } |
- void __libc_free(void* ptr) { free(ptr); } |
- void* __libc_realloc(void* ptr, size_t size) { return realloc(ptr, size); } |
- void* __libc_calloc(size_t n, size_t size) { return calloc(n, size); } |
- void __libc_cfree(void* ptr) { cfree(ptr); } |
- void* __libc_memalign(size_t align, size_t s) { return memalign(align, s); } |
- void* __libc_valloc(size_t size) { return valloc(size); } |
- void* __libc_pvalloc(size_t size) { return pvalloc(size); } |
- int __posix_memalign(void** r, size_t a, size_t s) { |
- return posix_memalign(r, a, s); |
- } |
-} |
+// Override __libc_memalign in libc on linux boxes. |
+// They have a bug in libc that causes them (very rarely) to allocate |
+// with __libc_memalign() yet deallocate with free(). |
+// This function is an exception to the rule of calling MallocHook method |
+// from the stack frame of the allocation function; |
+// heap-checker handles this special case explicitly. |
+static void *MemalignOverride(size_t align, size_t size, const void *caller) |
+ __THROW ATTRIBUTE_SECTION(google_malloc); |
-#endif // #ifdef TCMALLOC_FOR_DEBUGALLOCATION |
+static void *MemalignOverride(size_t align, size_t size, const void *caller) |
+ __THROW { |
+ void *p = do_debug_memalign_or_debug_cpp_memalign(align, size); |
+ MallocHook::InvokeNewHook(p, size); |
+ return p; |
+} |
+void *(*__memalign_hook)(size_t, size_t, const void *) = MemalignOverride; |