diff options
Diffstat (limited to 'third_party/tcmalloc/chromium/src/debugallocation.cc')
| -rw-r--r-- | third_party/tcmalloc/chromium/src/debugallocation.cc | 708 |
1 files changed, 341 insertions, 367 deletions
diff --git a/third_party/tcmalloc/chromium/src/debugallocation.cc b/third_party/tcmalloc/chromium/src/debugallocation.cc index 949fbe9..9de927a 100644 --- a/third_party/tcmalloc/chromium/src/debugallocation.cc +++ b/third_party/tcmalloc/chromium/src/debugallocation.cc @@ -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,48 +131,16 @@ DEFINE_bool(symbolize_stacktrace, 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 - -#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 +// 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) // ========================================================================= // @@ -190,7 +163,7 @@ class FreeQueue { 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 @@ class MallocBlock { // 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 @@ class MallocBlock { 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 @@ class MallocBlock { "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 @@ class MallocBlock { // 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 @@ class MallocBlock { 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 @@ class MallocBlock { // 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 @@ static inline void DebugDeallocate(void* ptr, int type) { // ========================================================================= // -// Alloc/free stuff for debug hooks for malloc & friends - -// 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. - -// Put all callers of MallocHook::Invoke* in this module into -// ATTRIBUTE_SECTION(google_malloc) section, -// so that MallocHook::GetCallerStackTrace can function accurately: - -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); - - 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); +// 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; + } + + 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(); + } + 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 + } +} + +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); } -static void *MemalignOverride(size_t align, size_t size, - const void *caller) __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); -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); - -extern "C" void* malloc(size_t size) __THROW { - void* ptr = DebugAllocate(size, MallocBlock::kMallocType); +// Exported routines + +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,20 +1150,59 @@ extern "C" void* realloc(void* ptr, size_t size) __THROW { 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. @@ -1088,6 +1211,7 @@ 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 @@ static void *do_debug_memalign(size_t alignment, size_t size) { 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 @@ inline void* cpp_debug_alloc(size_t size, int new_type, bool nothrow) { 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 @@ inline void* cpp_debug_alloc(size_t size, int new_type, bool nothrow) { } } -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; + } -// 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); + 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; + } } -// ========================================================================= // - -// Alloc/free stuff for debug operator new[] & friends +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; +} -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; |
