| Index: third_party/tcmalloc/chromium/src/memory_region_map.cc
|
| diff --git a/third_party/tcmalloc/chromium/src/memory_region_map.cc b/third_party/tcmalloc/chromium/src/memory_region_map.cc
|
| index 1a8117248055988d996bd8110e9e18c3a66024db..dcc73c4ac6dc0307c38b1f094f426dfdf7c434a5 100644
|
| --- a/third_party/tcmalloc/chromium/src/memory_region_map.cc
|
| +++ b/third_party/tcmalloc/chromium/src/memory_region_map.cc
|
| @@ -84,9 +84,10 @@
|
| // which (sometimes) causes mmap, which calls our hook, and so on.
|
| // We do this as follows: on a recursive call of MemoryRegionMap's
|
| // mmap/sbrk/mremap hook we record the data about the allocation in a
|
| -// static fixed-sized stack (saved_regions), when the recursion unwinds
|
| -// but before returning from the outer hook call we unwind this stack and
|
| -// move the data from saved_regions to its permanent place in the RegionSet,
|
| +// static fixed-sized stack (saved_regions and saved_buckets), when the
|
| +// recursion unwinds but before returning from the outer hook call we unwind
|
| +// this stack and move the data from saved_regions and saved_buckets to its
|
| +// permanent place in the RegionSet and "bucket_table" respectively,
|
| // which can cause more allocations and mmap-s and recursion and unwinding,
|
| // but the whole process ends eventually due to the fact that for the small
|
| // allocations we are doing LowLevelAlloc reuses one mmap call and parcels out
|
| @@ -147,6 +148,13 @@ int MemoryRegionMap::recursion_count_ = 0; // GUARDED_BY(owner_lock_)
|
| pthread_t MemoryRegionMap::lock_owner_tid_; // GUARDED_BY(owner_lock_)
|
| int64 MemoryRegionMap::map_size_ = 0;
|
| int64 MemoryRegionMap::unmap_size_ = 0;
|
| +HeapProfileBucket** MemoryRegionMap::bucket_table_ = NULL; // GUARDED_BY(lock_)
|
| +int MemoryRegionMap::num_buckets_ = 0; // GUARDED_BY(lock_)
|
| +int MemoryRegionMap::saved_buckets_count_ = 0; // GUARDED_BY(lock_)
|
| +HeapProfileBucket MemoryRegionMap::saved_buckets_[20]; // GUARDED_BY(lock_)
|
| +
|
| +// GUARDED_BY(lock_)
|
| +const void* MemoryRegionMap::saved_buckets_keys_[20][kMaxStackDepth];
|
|
|
| // ========================================================================= //
|
|
|
| @@ -182,7 +190,7 @@ static MemoryRegionMap::RegionSetRep regions_rep;
|
| // (or rather should we *not* use regions_ to record a hooked mmap).
|
| static bool recursive_insert = false;
|
|
|
| -void MemoryRegionMap::Init(int max_stack_depth) {
|
| +void MemoryRegionMap::Init(int max_stack_depth, bool use_buckets) {
|
| RAW_VLOG(10, "MemoryRegionMap Init");
|
| RAW_CHECK(max_stack_depth >= 0, "");
|
| // Make sure we don't overflow the memory in region stacks:
|
| @@ -214,6 +222,15 @@ void MemoryRegionMap::Init(int max_stack_depth) {
|
| // Can't instead use HandleSavedRegionsLocked(&DoInsertRegionLocked) before
|
| // recursive_insert = false; as InsertRegionLocked will also construct
|
| // regions_ on demand for us.
|
| + if (use_buckets) {
|
| + const int table_bytes = kHashTableSize * sizeof(*bucket_table_);
|
| + recursive_insert = true;
|
| + bucket_table_ = static_cast<HeapProfileBucket**>(
|
| + MyAllocator::Allocate(table_bytes));
|
| + recursive_insert = false;
|
| + memset(bucket_table_, 0, table_bytes);
|
| + num_buckets_ = 0;
|
| + }
|
| Unlock();
|
| RAW_VLOG(10, "MemoryRegionMap Init done");
|
| }
|
| @@ -228,6 +245,19 @@ bool MemoryRegionMap::Shutdown() {
|
| RAW_VLOG(10, "MemoryRegionMap Shutdown decrement done");
|
| return true;
|
| }
|
| + if (bucket_table_ != NULL) {
|
| + for (int i = 0; i < kHashTableSize; i++) {
|
| + for (HeapProfileBucket* curr = bucket_table_[i]; curr != 0; /**/) {
|
| + HeapProfileBucket* bucket = curr;
|
| + curr = curr->next;
|
| + MyAllocator::Free(bucket->stack, 0);
|
| + MyAllocator::Free(bucket, 0);
|
| + }
|
| + }
|
| + MyAllocator::Free(bucket_table_, 0);
|
| + num_buckets_ = 0;
|
| + bucket_table_ = NULL;
|
| + }
|
| RAW_CHECK(MallocHook::RemoveMmapHook(&MmapHook), "");
|
| RAW_CHECK(MallocHook::RemoveMremapHook(&MremapHook), "");
|
| RAW_CHECK(MallocHook::RemoveSbrkHook(&SbrkHook), "");
|
| @@ -245,6 +275,11 @@ bool MemoryRegionMap::Shutdown() {
|
| return deleted_arena;
|
| }
|
|
|
| +bool MemoryRegionMap::IsRecordingLocked() {
|
| + RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
|
| + return client_count_ > 0;
|
| +}
|
| +
|
| // Invariants (once libpthread_initialized is true):
|
| // * While lock_ is not held, recursion_count_ is 0 (and
|
| // lock_owner_tid_ is the previous owner, but we don't rely on
|
| @@ -336,6 +371,62 @@ bool MemoryRegionMap::FindAndMarkStackRegion(uintptr_t stack_top,
|
| return region != NULL;
|
| }
|
|
|
| +HeapProfileBucket* MemoryRegionMap::GetBucket(int depth,
|
| + const void* const key[]) {
|
| + RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
|
| + // Make hash-value
|
| + uintptr_t hash = 0;
|
| + for (int i = 0; i < depth; i++) {
|
| + hash += reinterpret_cast<uintptr_t>(key[i]);
|
| + hash += hash << 10;
|
| + hash ^= hash >> 6;
|
| + }
|
| + hash += hash << 3;
|
| + hash ^= hash >> 11;
|
| +
|
| + // Lookup stack trace in table
|
| + unsigned int hash_index = (static_cast<unsigned int>(hash)) % kHashTableSize;
|
| + for (HeapProfileBucket* bucket = bucket_table_[hash_index];
|
| + bucket != 0;
|
| + bucket = bucket->next) {
|
| + if ((bucket->hash == hash) && (bucket->depth == depth) &&
|
| + std::equal(key, key + depth, bucket->stack)) {
|
| + return bucket;
|
| + }
|
| + }
|
| +
|
| + // Create new bucket
|
| + const size_t key_size = sizeof(key[0]) * depth;
|
| + HeapProfileBucket* bucket;
|
| + if (recursive_insert) { // recursion: save in saved_buckets_
|
| + const void** key_copy = saved_buckets_keys_[saved_buckets_count_];
|
| + std::copy(key, key + depth, key_copy);
|
| + bucket = &saved_buckets_[saved_buckets_count_];
|
| + memset(bucket, 0, sizeof(*bucket));
|
| + ++saved_buckets_count_;
|
| + bucket->stack = key_copy;
|
| + bucket->next = NULL;
|
| + } else {
|
| + recursive_insert = true;
|
| + const void** key_copy = static_cast<const void**>(
|
| + MyAllocator::Allocate(key_size));
|
| + recursive_insert = false;
|
| + std::copy(key, key + depth, key_copy);
|
| + recursive_insert = true;
|
| + bucket = static_cast<HeapProfileBucket*>(
|
| + MyAllocator::Allocate(sizeof(HeapProfileBucket)));
|
| + recursive_insert = false;
|
| + memset(bucket, 0, sizeof(*bucket));
|
| + bucket->stack = key_copy;
|
| + bucket->next = bucket_table_[hash_index];
|
| + }
|
| + bucket->hash = hash;
|
| + bucket->depth = depth;
|
| + bucket_table_[hash_index] = bucket;
|
| + ++num_buckets_;
|
| + return bucket;
|
| +}
|
| +
|
| MemoryRegionMap::RegionIterator MemoryRegionMap::BeginRegionLocked() {
|
| RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
|
| RAW_CHECK(regions_ != NULL, "");
|
| @@ -404,6 +495,44 @@ inline void MemoryRegionMap::HandleSavedRegionsLocked(
|
| }
|
| }
|
|
|
| +void MemoryRegionMap::RestoreSavedBucketsLocked() {
|
| + RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
|
| + while (saved_buckets_count_ > 0) {
|
| + HeapProfileBucket bucket = saved_buckets_[--saved_buckets_count_];
|
| + unsigned int hash_index =
|
| + static_cast<unsigned int>(bucket.hash) % kHashTableSize;
|
| + bool is_found = false;
|
| + for (HeapProfileBucket* curr = bucket_table_[hash_index];
|
| + curr != 0;
|
| + curr = curr->next) {
|
| + if ((curr->hash == bucket.hash) && (curr->depth == bucket.depth) &&
|
| + std::equal(bucket.stack, bucket.stack + bucket.depth, curr->stack)) {
|
| + curr->allocs += bucket.allocs;
|
| + curr->alloc_size += bucket.alloc_size;
|
| + curr->frees += bucket.frees;
|
| + curr->free_size += bucket.free_size;
|
| + is_found = true;
|
| + break;
|
| + }
|
| + }
|
| + if (is_found) continue;
|
| +
|
| + const size_t key_size = sizeof(bucket.stack[0]) * bucket.depth;
|
| + const void** key_copy = static_cast<const void**>(
|
| + MyAllocator::Allocate(key_size));
|
| + std::copy(bucket.stack, bucket.stack + bucket.depth, key_copy);
|
| + HeapProfileBucket* new_bucket = static_cast<HeapProfileBucket*>(
|
| + MyAllocator::Allocate(sizeof(HeapProfileBucket)));
|
| + memset(new_bucket, 0, sizeof(*new_bucket));
|
| + new_bucket->hash = bucket.hash;
|
| + new_bucket->depth = bucket.depth;
|
| + new_bucket->stack = key_copy;
|
| + new_bucket->next = bucket_table_[hash_index];
|
| + bucket_table_[hash_index] = new_bucket;
|
| + ++num_buckets_;
|
| + }
|
| +}
|
| +
|
| inline void MemoryRegionMap::InsertRegionLocked(const Region& region) {
|
| RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
|
| // We can be called recursively, because RegionSet constructor
|
| @@ -468,6 +597,16 @@ void MemoryRegionMap::RecordRegionAddition(const void* start, size_t size) {
|
| InsertRegionLocked(region);
|
| // This will (eventually) allocate storage for and copy over the stack data
|
| // from region.call_stack_data_ that is pointed by region.call_stack().
|
| + if (bucket_table_ != NULL) {
|
| + HeapProfileBucket* b = GetBucket(depth, region.call_stack);
|
| + ++b->allocs;
|
| + b->alloc_size += size;
|
| + if (!recursive_insert) {
|
| + recursive_insert = true;
|
| + RestoreSavedBucketsLocked();
|
| + recursive_insert = false;
|
| + }
|
| + }
|
| Unlock();
|
| }
|
|
|
| @@ -486,6 +625,7 @@ void MemoryRegionMap::RecordRegionRemoval(const void* start, size_t size) {
|
| Region& r = saved_regions[i];
|
| if (r.start_addr == start_addr && r.end_addr == end_addr) {
|
| // An exact match, so it's safe to remove.
|
| + RecordRegionRemovalInBucket(r.call_stack_depth, r.call_stack, size);
|
| --saved_regions_count;
|
| --put_pos;
|
| RAW_VLOG(10, ("Insta-Removing saved region %p..%p; "
|
| @@ -530,6 +670,8 @@ void MemoryRegionMap::RecordRegionRemoval(const void* start, size_t size) {
|
| RAW_VLOG(12, "Deleting region %p..%p",
|
| reinterpret_cast<void*>(region->start_addr),
|
| reinterpret_cast<void*>(region->end_addr));
|
| + RecordRegionRemovalInBucket(region->call_stack_depth, region->call_stack,
|
| + region->end_addr - region->start_addr);
|
| RegionSet::iterator d = region;
|
| ++region;
|
| regions_->erase(d);
|
| @@ -539,6 +681,8 @@ void MemoryRegionMap::RecordRegionRemoval(const void* start, size_t size) {
|
| RAW_VLOG(12, "Splitting region %p..%p in two",
|
| reinterpret_cast<void*>(region->start_addr),
|
| reinterpret_cast<void*>(region->end_addr));
|
| + RecordRegionRemovalInBucket(region->call_stack_depth, region->call_stack,
|
| + end_addr - start_addr);
|
| // Make another region for the start portion:
|
| // The new region has to be the start portion because we can't
|
| // just modify region->end_addr as it's the sorting key.
|
| @@ -552,12 +696,16 @@ void MemoryRegionMap::RecordRegionRemoval(const void* start, size_t size) {
|
| RAW_VLOG(12, "Start-chopping region %p..%p",
|
| reinterpret_cast<void*>(region->start_addr),
|
| reinterpret_cast<void*>(region->end_addr));
|
| + RecordRegionRemovalInBucket(region->call_stack_depth, region->call_stack,
|
| + end_addr - region->start_addr);
|
| const_cast<Region&>(*region).set_start_addr(end_addr);
|
| } else if (start_addr > region->start_addr &&
|
| start_addr < region->end_addr) { // cut from end
|
| RAW_VLOG(12, "End-chopping region %p..%p",
|
| reinterpret_cast<void*>(region->start_addr),
|
| reinterpret_cast<void*>(region->end_addr));
|
| + RecordRegionRemovalInBucket(region->call_stack_depth, region->call_stack,
|
| + region->end_addr - start_addr);
|
| // Can't just modify region->end_addr (it's the sorting key):
|
| Region r = *region;
|
| r.set_end_addr(start_addr);
|
| @@ -580,6 +728,16 @@ void MemoryRegionMap::RecordRegionRemoval(const void* start, size_t size) {
|
| Unlock();
|
| }
|
|
|
| +void MemoryRegionMap::RecordRegionRemovalInBucket(int depth,
|
| + const void* const stack[],
|
| + size_t size) {
|
| + RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
|
| + if (bucket_table_ == NULL) return;
|
| + HeapProfileBucket* b = GetBucket(depth, stack);
|
| + ++b->frees;
|
| + b->free_size += size;
|
| +}
|
| +
|
| void MemoryRegionMap::MmapHook(const void* result,
|
| const void* start, size_t size,
|
| int prot, int flags,
|
|
|