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

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

Powered by Google App Engine
This is Rietveld 408576698