Index: third_party/tcmalloc/chromium/src/debugallocation.cc |
=================================================================== |
--- third_party/tcmalloc/chromium/src/debugallocation.cc (revision 124832) |
+++ 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; |