Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1028)

Side by Side Diff: components/metrics/leak_detector/leak_detector_impl.cc

Issue 986503002: components/metrics: Add runtime memory leak detector (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix portability issues for sizes and addresses Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "leak_detector_impl.h"
6
7 #include <inttypes.h>
8 #include <stddef.h>
9 #include <stdint.h>
10
11 #include <algorithm>
12 #include <new>
13
14 #include "base/hash.h"
15 #include "components/metrics/leak_detector/call_stack_table.h"
16 #include "components/metrics/leak_detector/ranked_list.h"
17
18 namespace leak_detector {
19
20 namespace {
21
22 // Look for leaks in the the top N entries in each tier, where N is this value.
23 const int kRankedListSize = 16;
24
25 // Initial hash table size for |LeakDetectorImpl::address_map_|.
26 const int kAddressMapNumBuckets = 100003;
27
28 // Number of entries in the alloc size table. As sizes are aligned to 32-bits
29 // the max supported allocation size is (kNumSizeEntries * 4 - 1). Any larger
30 // sizes are ignored. This value is chosen high enough that such large sizes
31 // are rare if not nonexistent.
32 const int kNumSizeEntries = 2048;
33
34 using ValueType = LeakDetectorValueType;
35
36 // Used for printing size values in LeakAnalyzer.
37 class SizeStringPrint : public LeakAnalyzer::StringPrint {
38 public:
39 // Gets the string representation of a value.
40 virtual const char* ValueToString(const ValueType& value, bool spacing_on) {
41 snprintf(buffer_, sizeof(buffer_), spacing_on ? "%10u" : "%u",
42 value.size());
43 return buffer_;
44 }
45
46 // Gets the word that describes the value type.
47 virtual const char* ValueTypeName(bool is_plural) {
48 return is_plural ? "sizes" : "size";
49 }
50 } size_string_print;
51
52 // Prints the input string buffer using RAW_LOG, pre-fixing each line with the
53 // process id.
54 void PrintWithPidOnEachLine(char* buf) {
55 char *ptr = strchr(buf, '\n');
56 do {
57 // Break up the string.
58 if (ptr)
59 *ptr = '\0';
60 // Print out the former part.
61 char line[1024];
62 snprintf(line, sizeof(line), "%d: %s\n", getpid(), buf);
63 RAW_LOG(ERROR, line);
64
65 // Re-point |buf| to the latter part.
66 if (ptr)
67 buf = ptr + 1;
68 ptr = strchr(buf, '\n');
69 } while (ptr);
70 }
71
72 // Functions to convert an allocation size to/from the array index used for
73 // |LeakDetectorImpl::size_entries_|.
74 int SizeToIndex(size_t size){
75 int result = static_cast<int>(size /= sizeof(uint32_t));
76 if (result < kNumSizeEntries)
77 return result;
78 return 0;
79 }
80
81 size_t IndexToSize(int index){
82 return sizeof(uint32_t) * index;
83 }
84
85 } // namespace
86
87 LeakDetectorImpl::LeakDetectorImpl(uint64_t mapping_addr,
88 uint64_t mapping_size,
89 int size_suspicion_threshold,
90 int call_stack_suspicion_threshold,
91 bool verbose)
92 : num_stack_tables_(0),
93 address_map_(kAddressMapNumBuckets),
94 size_leak_analyzer_(kRankedListSize, size_suspicion_threshold,
95 &size_string_print),
96 size_entries_(kNumSizeEntries, {0}),
97 mapping_addr_(mapping_addr),
98 mapping_size_(mapping_size),
99 call_stack_suspicion_threshold_(call_stack_suspicion_threshold),
100 verbose_(verbose) {
101 }
102
103 LeakDetectorImpl::~LeakDetectorImpl() {
104 for (CallStack* call_stack : call_stacks_)
105 CustomAllocator::Free(call_stack, sizeof(CallStack));
106
107 // Free any call stack tables.
108 for (AllocSizeEntry& entry : size_entries_) {
109 CallStackTable* table = entry.stack_table;
110 if (!table)
111 continue;
112 table->~CallStackTable();
113 CustomAllocator::Free(table, sizeof(CallStackTable));
114 }
115 }
116
117 bool LeakDetectorImpl::ShouldGetStackTraceForSize(size_t size) const {
118 return size_entries_[SizeToIndex(size)].stack_table != nullptr;
119 }
120
121 void LeakDetectorImpl::RecordAlloc(
122 const void* ptr, size_t size,
123 int stack_depth, const void* const stack[]) {
124 AllocInfo alloc_info;
125 alloc_info.size = size;
126
127 alloc_size_ += alloc_info.size;
128 ++num_allocs_;
129
130 AllocSizeEntry* entry = &size_entries_[SizeToIndex(size)];
131 ++entry->num_allocs;
132
133 if (stack_depth > 0) {
134 CallStack* call_stack = GetCallStack(stack_depth, stack);
135 alloc_info.call_stack = call_stack;
136
137 ++num_allocs_with_call_stack_;
138
139 if (entry->stack_table)
140 entry->stack_table->Add(call_stack);
141 }
142
143 address_map_.insert(std::pair<const void*, AllocInfo>(ptr, alloc_info));
144 }
145
146 void LeakDetectorImpl::RecordFree(const void* ptr) {
147 // Look up entry.
148 auto iter = address_map_.find(ptr);
149 if (iter == address_map_.end())
150 return;
151
152 const AllocInfo& alloc_info = iter->second;
153
154 AllocSizeEntry* entry = &size_entries_[SizeToIndex(alloc_info.size)];
155 ++entry->num_frees;
156
157 const CallStack* call_stack = alloc_info.call_stack;
158 if (call_stack) {
159 if (entry->stack_table)
160 entry->stack_table->Remove(call_stack);
161 }
162 ++num_frees_;
163 free_size_ += alloc_info.size;
164
165 address_map_.erase(iter);
166 }
167
168 void LeakDetectorImpl::TestForLeaks() {
169 // Add net alloc counts for each size to a ranked list.
170 RankedList size_ranked_list(kRankedListSize);
171 for (size_t i = 0; i < size_entries_.size(); ++i) {
172 const AllocSizeEntry& entry = size_entries_[i];
173 ValueType size_value(IndexToSize(i));
174 size_ranked_list.Add(size_value, entry.num_allocs - entry.num_frees);
175 }
176 size_leak_analyzer_.AddSample(size_ranked_list);
177
178 // Dump out the top entries.
179 char buf[0x4000];
180 if (verbose_) {
181 buf[0] = '\0';
182 size_leak_analyzer_.Dump(buf, sizeof(buf));
183 PrintWithPidOnEachLine(buf);
184 }
185
186 // Get suspected leaks by size.
187 for (const ValueType& size_value : size_leak_analyzer_.suspected_leaks()) {
188 uint32_t size = size_value.size();
189 AllocSizeEntry* entry = &size_entries_[SizeToIndex(size)];
190 if (entry->stack_table)
191 continue;
192 snprintf(buf, sizeof(buf), "Adding stack table for size %u\n", size);
193 PrintWithPidOnEachLine(buf);
194 entry->stack_table = new(CustomAllocator::Allocate(sizeof(CallStackTable)))
195 CallStackTable(call_stack_suspicion_threshold_);
196 ++num_stack_tables_;
197 }
198
199 // Check for leaks in each CallStackTable. It makes sense to this before
200 // checking the size allocations, because that could potentially create new
201 // CallStackTable. However, the overhead to check a new CallStackTable is
202 // small since this function is run very rarely. So handle the leak checks of
203 // Tier 2 here.
204 for (size_t i = 0; i < size_entries_.size(); ++i) {
205 const AllocSizeEntry& entry = size_entries_[i];
206 CallStackTable* stack_table = entry.stack_table;
207 if (!stack_table)
208 continue;
209
210 if (verbose_) {
211 // Dump table info.
212 snprintf(buf, sizeof(buf), "Stack table for size %zu:\n", IndexToSize(i));
213 PrintWithPidOnEachLine(buf);
214
215 buf[0] = '\0';
216 stack_table->Dump(buf, sizeof(buf));
217 PrintWithPidOnEachLine(buf);
218 }
219
220 // Get suspected leaks by call stack.
221 stack_table->TestForLeaks();
222 const LeakAnalyzer& leak_analyzer = stack_table->leak_analyzer();
223 for (const ValueType& call_stack_value : leak_analyzer.suspected_leaks()) {
224 const CallStack* call_stack = call_stack_value.call_stack();
225 int offset = snprintf(buf, sizeof(buf),
226 "Suspected call stack for size %zu, %p:\n",
227 IndexToSize(i), call_stack);
228 for (size_t j = 0; j < call_stack->depth; ++j) {
229 offset += snprintf(buf + offset, sizeof(buf) - offset,
230 "\t%" PRIxPTR "\n", GetOffset(call_stack->stack[j]));
231 }
232 PrintWithPidOnEachLine(buf);
233 }
234 }
235 }
236
237 void LeakDetectorImpl::DumpStats() const {
238 char buf[1024];
239 snprintf(buf, sizeof(buf),
240 "Alloc size: %" PRIu64"\n"
241 "Free size: %" PRIu64 "\n"
242 "Net alloc size: %" PRIu64 "\n"
243 "Number of stack tables: %u\n"
244 "Percentage of allocs with stack traces: %.2f%%\n"
245 "Number of call stack buckets: %zu\n",
246 alloc_size_, free_size_, alloc_size_ - free_size_, num_stack_tables_,
247 num_allocs_ ? 100.0f * num_allocs_with_call_stack_ / num_allocs_ : 0,
248 call_stacks_.bucket_count());
249 PrintWithPidOnEachLine(buf);
250 }
251
252 size_t LeakDetectorImpl::AddressHash::operator() (const void* ptr) const {
253 return base::Hash(reinterpret_cast<const char*>(&ptr), sizeof(ptr));
254 }
255
256 size_t LeakDetectorImpl::CallStackHash::operator() (
257 const CallStack* call_stack) const {
258 // Generate hash from call stack.
259 return base::Hash(reinterpret_cast<const char*>(call_stack->stack),
260 sizeof(*(call_stack->stack)) * call_stack->depth);
261 }
262
263 CallStack* LeakDetectorImpl::GetCallStack(
264 int depth, const void* const stack[]) {
265 // Temporarily create a call stack object for lookup in |call_stacks_|.
266 CallStack temp;
267 temp.depth = depth;
268 temp.stack = const_cast<const void**>(stack);
269
270 auto iter = call_stacks_.find(&temp);
271 if (iter != call_stacks_.end())
272 return *iter;
273
274 // Since |call_stacks_| stores CallStack pointers rather than actual objects,
275 // create new call objects manually here.
276 CallStack* new_call_stack =
277 new(CustomAllocator::Allocate(sizeof(CallStack))) CallStack;
278 memset(new_call_stack, 0, sizeof(*new_call_stack));
279 new_call_stack->depth = depth;
280 new_call_stack->hash = call_stacks_.hash_function()(&temp);
281 new_call_stack->stack =
282 reinterpret_cast<const void**>(
283 CustomAllocator::Allocate(sizeof(*stack) * depth));
284 std::copy(stack, stack + depth, new_call_stack->stack);
285
286 call_stacks_.insert(new_call_stack);
287 return new_call_stack;
288 }
289
290 uintptr_t LeakDetectorImpl::GetOffset(const void *ptr) const {
291 uintptr_t ptr_value = reinterpret_cast<uintptr_t>(ptr);
292 if (ptr_value >= mapping_addr_ && ptr_value < mapping_addr_ + mapping_size_)
293 return ptr_value - mapping_addr_;
294 return ptr_value;
295 }
296
297 } // namespace leak_detector
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698