| 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;
|
|
|