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