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

Side by Side Diff: third_party/tcmalloc/chromium/src/leak_analyzer.h

Issue 986503002: components/metrics: Add runtime memory leak detector (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix error in leak analyzer logic Created 5 years, 6 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 // ---
6 // Author: Simon Que
7
8 #ifndef _LEAK_ANALYZER_H_
9 #define _LEAK_ANALYZER_H_
10
11 #include <ranked_list.h>
12
13 // This class looks for possible leak patterns in allocation data over time.
14 // The typename T can be any data type associated with an allocation class, e.g.
15 // size or call stack.
16
17 namespace leak_detector {
18
19 template <typename T>
20 class LeakAnalyzer {
21 public:
22 // For local allocations.
23 typedef void* (*Allocator)(size_t size);
24 typedef void (*DeAllocator)(void* ptr);
25
26 // Interface for printing strings for the template class type.
27 class StringPrint {
28 public:
29 // Gets the string representation of a value. Returns the string stored in
30 // |buffer_|. The contents of |buffer_| will change if this is called again
31 // with different inputs.
32 virtual const char* ValueToString(const T& value, bool spacing_on) = 0;
33
34 // Gets the word that describes the value type.
35 virtual const char* ValueTypeName(bool is_plural) = 0;
36
37 protected:
38 // Buffer for returning the string representation of a value.
39 static const int kStringPrintBufferSize = 1024;
40 char buffer_[kStringPrintBufferSize];
41 };
42
43 LeakAnalyzer(int ranking_size, int num_suspicions_threshold,
44 Allocator alloc, DeAllocator dealloc,
45 StringPrint* string_print)
46 : ranking_size_(ranking_size),
47 score_increase_(1 << num_suspicions_threshold),
48 score_threshold_((1 << num_suspicions_threshold) * 2 - 1),
49 num_suspected_leaks_(0),
50 alloc_(alloc),
51 dealloc_(dealloc),
52 ranked_entries_(ranking_size, alloc, dealloc),
53 prev_ranked_entries_(ranking_size, alloc, dealloc),
54 string_print_(string_print) {
55 size_t suspected_histogram_size = ranking_size * sizeof(SuspicionEntry);
56 suspected_histogram_ =
57 static_cast<SuspicionEntry*>(alloc_(suspected_histogram_size));
58 memset(suspected_histogram_, 0, suspected_histogram_size);
59
60 size_t suspected_leak_size = ranking_size * sizeof(T);
61 suspected_leaks_ = static_cast<T*>(alloc_(suspected_leak_size));
62 memset(suspected_leaks_, 0, suspected_leak_size);
63 }
64
65 ~LeakAnalyzer() {
66 dealloc_(suspected_histogram_);
67 dealloc_(suspected_leaks_);
68
69 suspected_histogram_ = NULL;
70 suspected_leaks_ = NULL;
71 }
72
73 // Take in a list of allocations, sorted by count.
74 void AddSample(const RankedList<T>& ranked_list) {
75 // Save the ranked entries from the previous call.
76 prev_ranked_entries_ = ranked_entries_;
77
78 // Save the current entries.
79 ranked_entries_ = ranked_list;
80
81 RankedList<T> ranked_deltas(ranking_size_, alloc_, dealloc_);
82 for (int i = 0; i < ranked_list.size(); ++i) {
83 const RankedEntry* entry =
84 reinterpret_cast<const RankedEntry*>(&ranked_list.entries()[i]);
85
86 // Determine what count was recorded for this value last time.
87 const RankedEntry* prev_entry = GetPreviousEntryWithValue(entry->value);
88 if (prev_entry)
89 ranked_deltas.Add(entry->value, entry->count - prev_entry->count);
90 }
91
92 AnalyzeDeltas(ranked_deltas);
93 }
94
95 // Log the leak detector's top sizes and suspected sizes. |buffer| is a char
96 // buffer of size |buffer_size| that can eventually be logged.
97 int Dump(char* buffer, const int buffer_size) const {
98 int size_remaining = buffer_size;
99 int attempted_size = 0;
100
101 // Dump the top entries.
102 if (size_remaining > 0) {
103 attempted_size =
104 snprintf(buffer, size_remaining, "***** Top %d %s *****\n",
105 ranked_entries_.size(), string_print_->ValueTypeName(true));
106 size_remaining -= attempted_size;
107 buffer += attempted_size;
108 }
109
110 for (int i = 0; i < ranked_entries_.size() && size_remaining > 0; ++i) {
111 const RankedEntry& entry = ranked_entries_.entry(i);
112 if (entry.count == 0)
113 continue;
114
115 // Determine what count was recorded for this value last time.
116 const RankedEntry* prev_entry = GetPreviousEntryWithValue(entry.value);
117
118 char prev_entry_buffer[256];
119 prev_entry_buffer[0] = '\0';
120 if (prev_entry)
121 sprintf(prev_entry_buffer, "(%10d)", entry.count - prev_entry->count);
122
123 int attempted_size =
124 snprintf(buffer, size_remaining, "%s: %10u %s\n",
125 string_print_->ValueToString(entry.value, true), entry.count,
126 prev_entry ? prev_entry_buffer : "");
127 size_remaining -= attempted_size;
128 buffer += attempted_size;
129 }
130
131 // Now report the suspected sizes.
132 if (size_remaining > 0) {
133 attempted_size = snprintf(buffer, size_remaining, "Suspected %s: ",
134 string_print_->ValueTypeName(true));
135 size_remaining -= attempted_size;
136 buffer += attempted_size;
137 }
138 if (num_suspected_leaks_) {
139 for (int i = 0; i < num_suspected_leaks_ && size_remaining > 0; ++i) {
140 attempted_size =
141 snprintf(buffer, size_remaining, "%s, ",
142 string_print_->ValueToString(suspected_leaks_[i], false));
143 size_remaining -= attempted_size;
144 buffer += attempted_size;
145 }
146 // Erase the last comma and space.
147 buffer -= 2;
148 size_remaining += 2;
149 }
150 if (size_remaining > 0) {
151 attempted_size = snprintf(buffer, size_remaining, "\n");
152 size_remaining -= attempted_size;
153 buffer += attempted_size;
154 }
155
156 // Return the number of bytes written.
157 if (size_remaining >= 0)
158 return buffer_size - size_remaining;
159 return buffer_size;
160 }
161
162 // Used to report suspected leaks.
163 const T* suspected_leaks() const {
164 return suspected_leaks_;
165 }
166 int num_suspected_leaks() const {
167 return num_suspected_leaks_;
168 }
169
170 private:
171 using RankedEntry = typename RankedList<T>::Entry;
172
173 // An entry that is suspected as a possible leak. |score| is the level of
174 // suspicion, which can increase over time. Once it reaches |score_threshold_|
175 // the entry is reported as a suspected leak in |suspected_leaks_|.
176 struct SuspicionEntry {
177 int score;
178 T value;
179
180 // Flag indicating that this entry is valid, used in a fixed-size array.
181 bool valid;
182 };
183
184 // Analyze a list of allocation count deltas from the previous iteration. If
185 // anything looks like a possible leak, update the suspicion scores.
186 void AnalyzeDeltas(const RankedList<T>& ranked_deltas) {
187 // First, let the suspicion scores decay to deprecate older suspicions.
188 for (int i = 0; i < ranking_size_; ++i) {
189 suspected_histogram_[i].score /= 2;
190 // Invalidate entries with scores of 0 so that entries that are no longer
191 // under suspicion do not take up a slot that could be occupied by a new
192 // entry that is under suspicion.
193 if (suspected_histogram_[i].score == 0)
194 suspected_histogram_[i].valid = false;
195 }
196
197 const RankedEntry* entries =
198 reinterpret_cast<const RankedEntry*>(ranked_deltas.entries());
199 bool found_drop = false;
200 int drop_index = -1;
201 for (int i = 0; i < ranked_deltas.size() - 1; ++i) {
202 const RankedEntry& entry = entries[i];
203 const RankedEntry& next_entry = entries[i + 1];
204
205 // If the first entry is 0, that means all deltas are 0 or negative. Do
206 // not treat this as a suspicion of leaks; just quit.
207 if (i == 0 && entry.count == 0)
208 break;
209
210 // Find the first major drop in values.
211 if (entry.count > next_entry.count * 2) {
212 found_drop = true;
213 drop_index = i + 1;
214 break;
215 }
216 }
217
218 // Take the pre-drop sizes and increase their suspicion score.
219 for (int i = 0; found_drop && i < drop_index; ++i) {
220 for (int j = 0; j < ranking_size_; ++j) {
221 SuspicionEntry* suspected = &suspected_histogram_[j];
222 // Add a new suspected with the current value if there's an empty slot.
223 if (!suspected->valid) {
224 suspected->valid = true;
225 suspected->value = entries[i].value;
226 }
227 if (suspected->value == entries[i].value) {
228 suspected->score += score_increase_;
229 break;
230 }
231 }
232 }
233
234 // Now check the leak suspicion scores.
235 num_suspected_leaks_ = 0;
236 for (int i = 0;
237 i < ranking_size_ && num_suspected_leaks_ < ranking_size_;
238 ++i) {
239 // Only report suspected values that have accumulated a suspicion score.
240 // This is achieved by maintaining suspicion for several cycles, with few
241 // skips.
242 const SuspicionEntry& suspected = suspected_histogram_[i];
243 if (!suspected.valid || suspected.score < score_threshold_) {
244 continue;
245 }
246 // Store the size.
247 suspected_leaks_[num_suspected_leaks_++] = suspected.value;
248 }
249 }
250
251 // Returns the RankedEntry from the previous analysis that had the given
252 // value, or NULL if it didn't exist.
253 const RankedEntry* GetPreviousEntryWithValue(const T& value) const {
254 // Determine what count was recorded for this value last time.
255 for (int i = 0; i < prev_ranked_entries_.size(); ++i) {
256 if (prev_ranked_entries_.entry(i).value == value)
257 return &prev_ranked_entries_.entry(i);
258 }
259 return NULL;
260 }
261
262 // Look for the top |ranking_size_| entries when analyzing leaks.
263 const int ranking_size_;
264
265 // Each time an entry is suspected as a possible leak, increase its suspicion
266 // score by this much.
267 const int score_increase_;
268
269 // Report suspected leaks when the suspicion score reaches this value.
270 const int score_threshold_;
271
272 // The number of currently reported suspected leaks.
273 int num_suspected_leaks_;
274
275 // For local allocation.
276 const Allocator alloc_;
277 const DeAllocator dealloc_;
278
279 // Points to an array to track allocation values and their suspicion scores.
280 SuspicionEntry *suspected_histogram_;
281
282 // Array of allocated values that passed the suspicion threshold and are being
283 // reported.
284 T* suspected_leaks_;
285
286 // The most recent allocation entries, since the last call to AddSample().
287 RankedList<T> ranked_entries_;
288 // The previous allocation entries, from before the last call to AddSample().
289 RankedList<T> prev_ranked_entries_;
290
291 // For logging.
292 StringPrint* string_print_;
293
294 DISALLOW_COPY_AND_ASSIGN(LeakAnalyzer);
295 };
296
297 } // namespace leak_detector
298
299 #endif // _LEAK_ANALYZER_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698