| 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,
 | 
| 
 |