| Index: third_party/tcmalloc/chromium/src/debugallocation.cc
|
| ===================================================================
|
| --- third_party/tcmalloc/chromium/src/debugallocation.cc (revision 126022)
|
| +++ third_party/tcmalloc/chromium/src/debugallocation.cc (working copy)
|
| @@ -31,13 +31,6 @@
|
| // Author: Urs Holzle <opensource@google.com>
|
|
|
| #include "config.h"
|
| -#include <errno.h>
|
| -#ifdef HAVE_FCNTL_H
|
| -#include <fcntl.h>
|
| -#endif
|
| -#ifdef HAVE_INTTYPES_H
|
| -#include <inttypes.h>
|
| -#endif
|
| // We only need malloc.h for struct mallinfo.
|
| #ifdef HAVE_STRUCT_MALLINFO
|
| // Malloc can be in several places on older versions of OS X.
|
| @@ -49,29 +42,34 @@
|
| # include <sys/malloc.h>
|
| # endif
|
| #endif
|
| -#ifdef HAVE_PTHREAD
|
| #include <pthread.h>
|
| +#include <stdio.h>
|
| +#ifdef HAVE_INTTYPES_H
|
| +#include <inttypes.h>
|
| #endif
|
| #include <stdarg.h>
|
| -#include <stdio.h>
|
| -#include <string.h>
|
| #ifdef HAVE_MMAP
|
| #include <sys/mman.h>
|
| #endif
|
| -#include <sys/stat.h>
|
| #include <sys/types.h>
|
| +#include <sys/stat.h>
|
| +#ifdef HAVE_FCNTL_H
|
| +#include <fcntl.h>
|
| +#endif
|
| #ifdef HAVE_UNISTD_H
|
| #include <unistd.h>
|
| #endif
|
| +#include <errno.h>
|
| +#include <string.h>
|
|
|
| -#include <gperftools/malloc_extension.h>
|
| -#include <gperftools/malloc_hook.h>
|
| -#include <gperftools/stacktrace.h>
|
| -#include "addressmap-inl.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 "base/spinlock.h"
|
| +#include "addressmap-inl.h"
|
| #include "malloc_hook-inl.h"
|
| #include "symbolize.h"
|
|
|
| @@ -126,13 +124,6 @@
|
| EnvToBool("TCMALLOC_SYMBOLIZE_STACKTRACE", true),
|
| "Symbolize the stack trace when provided (on some error exits)");
|
|
|
| -// If we are LD_PRELOAD-ed against a non-pthreads app, then
|
| -// pthread_once won't be defined. We declare it here, for that
|
| -// case (with weak linkage) which will cause the non-definition to
|
| -// resolve to NULL. We can then check for NULL or not in Instance.
|
| -extern "C" int pthread_once(pthread_once_t *, void (*)(void))
|
| - ATTRIBUTE_WEAK;
|
| -
|
| // ========================================================================= //
|
|
|
| // A safe version of printf() that does not do any allocation and
|
| @@ -143,13 +134,13 @@
|
| // The do_* functions are defined in tcmalloc/tcmalloc.cc,
|
| // which is included before this file
|
| // when TCMALLOC_FOR_DEBUGALLOCATION is defined
|
| -// TODO(csilvers): get rid of these now that we are tied to tcmalloc.
|
| -#define BASE_MALLOC_NEW do_malloc
|
| +#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)
|
|
|
| // ========================================================================= //
|
|
|
| @@ -178,7 +169,6 @@
|
| }
|
|
|
| QueueEntry Pop() {
|
| - RAW_CHECK(q_back_ != q_front_, "Queue is empty");
|
| const QueueEntry& ret = q_[q_back_];
|
| q_back_ = (q_back_ + 1) % kFreeQueueSize;
|
| return ret;
|
| @@ -201,7 +191,7 @@
|
| MallocBlockQueueEntry() : block(NULL), size(0),
|
| num_deleter_pcs(0), deleter_threadid(0) {}
|
| MallocBlockQueueEntry(MallocBlock* b, size_t s) : block(b), size(s) {
|
| - if (FLAGS_max_free_queue_size != 0 && b != NULL) {
|
| + if (FLAGS_max_free_queue_size != 0) {
|
| // Adjust the number of frames to skip (4) if you change the
|
| // location of this call.
|
| num_deleter_pcs =
|
| @@ -276,8 +266,7 @@
|
|
|
| // This array will be filled with 0xCD, for use with memcmp.
|
| static unsigned char kMagicDeletedBuffer[1024];
|
| - static pthread_once_t deleted_buffer_initialized_;
|
| - static bool deleted_buffer_initialized_no_pthreads_;
|
| + static bool deleted_buffer_initialized_;
|
|
|
| private: // data layout
|
|
|
| @@ -572,18 +561,14 @@
|
|
|
| static void ProcessFreeQueue(MallocBlock* b, size_t size,
|
| int max_free_queue_size) {
|
| - // MallocBlockQueueEntry are about 144 in size, so we can only
|
| - // use a small array of them on the stack.
|
| - MallocBlockQueueEntry entries[4];
|
| - int num_entries = 0;
|
| - MallocBlockQueueEntry new_entry(b, size);
|
| - free_queue_lock_.Lock();
|
| + SpinLockHolder l(&free_queue_lock_);
|
| if (free_queue_ == NULL)
|
| free_queue_ = new FreeQueue<MallocBlockQueueEntry>;
|
| RAW_CHECK(!free_queue_->Full(), "Free queue mustn't be full!");
|
|
|
| if (b != NULL) {
|
| free_queue_size_ += size + sizeof(MallocBlockQueueEntry);
|
| + MallocBlockQueueEntry new_entry(b, size);
|
| free_queue_->Push(new_entry);
|
| }
|
|
|
| @@ -591,46 +576,20 @@
|
| // max_free_queue_size, and the free queue has at least one free
|
| // space in it.
|
| while (free_queue_size_ > max_free_queue_size || free_queue_->Full()) {
|
| - RAW_CHECK(num_entries < arraysize(entries), "entries array overflow");
|
| - entries[num_entries] = free_queue_->Pop();
|
| - free_queue_size_ -=
|
| - entries[num_entries].size + sizeof(MallocBlockQueueEntry);
|
| - num_entries++;
|
| - if (num_entries == arraysize(entries)) {
|
| - // The queue will not be full at this point, so it is ok to
|
| - // release the lock. The queue may still contain more than
|
| - // max_free_queue_size, but this is not a strict invariant.
|
| - free_queue_lock_.Unlock();
|
| - for (int i = 0; i < num_entries; i++) {
|
| - CheckForDanglingWrites(entries[i]);
|
| - BASE_FREE(entries[i].block);
|
| - }
|
| - num_entries = 0;
|
| - free_queue_lock_.Lock();
|
| - }
|
| + MallocBlockQueueEntry cur = free_queue_->Pop();
|
| + CheckForDanglingWrites(cur);
|
| + free_queue_size_ -= cur.size + sizeof(MallocBlockQueueEntry);
|
| + BASE_FREE(cur.block);
|
| }
|
| RAW_CHECK(free_queue_size_ >= 0, "Free queue size went negative!");
|
| - free_queue_lock_.Unlock();
|
| - for (int i = 0; i < num_entries; i++) {
|
| - CheckForDanglingWrites(entries[i]);
|
| - BASE_FREE(entries[i].block);
|
| - }
|
| }
|
|
|
| - static void InitDeletedBuffer() {
|
| - memset(kMagicDeletedBuffer, kMagicDeletedByte, sizeof(kMagicDeletedBuffer));
|
| - deleted_buffer_initialized_no_pthreads_ = true;
|
| - }
|
| -
|
| static void CheckForDanglingWrites(const MallocBlockQueueEntry& queue_entry) {
|
| // Initialize the buffer if necessary.
|
| - if (pthread_once)
|
| - pthread_once(&deleted_buffer_initialized_, &InitDeletedBuffer);
|
| - if (!deleted_buffer_initialized_no_pthreads_) {
|
| - // This will be the case on systems that don't link in pthreads,
|
| - // including on FreeBSD where pthread_once has a non-zero address
|
| - // (but doesn't do anything) even when pthreads isn't linked in.
|
| - InitDeletedBuffer();
|
| + if (!deleted_buffer_initialized_) {
|
| + // This is threadsafe. We hold free_queue_lock_.
|
| + memset(kMagicDeletedBuffer, 0xcd, sizeof(kMagicDeletedBuffer));
|
| + deleted_buffer_initialized_ = true;
|
| }
|
|
|
| const unsigned char* p =
|
| @@ -666,9 +625,9 @@
|
| // lines we'll output:
|
| if (size_of_buffer <= 1024) {
|
| for (int i = 0; i < size_of_buffer; ++i) {
|
| - if (buffer[i] != kMagicDeletedByte) {
|
| - RAW_LOG(ERROR, "Buffer byte %d is 0x%02x (should be 0x%02x).",
|
| - i, buffer[i], kMagicDeletedByte);
|
| + if (buffer[i] != 0xcd) {
|
| + RAW_LOG(ERROR, "Buffer byte %d is 0x%02x (should be 0xcd).",
|
| + i, buffer[i]);
|
| }
|
| }
|
| } else {
|
| @@ -712,10 +671,8 @@
|
| RAW_LOG(FATAL,
|
| "Memory was written to after being freed. MallocBlock: %p, user "
|
| "ptr: %p, size: %zd. If you can't find the source of the error, "
|
| - "try using ASan (http://code.google.com/p/address-sanitizer/), "
|
| - "Valgrind, or Purify, or study the "
|
| - "output of the deleter's stack printed above.",
|
| - b, b->data_addr(), size);
|
| + "try using valgrind or purify, or study the output of the "
|
| + "deleter's stack printed above.", b, b->data_addr(), size);
|
| }
|
|
|
| static MallocBlock* FromRawPointer(void* p) {
|
| @@ -742,7 +699,7 @@
|
| return FromRawPointer(const_cast<void*>(p));
|
| }
|
|
|
| - void Check(int type) const {
|
| + void Check(int type) {
|
| alloc_map_lock_.Lock();
|
| CheckLocked(type);
|
| alloc_map_lock_.Unlock();
|
| @@ -820,8 +777,7 @@
|
| SpinLock MallocBlock::free_queue_lock_(SpinLock::LINKER_INITIALIZED);
|
|
|
| unsigned char MallocBlock::kMagicDeletedBuffer[1024];
|
| -pthread_once_t MallocBlock::deleted_buffer_initialized_ = PTHREAD_ONCE_INIT;
|
| -bool MallocBlock::deleted_buffer_initialized_no_pthreads_ = false;
|
| +bool MallocBlock::deleted_buffer_initialized_ = false;
|
|
|
| const char* const MallocBlock::kAllocName[] = {
|
| "malloc",
|
| @@ -1015,17 +971,17 @@
|
| return result;
|
| }
|
|
|
| - virtual bool VerifyNewMemory(const void* p) {
|
| + virtual bool VerifyNewMemory(void* p) {
|
| if (p) MallocBlock::FromRawPointer(p)->Check(MallocBlock::kNewType);
|
| return true;
|
| }
|
|
|
| - virtual bool VerifyArrayNewMemory(const void* p) {
|
| + virtual bool VerifyArrayNewMemory(void* p) {
|
| if (p) MallocBlock::FromRawPointer(p)->Check(MallocBlock::kArrayNewType);
|
| return true;
|
| }
|
|
|
| - virtual bool VerifyMallocMemory(const void* p) {
|
| + virtual bool VerifyMallocMemory(void* p) {
|
| if (p) MallocBlock::FromRawPointer(p)->Check(MallocBlock::kMallocType);
|
| return true;
|
| }
|
| @@ -1039,25 +995,14 @@
|
| return MallocBlock::MemoryStats(blocks, total, histogram);
|
| }
|
|
|
| - virtual size_t GetEstimatedAllocatedSize(size_t size) {
|
| - return size;
|
| - }
|
| -
|
| - virtual size_t GetAllocatedSize(const void* p) {
|
| + virtual size_t GetAllocatedSize(void* p) {
|
| if (p) {
|
| - RAW_CHECK(GetOwnership(p) != MallocExtension::kNotOwned,
|
| - "ptr not allocated by tcmalloc");
|
| return MallocBlock::FromRawPointer(p)->data_size();
|
| }
|
| return 0;
|
| }
|
| -
|
| - virtual MallocExtension::Ownership GetOwnership(const void* p) {
|
| - if (p) {
|
| - const MallocBlock* mb = MallocBlock::FromRawPointer(p);
|
| - return TCMallocImplementation::GetOwnership(mb);
|
| - }
|
| - return MallocExtension::kNotOwned; // nobody owns NULL
|
| + virtual size_t GetEstimatedAllocatedSize(size_t size) {
|
| + return size;
|
| }
|
|
|
| virtual void GetFreeListSizes(vector<MallocExtension::FreeListInfo>* v) {
|
| @@ -1079,28 +1024,22 @@
|
|
|
| REGISTER_MODULE_INITIALIZER(debugallocation, {
|
| // Either we or valgrind will control memory management. We
|
| - // register our extension if we're the winner. Otherwise let
|
| - // Valgrind use its own malloc (so don't register our extension).
|
| - if (!RunningOnValgrind()) {
|
| + // 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);
|
| - }
|
| -});
|
| -
|
| -REGISTER_MODULE_DESTRUCTOR(debugallocation, {
|
| - if (!RunningOnValgrind()) {
|
| // When the program exits, check all blocks still in the free
|
| // queue for corruption.
|
| - DanglingWriteChecker();
|
| + atexit(DanglingWriteChecker);
|
| }
|
| });
|
|
|
| // ========================================================================= //
|
|
|
| // This is mostly the same a cpp_alloc in tcmalloc.cc.
|
| -// TODO(csilvers): change Allocate() above to call cpp_alloc, so we
|
| -// don't have to reproduce the logic here. To make tc_new_mode work
|
| -// properly, I think we'll need to separate out the logic of throwing
|
| -// from the logic of calling the new-handler.
|
| +// 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);
|
| @@ -1416,5 +1355,29 @@
|
| #endif
|
|
|
| extern "C" PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) __THROW {
|
| - return MallocExtension::instance()->GetAllocatedSize(ptr);
|
| + if (!ptr) {
|
| + return 0;
|
| + }
|
| + 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();
|
| }
|
| +
|
| +// 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);
|
| +
|
| +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;
|
|
|