This source file includes following definitions.
- InvalidFree
- InvalidGetSizeForRealloc
- InvalidGetAllocatedSize
- ExtractStats
- PagesToMiB
- DumpStats
- PrintStats
- DumpHeapGrowthStackTraces
- IterateOverRanges
- GetStats
- GetHeapSample
- ReadStackTraces
- ReadHeapGrowthStackTraces
- Ranges
- GetNumericProperty
- SetNumericProperty
- MarkThreadIdle
- GetSystemAllocator
- SetSystemAllocator
- ReleaseToSystem
- SetMemoryReleaseRate
- GetMemoryReleaseRate
- GetEstimatedAllocatedSize
- GetOwnership
- GetFreeListSizes
- CheckCachedSizeClass
- CheckedMallocResult
- SpanToMallocResult
- DoSampledAllocation
- ReportLargeAlloc
- do_malloc_or_cpp_alloc
- do_memalign_or_cpp_memalign
- should_report_large
- do_malloc_pages
- do_malloc
- do_calloc
- GetCacheIfPresent
- do_free_with_callback
- do_free
- GetSizeWithCallback
- do_realloc_with_callback
- do_realloc
- do_memalign
- do_malloc_stats
- do_mallopt
- do_mallinfo
- cpp_alloc
- cpp_memalign
- GetAllocatedSize
- MarkThreadBusy
- tc_version
- tc_set_new_mode
- tc_malloc
- tc_free
- tc_calloc
- tc_cfree
- tc_realloc
- tc_new
- tc_new_nothrow
- tc_delete
- tc_delete_nothrow
- tc_newarray
- tc_newarray_nothrow
- tc_deletearray
- tc_deletearray_nothrow
- tc_memalign
- tc_posix_memalign
- tc_valloc
- tc_pvalloc
- tc_malloc_stats
- tc_mallopt
- tc_mallinfo
- tc_malloc_size
#include "config.h"
#include <gperftools/tcmalloc.h>
#include <errno.h>
#ifdef HAVE_SYS_CDEFS_H
#include <sys/cdefs.h>
#endif
#if defined HAVE_STDINT_H
#include <stdint.h>
#elif defined HAVE_INTTYPES_H
#include <inttypes.h>
#else
#include <sys/types.h>
#endif
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <algorithm>
#include <limits>
#include <new>
#include <vector>
#include <gperftools/malloc_extension.h>
#include <gperftools/malloc_hook.h>
#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "base/dynamic_annotations.h"
#include "base/spinlock.h"
#include "central_freelist.h"
#include "common.h"
#include "internal_logging.h"
#include "linked_list.h"
#include "malloc_hook-inl.h"
#include "page_heap.h"
#include "page_heap_allocator.h"
#include "span.h"
#include "stack_trace_table.h"
#include "static_vars.h"
#include "system-alloc.h"
#include "tcmalloc_guard.h"
#include "thread_cache.h"
#ifdef HAVE_STRUCT_MALLINFO
# if defined(HAVE_MALLOC_H)
# include <malloc.h>
# elif defined(HAVE_SYS_MALLOC_H)
# include <sys/malloc.h>
# elif defined(HAVE_MALLOC_MALLOC_H)
# include <malloc/malloc.h>
# endif
#endif
#if (defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)) && !defined(WIN32_OVERRIDE_ALLOCATORS)
# define WIN32_DO_PATCHING 1
#endif
#undef small
using STL_NAMESPACE::max;
using STL_NAMESPACE::numeric_limits;
using STL_NAMESPACE::vector;
#include "libc_override.h"
#ifndef __THROW
# define __THROW
#endif
using tcmalloc::AlignmentForSize;
using tcmalloc::kLog;
using tcmalloc::kCrash;
using tcmalloc::kCrashWithStats;
using tcmalloc::Log;
using tcmalloc::PageHeap;
using tcmalloc::PageHeapAllocator;
using tcmalloc::SizeMap;
using tcmalloc::Span;
using tcmalloc::StackTrace;
using tcmalloc::Static;
using tcmalloc::ThreadCache;
DECLARE_int64(tcmalloc_sample_parameter);
DECLARE_double(tcmalloc_release_rate);
#ifdef _WIN32
const int64 kDefaultLargeAllocReportThreshold = static_cast<int64>(1) << 62;
#else
const int64 kDefaultLargeAllocReportThreshold = static_cast<int64>(1) << 30;
#endif
DEFINE_int64(tcmalloc_large_alloc_report_threshold,
EnvToInt64("TCMALLOC_LARGE_ALLOC_REPORT_THRESHOLD",
kDefaultLargeAllocReportThreshold),
"Allocations larger than this value cause a stack "
"trace to be dumped to stderr. The threshold for "
"dumping stack traces is increased by a factor of 1.125 "
"every time we print a message so that the threshold "
"automatically goes up by a factor of ~1000 every 60 "
"messages. This bounds the amount of extra logging "
"generated by this flag. Default value of this flag "
"is very large and therefore you should see no extra "
"logging unless the flag is overridden. Set to 0 to "
"disable reporting entirely.");
#ifndef _WIN32
extern "C" {
void* tc_malloc(size_t size) __THROW
ATTRIBUTE_SECTION(google_malloc);
void tc_free(void* ptr) __THROW
ATTRIBUTE_SECTION(google_malloc);
void* tc_realloc(void* ptr, size_t size) __THROW
ATTRIBUTE_SECTION(google_malloc);
void* tc_calloc(size_t nmemb, size_t size) __THROW
ATTRIBUTE_SECTION(google_malloc);
void tc_cfree(void* ptr) __THROW
ATTRIBUTE_SECTION(google_malloc);
void* tc_memalign(size_t __alignment, size_t __size) __THROW
ATTRIBUTE_SECTION(google_malloc);
int tc_posix_memalign(void** ptr, size_t align, size_t size) __THROW
ATTRIBUTE_SECTION(google_malloc);
void* tc_valloc(size_t __size) __THROW
ATTRIBUTE_SECTION(google_malloc);
void* tc_pvalloc(size_t __size) __THROW
ATTRIBUTE_SECTION(google_malloc);
void tc_malloc_stats(void) __THROW
ATTRIBUTE_SECTION(google_malloc);
int tc_mallopt(int cmd, int value) __THROW
ATTRIBUTE_SECTION(google_malloc);
#ifdef HAVE_STRUCT_MALLINFO
struct mallinfo tc_mallinfo(void) __THROW
ATTRIBUTE_SECTION(google_malloc);
#endif
void* tc_new(size_t size)
ATTRIBUTE_SECTION(google_malloc);
void tc_delete(void* p) __THROW
ATTRIBUTE_SECTION(google_malloc);
void* tc_newarray(size_t size)
ATTRIBUTE_SECTION(google_malloc);
void tc_deletearray(void* p) __THROW
ATTRIBUTE_SECTION(google_malloc);
void* tc_new_nothrow(size_t size, const std::nothrow_t&) __THROW
ATTRIBUTE_SECTION(google_malloc);
void* tc_newarray_nothrow(size_t size, const std::nothrow_t&) __THROW
ATTRIBUTE_SECTION(google_malloc);
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);
size_t tc_malloc_size(void* p) __THROW
ATTRIBUTE_SECTION(google_malloc);
}
#endif
static int tc_new_mode = 0;
namespace {
void InvalidFree(void* ptr) {
Log(kCrash, __FILE__, __LINE__, "Attempt to free invalid pointer", ptr);
}
size_t InvalidGetSizeForRealloc(const void* old_ptr) {
Log(kCrash, __FILE__, __LINE__,
"Attempt to realloc invalid pointer", old_ptr);
return 0;
}
size_t InvalidGetAllocatedSize(const void* ptr) {
Log(kCrash, __FILE__, __LINE__,
"Attempt to get the size of an invalid pointer", ptr);
return 0;
}
}
struct TCMallocStats {
uint64_t thread_bytes;
uint64_t central_bytes;
uint64_t transfer_bytes;
uint64_t metadata_bytes;
PageHeap::Stats pageheap;
};
static void ExtractStats(TCMallocStats* r, uint64_t* class_count,
PageHeap::SmallSpanStats* small_spans,
PageHeap::LargeSpanStats* large_spans) {
r->central_bytes = 0;
r->transfer_bytes = 0;
for (int cl = 0; cl < kNumClasses; ++cl) {
const int length = Static::central_cache()[cl].length();
const int tc_length = Static::central_cache()[cl].tc_length();
const size_t cache_overhead = Static::central_cache()[cl].OverheadBytes();
const size_t size = static_cast<uint64_t>(
Static::sizemap()->ByteSizeForClass(cl));
r->central_bytes += (size * length) + cache_overhead;
r->transfer_bytes += (size * tc_length);
if (class_count) class_count[cl] = length + tc_length;
}
r->thread_bytes = 0;
{
SpinLockHolder h(Static::pageheap_lock());
ThreadCache::GetThreadStats(&r->thread_bytes, class_count);
r->metadata_bytes = tcmalloc::metadata_system_bytes();
r->pageheap = Static::pageheap()->stats();
if (small_spans != NULL) {
Static::pageheap()->GetSmallSpanStats(small_spans);
}
if (large_spans != NULL) {
Static::pageheap()->GetLargeSpanStats(large_spans);
}
}
}
static double PagesToMiB(uint64_t pages) {
return (pages << kPageShift) / 1048576.0;
}
static void DumpStats(TCMalloc_Printer* out, int level) {
TCMallocStats stats;
uint64_t class_count[kNumClasses];
PageHeap::SmallSpanStats small;
PageHeap::LargeSpanStats large;
if (level >= 2) {
ExtractStats(&stats, class_count, &small, &large);
} else {
ExtractStats(&stats, NULL, NULL, NULL);
}
static const double MiB = 1048576.0;
const uint64_t virtual_memory_used = (stats.pageheap.system_bytes
+ stats.metadata_bytes);
const uint64_t physical_memory_used = (virtual_memory_used
- stats.pageheap.unmapped_bytes);
const uint64_t bytes_in_use_by_app = (physical_memory_used
- stats.metadata_bytes
- stats.pageheap.free_bytes
- stats.central_bytes
- stats.transfer_bytes
- stats.thread_bytes);
#ifdef TCMALLOC_SMALL_BUT_SLOW
out->printf(
"NOTE: SMALL MEMORY MODEL IS IN USE, PERFORMANCE MAY SUFFER.\n");
#endif
out->printf(
"------------------------------------------------\n"
"MALLOC: %12" PRIu64 " (%7.1f MiB) Bytes in use by application\n"
"MALLOC: + %12" PRIu64 " (%7.1f MiB) Bytes in page heap freelist\n"
"MALLOC: + %12" PRIu64 " (%7.1f MiB) Bytes in central cache freelist\n"
"MALLOC: + %12" PRIu64 " (%7.1f MiB) Bytes in transfer cache freelist\n"
"MALLOC: + %12" PRIu64 " (%7.1f MiB) Bytes in thread cache freelists\n"
"MALLOC: + %12" PRIu64 " (%7.1f MiB) Bytes in malloc metadata\n"
"MALLOC: ------------\n"
"MALLOC: = %12" PRIu64 " (%7.1f MiB) Actual memory used (physical + swap)\n"
"MALLOC: + %12" PRIu64 " (%7.1f MiB) Bytes released to OS (aka unmapped)\n"
"MALLOC: ------------\n"
"MALLOC: = %12" PRIu64 " (%7.1f MiB) Virtual address space used\n"
"MALLOC:\n"
"MALLOC: %12" PRIu64 " Spans in use\n"
"MALLOC: %12" PRIu64 " Thread heaps in use\n"
"MALLOC: %12" PRIu64 " Tcmalloc page size\n"
"------------------------------------------------\n"
"Call ReleaseFreeMemory() to release freelist memory to the OS"
" (via madvise()).\n"
"Bytes released to the OS take up virtual address space"
" but no physical memory.\n",
bytes_in_use_by_app, bytes_in_use_by_app / MiB,
stats.pageheap.free_bytes, stats.pageheap.free_bytes / MiB,
stats.central_bytes, stats.central_bytes / MiB,
stats.transfer_bytes, stats.transfer_bytes / MiB,
stats.thread_bytes, stats.thread_bytes / MiB,
stats.metadata_bytes, stats.metadata_bytes / MiB,
physical_memory_used, physical_memory_used / MiB,
stats.pageheap.unmapped_bytes, stats.pageheap.unmapped_bytes / MiB,
virtual_memory_used, virtual_memory_used / MiB,
uint64_t(Static::span_allocator()->inuse()),
uint64_t(ThreadCache::HeapsInUse()),
uint64_t(kPageSize));
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) {
uint64_t class_bytes =
class_count[cl] * Static::sizemap()->ByteSizeForClass(cl);
cumulative += class_bytes;
out->printf("class %3d [ %8" PRIuS " bytes ] : "
"%8" PRIu64 " objs; %5.1f MiB; %5.1f cum MiB\n",
cl, Static::sizemap()->ByteSizeForClass(cl),
class_count[cl],
class_bytes / MiB,
cumulative / MiB);
}
}
int nonempty_sizes = 0;
for (int s = 0; s < kMaxPages; s++) {
if (small.normal_length[s] + small.returned_length[s] > 0) {
nonempty_sizes++;
}
}
out->printf("------------------------------------------------\n");
out->printf("PageHeap: %d sizes; %6.1f MiB free; %6.1f MiB unmapped\n",
nonempty_sizes, stats.pageheap.free_bytes / MiB,
stats.pageheap.unmapped_bytes / MiB);
out->printf("------------------------------------------------\n");
uint64_t total_normal = 0;
uint64_t total_returned = 0;
for (int s = 0; s < kMaxPages; s++) {
const int n_length = small.normal_length[s];
const int r_length = small.returned_length[s];
if (n_length + r_length > 0) {
uint64_t n_pages = s * n_length;
uint64_t r_pages = s * r_length;
total_normal += n_pages;
total_returned += r_pages;
out->printf("%6u pages * %6u spans ~ %6.1f MiB; %6.1f MiB cum"
"; unmapped: %6.1f MiB; %6.1f MiB cum\n",
s,
(n_length + r_length),
PagesToMiB(n_pages + r_pages),
PagesToMiB(total_normal + total_returned),
PagesToMiB(r_pages),
PagesToMiB(total_returned));
}
}
total_normal += large.normal_pages;
total_returned += large.returned_pages;
out->printf(">255 large * %6u spans ~ %6.1f MiB; %6.1f MiB cum"
"; unmapped: %6.1f MiB; %6.1f MiB cum\n",
static_cast<unsigned int>(large.spans),
PagesToMiB(large.normal_pages + large.returned_pages),
PagesToMiB(total_normal + total_returned),
PagesToMiB(large.returned_pages),
PagesToMiB(total_returned));
}
}
static void PrintStats(int level) {
const int kBufferSize = 16 << 10;
char* buffer = new char[kBufferSize];
TCMalloc_Printer printer(buffer, kBufferSize);
DumpStats(&printer, level);
write(STDERR_FILENO, buffer, strlen(buffer));
delete[] buffer;
}
static void** DumpHeapGrowthStackTraces() {
int needed_slots = 0;
{
SpinLockHolder h(Static::pageheap_lock());
for (StackTrace* t = Static::growth_stacks();
t != NULL;
t = reinterpret_cast<StackTrace*>(
t->stack[tcmalloc::kMaxStackDepth-1])) {
needed_slots += 3 + t->depth;
}
needed_slots += 100;
needed_slots += needed_slots/8;
}
void** result = new void*[needed_slots];
if (result == NULL) {
Log(kLog, __FILE__, __LINE__,
"tcmalloc: allocation failed for stack trace slots",
needed_slots * sizeof(*result));
return NULL;
}
SpinLockHolder h(Static::pageheap_lock());
int used_slots = 0;
for (StackTrace* t = Static::growth_stacks();
t != NULL;
t = reinterpret_cast<StackTrace*>(
t->stack[tcmalloc::kMaxStackDepth-1])) {
ASSERT(used_slots < needed_slots);
if (used_slots + 3 + t->depth >= needed_slots) {
break;
}
result[used_slots+0] = reinterpret_cast<void*>(static_cast<uintptr_t>(1));
result[used_slots+1] = reinterpret_cast<void*>(t->size);
result[used_slots+2] = reinterpret_cast<void*>(t->depth);
for (int d = 0; d < t->depth; d++) {
result[used_slots+3+d] = t->stack[d];
}
used_slots += 3 + t->depth;
}
result[used_slots] = reinterpret_cast<void*>(static_cast<uintptr_t>(0));
return result;
}
static void IterateOverRanges(void* arg, MallocExtension::RangeFunction func) {
PageID page = 1;
bool done = false;
while (!done) {
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]);
}
}
}
class TCMallocImplementation : public MallocExtension {
private:
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);
if (buffer_length < 10000) {
DumpStats(&printer, 1);
} else {
DumpStats(&printer, 2);
}
}
virtual void GetHeapSample(MallocExtensionWriter* writer) {
if (FLAGS_tcmalloc_sample_parameter == 0) {
const char* const kWarningMsg =
"%warn\n"
"%warn This heap profile does not have any data in it, because\n"
"%warn the application was run with heap sampling turned off.\n"
"%warn To get useful data from GetHeapSample(), you must\n"
"%warn set the environment variable TCMALLOC_SAMPLE_PARAMETER to\n"
"%warn a positive sampling period, such as 524288.\n"
"%warn\n";
writer->append(kWarningMsg, strlen(kWarningMsg));
}
MallocExtension::GetHeapSample(writer);
}
virtual void** ReadStackTraces(int* sample_period) {
tcmalloc::StackTraceTable table;
{
SpinLockHolder h(Static::pageheap_lock());
Span* sampled = Static::sampled_objects();
for (Span* s = sampled->next; s != sampled; s = s->next) {
table.AddTrace(*reinterpret_cast<StackTrace*>(s->objects));
}
}
*sample_period = ThreadCache::GetCache()->GetSamplePeriod();
return table.ReadStackTracesAndClear();
}
virtual void** ReadHeapGrowthStackTraces() {
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, NULL, NULL);
*value = stats.pageheap.system_bytes
- stats.thread_bytes
- stats.central_bytes
- stats.transfer_bytes
- stats.pageheap.free_bytes
- stats.pageheap.unmapped_bytes;
return true;
}
if (strcmp(name, "generic.heap_size") == 0) {
TCMallocStats stats;
ExtractStats(&stats, NULL, NULL, NULL);
*value = stats.pageheap.system_bytes;
return true;
}
if (strcmp(name, "tcmalloc.slack_bytes") == 0) {
SpinLockHolder l(Static::pageheap_lock());
PageHeap::Stats stats = Static::pageheap()->stats();
*value = stats.free_bytes + stats.unmapped_bytes;
return true;
}
if (strcmp(name, "tcmalloc.central_cache_free_bytes") == 0) {
TCMallocStats stats;
ExtractStats(&stats, NULL, NULL, NULL);
*value = stats.central_bytes;
return true;
}
if (strcmp(name, "tcmalloc.transfer_cache_free_bytes") == 0) {
TCMallocStats stats;
ExtractStats(&stats, NULL, NULL, NULL);
*value = stats.transfer_bytes;
return true;
}
if (strcmp(name, "tcmalloc.thread_cache_free_bytes") == 0) {
TCMallocStats stats;
ExtractStats(&stats, NULL, NULL, NULL);
*value = stats.thread_bytes;
return true;
}
if (strcmp(name, "tcmalloc.pageheap_free_bytes") == 0) {
SpinLockHolder l(Static::pageheap_lock());
*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();
return true;
}
if (strcmp(name, "tcmalloc.current_total_thread_cache_bytes") == 0) {
TCMallocStats stats;
ExtractStats(&stats, NULL, NULL, NULL);
*value = stats.thread_bytes;
return true;
}
return false;
}
virtual bool SetNumericProperty(const char* name, size_t value) {
ASSERT(name != NULL);
if (strcmp(name, "tcmalloc.max_total_thread_cache_bytes") == 0) {
SpinLockHolder l(Static::pageheap_lock());
ThreadCache::set_overall_thread_cache_size(value);
return true;
}
return false;
}
virtual void MarkThreadIdle() {
ThreadCache::BecomeIdle();
}
virtual void MarkThreadBusy();
virtual SysAllocator* GetSystemAllocator() {
SpinLockHolder h(Static::pageheap_lock());
return sys_alloc;
}
virtual void SetSystemAllocator(SysAllocator* alloc) {
SpinLockHolder h(Static::pageheap_lock());
sys_alloc = alloc;
}
virtual void ReleaseToSystem(size_t num_bytes) {
SpinLockHolder h(Static::pageheap_lock());
if (num_bytes <= extra_bytes_released_) {
extra_bytes_released_ = extra_bytes_released_ - num_bytes;
return;
}
num_bytes = num_bytes - extra_bytes_released_;
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 {
extra_bytes_released_ = 0;
}
}
virtual void SetMemoryReleaseRate(double rate) {
FLAGS_tcmalloc_release_rate = rate;
}
virtual double GetMemoryReleaseRate() {
return FLAGS_tcmalloc_release_rate;
}
virtual size_t GetEstimatedAllocatedSize(size_t size) {
if (size <= kMaxSize) {
const size_t cl = Static::sizemap()->SizeClass(size);
const size_t alloc_size = Static::sizemap()->ByteSizeForClass(cl);
return alloc_size;
} else {
return tcmalloc::pages(size) << kPageShift;
}
}
virtual size_t GetAllocatedSize(const void* ptr);
virtual Ownership GetOwnership(const void* ptr) {
const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
if ((p >> (kAddressBits - kPageShift)) > 0) {
return kNotOwned;
}
size_t cl = Static::pageheap()->GetSizeClassIfCached(p);
if (cl != 0) {
return kOwned;
}
const Span *span = Static::pageheap()->GetDescriptor(p);
return span ? kOwned : kNotOwned;
}
virtual void GetFreeListSizes(vector<MallocExtension::FreeListInfo>* v) {
static const char* kCentralCacheType = "tcmalloc.central";
static const char* kTransferCacheType = "tcmalloc.transfer";
static const char* kThreadCacheType = "tcmalloc.thread";
static const char* kPageHeapType = "tcmalloc.page";
static const char* kPageHeapUnmappedType = "tcmalloc.page_unmapped";
static const char* kLargeSpanType = "tcmalloc.large";
static const char* kLargeUnmappedSpanType = "tcmalloc.large_unmapped";
v->clear();
int64 prev_class_size = 0;
for (int cl = 1; cl < kNumClasses; ++cl) {
size_t class_size = Static::sizemap()->ByteSizeForClass(cl);
MallocExtension::FreeListInfo i;
i.min_object_size = prev_class_size + 1;
i.max_object_size = class_size;
i.total_bytes_free =
Static::central_cache()[cl].length() * class_size;
i.type = kCentralCacheType;
v->push_back(i);
i.total_bytes_free =
Static::central_cache()[cl].tc_length() * class_size;
i.type = kTransferCacheType;
v->push_back(i);
prev_class_size = Static::sizemap()->ByteSizeForClass(cl);
}
uint64_t class_count[kNumClasses];
memset(class_count, 0, sizeof(class_count));
{
SpinLockHolder h(Static::pageheap_lock());
uint64_t thread_bytes = 0;
ThreadCache::GetThreadStats(&thread_bytes, class_count);
}
prev_class_size = 0;
for (int cl = 1; cl < kNumClasses; ++cl) {
MallocExtension::FreeListInfo i;
i.min_object_size = prev_class_size + 1;
i.max_object_size = Static::sizemap()->ByteSizeForClass(cl);
i.total_bytes_free =
class_count[cl] * Static::sizemap()->ByteSizeForClass(cl);
i.type = kThreadCacheType;
v->push_back(i);
}
PageHeap::SmallSpanStats small;
PageHeap::LargeSpanStats large;
{
SpinLockHolder h(Static::pageheap_lock());
Static::pageheap()->GetSmallSpanStats(&small);
Static::pageheap()->GetLargeSpanStats(&large);
}
MallocExtension::FreeListInfo span_info;
span_info.type = kLargeSpanType;
span_info.max_object_size = (numeric_limits<size_t>::max)();
span_info.min_object_size = kMaxPages << kPageShift;
span_info.total_bytes_free = large.normal_pages << kPageShift;
v->push_back(span_info);
span_info.type = kLargeUnmappedSpanType;
span_info.total_bytes_free = large.returned_pages << kPageShift;
v->push_back(span_info);
for (int s = 1; s < kMaxPages; s++) {
MallocExtension::FreeListInfo i;
i.max_object_size = (s << kPageShift);
i.min_object_size = ((s - 1) << kPageShift);
i.type = kPageHeapType;
i.total_bytes_free = (s << kPageShift) * small.normal_length[s];
v->push_back(i);
i.type = kPageHeapUnmappedType;
i.total_bytes_free = (s << kPageShift) * small.returned_length[s];
v->push_back(i);
}
}
};
static int tcmallocguard_refcount = 0;
TCMallocGuard::TCMallocGuard() {
if (tcmallocguard_refcount++ == 0) {
#ifdef HAVE_TLS
tcmalloc::CheckIfKernelSupportsTLS();
#endif
ReplaceSystemAlloc();
tc_free(tc_malloc(1));
ThreadCache::InitTSD();
tc_free(tc_malloc(1));
#ifdef TCMALLOC_USING_DEBUGALLOCATION
#else
if (RunningOnValgrind()) {
} else {
MallocExtension::Register(new TCMallocImplementation);
}
#endif
}
}
TCMallocGuard::~TCMallocGuard() {
if (--tcmallocguard_refcount == 0) {
const char* env = getenv("MALLOCSTATS");
if (env != NULL) {
int level = atoi(env);
if (level < 1) level = 1;
PrintStats(level);
}
}
}
#ifndef WIN32_OVERRIDE_ALLOCATORS
static TCMallocGuard module_enter_exit_hook;
#endif
static inline bool CheckCachedSizeClass(void *ptr) {
PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
size_t cached_value = Static::pageheap()->GetSizeClassIfCached(p);
return cached_value == 0 ||
cached_value == Static::pageheap()->GetDescriptor(p)->sizeclass;
}
static inline void* CheckedMallocResult(void *result) {
ASSERT(result == NULL || CheckCachedSizeClass(result));
return result;
}
static inline void* SpanToMallocResult(Span *span) {
Static::pageheap()->CacheSizeClass(span->start, 0);
return
CheckedMallocResult(reinterpret_cast<void*>(span->start << kPageShift));
}
static void* DoSampledAllocation(size_t size) {
StackTrace tmp;
tmp.depth = GetStackTrace(tmp.stack, tcmalloc::kMaxStackDepth, 1);
tmp.size = size;
SpinLockHolder h(Static::pageheap_lock());
Span *span = Static::pageheap()->New(tcmalloc::pages(size == 0 ? 1 : size));
if (span == NULL) {
return NULL;
}
StackTrace *stack = Static::stacktrace_allocator()->New();
if (stack == NULL) {
return span;
}
*stack = tmp;
span->sample = 1;
span->objects = stack;
tcmalloc::DLL_Prepend(Static::sampled_objects(), span);
return SpanToMallocResult(span);
}
namespace {
static int64_t large_alloc_threshold =
(kPageSize > FLAGS_tcmalloc_large_alloc_report_threshold
? kPageSize : FLAGS_tcmalloc_large_alloc_report_threshold);
static void ReportLargeAlloc(Length num_pages, void* result) {
StackTrace stack;
stack.depth = GetStackTrace(stack.stack, tcmalloc::kMaxStackDepth, 1);
static const int N = 1000;
char buffer[N];
TCMalloc_Printer printer(buffer, N);
printer.printf("tcmalloc: large alloc %"PRIu64" bytes == %p @ ",
static_cast<uint64>(num_pages) << kPageShift,
result);
for (int i = 0; i < stack.depth; i++) {
printer.printf(" %p", stack.stack[i]);
}
printer.printf("\n");
write(STDERR_FILENO, buffer, strlen(buffer));
}
inline void* cpp_alloc(size_t size, bool nothrow);
inline void* do_malloc(size_t size);
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);
}
inline bool should_report_large(Length num_pages) {
const int64 threshold = large_alloc_threshold;
if (threshold > 0 && num_pages >= (threshold >> kPageShift)) {
large_alloc_threshold = (threshold + threshold/8 < 8ll<<30
? threshold + threshold/8 : 8ll<<30);
return true;
}
return false;
}
inline void* do_malloc_pages(ThreadCache* heap, size_t size) {
void* result;
bool report_large;
Length num_pages = tcmalloc::pages(size);
size = num_pages << kPageShift;
if ((FLAGS_tcmalloc_sample_parameter > 0) && heap->SampleAllocation(size)) {
result = DoSampledAllocation(size);
SpinLockHolder h(Static::pageheap_lock());
report_large = should_report_large(num_pages);
} else {
SpinLockHolder h(Static::pageheap_lock());
Span* span = Static::pageheap()->New(num_pages);
result = (span == NULL ? NULL : SpanToMallocResult(span));
report_large = should_report_large(num_pages);
}
if (report_large) {
ReportLargeAlloc(num_pages, result);
}
return result;
}
inline void* do_malloc(size_t size) {
void* ret = NULL;
ThreadCache* heap = ThreadCache::GetCache();
if (size <= kMaxSize) {
size_t cl = Static::sizemap()->SizeClass(size);
size = Static::sizemap()->class_to_size(cl);
if ((FLAGS_tcmalloc_sample_parameter > 0) && heap->SampleAllocation(size)) {
ret = DoSampledAllocation(size);
} else {
ret = CheckedMallocResult(heap->Allocate(size, cl));
}
} else {
ret = do_malloc_pages(heap, size);
}
if (ret == NULL) errno = ENOMEM;
return ret;
}
inline void* do_calloc(size_t n, size_t elem_size) {
const size_t size = n * elem_size;
if (elem_size != 0 && size / elem_size != n) return NULL;
void* result = do_malloc_or_cpp_alloc(size);
if (result != NULL) {
memset(result, 0, size);
}
return result;
}
static inline ThreadCache* GetCacheIfPresent() {
void* const p = ThreadCache::GetCacheIfPresent();
return reinterpret_cast<ThreadCache*>(p);
}
inline void do_free_with_callback(void* ptr, void (*invalid_free_fn)(void*)) {
if (ptr == NULL) return;
if (Static::pageheap() == NULL) {
(*invalid_free_fn)(ptr);
return;
}
const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
Span* span = NULL;
size_t cl = Static::pageheap()->GetSizeClassIfCached(p);
if (cl == 0) {
span = Static::pageheap()->GetDescriptor(p);
if (!span) {
(*invalid_free_fn)(ptr);
return;
}
cl = span->sizeclass;
Static::pageheap()->CacheSizeClass(p, cl);
}
if (cl != 0) {
ASSERT(!Static::pageheap()->GetDescriptor(p)->sample);
ThreadCache* heap = GetCacheIfPresent();
if (heap != NULL) {
heap->Deallocate(ptr, cl);
} else {
tcmalloc::SLL_SetNext(ptr, NULL);
Static::central_cache()[cl].InsertRange(ptr, ptr, 1);
}
} else {
SpinLockHolder h(Static::pageheap_lock());
ASSERT(reinterpret_cast<uintptr_t>(ptr) % kPageSize == 0);
ASSERT(span != NULL && span->start == p);
if (span->sample) {
StackTrace* st = reinterpret_cast<StackTrace*>(span->objects);
tcmalloc::DLL_Remove(span);
Static::stacktrace_allocator()->Delete(st);
span->objects = NULL;
}
Static::pageheap()->Delete(span);
}
}
inline void do_free(void* ptr) {
return do_free_with_callback(ptr, &InvalidFree);
}
inline size_t GetSizeWithCallback(const void* ptr,
size_t (*invalid_getsize_fn)(const void*)) {
if (ptr == NULL)
return 0;
const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
size_t cl = Static::pageheap()->GetSizeClassIfCached(p);
if (cl != 0) {
return Static::sizemap()->ByteSizeForClass(cl);
} else {
const Span *span = Static::pageheap()->GetDescriptor(p);
if (span == NULL) {
return (*invalid_getsize_fn)(ptr);
} else if (span->sizeclass != 0) {
Static::pageheap()->CacheSizeClass(p, span->sizeclass);
return Static::sizemap()->ByteSizeForClass(span->sizeclass);
} else {
return span->length << kPageShift;
}
}
}
inline void* do_realloc_with_callback(
void* old_ptr, size_t new_size,
void (*invalid_free_fn)(void*),
size_t (*invalid_get_size_fn)(const void*)) {
const size_t old_size = GetSizeWithCallback(old_ptr, invalid_get_size_fn);
const int lower_bound_to_grow = old_size + old_size / 4;
const int upper_bound_to_shrink = old_size / 2;
if ((new_size > old_size) || (new_size < upper_bound_to_shrink)) {
void* new_ptr = NULL;
if (new_size > old_size && new_size < lower_bound_to_grow) {
new_ptr = do_malloc_or_cpp_alloc(lower_bound_to_grow);
}
if (new_ptr == NULL) {
new_ptr = do_malloc_or_cpp_alloc(new_size);
}
if (new_ptr == NULL) {
return NULL;
}
MallocHook::InvokeNewHook(new_ptr, new_size);
memcpy(new_ptr, old_ptr, ((old_size < new_size) ? old_size : new_size));
MallocHook::InvokeDeleteHook(old_ptr);
do_free_with_callback(old_ptr, invalid_free_fn);
return new_ptr;
} else {
MallocHook::InvokeDeleteHook(old_ptr);
MallocHook::InvokeNewHook(old_ptr, new_size);
return old_ptr;
}
}
inline void* do_realloc(void* old_ptr, size_t new_size) {
return do_realloc_with_callback(old_ptr, new_size,
&InvalidFree, &InvalidGetSizeForRealloc);
}
void* do_memalign(size_t align, size_t size) {
ASSERT((align & (align - 1)) == 0);
ASSERT(align > 0);
if (size + align < size) return NULL;
if (align <= AlignmentForSize(size)) {
void* p = do_malloc(size);
ASSERT((reinterpret_cast<uintptr_t>(p) % align) == 0);
return p;
}
if (Static::pageheap() == NULL) ThreadCache::InitModule();
if (size == 0) size = 1;
if (size <= kMaxSize && align < kPageSize) {
int cl = Static::sizemap()->SizeClass(size);
while (cl < kNumClasses &&
((Static::sizemap()->class_to_size(cl) & (align - 1)) != 0)) {
cl++;
}
if (cl < kNumClasses) {
ThreadCache* heap = ThreadCache::GetCache();
size = Static::sizemap()->class_to_size(cl);
return CheckedMallocResult(heap->Allocate(size, cl));
}
}
SpinLockHolder h(Static::pageheap_lock());
if (align <= kPageSize) {
Span* span = Static::pageheap()->New(tcmalloc::pages(size));
return span == NULL ? NULL : SpanToMallocResult(span);
}
const Length alloc = tcmalloc::pages(size + align);
Span* span = Static::pageheap()->New(alloc);
if (span == NULL) return NULL;
Length skip = 0;
while ((((span->start+skip) << kPageShift) & (align - 1)) != 0) {
skip++;
}
ASSERT(skip < alloc);
if (skip > 0) {
Span* rest = Static::pageheap()->Split(span, skip);
Static::pageheap()->Delete(span);
span = rest;
}
const Length needed = tcmalloc::pages(size);
ASSERT(span->length >= needed);
if (span->length > needed) {
Span* trailer = Static::pageheap()->Split(span, needed);
Static::pageheap()->Delete(trailer);
}
return SpanToMallocResult(span);
}
inline void do_malloc_stats() {
PrintStats(1);
}
inline int do_mallopt(int cmd, int value) {
return 1;
}
#ifdef HAVE_STRUCT_MALLINFO
inline struct mallinfo do_mallinfo() {
TCMallocStats stats;
ExtractStats(&stats, NULL, NULL, NULL);
struct mallinfo info;
memset(&info, 0, sizeof(info));
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.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.free_bytes
- stats.pageheap.unmapped_bytes);
return info;
}
#endif
static SpinLock set_new_handler_lock(SpinLock::LINKER_INITIALIZED);
inline void* cpp_alloc(size_t size, bool nothrow) {
for (;;) {
void* p = do_malloc(size);
#ifdef PREANSINEW
return p;
#else
if (p == NULL) {
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) {
(*nh)();
continue;
}
return 0;
#else
if (!nh) {
if (nothrow) return 0;
throw std::bad_alloc();
}
try {
(*nh)();
} catch (const std::bad_alloc&) {
if (!nothrow) throw;
return p;
}
#endif
} else {
return p;
}
#endif
}
}
void* cpp_memalign(size_t align, size_t size) {
for (;;) {
void* p = do_memalign(align, size);
#ifdef PREANSINEW
return p;
#else
if (p == NULL) {
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) {
(*nh)();
continue;
}
return 0;
#else
if (!nh)
return 0;
try {
(*nh)();
} catch (const std::bad_alloc&) {
return p;
}
#endif
} else {
return p;
}
#endif
}
}
}
size_t TCMallocImplementation::GetAllocatedSize(const void* ptr) {
ASSERT(TCMallocImplementation::GetOwnership(ptr)
!= TCMallocImplementation::kNotOwned);
return GetSizeWithCallback(ptr, &InvalidGetAllocatedSize);
}
void TCMallocImplementation::MarkThreadBusy() {
do_free(do_malloc(0));
}
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;
}
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;
}
#ifndef TCMALLOC_USING_DEBUGALLOCATION
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" PERFTOOLS_DLL_DECL void tc_free(void* ptr) __THROW {
MallocHook::InvokeDeleteHook(ptr);
do_free(ptr);
}
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" PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) __THROW {
MallocHook::InvokeDeleteHook(ptr);
do_free(ptr);
}
extern "C" PERFTOOLS_DLL_DECL void* tc_realloc(void* old_ptr,
size_t new_size) __THROW {
if (old_ptr == NULL) {
void* result = do_malloc_or_cpp_alloc(new_size);
MallocHook::InvokeNewHook(result, new_size);
return result;
}
if (new_size == 0) {
MallocHook::InvokeDeleteHook(old_ptr);
do_free(old_ptr);
return NULL;
}
return do_realloc(old_ptr, new_size);
}
extern "C" PERFTOOLS_DLL_DECL void* tc_new(size_t size) {
void* p = cpp_alloc(size, false);
MallocHook::InvokeNewHook(p, size);
return p;
}
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" PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW {
MallocHook::InvokeDeleteHook(p);
do_free(p);
}
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);
MallocHook::InvokeNewHook(p, size);
return p;
}
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" PERFTOOLS_DLL_DECL void tc_deletearray(void* p) __THROW {
MallocHook::InvokeDeleteHook(p);
do_free(p);
}
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" 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_or_cpp_memalign(align, size);
MallocHook::InvokeNewHook(result, size);
if (result == NULL) {
return ENOMEM;
} else {
*result_ptr = result;
return 0;
}
}
static size_t pagesize = 0;
extern "C" PERFTOOLS_DLL_DECL void* tc_valloc(size_t size) __THROW {
if (pagesize == 0) pagesize = getpagesize();
void* result = do_memalign_or_cpp_memalign(pagesize, size);
MallocHook::InvokeNewHook(result, size);
return result;
}
extern "C" PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t size) __THROW {
if (pagesize == 0) pagesize = getpagesize();
if (size == 0) {
size = pagesize;
}
size = (size + pagesize - 1) & ~(pagesize - 1);
void* result = do_memalign_or_cpp_memalign(pagesize, size);
MallocHook::InvokeNewHook(result, size);
return result;
}
extern "C" PERFTOOLS_DLL_DECL void tc_malloc_stats(void) __THROW {
do_malloc_stats();
}
extern "C" PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) __THROW {
return do_mallopt(cmd, value);
}
#ifdef HAVE_STRUCT_MALLINFO
extern "C" PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) __THROW {
return do_mallinfo();
}
#endif
extern "C" PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) __THROW {
return MallocExtension::instance()->GetAllocatedSize(ptr);
}
#endif