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

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: LeakDetectorImpl stores addrs as uintptr_t; Implement move semantics for RankedList 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
10 #include <algorithm>
11 #include <new>
12 #include <utility>
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 // Print the contents of |str| prefixed with the current pid.
37 void PrintWithPid(const char* str) {
38 char line[1024];
39 snprintf(line, sizeof(line), "%d: %s\n", getpid(), str);
40 RAW_LOG(ERROR, line);
41 }
42
43 // Prints the input string buffer using RAW_LOG, pre-fixing each line with the
44 // process id. Will modify |str| temporarily but restore it at the end.
45 void PrintWithPidOnEachLine(char* str) {
46 char* current_line = str;
47 // Attempt to find a newline that will indicate the end of the first line
48 // and the start of the second line.
49 while (char *newline_ptr = strchr(current_line, '\n')) {
50 // Terminate the current line so it can be printed as a separate string.
51 // Restore the original string when done.
52 *newline_ptr = '\0';
53 PrintWithPid(current_line);
54 *newline_ptr = '\n';
55
56 // Point |current_line| to the next line.
57 current_line = newline_ptr + 1;
58 }
59 // There may be an extra line at the end of the input string that is not
60 // newline-terminated. e.g. if the input was only one line, or the last line
61 // did not end with a newline.
62 if (current_line[0] != '\0')
63 PrintWithPid(current_line);
64 }
65
66 // Functions to convert an allocation size to/from the array index used for
67 // |LeakDetectorImpl::size_entries_|.
68 int SizeToIndex(const size_t size) {
69 int result = static_cast<int>(size / sizeof(uint32_t));
70 if (result < kNumSizeEntries)
71 return result;
72 return 0;
73 }
74
75 size_t IndexToSize(int index){
76 return sizeof(uint32_t) * index;
77 }
78
79 } // namespace
80
81 LeakDetectorImpl::LeakDetectorImpl(uintptr_t mapping_addr,
82 size_t mapping_size,
83 int size_suspicion_threshold,
84 int call_stack_suspicion_threshold,
85 bool verbose)
86 : num_stack_tables_(0),
87 address_map_(kAddressMapNumBuckets),
88 size_leak_analyzer_(kRankedListSize, size_suspicion_threshold),
89 size_entries_(kNumSizeEntries, {0}),
90 mapping_addr_(mapping_addr),
91 mapping_size_(mapping_size),
92 call_stack_suspicion_threshold_(call_stack_suspicion_threshold),
93 verbose_(verbose) {
94 }
95
96 LeakDetectorImpl::~LeakDetectorImpl() {
97 for (CallStack* call_stack : call_stacks_)
98 CustomAllocator::Free(call_stack, sizeof(CallStack));
99 call_stacks_.clear();
100
101 // Free any call stack tables.
102 for (AllocSizeEntry& entry : size_entries_) {
103 CallStackTable* table = entry.stack_table;
104 if (!table)
105 continue;
106 table->~CallStackTable();
107 CustomAllocator::Free(table, sizeof(CallStackTable));
108 }
109 size_entries_.clear();
110 }
111
112 bool LeakDetectorImpl::ShouldGetStackTraceForSize(size_t size) const {
113 return size_entries_[SizeToIndex(size)].stack_table != nullptr;
114 }
115
116 void LeakDetectorImpl::RecordAlloc(
117 const void* ptr, size_t size,
118 int stack_depth, const void* const stack[]) {
119 AllocInfo alloc_info;
120 alloc_info.size = size;
121
122 alloc_size_ += alloc_info.size;
123 ++num_allocs_;
124
125 AllocSizeEntry* entry = &size_entries_[SizeToIndex(size)];
126 ++entry->num_allocs;
127
128 if (entry->stack_table && stack_depth > 0) {
129 alloc_info.call_stack = GetCallStack(stack_depth, stack);
130 entry->stack_table->Add(alloc_info.call_stack);
131
132 ++num_allocs_with_call_stack_;
133 }
134
135 uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
136 address_map_.insert(std::pair<uintptr_t, AllocInfo>(addr, alloc_info));
137 }
138
139 void LeakDetectorImpl::RecordFree(const void* ptr) {
140 // Look up address.
141 uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
142 auto iter = address_map_.find(addr);
143 if (iter == address_map_.end())
144 return;
145
146 const AllocInfo& alloc_info = iter->second;
147
148 AllocSizeEntry* entry = &size_entries_[SizeToIndex(alloc_info.size)];
149 ++entry->num_frees;
150
151 const CallStack* call_stack = alloc_info.call_stack;
152 if (call_stack) {
153 if (entry->stack_table)
154 entry->stack_table->Remove(call_stack);
155 }
156 ++num_frees_;
157 free_size_ += alloc_info.size;
158
159 address_map_.erase(iter);
160 }
161
162 void LeakDetectorImpl::TestForLeaks() {
163 // Add net alloc counts for each size to a ranked list.
164 RankedList size_ranked_list(kRankedListSize);
165 for (size_t i = 0; i < size_entries_.size(); ++i) {
166 const AllocSizeEntry& entry = size_entries_[i];
167 ValueType size_value(IndexToSize(i));
168 size_ranked_list.Add(size_value, entry.num_allocs - entry.num_frees);
169 }
170 size_leak_analyzer_.AddSample(std::move(size_ranked_list));
171
172 // Dump out the top entries.
173 char buf[0x4000];
174 if (verbose_) {
175 if (size_leak_analyzer_.Dump(sizeof(buf), buf) < sizeof(buf))
176 PrintWithPidOnEachLine(buf);
177 }
178
179 // Get suspected leaks by size.
180 for (const ValueType& size_value : size_leak_analyzer_.suspected_leaks()) {
181 uint32_t size = size_value.size();
182 AllocSizeEntry* entry = &size_entries_[SizeToIndex(size)];
183 if (entry->stack_table)
184 continue;
185 snprintf(buf, sizeof(buf), "Adding stack table for size %u\n", size);
186 PrintWithPidOnEachLine(buf);
187 entry->stack_table = new(CustomAllocator::Allocate(sizeof(CallStackTable)))
188 CallStackTable(call_stack_suspicion_threshold_);
189 ++num_stack_tables_;
190 }
191
192 // Check for leaks in each CallStackTable. It makes sense to this before
193 // checking the size allocations, because that could potentially create new
194 // CallStackTable. However, the overhead to check a new CallStackTable is
195 // small since this function is run very rarely. So handle the leak checks of
196 // Tier 2 here.
197 for (size_t i = 0; i < size_entries_.size(); ++i) {
198 const AllocSizeEntry& entry = size_entries_[i];
199 CallStackTable* stack_table = entry.stack_table;
200 if (!stack_table || stack_table->empty())
201 continue;
202
203 if (verbose_) {
204 // Dump table info.
205 snprintf(buf, sizeof(buf), "Stack table for size %zu:\n", IndexToSize(i));
206 PrintWithPidOnEachLine(buf);
207
208 if (stack_table->Dump(sizeof(buf), buf) < sizeof(buf))
209 PrintWithPidOnEachLine(buf);
210 }
211
212 // Get suspected leaks by call stack.
213 stack_table->TestForLeaks();
214 const LeakAnalyzer& leak_analyzer = stack_table->leak_analyzer();
215 for (const ValueType& call_stack_value : leak_analyzer.suspected_leaks()) {
216 const CallStack* call_stack = call_stack_value.call_stack();
217 int offset = snprintf(buf, sizeof(buf),
218 "Suspected call stack for size %zu, %p:\n",
219 IndexToSize(i), call_stack);
220 for (size_t j = 0; j < call_stack->depth; ++j) {
221 offset += snprintf(buf + offset, sizeof(buf) - offset,
222 "\t%" PRIxPTR "\n", GetOffset(call_stack->stack[j]));
223 }
224 PrintWithPidOnEachLine(buf);
225 }
226 }
227 }
228
229 void LeakDetectorImpl::DumpStats() const {
230 char buf[1024];
231 snprintf(buf, sizeof(buf),
232 "Alloc size: %" PRIu64"\n"
233 "Free size: %" PRIu64 "\n"
234 "Net alloc size: %" PRIu64 "\n"
235 "Number of stack tables: %u\n"
236 "Percentage of allocs with stack traces: %.2f%%\n"
237 "Number of call stack buckets: %zu\n",
238 alloc_size_, free_size_, alloc_size_ - free_size_, num_stack_tables_,
239 num_allocs_ ? 100.0f * num_allocs_with_call_stack_ / num_allocs_ : 0,
240 call_stacks_.bucket_count());
241 PrintWithPidOnEachLine(buf);
242 }
243
244 size_t LeakDetectorImpl::AddressHash::operator() (uintptr_t addr) const {
245 return base::Hash(reinterpret_cast<const char*>(&addr), sizeof(addr));
246 }
247
248 CallStack* LeakDetectorImpl::GetCallStack(
249 int depth, const void* const stack[]) {
250 // Temporarily create a call stack object for lookup in |call_stacks_|.
251 CallStack temp;
252 temp.depth = depth;
253 temp.stack = const_cast<const void**>(stack);
254
255 auto iter = call_stacks_.find(&temp);
256 if (iter != call_stacks_.end())
257 return *iter;
258
259 // Since |call_stacks_| stores CallStack pointers rather than actual objects,
260 // create new call objects manually here.
261 CallStack* new_call_stack =
262 new(CustomAllocator::Allocate(sizeof(CallStack))) CallStack;
263 memset(new_call_stack, 0, sizeof(*new_call_stack));
264 new_call_stack->depth = depth;
265 new_call_stack->hash = call_stacks_.hash_function()(&temp);
266 new_call_stack->stack =
267 reinterpret_cast<const void**>(
268 CustomAllocator::Allocate(sizeof(*stack) * depth));
269 std::copy(stack, stack + depth, new_call_stack->stack);
270
271 call_stacks_.insert(new_call_stack);
272 return new_call_stack;
273 }
274
275 uintptr_t LeakDetectorImpl::GetOffset(const void *ptr) const {
276 uintptr_t ptr_value = reinterpret_cast<uintptr_t>(ptr);
277 if (ptr_value >= mapping_addr_ && ptr_value < mapping_addr_ + mapping_size_)
278 return ptr_value - mapping_addr_;
279 return ptr_value;
280 }
281
282 } // namespace leak_detector
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698