| Index: third_party/tcmalloc/chromium/src/tcmalloc.cc
|
| ===================================================================
|
| --- third_party/tcmalloc/chromium/src/tcmalloc.cc (revision 41942)
|
| +++ third_party/tcmalloc/chromium/src/tcmalloc.cc (working copy)
|
| @@ -124,7 +124,6 @@
|
| #include "linked_list.h"
|
| #include "maybe_threads.h"
|
| #include "page_heap.h"
|
| -#include "page_heap_allocator.h"
|
| #include "pagemap.h"
|
| #include "span.h"
|
| #include "static_vars.h"
|
| @@ -136,8 +135,8 @@
|
| # define WIN32_DO_PATCHING 1
|
| #endif
|
|
|
| +using std::max;
|
| using tcmalloc::PageHeap;
|
| -using tcmalloc::PageHeapAllocator;
|
| using tcmalloc::SizeMap;
|
| using tcmalloc::Span;
|
| using tcmalloc::StackTrace;
|
| @@ -229,23 +228,30 @@
|
| ATTRIBUTE_SECTION(google_malloc);
|
| void* tc_newarray_nothrow(size_t size, const std::nothrow_t&) __THROW
|
| ATTRIBUTE_SECTION(google_malloc);
|
| -}
|
| + // Surprisingly, compilers use a nothrow-delete internally. See, eg:
|
| + // http://www.dinkumware.com/manuals/?manual=compleat&page=new.html
|
| + void tc_delete_nothrow(void* ptr, const std::nothrow_t&) __THROW
|
| + ATTRIBUTE_SECTION(google_malloc);
|
| + void tc_deletearray_nothrow(void* ptr, const std::nothrow_t&) __THROW
|
| + ATTRIBUTE_SECTION(google_malloc);
|
| +} // extern "C"
|
|
|
| // Override the libc functions to prefer our own instead. This comes
|
| // first so code in tcmalloc.cc can use the overridden versions. One
|
| // exception: in windows, by default, we patch our code into these
|
| // functions (via src/windows/patch_function.cc) rather than override
|
| // them. In that case, we don't want to do this overriding here.
|
| -#ifndef WIN32_DO_PATCHING
|
| +#if !defined(WIN32_DO_PATCHING) && !defined(TCMALLOC_FOR_DEBUGALLOCATION)
|
|
|
| // TODO(mbelshe): Turn off TCMalloc's symbols for libc. We do that
|
| // elsewhere.
|
| -#if 0
|
| +#ifndef _WIN32
|
|
|
| #if defined(__GNUC__) && !defined(__MACH__)
|
| // Potentially faster variants that use the gcc alias extension.
|
| - // Mach-O (Darwin) does not support weak aliases, hence the __MACH__ check.
|
| // FreeBSD does support aliases, but apparently not correctly. :-(
|
| + // NOTE: we make many of these symbols weak, but do so in the makefile
|
| + // (via objcopy -W) and not here. That ends up being more portable.
|
| # define ALIAS(x) __attribute__ ((alias (x)))
|
| void* operator new(size_t size) ALIAS("tc_new");
|
| void operator delete(void* p) __THROW ALIAS("tc_delete");
|
| @@ -255,6 +261,10 @@
|
| ALIAS("tc_new_nothrow");
|
| void* operator new[](size_t size, const std::nothrow_t&) __THROW
|
| ALIAS("tc_newarray_nothrow");
|
| +void operator delete(void* size, const std::nothrow_t&) __THROW
|
| + ALIAS("tc_delete_nothrow");
|
| +void operator delete[](void* size, const std::nothrow_t&) __THROW
|
| + ALIAS("tc_deletearray_nothrow");
|
| extern "C" {
|
| void* malloc(size_t size) __THROW ALIAS("tc_malloc");
|
| void free(void* ptr) __THROW ALIAS("tc_free");
|
| @@ -271,26 +281,8 @@
|
| #ifdef HAVE_STRUCT_MALLINFO
|
| struct mallinfo mallinfo(void) __THROW ALIAS("tc_mallinfo");
|
| #endif
|
| - // 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, we need to make sure that
|
| - // the __libc_XXX variants (defined as part of glibc) also point to
|
| - // the same implementations.
|
| -# if defined(__GLIBC__)
|
| - void* __libc_malloc(size_t size) ALIAS("tc_malloc");
|
| - void __libc_free(void* ptr) ALIAS("tc_free");
|
| - void* __libc_realloc(void* ptr, size_t size) ALIAS("tc_realloc");
|
| - void* __libc_calloc(size_t n, size_t size) ALIAS("tc_calloc");
|
| - void __libc_cfree(void* ptr) ALIAS("tc_cfree");
|
| - void* __libc_memalign(size_t align, size_t s) ALIAS("tc_memalign");
|
| - void* __libc_valloc(size_t size) ALIAS("tc_valloc");
|
| - void* __libc_pvalloc(size_t size) ALIAS("tc_pvalloc");
|
| - int __posix_memalign(void** r, size_t a, size_t s) ALIAS("tc_posix_memalign");
|
| -# define HAVE_ALIASED___LIBC 1
|
| -# endif // #if defined(__GLIBC__)
|
| } // extern "C"
|
| -# undef ALIAS
|
| -#else
|
| +#else // #if defined(__GNUC__) && !defined(__MACH__)
|
| // Portable wrappers
|
| void* operator new(size_t size) { return tc_new(size); }
|
| void operator delete(void* p) __THROW { tc_delete(p); }
|
| @@ -302,6 +294,12 @@
|
| void* operator new[](size_t size, const std::nothrow_t& nt) __THROW {
|
| return tc_newarray_nothrow(size, nt);
|
| }
|
| +void operator delete(void* ptr, const std::nothrow_t& nt) __THROW {
|
| + return tc_delete_nothrow(ptr, nt);
|
| +}
|
| +void operator delete[](void* ptr, const std::nothrow_t& nt) __THROW {
|
| + return tc_deletearray_nothrow(ptr, nt);
|
| +}
|
| extern "C" {
|
| void* malloc(size_t s) __THROW { return tc_malloc(s); }
|
| void free(void* p) __THROW { tc_free(p); }
|
| @@ -319,11 +317,27 @@
|
| #ifdef HAVE_STRUCT_MALLINFO
|
| struct mallinfo mallinfo(void) __THROW { return tc_mallinfo(); }
|
| #endif
|
| -} // extern C
|
| +} // extern "C"
|
| #endif // #if defined(__GNUC__)
|
|
|
| -#ifndef HAVE_ALIASED___LIBC
|
| +// 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, we need to make sure that
|
| +// the __libc_XXX variants (defined as part of glibc) also point to
|
| +// the same implementations.
|
| +#ifdef __GLIBC__ // only glibc defines __libc_*
|
| extern "C" {
|
| +#ifdef ALIAS
|
| + void* __libc_malloc(size_t size) ALIAS("tc_malloc");
|
| + void __libc_free(void* ptr) ALIAS("tc_free");
|
| + void* __libc_realloc(void* ptr, size_t size) ALIAS("tc_realloc");
|
| + void* __libc_calloc(size_t n, size_t size) ALIAS("tc_calloc");
|
| + void __libc_cfree(void* ptr) ALIAS("tc_cfree");
|
| + void* __libc_memalign(size_t align, size_t s) ALIAS("tc_memalign");
|
| + void* __libc_valloc(size_t size) ALIAS("tc_valloc");
|
| + void* __libc_pvalloc(size_t size) ALIAS("tc_pvalloc");
|
| + int __posix_memalign(void** r, size_t a, size_t s) ALIAS("tc_posix_memalign");
|
| +#else // #ifdef ALIAS
|
| 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); }
|
| @@ -335,19 +349,25 @@
|
| int __posix_memalign(void** r, size_t a, size_t s) {
|
| return posix_memalign(r, a, s);
|
| }
|
| +#endif // #ifdef ALIAS
|
| } // extern "C"
|
| -#endif // #ifndef HAVE_ALIASED___LIBC
|
| +#endif // ifdef __GLIBC__
|
|
|
| -#endif // #ifdef 0
|
| +#endif // #ifndef _WIN32
|
| +#undef ALIAS
|
|
|
| -#endif // #ifndef WIN32_DO_PATCHING
|
| +#endif // #ifndef(WIN32_DO_PATCHING) && ndef(TCMALLOC_FOR_DEBUGALLOCATION)
|
|
|
|
|
| // ----------------------- IMPLEMENTATION -------------------------------
|
|
|
| -// These routines are called by free(), realloc(), etc. if the pointer is
|
| -// invalid. This is a cheap (source-editing required) kind of exception
|
| -// handling for these routines.
|
| +static int tc_new_mode = 0; // See tc_set_new_mode().
|
| +
|
| +// Routines such as free() and realloc() catch some erroneous pointers
|
| +// passed to them, and invoke the below when they do. (An erroneous pointer
|
| +// won't be caught if it's within a valid span or a stale span for which
|
| +// the pagemap cache has a non-zero sizeclass.) This is a cheap (source-editing
|
| +// required) kind of exception handling for these routines.
|
| namespace {
|
| void InvalidFree(void* ptr) {
|
| CRASH("Attempt to free invalid pointer: %p\n", ptr);
|
| @@ -366,13 +386,11 @@
|
|
|
| // Extract interesting stats
|
| struct TCMallocStats {
|
| - uint64_t system_bytes; // Bytes alloced from system
|
| - uint64_t committed_bytes; // Bytes alloced and committed from system
|
| - uint64_t thread_bytes; // Bytes in thread caches
|
| - uint64_t central_bytes; // Bytes in central cache
|
| - uint64_t transfer_bytes; // Bytes in central transfer cache
|
| - uint64_t pageheap_bytes; // Bytes in page heap
|
| - uint64_t metadata_bytes; // Bytes alloced for metadata
|
| + uint64_t thread_bytes; // Bytes in thread caches
|
| + uint64_t central_bytes; // Bytes in central cache
|
| + uint64_t transfer_bytes; // Bytes in central transfer cache
|
| + uint64_t metadata_bytes; // Bytes alloced for metadata
|
| + PageHeap::Stats pageheap; // Stats from page heap
|
| };
|
|
|
| // Get stats into "r". Also get per-size-class counts if class_count != NULL
|
| @@ -394,14 +412,8 @@
|
| { // scope
|
| SpinLockHolder h(Static::pageheap_lock());
|
| ThreadCache::GetThreadStats(&r->thread_bytes, class_count);
|
| - }
|
| -
|
| - { //scope
|
| - SpinLockHolder h(Static::pageheap_lock());
|
| - r->system_bytes = Static::pageheap()->SystemBytes();
|
| - r->committed_bytes = Static::pageheap()->CommittedBytes();
|
| r->metadata_bytes = tcmalloc::metadata_system_bytes();
|
| - r->pageheap_bytes = Static::pageheap()->FreeBytes();
|
| + r->pageheap = Static::pageheap()->stats();
|
| }
|
| }
|
|
|
| @@ -413,8 +425,9 @@
|
|
|
| static const double MB = 1048576.0;
|
|
|
| - const uint64_t bytes_in_use = stats.system_bytes
|
| - - stats.pageheap_bytes
|
| + const uint64_t bytes_in_use = stats.pageheap.system_bytes
|
| + - stats.pageheap.free_bytes
|
| + - stats.pageheap.unmapped_bytes
|
| - stats.central_bytes
|
| - stats.transfer_bytes
|
| - stats.thread_bytes;
|
| @@ -422,13 +435,15 @@
|
| out->printf("WASTE: %7.1f MB committed but not used\n"
|
| "WASTE: %7.1f MB bytes committed, %7.1f MB bytes in use\n"
|
| "WASTE: committed/used ratio of %f\n",
|
| - (stats.committed_bytes - bytes_in_use) / MB,
|
| - stats.committed_bytes / MB,
|
| + (stats.pageheap.committed_bytes - bytes_in_use) / MB,
|
| + stats.pageheap.committed_bytes / MB,
|
| bytes_in_use / MB,
|
| - stats.committed_bytes / static_cast<double>(bytes_in_use));
|
| + stats.pageheap.committed_bytes / static_cast<double>(bytes_in_use));
|
|
|
| if (level >= 2) {
|
| out->printf("------------------------------------------------\n");
|
| + out->printf("Size class breakdown\n");
|
| + out->printf("------------------------------------------------\n");
|
| uint64_t cumulative = 0;
|
| for (int cl = 0; cl < kNumClasses; ++cl) {
|
| if (class_count[cl] > 0) {
|
| @@ -456,6 +471,7 @@
|
| "MALLOC: %12" PRIu64 " (%7.1f MB) Bytes committed\n"
|
| "MALLOC: %12" PRIu64 " (%7.1f MB) Bytes in use by application\n"
|
| "MALLOC: %12" PRIu64 " (%7.1f MB) Bytes free in page heap\n"
|
| + "MALLOC: %12" PRIu64 " (%7.1f MB) Bytes unmapped in page heap\n"
|
| "MALLOC: %12" PRIu64 " (%7.1f MB) Bytes free in central cache\n"
|
| "MALLOC: %12" PRIu64 " (%7.1f MB) Bytes free in transfer cache\n"
|
| "MALLOC: %12" PRIu64 " (%7.1f MB) Bytes free in thread caches\n"
|
| @@ -463,10 +479,11 @@
|
| "MALLOC: %12" PRIu64 " Thread heaps in use\n"
|
| "MALLOC: %12" PRIu64 " (%7.1f MB) Metadata allocated\n"
|
| "------------------------------------------------\n",
|
| - stats.system_bytes, stats.system_bytes / MB,
|
| - stats.committed_bytes, stats.committed_bytes / MB,
|
| + stats.pageheap.system_bytes, stats.pageheap.system_bytes / MB,
|
| + stats.pageheap.committed_bytes, stats.pageheap.committed_bytes / MB,
|
| bytes_in_use, bytes_in_use / MB,
|
| - stats.pageheap_bytes, stats.pageheap_bytes / MB,
|
| + stats.pageheap.free_bytes, stats.pageheap.free_bytes / MB,
|
| + stats.pageheap.unmapped_bytes, stats.pageheap.unmapped_bytes / MB,
|
| stats.central_bytes, stats.central_bytes / MB,
|
| stats.transfer_bytes, stats.transfer_bytes / MB,
|
| stats.thread_bytes, stats.thread_bytes / MB,
|
| @@ -530,9 +547,50 @@
|
| return result;
|
| }
|
|
|
| +static void IterateOverRanges(void* arg, MallocExtension::RangeFunction func) {
|
| + PageID page = 1; // Some code may assume that page==0 is never used
|
| + bool done = false;
|
| + while (!done) {
|
| + // Accumulate a small number of ranges in a local buffer
|
| + static const int kNumRanges = 16;
|
| + static base::MallocRange ranges[kNumRanges];
|
| + int n = 0;
|
| + {
|
| + SpinLockHolder h(Static::pageheap_lock());
|
| + while (n < kNumRanges) {
|
| + if (!Static::pageheap()->GetNextRange(page, &ranges[n])) {
|
| + done = true;
|
| + break;
|
| + } else {
|
| + uintptr_t limit = ranges[n].address + ranges[n].length;
|
| + page = (limit + kPageSize - 1) >> kPageShift;
|
| + n++;
|
| + }
|
| + }
|
| + }
|
| +
|
| + for (int i = 0; i < n; i++) {
|
| + (*func)(arg, &ranges[i]);
|
| + }
|
| + }
|
| +}
|
| +
|
| // TCMalloc's support for extra malloc interfaces
|
| class TCMallocImplementation : public MallocExtension {
|
| + private:
|
| + // ReleaseToSystem() might release more than the requested bytes because
|
| + // the page heap releases at the span granularity, and spans are of wildly
|
| + // different sizes. This member keeps track of the extra bytes bytes
|
| + // released so that the app can periodically call ReleaseToSystem() to
|
| + // release memory at a constant rate.
|
| + // NOTE: Protected by Static::pageheap_lock().
|
| + size_t extra_bytes_released_;
|
| +
|
| public:
|
| + TCMallocImplementation()
|
| + : extra_bytes_released_(0) {
|
| + }
|
| +
|
| virtual void GetStats(char* buffer, int buffer_length) {
|
| ASSERT(buffer_length > 0);
|
| TCMalloc_Printer printer(buffer, buffer_length);
|
| @@ -562,42 +620,54 @@
|
| return DumpHeapGrowthStackTraces();
|
| }
|
|
|
| + virtual void Ranges(void* arg, RangeFunction func) {
|
| + IterateOverRanges(arg, func);
|
| + }
|
| +
|
| virtual bool GetNumericProperty(const char* name, size_t* value) {
|
| ASSERT(name != NULL);
|
|
|
| if (strcmp(name, "generic.current_allocated_bytes") == 0) {
|
| TCMallocStats stats;
|
| ExtractStats(&stats, NULL);
|
| - *value = stats.system_bytes
|
| + *value = stats.pageheap.system_bytes
|
| - stats.thread_bytes
|
| - stats.central_bytes
|
| - stats.transfer_bytes
|
| - - stats.pageheap_bytes;
|
| + - stats.pageheap.free_bytes
|
| + - stats.pageheap.unmapped_bytes;
|
| return true;
|
| }
|
|
|
| if (strcmp(name, "generic.heap_size") == 0) {
|
| TCMallocStats stats;
|
| ExtractStats(&stats, NULL);
|
| - *value = stats.system_bytes;
|
| + *value = stats.pageheap.system_bytes;
|
| return true;
|
| }
|
|
|
| - if (strcmp(name, "generic.committed_bytes") == 0) {
|
| - TCMallocStats stats;
|
| - ExtractStats(&stats, NULL);
|
| - *value = stats.committed_bytes + stats.metadata_bytes;
|
| + if (strcmp(name, "tcmalloc.slack_bytes") == 0) {
|
| + // We assume that bytes in the page heap are not fragmented too
|
| + // badly, and are therefore available for allocation without
|
| + // growing the pageheap system byte count.
|
| + SpinLockHolder l(Static::pageheap_lock());
|
| + PageHeap::Stats stats = Static::pageheap()->stats();
|
| + *value = stats.free_bytes + stats.unmapped_bytes;
|
| return true;
|
| }
|
|
|
| - if (strcmp(name, "tcmalloc.slack_bytes") == 0) {
|
| - // We assume that bytes in the page heap are not fragmented too
|
| - // badly, and are therefore available for allocation.
|
| + if (strcmp(name, "tcmalloc.pageheap_free_bytes") == 0) {
|
| SpinLockHolder l(Static::pageheap_lock());
|
| - *value = Static::pageheap()->FreeBytes();
|
| + *value = Static::pageheap()->stats().free_bytes;
|
| return true;
|
| }
|
|
|
| + if (strcmp(name, "tcmalloc.pageheap_unmapped_bytes") == 0) {
|
| + SpinLockHolder l(Static::pageheap_lock());
|
| + *value = Static::pageheap()->stats().unmapped_bytes;
|
| + return true;
|
| + }
|
| +
|
| if (strcmp(name, "tcmalloc.max_total_thread_cache_bytes") == 0) {
|
| SpinLockHolder l(Static::pageheap_lock());
|
| *value = ThreadCache::overall_thread_cache_size();
|
| @@ -630,9 +700,31 @@
|
| ThreadCache::BecomeIdle();
|
| }
|
|
|
| - virtual void ReleaseFreeMemory() {
|
| + virtual void MarkThreadBusy(); // Implemented below
|
| +
|
| + virtual void ReleaseToSystem(size_t num_bytes) {
|
| SpinLockHolder h(Static::pageheap_lock());
|
| - Static::pageheap()->ReleaseFreePages();
|
| + if (num_bytes <= extra_bytes_released_) {
|
| + // We released too much on a prior call, so don't release any
|
| + // more this time.
|
| + extra_bytes_released_ = extra_bytes_released_ - num_bytes;
|
| + return;
|
| + }
|
| + num_bytes = num_bytes - extra_bytes_released_;
|
| + // num_bytes might be less than one page. If we pass zero to
|
| + // ReleaseAtLeastNPages, it won't do anything, so we release a whole
|
| + // page now and let extra_bytes_released_ smooth it out over time.
|
| + Length num_pages = max<Length>(num_bytes >> kPageShift, 1);
|
| + size_t bytes_released = Static::pageheap()->ReleaseAtLeastNPages(
|
| + num_pages) << kPageShift;
|
| + if (bytes_released > num_bytes) {
|
| + extra_bytes_released_ = bytes_released - num_bytes;
|
| + } else {
|
| + // The PageHeap wasn't able to release num_bytes. Don't try to
|
| + // compensate with a big release next time. Specifically,
|
| + // ReleaseFreeMemory() calls ReleaseToSystem(LONG_MAX).
|
| + extra_bytes_released_ = 0;
|
| + }
|
| }
|
|
|
| virtual void SetMemoryReleaseRate(double rate) {
|
| @@ -681,9 +773,9 @@
|
| // patch the windows VirtualAlloc, etc.
|
| PatchWindowsFunctions(); // defined in windows/patch_functions.cc
|
| #endif
|
| - free(malloc(1));
|
| + tc_free(tc_malloc(1));
|
| ThreadCache::InitTSD();
|
| - free(malloc(1));
|
| + tc_free(tc_malloc(1));
|
| MallocExtension::Register(new TCMallocImplementation);
|
| }
|
| }
|
| @@ -778,6 +870,24 @@
|
|
|
| namespace {
|
|
|
| +inline void* cpp_alloc(size_t size, bool nothrow);
|
| +inline void* do_malloc(size_t size);
|
| +
|
| +// TODO(willchan): Investigate whether or not inlining this much is harmful to
|
| +// performance.
|
| +// This is equivalent to do_malloc() except when tc_new_mode is set to true.
|
| +// Otherwise, it will run the std::new_handler if set.
|
| +inline void* do_malloc_or_cpp_alloc(size_t size) {
|
| + return tc_new_mode ? cpp_alloc(size, true) : do_malloc(size);
|
| +}
|
| +
|
| +void* cpp_memalign(size_t align, size_t size);
|
| +void* do_memalign(size_t align, size_t size);
|
| +
|
| +inline void* do_memalign_or_cpp_memalign(size_t align, size_t size) {
|
| + return tc_new_mode ? cpp_memalign(align, size) : do_memalign(align, size);
|
| +}
|
| +
|
| // Helper for do_malloc().
|
| inline void* do_malloc_pages(Length num_pages) {
|
| Span *span;
|
| @@ -828,7 +938,7 @@
|
| const size_t size = n * elem_size;
|
| if (elem_size != 0 && size / elem_size != n) return NULL;
|
|
|
| - void* result = do_malloc(size);
|
| + void* result = do_malloc_or_cpp_alloc(size);
|
| if (result != NULL) {
|
| memset(result, 0, size);
|
| }
|
| @@ -937,11 +1047,11 @@
|
| void* new_ptr = NULL;
|
|
|
| if (new_size > old_size && new_size < lower_bound_to_grow) {
|
| - new_ptr = do_malloc(lower_bound_to_grow);
|
| + new_ptr = do_malloc_or_cpp_alloc(lower_bound_to_grow);
|
| }
|
| if (new_ptr == NULL) {
|
| // Either new_size is not a tiny increment, or last do_malloc failed.
|
| - new_ptr = do_malloc(new_size);
|
| + new_ptr = do_malloc_or_cpp_alloc(new_size);
|
| }
|
| if (new_ptr == NULL) {
|
| return NULL;
|
| @@ -1062,16 +1172,18 @@
|
|
|
| // Unfortunately, the struct contains "int" field, so some of the
|
| // size values will be truncated.
|
| - info.arena = static_cast<int>(stats.system_bytes);
|
| + info.arena = static_cast<int>(stats.pageheap.system_bytes);
|
| info.fsmblks = static_cast<int>(stats.thread_bytes
|
| + stats.central_bytes
|
| + stats.transfer_bytes);
|
| - info.fordblks = static_cast<int>(stats.pageheap_bytes);
|
| - info.uordblks = static_cast<int>(stats.system_bytes
|
| + info.fordblks = static_cast<int>(stats.pageheap.free_bytes +
|
| + stats.pageheap.unmapped_bytes);
|
| + info.uordblks = static_cast<int>(stats.pageheap.system_bytes
|
| - stats.thread_bytes
|
| - stats.central_bytes
|
| - stats.transfer_bytes
|
| - - stats.pageheap_bytes);
|
| + - stats.pageheap.free_bytes
|
| + - stats.pageheap.unmapped_bytes);
|
|
|
| return info;
|
| }
|
| @@ -1127,6 +1239,52 @@
|
| }
|
| }
|
|
|
| +void* cpp_memalign(size_t align, size_t size) {
|
| + for (;;) {
|
| + void* p = do_memalign(align, size);
|
| +#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)
|
| + 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&) {
|
| + return p;
|
| + }
|
| +#endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
|
| + } else { // allocation success
|
| + return p;
|
| + }
|
| +#endif // PREANSINEW
|
| + }
|
| +}
|
| +
|
| } // end unnamed namespace
|
|
|
| // As promised, the definition of this function, declared above.
|
| @@ -1134,41 +1292,56 @@
|
| return GetSizeWithCallback(ptr, &InvalidGetAllocatedSize);
|
| }
|
|
|
| +void TCMallocImplementation::MarkThreadBusy() {
|
| + // Allocate to force the creation of a thread cache, but avoid
|
| + // invoking any hooks.
|
| + do_free(do_malloc(0));
|
| +}
|
| +
|
| //-------------------------------------------------------------------
|
| // Exported routines
|
| //-------------------------------------------------------------------
|
|
|
| +extern "C" PERFTOOLS_DLL_DECL const char* tc_version(
|
| + int* major, int* minor, const char** patch) __THROW {
|
| + if (major) *major = TC_VERSION_MAJOR;
|
| + if (minor) *minor = TC_VERSION_MINOR;
|
| + if (patch) *patch = TC_VERSION_PATCH;
|
| + return TC_VERSION_STRING;
|
| +}
|
| +
|
| // 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.
|
|
|
| -static int tc_new_mode = 0; // See tc_set_new_mode().
|
| -extern "C" void* tc_malloc(size_t size) __THROW {
|
| - void* result = (tc_new_mode ? cpp_alloc(size, false) : do_malloc(size));
|
| +extern "C" PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) __THROW {
|
| + void* result = do_malloc_or_cpp_alloc(size);
|
| MallocHook::InvokeNewHook(result, size);
|
| return result;
|
| }
|
|
|
| -extern "C" void tc_free(void* ptr) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL void tc_free(void* ptr) __THROW {
|
| MallocHook::InvokeDeleteHook(ptr);
|
| do_free(ptr);
|
| }
|
|
|
| -extern "C" void* tc_calloc(size_t n, size_t elem_size) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL void* tc_calloc(size_t n,
|
| + size_t elem_size) __THROW {
|
| void* result = do_calloc(n, elem_size);
|
| MallocHook::InvokeNewHook(result, n * elem_size);
|
| return result;
|
| }
|
|
|
| -extern "C" void tc_cfree(void* ptr) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) __THROW {
|
| MallocHook::InvokeDeleteHook(ptr);
|
| do_free(ptr);
|
| }
|
|
|
| -extern "C" void* tc_realloc(void* old_ptr, size_t new_size) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL void* tc_realloc(void* old_ptr,
|
| + size_t new_size) __THROW {
|
| if (old_ptr == NULL) {
|
| - void* result = do_malloc(new_size);
|
| + void* result = do_malloc_or_cpp_alloc(new_size);
|
| MallocHook::InvokeNewHook(result, new_size);
|
| return result;
|
| }
|
| @@ -1180,7 +1353,7 @@
|
| return do_realloc(old_ptr, new_size);
|
| }
|
|
|
| -extern "C" void* tc_new(size_t size) {
|
| +extern "C" PERFTOOLS_DLL_DECL void* tc_new(size_t size) {
|
| void* p = cpp_alloc(size, false);
|
| // We keep this next instruction out of cpp_alloc for a reason: when
|
| // it's in, and new just calls cpp_alloc, the optimizer may fold the
|
| @@ -1191,18 +1364,27 @@
|
| return p;
|
| }
|
|
|
| -extern "C" void* tc_new_nothrow(size_t size, const std::nothrow_t&) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL void* tc_new_nothrow(
|
| + size_t size, const std::nothrow_t&) __THROW {
|
| void* p = cpp_alloc(size, true);
|
| MallocHook::InvokeNewHook(p, size);
|
| return p;
|
| }
|
|
|
| -extern "C" void tc_delete(void* p) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW {
|
| MallocHook::InvokeDeleteHook(p);
|
| do_free(p);
|
| }
|
|
|
| -extern "C" void* tc_newarray(size_t size) {
|
| +// Compilers define and use this (via ::operator delete(ptr, nothrow)).
|
| +// But it's really the same as normal delete, so we just do the same thing.
|
| +extern "C" PERFTOOLS_DLL_DECL void tc_delete_nothrow(
|
| + void* p, const std::nothrow_t&) __THROW {
|
| + MallocHook::InvokeDeleteHook(p);
|
| + do_free(p);
|
| +}
|
| +
|
| +extern "C" PERFTOOLS_DLL_DECL void* tc_newarray(size_t size) {
|
| void* p = cpp_alloc(size, false);
|
| // We keep this next instruction out of cpp_alloc for a reason: when
|
| // it's in, and new just calls cpp_alloc, the optimizer may fold the
|
| @@ -1213,32 +1395,40 @@
|
| return p;
|
| }
|
|
|
| -extern "C" void* tc_newarray_nothrow(size_t size, const std::nothrow_t&) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(
|
| + size_t size, const std::nothrow_t&) __THROW {
|
| void* p = cpp_alloc(size, true);
|
| MallocHook::InvokeNewHook(p, size);
|
| return p;
|
| }
|
|
|
| -extern "C" void tc_deletearray(void* p) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray(void* p) __THROW {
|
| MallocHook::InvokeDeleteHook(p);
|
| do_free(p);
|
| }
|
|
|
| -extern "C" void* tc_memalign(size_t align, size_t size) __THROW {
|
| - void* result = do_memalign(align, size);
|
| +extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(
|
| + void* p, const std::nothrow_t&) __THROW {
|
| + MallocHook::InvokeDeleteHook(p);
|
| + do_free(p);
|
| +}
|
| +
|
| +extern "C" PERFTOOLS_DLL_DECL void* tc_memalign(size_t align,
|
| + size_t size) __THROW {
|
| + void* result = do_memalign_or_cpp_memalign(align, size);
|
| MallocHook::InvokeNewHook(result, size);
|
| return result;
|
| }
|
|
|
| -extern "C" int tc_posix_memalign(void** result_ptr, size_t align, size_t size)
|
| - __THROW {
|
| +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_memalign(align, size);
|
| + void* result = do_memalign_or_cpp_memalign(align, size);
|
| MallocHook::InvokeNewHook(result, size);
|
| if (result == NULL) {
|
| return ENOMEM;
|
| @@ -1250,33 +1440,36 @@
|
|
|
| static size_t pagesize = 0;
|
|
|
| -extern "C" void* tc_valloc(size_t size) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL void* tc_valloc(size_t size) __THROW {
|
| // Allocate page-aligned object of length >= size bytes
|
| if (pagesize == 0) pagesize = getpagesize();
|
| - void* result = do_memalign(pagesize, size);
|
| + void* result = do_memalign_or_cpp_memalign(pagesize, size);
|
| MallocHook::InvokeNewHook(result, size);
|
| return result;
|
| }
|
|
|
| -extern "C" void* tc_pvalloc(size_t size) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t size) __THROW {
|
| // Round up size to a multiple of pagesize
|
| if (pagesize == 0) pagesize = getpagesize();
|
| + if (size == 0) { // pvalloc(0) should allocate one page, according to
|
| + size = pagesize; // http://man.free4web.biz/man3/libmpatrol.3.html
|
| + }
|
| size = (size + pagesize - 1) & ~(pagesize - 1);
|
| - void* result = do_memalign(pagesize, size);
|
| + void* result = do_memalign_or_cpp_memalign(pagesize, size);
|
| MallocHook::InvokeNewHook(result, size);
|
| return result;
|
| }
|
|
|
| -extern "C" void tc_malloc_stats(void) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL void tc_malloc_stats(void) __THROW {
|
| do_malloc_stats();
|
| }
|
|
|
| -extern "C" int tc_mallopt(int cmd, int value) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) __THROW {
|
| return do_mallopt(cmd, value);
|
| }
|
|
|
| #ifdef HAVE_STRUCT_MALLINFO
|
| -extern "C" struct mallinfo tc_mallinfo(void) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) __THROW {
|
| return do_mallinfo();
|
| }
|
| #endif
|
| @@ -1286,7 +1479,7 @@
|
| // If flag is 1, calls to malloc will behave like calls to new,
|
| // and the std_new_handler will be invoked on failure.
|
| // Returns the previous mode.
|
| -extern "C" int tc_set_new_mode(int flag) __THROW {
|
| +extern "C" PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) __THROW {
|
| int old_mode = tc_new_mode;
|
| tc_new_mode = flag;
|
| return old_mode;
|
| @@ -1300,13 +1493,15 @@
|
| // 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.
|
| +#ifndef TCMALLOC_FOR_DEBUGALLOCATION
|
| 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* result = do_memalign(align, size);
|
| + void* result = do_memalign_or_cpp_memalign(align, size);
|
| MallocHook::InvokeNewHook(result, size);
|
| return result;
|
| }
|
| void *(*__memalign_hook)(size_t, size_t, const void *) = MemalignOverride;
|
| +#endif // #ifndef TCMALLOC_FOR_DEBUGALLOCATION
|
|
|