[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 1 | // Copyright (c) 2012 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 "base/metrics/statistics_recorder.h" |
| 6 | |
[email protected] | 08ea145 | 2013-05-02 03:09:25 | [diff] [blame] | 7 | #include "base/at_exit.h" |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 8 | #include "base/debug/leak_annotations.h" |
[email protected] | 38076f1 | 2013-11-14 18:33:53 | [diff] [blame] | 9 | #include "base/json/string_escape.h" |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 10 | #include "base/logging.h" |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 11 | #include "base/memory/scoped_ptr.h" |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 12 | #include "base/metrics/histogram.h" |
bcwhite | b036e432 | 2015-12-10 18:36:34 | [diff] [blame] | 13 | #include "base/metrics/metrics_hashes.h" |
simonhatch | df5a814 | 2015-07-15 22:22:57 | [diff] [blame] | 14 | #include "base/stl_util.h" |
[email protected] | d529cb0 | 2013-06-10 19:06:57 | [diff] [blame] | 15 | #include "base/strings/stringprintf.h" |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 16 | #include "base/synchronization/lock.h" |
[email protected] | 38076f1 | 2013-11-14 18:33:53 | [diff] [blame] | 17 | #include "base/values.h" |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 18 | |
[email protected] | fce44c1 | 2012-07-19 19:17:32 | [diff] [blame] | 19 | namespace { |
bcwhite | e497d2d | 2016-01-22 15:55:09 | [diff] [blame] | 20 | |
[email protected] | fce44c1 | 2012-07-19 19:17:32 | [diff] [blame] | 21 | // Initialize histogram statistics gathering system. |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 22 | base::LazyInstance<base::StatisticsRecorder>::Leaky g_statistics_recorder_ = |
| 23 | LAZY_INSTANCE_INITIALIZER; |
bcwhite | e497d2d | 2016-01-22 15:55:09 | [diff] [blame] | 24 | |
| 25 | bool HistogramNameLesser(const base::HistogramBase* a, |
| 26 | const base::HistogramBase* b) { |
| 27 | return a->histogram_name() < b->histogram_name(); |
| 28 | } |
| 29 | |
[email protected] | fce44c1 | 2012-07-19 19:17:32 | [diff] [blame] | 30 | } // namespace |
| 31 | |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 32 | namespace base { |
| 33 | |
bcwhite | 5cb99eb | 2016-02-01 21:07:56 | [diff] [blame] | 34 | StatisticsRecorder::HistogramIterator::HistogramIterator( |
| 35 | const HistogramMap::iterator& iter, bool include_persistent) |
| 36 | : iter_(iter), |
| 37 | include_persistent_(include_persistent) { |
| 38 | } |
| 39 | |
| 40 | StatisticsRecorder::HistogramIterator::HistogramIterator( |
| 41 | const HistogramIterator& rhs) |
| 42 | : iter_(rhs.iter_), |
| 43 | include_persistent_(rhs.include_persistent_) { |
| 44 | } |
| 45 | |
| 46 | StatisticsRecorder::HistogramIterator::~HistogramIterator() {} |
| 47 | |
| 48 | StatisticsRecorder::HistogramIterator& |
| 49 | StatisticsRecorder::HistogramIterator::operator++() { |
| 50 | const HistogramMap::iterator histograms_end = histograms_->end(); |
bcwhite | 373ce21 | 2016-02-02 17:57:41 | [diff] [blame] | 51 | if (iter_ == histograms_end || lock_ == NULL) |
| 52 | return *this; |
| 53 | |
| 54 | base::AutoLock auto_lock(*lock_); |
| 55 | |
| 56 | for (;;) { |
bcwhite | 5cb99eb | 2016-02-01 21:07:56 | [diff] [blame] | 57 | ++iter_; |
| 58 | if (iter_ == histograms_end) |
| 59 | break; |
| 60 | if (!include_persistent_ && (iter_->second->flags() & |
| 61 | HistogramBase::kIsPersistent)) { |
| 62 | continue; |
| 63 | } |
| 64 | break; |
| 65 | } |
bcwhite | 373ce21 | 2016-02-02 17:57:41 | [diff] [blame] | 66 | |
bcwhite | 5cb99eb | 2016-02-01 21:07:56 | [diff] [blame] | 67 | return *this; |
| 68 | } |
| 69 | |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 70 | // static |
[email protected] | fce44c1 | 2012-07-19 19:17:32 | [diff] [blame] | 71 | void StatisticsRecorder::Initialize() { |
| 72 | // Ensure that an instance of the StatisticsRecorder object is created. |
| 73 | g_statistics_recorder_.Get(); |
| 74 | } |
| 75 | |
[email protected] | fce44c1 | 2012-07-19 19:17:32 | [diff] [blame] | 76 | // static |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 77 | bool StatisticsRecorder::IsActive() { |
| 78 | if (lock_ == NULL) |
| 79 | return false; |
| 80 | base::AutoLock auto_lock(*lock_); |
| 81 | return NULL != histograms_; |
| 82 | } |
| 83 | |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 84 | // static |
[email protected] | cc7dec21 | 2013-03-01 03:53:25 | [diff] [blame] | 85 | HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate( |
| 86 | HistogramBase* histogram) { |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 87 | // As per crbug.com/79322 the histograms are intentionally leaked, so we need |
| 88 | // to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used only once |
| 89 | // for an object, the duplicates should not be annotated. |
| 90 | // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr) |
| 91 | // twice if (lock_ == NULL) || (!histograms_). |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 92 | if (lock_ == NULL) { |
| 93 | ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 |
| 94 | return histogram; |
| 95 | } |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 96 | |
[email protected] | cc7dec21 | 2013-03-01 03:53:25 | [diff] [blame] | 97 | HistogramBase* histogram_to_delete = NULL; |
| 98 | HistogramBase* histogram_to_return = NULL; |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 99 | { |
| 100 | base::AutoLock auto_lock(*lock_); |
| 101 | if (histograms_ == NULL) { |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 102 | histogram_to_return = histogram; |
| 103 | } else { |
asvitkine | 24d3e9a | 2015-05-27 05:22:14 | [diff] [blame] | 104 | const std::string& name = histogram->histogram_name(); |
bcwhite | 5cb99eb | 2016-02-01 21:07:56 | [diff] [blame] | 105 | const uint64_t name_hash = histogram->name_hash(); |
| 106 | DCHECK_NE(0U, name_hash); |
bcwhite | b036e432 | 2015-12-10 18:36:34 | [diff] [blame] | 107 | HistogramMap::iterator it = histograms_->find(name_hash); |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 108 | if (histograms_->end() == it) { |
bcwhite | b036e432 | 2015-12-10 18:36:34 | [diff] [blame] | 109 | (*histograms_)[name_hash] = histogram; |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 110 | ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 |
simonhatch | df5a814 | 2015-07-15 22:22:57 | [diff] [blame] | 111 | // If there are callbacks for this histogram, we set the kCallbackExists |
| 112 | // flag. |
| 113 | auto callback_iterator = callbacks_->find(name); |
| 114 | if (callback_iterator != callbacks_->end()) { |
| 115 | if (!callback_iterator->second.is_null()) |
| 116 | histogram->SetFlags(HistogramBase::kCallbackExists); |
| 117 | else |
| 118 | histogram->ClearFlags(HistogramBase::kCallbackExists); |
| 119 | } |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 120 | histogram_to_return = histogram; |
| 121 | } else if (histogram == it->second) { |
| 122 | // The histogram was registered before. |
| 123 | histogram_to_return = histogram; |
| 124 | } else { |
| 125 | // We already have one histogram with this name. |
bcwhite | 9a97049 | 2016-03-08 18:21:02 | [diff] [blame] | 126 | DCHECK_EQ(histogram->histogram_name(), |
| 127 | it->second->histogram_name()) << "hash collision"; |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 128 | histogram_to_return = it->second; |
| 129 | histogram_to_delete = histogram; |
| 130 | } |
| 131 | } |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 132 | } |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 133 | delete histogram_to_delete; |
| 134 | return histogram_to_return; |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 135 | } |
| 136 | |
| 137 | // static |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 138 | const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges( |
| 139 | const BucketRanges* ranges) { |
| 140 | DCHECK(ranges->HasValidChecksum()); |
| 141 | scoped_ptr<const BucketRanges> ranges_deleter; |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 142 | |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 143 | if (lock_ == NULL) { |
| 144 | ANNOTATE_LEAKING_OBJECT_PTR(ranges); |
| 145 | return ranges; |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 146 | } |
| 147 | |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 148 | base::AutoLock auto_lock(*lock_); |
| 149 | if (ranges_ == NULL) { |
| 150 | ANNOTATE_LEAKING_OBJECT_PTR(ranges); |
| 151 | return ranges; |
| 152 | } |
| 153 | |
asvitkine | 24d3e9a | 2015-05-27 05:22:14 | [diff] [blame] | 154 | std::list<const BucketRanges*>* checksum_matching_list; |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 155 | RangesMap::iterator ranges_it = ranges_->find(ranges->checksum()); |
| 156 | if (ranges_->end() == ranges_it) { |
| 157 | // Add a new matching list to map. |
asvitkine | 24d3e9a | 2015-05-27 05:22:14 | [diff] [blame] | 158 | checksum_matching_list = new std::list<const BucketRanges*>(); |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 159 | ANNOTATE_LEAKING_OBJECT_PTR(checksum_matching_list); |
| 160 | (*ranges_)[ranges->checksum()] = checksum_matching_list; |
| 161 | } else { |
| 162 | checksum_matching_list = ranges_it->second; |
| 163 | } |
| 164 | |
asvitkine | 24d3e9a | 2015-05-27 05:22:14 | [diff] [blame] | 165 | for (const BucketRanges* existing_ranges : *checksum_matching_list) { |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 166 | if (existing_ranges->Equals(ranges)) { |
| 167 | if (existing_ranges == ranges) { |
| 168 | return ranges; |
| 169 | } else { |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 170 | ranges_deleter.reset(ranges); |
| 171 | return existing_ranges; |
| 172 | } |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 173 | } |
| 174 | } |
[email protected] | 4a32f12 | 2012-07-25 20:02:48 | [diff] [blame] | 175 | // We haven't found a BucketRanges which has the same ranges. Register the |
| 176 | // new BucketRanges. |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 177 | checksum_matching_list->push_front(ranges); |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 178 | return ranges; |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | // static |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 182 | void StatisticsRecorder::WriteHTMLGraph(const std::string& query, |
| 183 | std::string* output) { |
| 184 | if (!IsActive()) |
| 185 | return; |
| 186 | |
| 187 | Histograms snapshot; |
| 188 | GetSnapshot(query, &snapshot); |
bcwhite | e497d2d | 2016-01-22 15:55:09 | [diff] [blame] | 189 | std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser); |
asvitkine | 24d3e9a | 2015-05-27 05:22:14 | [diff] [blame] | 190 | for (const HistogramBase* histogram : snapshot) { |
| 191 | histogram->WriteHTMLGraph(output); |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 192 | output->append("<br><hr><br>"); |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | // static |
| 197 | void StatisticsRecorder::WriteGraph(const std::string& query, |
| 198 | std::string* output) { |
| 199 | if (!IsActive()) |
| 200 | return; |
| 201 | if (query.length()) |
| 202 | StringAppendF(output, "Collections of histograms for %s\n", query.c_str()); |
| 203 | else |
| 204 | output->append("Collections of all histograms\n"); |
| 205 | |
| 206 | Histograms snapshot; |
| 207 | GetSnapshot(query, &snapshot); |
bcwhite | e497d2d | 2016-01-22 15:55:09 | [diff] [blame] | 208 | std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser); |
asvitkine | 24d3e9a | 2015-05-27 05:22:14 | [diff] [blame] | 209 | for (const HistogramBase* histogram : snapshot) { |
| 210 | histogram->WriteAscii(output); |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 211 | output->append("\n"); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | // static |
[email protected] | 38076f1 | 2013-11-14 18:33:53 | [diff] [blame] | 216 | std::string StatisticsRecorder::ToJSON(const std::string& query) { |
| 217 | if (!IsActive()) |
| 218 | return std::string(); |
| 219 | |
| 220 | std::string output("{"); |
| 221 | if (!query.empty()) { |
| 222 | output += "\"query\":"; |
[email protected] | bbe1571 | 2013-12-11 22:10:45 | [diff] [blame] | 223 | EscapeJSONString(query, true, &output); |
[email protected] | 38076f1 | 2013-11-14 18:33:53 | [diff] [blame] | 224 | output += ","; |
| 225 | } |
| 226 | |
| 227 | Histograms snapshot; |
| 228 | GetSnapshot(query, &snapshot); |
| 229 | output += "\"histograms\":["; |
[email protected] | 38076f1 | 2013-11-14 18:33:53 | [diff] [blame] | 230 | bool first_histogram = true; |
asvitkine | 24d3e9a | 2015-05-27 05:22:14 | [diff] [blame] | 231 | for (const HistogramBase* histogram : snapshot) { |
[email protected] | 38076f1 | 2013-11-14 18:33:53 | [diff] [blame] | 232 | if (first_histogram) |
| 233 | first_histogram = false; |
| 234 | else |
| 235 | output += ","; |
[email protected] | 75874a8 | 2013-11-21 07:48:58 | [diff] [blame] | 236 | std::string json; |
asvitkine | 24d3e9a | 2015-05-27 05:22:14 | [diff] [blame] | 237 | histogram->WriteJSON(&json); |
[email protected] | 38076f1 | 2013-11-14 18:33:53 | [diff] [blame] | 238 | output += json; |
| 239 | } |
| 240 | output += "]}"; |
| 241 | return output; |
| 242 | } |
| 243 | |
| 244 | // static |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 245 | void StatisticsRecorder::GetHistograms(Histograms* output) { |
| 246 | if (lock_ == NULL) |
| 247 | return; |
| 248 | base::AutoLock auto_lock(*lock_); |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 249 | if (histograms_ == NULL) |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 250 | return; |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 251 | |
asvitkine | 24d3e9a | 2015-05-27 05:22:14 | [diff] [blame] | 252 | for (const auto& entry : *histograms_) { |
bcwhite | b036e432 | 2015-12-10 18:36:34 | [diff] [blame] | 253 | DCHECK_EQ(entry.first, entry.second->name_hash()); |
asvitkine | 24d3e9a | 2015-05-27 05:22:14 | [diff] [blame] | 254 | output->push_back(entry.second); |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 255 | } |
| 256 | } |
| 257 | |
[email protected] | 993ae74 | 2012-07-18 18:06:30 | [diff] [blame] | 258 | // static |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 259 | void StatisticsRecorder::GetBucketRanges( |
| 260 | std::vector<const BucketRanges*>* output) { |
| 261 | if (lock_ == NULL) |
| 262 | return; |
| 263 | base::AutoLock auto_lock(*lock_); |
| 264 | if (ranges_ == NULL) |
| 265 | return; |
| 266 | |
asvitkine | 24d3e9a | 2015-05-27 05:22:14 | [diff] [blame] | 267 | for (const auto& entry : *ranges_) { |
| 268 | for (const auto& range_entry : *entry.second) { |
| 269 | output->push_back(range_entry); |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 270 | } |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | // static |
bcwhite | 61e5eff5 | 2016-02-19 17:12:37 | [diff] [blame] | 275 | HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) { |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 276 | if (lock_ == NULL) |
[email protected] | 993ae74 | 2012-07-18 18:06:30 | [diff] [blame] | 277 | return NULL; |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 278 | base::AutoLock auto_lock(*lock_); |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 279 | if (histograms_ == NULL) |
[email protected] | 993ae74 | 2012-07-18 18:06:30 | [diff] [blame] | 280 | return NULL; |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 281 | |
bcwhite | 9a97049 | 2016-03-08 18:21:02 | [diff] [blame] | 282 | HistogramMap::iterator it = histograms_->find(HashMetricName(name)); |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 283 | if (histograms_->end() == it) |
[email protected] | 993ae74 | 2012-07-18 18:06:30 | [diff] [blame] | 284 | return NULL; |
bcwhite | 9a97049 | 2016-03-08 18:21:02 | [diff] [blame] | 285 | DCHECK_EQ(name, it->second->histogram_name()) << "hash collision"; |
[email protected] | 993ae74 | 2012-07-18 18:06:30 | [diff] [blame] | 286 | return it->second; |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 287 | } |
| 288 | |
simonhatch | df5a814 | 2015-07-15 22:22:57 | [diff] [blame] | 289 | // static |
bcwhite | 008bf409 | 2016-02-17 22:40:35 | [diff] [blame] | 290 | StatisticsRecorder::HistogramIterator StatisticsRecorder::begin( |
| 291 | bool include_persistent) { |
| 292 | return HistogramIterator(histograms_->begin(), include_persistent); |
| 293 | } |
| 294 | |
| 295 | // static |
| 296 | StatisticsRecorder::HistogramIterator StatisticsRecorder::end() { |
| 297 | return HistogramIterator(histograms_->end(), true); |
| 298 | } |
| 299 | |
| 300 | // static |
| 301 | void StatisticsRecorder::GetSnapshot(const std::string& query, |
| 302 | Histograms* snapshot) { |
| 303 | if (lock_ == NULL) |
| 304 | return; |
| 305 | base::AutoLock auto_lock(*lock_); |
| 306 | if (histograms_ == NULL) |
| 307 | return; |
| 308 | |
| 309 | for (const auto& entry : *histograms_) { |
| 310 | if (entry.second->histogram_name().find(query) != std::string::npos) |
| 311 | snapshot->push_back(entry.second); |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | // static |
simonhatch | df5a814 | 2015-07-15 22:22:57 | [diff] [blame] | 316 | bool StatisticsRecorder::SetCallback( |
| 317 | const std::string& name, |
| 318 | const StatisticsRecorder::OnSampleCallback& cb) { |
| 319 | DCHECK(!cb.is_null()); |
| 320 | if (lock_ == NULL) |
| 321 | return false; |
| 322 | base::AutoLock auto_lock(*lock_); |
| 323 | if (histograms_ == NULL) |
| 324 | return false; |
| 325 | |
| 326 | if (ContainsKey(*callbacks_, name)) |
| 327 | return false; |
| 328 | callbacks_->insert(std::make_pair(name, cb)); |
| 329 | |
bcwhite | 5cb99eb | 2016-02-01 21:07:56 | [diff] [blame] | 330 | auto it = histograms_->find(HashMetricName(name)); |
bcwhite | b036e432 | 2015-12-10 18:36:34 | [diff] [blame] | 331 | if (it != histograms_->end()) { |
| 332 | DCHECK_EQ(name, it->second->histogram_name()) << "hash collision"; |
| 333 | it->second->SetFlags(HistogramBase::kCallbackExists); |
| 334 | } |
simonhatch | df5a814 | 2015-07-15 22:22:57 | [diff] [blame] | 335 | |
| 336 | return true; |
| 337 | } |
| 338 | |
| 339 | // static |
| 340 | void StatisticsRecorder::ClearCallback(const std::string& name) { |
| 341 | if (lock_ == NULL) |
| 342 | return; |
| 343 | base::AutoLock auto_lock(*lock_); |
| 344 | if (histograms_ == NULL) |
| 345 | return; |
| 346 | |
| 347 | callbacks_->erase(name); |
| 348 | |
| 349 | // We also clear the flag from the histogram (if it exists). |
bcwhite | 5cb99eb | 2016-02-01 21:07:56 | [diff] [blame] | 350 | auto it = histograms_->find(HashMetricName(name)); |
bcwhite | b036e432 | 2015-12-10 18:36:34 | [diff] [blame] | 351 | if (it != histograms_->end()) { |
| 352 | DCHECK_EQ(name, it->second->histogram_name()) << "hash collision"; |
| 353 | it->second->ClearFlags(HistogramBase::kCallbackExists); |
| 354 | } |
simonhatch | df5a814 | 2015-07-15 22:22:57 | [diff] [blame] | 355 | } |
| 356 | |
| 357 | // static |
| 358 | StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback( |
| 359 | const std::string& name) { |
| 360 | if (lock_ == NULL) |
| 361 | return OnSampleCallback(); |
| 362 | base::AutoLock auto_lock(*lock_); |
| 363 | if (histograms_ == NULL) |
| 364 | return OnSampleCallback(); |
| 365 | |
| 366 | auto callback_iterator = callbacks_->find(name); |
| 367 | return callback_iterator != callbacks_->end() ? callback_iterator->second |
| 368 | : OnSampleCallback(); |
| 369 | } |
| 370 | |
bcwhite | 5cb99eb | 2016-02-01 21:07:56 | [diff] [blame] | 371 | // static |
bcwhite | 008bf409 | 2016-02-17 22:40:35 | [diff] [blame] | 372 | void StatisticsRecorder::ResetForTesting() { |
| 373 | // Just call the private version that is used also by the destructor. |
| 374 | Reset(); |
bcwhite | 5cb99eb | 2016-02-01 21:07:56 | [diff] [blame] | 375 | } |
| 376 | |
| 377 | // static |
bcwhite | 008bf409 | 2016-02-17 22:40:35 | [diff] [blame] | 378 | void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) { |
| 379 | if (histograms_) |
| 380 | histograms_->erase(HashMetricName(name.as_string())); |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 381 | } |
| 382 | |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 383 | // This singleton instance should be started during the single threaded portion |
| 384 | // of main(), and hence it is not thread safe. It initializes globals to |
| 385 | // provide support for all future calls. |
| 386 | StatisticsRecorder::StatisticsRecorder() { |
| 387 | DCHECK(!histograms_); |
| 388 | if (lock_ == NULL) { |
| 389 | // This will leak on purpose. It's the only way to make sure we won't race |
| 390 | // against the static uninitialization of the module while one of our |
| 391 | // static methods relying on the lock get called at an inappropriate time |
| 392 | // during the termination phase. Since it's a static data member, we will |
| 393 | // leak one per process, which would be similar to the instance allocated |
| 394 | // during static initialization and released only on process termination. |
| 395 | lock_ = new base::Lock; |
| 396 | } |
| 397 | base::AutoLock auto_lock(*lock_); |
| 398 | histograms_ = new HistogramMap; |
simonhatch | df5a814 | 2015-07-15 22:22:57 | [diff] [blame] | 399 | callbacks_ = new CallbackMap; |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 400 | ranges_ = new RangesMap; |
[email protected] | 08ea145 | 2013-05-02 03:09:25 | [diff] [blame] | 401 | |
| 402 | if (VLOG_IS_ON(1)) |
| 403 | AtExitManager::RegisterCallback(&DumpHistogramsToVlog, this); |
| 404 | } |
| 405 | |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 406 | StatisticsRecorder::~StatisticsRecorder() { |
| 407 | DCHECK(histograms_ && ranges_ && lock_); |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 408 | |
bcwhite | 008bf409 | 2016-02-17 22:40:35 | [diff] [blame] | 409 | // Global clean up. |
| 410 | Reset(); |
| 411 | } |
| 412 | |
| 413 | // static |
| 414 | void StatisticsRecorder::Reset() { |
| 415 | // If there's no lock then there is nothing to reset. |
| 416 | if (!lock_) |
| 417 | return; |
| 418 | |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 419 | scoped_ptr<HistogramMap> histograms_deleter; |
simonhatch | df5a814 | 2015-07-15 22:22:57 | [diff] [blame] | 420 | scoped_ptr<CallbackMap> callbacks_deleter; |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 421 | scoped_ptr<RangesMap> ranges_deleter; |
| 422 | // We don't delete lock_ on purpose to avoid having to properly protect |
| 423 | // against it going away after we checked for NULL in the static methods. |
| 424 | { |
| 425 | base::AutoLock auto_lock(*lock_); |
| 426 | histograms_deleter.reset(histograms_); |
simonhatch | df5a814 | 2015-07-15 22:22:57 | [diff] [blame] | 427 | callbacks_deleter.reset(callbacks_); |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 428 | ranges_deleter.reset(ranges_); |
| 429 | histograms_ = NULL; |
simonhatch | df5a814 | 2015-07-15 22:22:57 | [diff] [blame] | 430 | callbacks_ = NULL; |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 431 | ranges_ = NULL; |
| 432 | } |
| 433 | // We are going to leak the histograms and the ranges. |
| 434 | } |
| 435 | |
bcwhite | 008bf409 | 2016-02-17 22:40:35 | [diff] [blame] | 436 | // static |
| 437 | void StatisticsRecorder::DumpHistogramsToVlog(void* instance) { |
| 438 | std::string output; |
| 439 | StatisticsRecorder::WriteGraph(std::string(), &output); |
| 440 | VLOG(1) << output; |
| 441 | } |
| 442 | |
[email protected] | 34d06232 | 2012-08-01 21:34:08 | [diff] [blame] | 443 | |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 444 | // static |
| 445 | StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; |
| 446 | // static |
simonhatch | df5a814 | 2015-07-15 22:22:57 | [diff] [blame] | 447 | StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = NULL; |
| 448 | // static |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 449 | StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = NULL; |
| 450 | // static |
| 451 | base::Lock* StatisticsRecorder::lock_ = NULL; |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 452 | |
| 453 | } // namespace base |