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

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

Issue 986503002: components/metrics: Add runtime memory leak detector (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Create CallStackManager out of LeakDetectorImpl Created 5 years, 3 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 "components/metrics/leak_detector/leak_analyzer.h"
6
7 #include <set>
8 #include <utility>
9
10 namespace leak_detector {
11
12 namespace {
13
14 using RankedEntry = RankedList::Entry;
15
16 // Increase suspicion scores by this much each time an entry is suspected as
17 // being a leak.
18 const int kSuspicionScoreIncrease = 1;
19
20 } // namespace
21
22 void LeakAnalyzer::AddSample(RankedList&& ranked_list) {
23 // Save the ranked entries from the previous call.
24 prev_ranked_entries_ = std::move(ranked_entries_);
25
26 // Save the current entries.
27 ranked_entries_ = std::move(ranked_list);
28
29 RankedList ranked_deltas(ranking_size_);
30 for (const RankedEntry& entry : ranked_entries_) {
31 // Determine what count was recorded for this value last time.
32 uint32_t prev_count = 0;
33 if (GetPreviousCountForValue(entry.value, &prev_count))
34 ranked_deltas.Add(entry.value, entry.count - prev_count);
35 }
36
37 AnalyzeDeltas(ranked_deltas);
38 }
39
40 size_t LeakAnalyzer::Dump(const size_t buffer_size, char* buffer) const {
41 size_t size_remaining = buffer_size;
42 int attempted_size = 0;
43
44 // Add a null terminator in case the rest of the code (which is conditional)
45 // doesn't print anything.
46 if (size_remaining)
47 buffer[0] = '\0';
48
49 // Buffer used for calling LeakDetectorValueType::ToString().
50 char to_string_buffer[256];
51
52 if (ranked_entries_.size() > 0) {
53 // Dump the top entries.
54 if (size_remaining > 1) {
55 attempted_size =
56 snprintf(buffer, size_remaining, "***** Top %zu %ss *****\n",
57 ranked_entries_.size(),
58 ranked_entries_.begin()->value.GetTypeName());
59 size_remaining -= attempted_size;
60 buffer += attempted_size;
61 }
62
63 for (const RankedEntry& entry : ranked_entries_) {
64 if (size_remaining <= 1)
65 break;
66 if (entry.count == 0)
67 break;
68
69 // Determine what count was recorded for this value last time.
70 char prev_entry_buffer[256];
71 prev_entry_buffer[0] = '\0';
72
73 uint32_t prev_count = 0;
74 if (GetPreviousCountForValue(entry.value, &prev_count)) {
75 snprintf(prev_entry_buffer, sizeof(prev_entry_buffer),
76 "(%10d)", entry.count - prev_count);
77 }
78
79 attempted_size =
80 snprintf(
81 buffer, size_remaining, "%10s: %10u %s\n",
82 entry.value.ToString(sizeof(to_string_buffer), to_string_buffer),
83 entry.count, prev_entry_buffer);
84 size_remaining -= attempted_size;
85 buffer += attempted_size;
86 }
87 }
88
89 if (!suspected_leaks_.empty()) {
90 // Report the suspected sizes.
91 if (size_remaining > 1) {
92 const ValueType& first_leak_value = suspected_leaks_[0];
93 attempted_size = snprintf(buffer, size_remaining, "Suspected %ss: ",
94 first_leak_value.GetTypeName());
95 size_remaining -= attempted_size;
96 buffer += attempted_size;
97 }
98 if (size_remaining > 1) {
99 // Change this to a comma + space after the first item is printed, so that
100 // subsequent items will be separated by a comma.
101 const char* optional_comma = "";
102 for (const ValueType& leak_value : suspected_leaks_) {
103 attempted_size =
104 snprintf(buffer, size_remaining, "%s%s",
105 optional_comma,
106 leak_value.ToString(
107 sizeof(to_string_buffer), to_string_buffer));
108 size_remaining -= attempted_size;
109 buffer += attempted_size;
110 optional_comma = ", ";
111 }
112 }
113 if (size_remaining > 1) {
114 attempted_size = snprintf(buffer, size_remaining, "\n");
115 size_remaining -= attempted_size;
116 buffer += attempted_size;
117 }
118 }
119
120 // Return the number of bytes written, excluding the null terminator.
121 return buffer_size - size_remaining;
122 }
123
124 void LeakAnalyzer::AnalyzeDeltas(const RankedList& ranked_deltas) {
125 bool found_drop = false;
126 RankedList::const_iterator drop_position = ranked_deltas.end();
127
128 if (ranked_deltas.size() > 1) {
129 RankedList::const_iterator entry_iter = ranked_deltas.begin();
130 RankedList::const_iterator next_entry_iter = ranked_deltas.begin();
131 ++next_entry_iter;
132
133 // If the first entry is 0, that means all deltas are 0 or negative. Do
134 // not treat this as a suspicion of leaks; just quit.
135 if (entry_iter->count > 0) {
136 while (next_entry_iter != ranked_deltas.end()) {
137 const RankedEntry& entry = *entry_iter;
138 const RankedEntry& next_entry = *next_entry_iter;
139
140 // Find the first major drop in values (i.e. by 50% or more).
141 if (entry.count > next_entry.count * 2) {
142 found_drop = true;
143 drop_position = next_entry_iter;
144 break;
145 }
146 ++entry_iter;
147 ++next_entry_iter;
148 }
149 }
150 }
151
152 // All leak values before the drop are suspected during this analysis.
153 std::set<ValueType,
154 std::less<ValueType>,
155 Allocator<ValueType>> current_suspects;
156 if (found_drop) {
157 for (RankedList::const_iterator ranked_list_iter = ranked_deltas.begin();
158 ranked_list_iter != drop_position;
159 ++ranked_list_iter) {
160 current_suspects.insert(ranked_list_iter->value);
161 }
162 }
163
164 // Reset the score to 0 for all previously suspected leak values that did
165 // not get suspected this time.
166 auto iter = suspected_histogram_.begin();
167 while (iter != suspected_histogram_.end()) {
168 const ValueType& value = iter->first;
169 // Erase entries whose suspicion score reaches 0.
170 auto erase_iter = iter++;
171 if (current_suspects.find(value) == current_suspects.end())
172 suspected_histogram_.erase(erase_iter);
173 }
174
175 // For currently suspected values, increase the leak score.
176 for (const ValueType& value : current_suspects) {
177 auto histogram_iter = suspected_histogram_.find(value);
178 if (histogram_iter != suspected_histogram_.end()) {
179 histogram_iter->second += kSuspicionScoreIncrease;
180 } else if (suspected_histogram_.size() < ranking_size_) {
181 // Create a new entry if it didn't already exist.
182 suspected_histogram_[value] = kSuspicionScoreIncrease;
183 }
184 }
185
186 // Now check the leak suspicion scores. Make sure to erase the suspected
187 // leaks from the previous call.
188 suspected_leaks_.clear();
189 for (const auto& entry : suspected_histogram_) {
190 if (suspected_leaks_.size() > ranking_size_)
191 break;
192
193 // Only report suspected values that have accumulated a suspicion score.
194 // This is achieved by maintaining suspicion for several cycles, with few
195 // skips.
196 if (entry.second >= score_threshold_)
197 suspected_leaks_.emplace_back(entry.first);
198 }
199 }
200
201 bool LeakAnalyzer::GetPreviousCountForValue(const ValueType& value,
202 uint32_t* count) const {
203 // Determine what count was recorded for this value last time.
204 for (const RankedEntry& entry : prev_ranked_entries_) {
205 if (entry.value == value) {
206 *count = entry.count;
207 return true;
208 }
209 }
210 return false;
211 }
212
213 } // namespace leak_detector
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698