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

Side by Side Diff: third_party/tcmalloc/chromium/src/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: Remove Author label from new files; Check type in LeakDetectorValueType comparators; Add missing fi… Created 5 years, 5 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 (c) 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 <cstddef>
8 #include <cstring>
9
10 #include <algorithm>
11
12 #include "base/basictypes.h"
13 #include "base/commandlineflags.h"
14 #include "base/logging.h"
15 #include "call_stack_table.h"
16 #include "farmhash.h"
17 #include "ranked_list.h"
18
19 // A size must be suspected this many times to be reported as a leak suspect.
20 DECLARE_int32(size_suspicion_threshold);
21
22 // If set, dumps all leak analysis data, not just suspected leak reports.
23 DECLARE_bool(dump_leak_analysis);
24
25 namespace leak_detector {
26
27 namespace {
28
29 // Look for leaks in the the top N entries in each tier, where N is this value.
30 const int kRankedListSize = 16;
31
32 using ValueType = LeakDetectorValueType;
33
34 // Used for printing size values in LeakAnalyzer.
35 class SizeStringPrint : public LeakAnalyzer::StringPrint {
36 public:
37 // Gets the string representation of a value.
38 virtual const char* ValueToString(const ValueType& value, bool spacing_on) {
39 snprintf(buffer_, sizeof(buffer_), spacing_on ? "%10u" : "%u",
40 value.size());
41 return buffer_;
42 }
43
44 // Gets the word that describes the value type.
45 virtual const char* ValueTypeName(bool is_plural) {
46 return is_plural ? "sizes" : "size";
47 }
48 } size_string_print;
49
50 // Prints the input string buffer using RAW_LOG, pre-fixing each line with the
51 // process id.
52 void PrintWithPidOnEachLine(char* buf) {
53 char *ptr = strchr(buf, '\n');
54 do {
55 // Break up the string.
56 if (ptr)
57 *ptr = '\0';
58 // Print out the former part.
59 RAW_LOG(0, "%d: %s", getpid(), buf);
60 // Re-point |buf| to the latter part.
61 if (ptr)
62 buf = ptr + 1;
63 } while (ptr = strchr(buf, '\n'));
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 mapping_addr, uint64 mapping_size)
77 : num_stack_tables_(0),
78 address_map_(Alloc, DeAlloc),
79 size_leak_analyzer_(kRankedListSize, FLAGS_size_suspicion_threshold,
80 &size_string_print),
81 mapping_addr_(mapping_addr),
82 mapping_size_(mapping_size) {
83 // Initialize.
84 memset(entries_, 0, sizeof(entries_));
85 memset(&stats_, 0, sizeof(stats_));
86 memset(&call_stack_stats_, 0, sizeof(call_stack_stats_));
87 }
88
89 LeakDetectorImpl::~LeakDetectorImpl() {
90 for (CallStack* call_stack : call_stacks_)
91 DeAlloc(call_stack);
92
93 // Free any call stack tables.
94 for (int i = 0; i < kNumSizeEntries; ++i) {
95 CallStackTable* table = entries_[i].stack_table;
96 if (!table)
97 continue;
98 table->~CallStackTable();
99 DeAlloc(table);
100 }
101 }
102
103 bool LeakDetectorImpl::ShouldGetStackTraceForSize(size_t size) const {
104 return entries_[SizeToIndex(size)].stack_table != NULL;
105 }
106
107 void LeakDetectorImpl::RecordAlloc(
108 const void* ptr, size_t size,
109 int stack_depth, const void* const stack[]) {
110 AllocInfo alloc_info;
111 alloc_info.bytes = size;
112
113 stats_.alloc_size += alloc_info.bytes;
114 stats_.allocs++;
115
116 AllocSizeEntry* entry = GetEntryForSize(size);
117 ++entry->num_allocs;
118
119 if (stack_depth > 0) {
120 CallStack* call_stack = GetCallStack(stack_depth, stack);
121 call_stack->allocs++;
122 call_stack->alloc_size += size;
123 alloc_info.call_stack = call_stack;
124
125 call_stack_stats_.alloc_size += alloc_info.bytes;
126 call_stack_stats_.allocs++;
127
128 if (entry->stack_table)
129 entry->stack_table->Add(call_stack);
130 }
131
132 address_map_.Insert(ptr, alloc_info);
133 }
134
135 void LeakDetectorImpl::RecordFree(const void* ptr) {
136 // Look up entry.
137 AllocInfo alloc_info;
138 if (!address_map_.FindAndRemove(ptr, &alloc_info))
139 return;
140
141 AllocSizeEntry* entry = GetEntryForSize(alloc_info.bytes);
142 ++entry->num_frees;
143
144 CallStack* call_stack = alloc_info.call_stack;
145 if (call_stack) {
146 call_stack->frees++;
147 call_stack->free_size += alloc_info.bytes;
148
149 call_stack_stats_.frees++;
150 call_stack_stats_.free_size += alloc_info.bytes;
151
152 if (entry->stack_table)
153 entry->stack_table->Remove(call_stack);
154 }
155 stats_.frees++;
156 stats_.free_size += alloc_info.bytes;
157 }
158
159 void LeakDetectorImpl::TestForLeaks() {
160 // Add net alloc counts for each size to a ranked list.
161 RankedList size_ranked_list(kRankedListSize);
162 for (int i = 0; i < kNumSizeEntries; ++i) {
163 const AllocSizeEntry& entry = entries_[i];
164 ValueType size_value(IndexToSize(i));
165 size_ranked_list.Add(size_value, entry.num_allocs - entry.num_frees);
166 }
167 size_leak_analyzer_.AddSample(size_ranked_list);
168
169 // Dump out the top entries.
170 if (FLAGS_dump_leak_analysis) {
171 char buf[0x4000];
172 buf[0] = '\0';
173 size_leak_analyzer_.Dump(buf, sizeof(buf));
174 PrintWithPidOnEachLine(buf);
175 }
176
177 // Get suspected leaks by size.
178 for (const ValueType& size_value : size_leak_analyzer_.suspected_leaks()) {
179 uint32_t size = size_value.size();
180 AllocSizeEntry* entry = GetEntryForSize(size);
181 if (entry->stack_table)
182 continue;
183 RAW_VLOG(0, "%d: Adding stack table for size %u", getpid(), size);
184 entry->stack_table = new(Alloc(sizeof(CallStackTable))) CallStackTable;
185 ++num_stack_tables_;
186 }
187
188 // Check for leaks in each CallStackTable. It makes sense to this before
189 // checking the size allocations, because that could potentially create new
190 // CallStackTable. However, the overhead to check a new CallStackTable is
191 // small since this function is run very rarely. So handle the leak checks of
192 // Tier 2 here.
193 for (int i = 0; i < arraysize(entries_); ++i) {
194 const AllocSizeEntry& entry = entries_[i];
195 CallStackTable* stack_table = entry.stack_table;
196 if (!stack_table)
197 continue;
198
199 if (FLAGS_dump_leak_analysis) {
200 // Dump table info.
201 char buf[0x4000];
202 RAW_VLOG(0, "%d: Stack table for size %d", getpid(), IndexToSize(i));
203 buf[0] = '\0';
204 stack_table->Dump(buf, sizeof(buf));
205 PrintWithPidOnEachLine(buf);
206 }
207
208 // Get suspected leaks by call stack.
209 stack_table->TestForLeaks();
210 const LeakAnalyzer& leak_analyzer = stack_table->leak_analyzer();
211 for (const ValueType& call_stack_value : leak_analyzer.suspected_leaks()) {
212 const CallStack* call_stack = call_stack_value.call_stack();
213 RAW_VLOG(0, "%d: Suspected call stack for size %u: %p",
214 getpid(), IndexToSize(i), call_stack);
215 for (int k = 0; k < call_stack->depth; ++k)
216 RAW_VLOG(0, "%d: %p", getpid(), GetOffset(call_stack->stack[k]));
217 }
218 }
219 }
220
221 void LeakDetectorImpl::DumpStats() const {
222 RAW_VLOG(0, "%d: Alloc size: %lu\n", getpid(), stats_.alloc_size);
223 RAW_VLOG(0, "%d: Free size: %lu\n", getpid(), stats_.free_size);
224 RAW_VLOG(0, "%d: Net alloc size: %lu\n", getpid(),
225 stats_.alloc_size - stats_.free_size);
226 RAW_VLOG(0, "%d: Number of stack tables: %d\n", getpid(), num_stack_tables_);
227 if (stats_.alloc_size) {
228 RAW_VLOG(0, "%d: %% of calls with stack trace: %.2f%%\n", getpid(),
229 static_cast<double>(call_stack_stats_.alloc_size * 100) /
230 stats_.alloc_size);
231 }
232 RAW_VLOG(0, "%d: Number of call stack buckets: %d\n", getpid(),
233 call_stacks_.bucket_count());
234 }
235
236 // static
237 inline int LeakDetectorImpl::SizeToIndex(size_t size) {
238 int result = static_cast<int>(size /= sizeof(uint32));
239 if (result < kNumSizeEntries)
240 return result;
241 return 0;
242 }
243
244 // static
245 inline size_t LeakDetectorImpl::IndexToSize(int index) {
246 return sizeof(uint32) * index;
247 }
248
249 LeakDetectorImpl::CallStack* LeakDetectorImpl::GetCallStack(
250 int depth, const void* const stack[]) {
251 // Temporarily create a call stack object for lookup in |call_stacks_|.
252 CallStack temp;
253 temp.depth = depth;
254 temp.stack = const_cast<const void**>(stack);
255
256 auto iter = call_stacks_.find(&temp);
257 if (iter != call_stacks_.end())
258 return *iter;
259
260 // Since |call_stacks_| stores CallStack pointers rather than actual objects,
261 // create new call objects manually here.
262 CallStack* new_call_stack = new(Alloc(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**>(Alloc(sizeof(*stack) * depth));
268 std::copy(stack, stack + depth, new_call_stack->stack);
269
270 call_stacks_.insert(new_call_stack);
271 return new_call_stack;
272 }
273
274 uint64 LeakDetectorImpl::GetOffset(const void *ptr) const {
275 uint64 ptr_value = reinterpret_cast<uint64>(ptr);
276 if (ptr_value >= mapping_addr_ && ptr_value < mapping_addr_ + mapping_size_)
277 return ptr_value - mapping_addr_;
278 return ptr_value;
279 }
280
281 // static
282 // Use FarmHash to hash a call stack.
283 uint64 LeakDetectorImpl::CallStackToHash(const CallStack* call_stack) {
284 return util::Hash(reinterpret_cast<const char*>(call_stack->stack),
285 sizeof(*(call_stack->stack)) * call_stack->depth);
286 }
287
288 } // namespace leak_detector
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698