1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/trace_event/heap_profiler_allocation_register.h"
6
7 #include <algorithm>
8 #include <limits>
9
10 #include "base/trace_event/trace_event_memory_overhead.h"
11
12 namespace base {
13 namespace trace_event {
14
ConstIterator(const AllocationRegister & alloc_register,AllocationIndex index)15 AllocationRegister::ConstIterator::ConstIterator(
16 const AllocationRegister& alloc_register,
17 AllocationIndex index)
18 : register_(alloc_register), index_(index) {}
19
operator ++()20 void AllocationRegister::ConstIterator::operator++() {
21 index_ = register_.allocations_.Next(index_ + 1);
22 }
23
operator !=(const ConstIterator & other) const24 bool AllocationRegister::ConstIterator::operator!=(
25 const ConstIterator& other) const {
26 return index_ != other.index_;
27 }
28
operator *() const29 AllocationRegister::Allocation AllocationRegister::ConstIterator::operator*()
30 const {
31 return register_.GetAllocation(index_);
32 }
33
operator ()(const Backtrace & backtrace) const34 size_t AllocationRegister::BacktraceHasher::operator()(
35 const Backtrace& backtrace) const {
36 const size_t kSampleLength = 10;
37
38 uintptr_t total_value = 0;
39
40 size_t head_end = std::min(backtrace.frame_count, kSampleLength);
41 for (size_t i = 0; i != head_end; ++i) {
42 total_value += reinterpret_cast<uintptr_t>(backtrace.frames[i].value);
43 }
44
45 size_t tail_start = backtrace.frame_count -
46 std::min(backtrace.frame_count - head_end, kSampleLength);
47 for (size_t i = tail_start; i != backtrace.frame_count; ++i) {
48 total_value += reinterpret_cast<uintptr_t>(backtrace.frames[i].value);
49 }
50
51 total_value += backtrace.frame_count;
52
53 // These magic constants give best results in terms of average collisions
54 // per backtrace. They were found by replaying real backtraces from Linux
55 // and Android against different hash functions.
56 return (total_value * 131101) >> 14;
57 }
58
operator ()(const void * address) const59 size_t AllocationRegister::AddressHasher::operator()(
60 const void* address) const {
61 // The multiplicative hashing scheme from [Knuth 1998]. The value of |a| has
62 // been chosen carefully based on measurements with real-word data (addresses
63 // recorded from a Chrome trace run). It is the first prime after 2^17. For
64 // |shift|, 15 yield good results for both 2^18 and 2^19 bucket sizes.
65 // Microbenchmarks show that this simple scheme outperforms fancy hashes like
66 // Murmur3 by 20 to 40 percent.
67 const uintptr_t key = reinterpret_cast<uintptr_t>(address);
68 const uintptr_t a = 131101;
69 const uintptr_t shift = 15;
70 const uintptr_t h = (key * a) >> shift;
71 return h;
72 }
73
AllocationRegister()74 AllocationRegister::AllocationRegister()
75 : AllocationRegister(kAllocationCapacity, kBacktraceCapacity) {}
76
AllocationRegister(size_t allocation_capacity,size_t backtrace_capacity)77 AllocationRegister::AllocationRegister(size_t allocation_capacity,
78 size_t backtrace_capacity)
79 : allocations_(allocation_capacity), backtraces_(backtrace_capacity) {
80 Backtrace sentinel = {};
81 sentinel.frames[0] = StackFrame::FromThreadName("[out of heap profiler mem]");
82 sentinel.frame_count = 1;
83
84 // Rationale for max / 2: in theory we could just start the sentinel with a
85 // refcount == 0. However, using max / 2 allows short circuiting of the
86 // conditional in RemoveBacktrace() keeping the sentinel logic out of the fast
87 // path. From a functional viewpoint, the sentinel is safe even if we wrap
88 // over refcount because .
89 BacktraceMap::KVPair::second_type sentinel_refcount =
90 std::numeric_limits<BacktraceMap::KVPair::second_type>::max() / 2;
91 auto index_and_flag = backtraces_.Insert(sentinel, sentinel_refcount);
92 DCHECK(index_and_flag.second);
93 DCHECK_EQ(index_and_flag.first, kOutOfStorageBacktraceIndex);
94 }
95
~AllocationRegister()96 AllocationRegister::~AllocationRegister() {}
97
Insert(const void * address,size_t size,const AllocationContext & context)98 bool AllocationRegister::Insert(const void* address,
99 size_t size,
100 const AllocationContext& context) {
101 DCHECK(address != nullptr);
102 if (size == 0) {
103 return false;
104 }
105
106 AllocationInfo info = {size, context.type_name,
107 InsertBacktrace(context.backtrace)};
108
109 // Try to insert the allocation.
110 auto index_and_flag = allocations_.Insert(address, info);
111 if (!index_and_flag.second &&
112 index_and_flag.first != AllocationMap::kInvalidKVIndex) {
113 // |address| is already there - overwrite the allocation info.
114 auto& old_info = allocations_.Get(index_and_flag.first).second;
115 RemoveBacktrace(old_info.backtrace_index);
116 old_info = info;
117 return true;
118 }
119
120 return index_and_flag.second;
121 }
122
Remove(const void * address)123 void AllocationRegister::Remove(const void* address) {
124 auto index = allocations_.Find(address);
125 if (index == AllocationMap::kInvalidKVIndex) {
126 return;
127 }
128
129 const AllocationInfo& info = allocations_.Get(index).second;
130 RemoveBacktrace(info.backtrace_index);
131 allocations_.Remove(index);
132 }
133
Get(const void * address,Allocation * out_allocation) const134 bool AllocationRegister::Get(const void* address,
135 Allocation* out_allocation) const {
136 auto index = allocations_.Find(address);
137 if (index == AllocationMap::kInvalidKVIndex) {
138 return false;
139 }
140
141 if (out_allocation) {
142 *out_allocation = GetAllocation(index);
143 }
144 return true;
145 }
146
begin() const147 AllocationRegister::ConstIterator AllocationRegister::begin() const {
148 return ConstIterator(*this, allocations_.Next(0));
149 }
150
end() const151 AllocationRegister::ConstIterator AllocationRegister::end() const {
152 return ConstIterator(*this, AllocationMap::kInvalidKVIndex);
153 }
154
EstimateTraceMemoryOverhead(TraceEventMemoryOverhead * overhead) const155 void AllocationRegister::EstimateTraceMemoryOverhead(
156 TraceEventMemoryOverhead* overhead) const {
157 size_t allocated = sizeof(AllocationRegister);
158 size_t resident = sizeof(AllocationRegister) +
159 allocations_.EstimateUsedMemory() +
160 backtraces_.EstimateUsedMemory();
161 overhead->Add("AllocationRegister", allocated, resident);
162 }
163
InsertBacktrace(const Backtrace & backtrace)164 AllocationRegister::BacktraceMap::KVIndex AllocationRegister::InsertBacktrace(
165 const Backtrace& backtrace) {
166 auto index = backtraces_.Insert(backtrace, 0).first;
167 if (index == BacktraceMap::kInvalidKVIndex)
168 return kOutOfStorageBacktraceIndex;
169 auto& backtrace_and_count = backtraces_.Get(index);
170 backtrace_and_count.second++;
171 return index;
172 }
173
RemoveBacktrace(BacktraceMap::KVIndex index)174 void AllocationRegister::RemoveBacktrace(BacktraceMap::KVIndex index) {
175 auto& backtrace_and_count = backtraces_.Get(index);
176 if (--backtrace_and_count.second == 0 &&
177 index != kOutOfStorageBacktraceIndex) {
178 // Backtrace is not referenced anymore - remove it.
179 backtraces_.Remove(index);
180 }
181 }
182
GetAllocation(AllocationMap::KVIndex index) const183 AllocationRegister::Allocation AllocationRegister::GetAllocation(
184 AllocationMap::KVIndex index) const {
185 const auto& address_and_info = allocations_.Get(index);
186 const auto& backtrace_and_count =
187 backtraces_.Get(address_and_info.second.backtrace_index);
188 return {address_and_info.first, address_and_info.second.size,
189 AllocationContext(backtrace_and_count.first,
190 address_and_info.second.type_name)};
191 }
192
193 } // namespace trace_event
194 } // namespace base
195