blob: d0fa2add3603b3f574dfa9ef9e5ee2e6b152aafc [file] [log] [blame]
[email protected]567d30e2012-07-13 21:48:291// 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]08ea1452013-05-02 03:09:257#include "base/at_exit.h"
[email protected]567d30e2012-07-13 21:48:298#include "base/debug/leak_annotations.h"
[email protected]38076f12013-11-14 18:33:539#include "base/json/string_escape.h"
[email protected]567d30e2012-07-13 21:48:2910#include "base/logging.h"
[email protected]34d062322012-08-01 21:34:0811#include "base/memory/scoped_ptr.h"
[email protected]567d30e2012-07-13 21:48:2912#include "base/metrics/histogram.h"
bcwhiteb036e4322015-12-10 18:36:3413#include "base/metrics/metrics_hashes.h"
simonhatchdf5a8142015-07-15 22:22:5714#include "base/stl_util.h"
[email protected]d529cb02013-06-10 19:06:5715#include "base/strings/stringprintf.h"
[email protected]567d30e2012-07-13 21:48:2916#include "base/synchronization/lock.h"
[email protected]38076f12013-11-14 18:33:5317#include "base/values.h"
[email protected]567d30e2012-07-13 21:48:2918
[email protected]fce44c12012-07-19 19:17:3219namespace {
bcwhitee497d2d2016-01-22 15:55:0920
[email protected]fce44c12012-07-19 19:17:3221// Initialize histogram statistics gathering system.
[email protected]34d062322012-08-01 21:34:0822base::LazyInstance<base::StatisticsRecorder>::Leaky g_statistics_recorder_ =
23 LAZY_INSTANCE_INITIALIZER;
bcwhitee497d2d2016-01-22 15:55:0924
25bool HistogramNameLesser(const base::HistogramBase* a,
26 const base::HistogramBase* b) {
27 return a->histogram_name() < b->histogram_name();
28}
29
[email protected]fce44c12012-07-19 19:17:3230} // namespace
31
[email protected]567d30e2012-07-13 21:48:2932namespace base {
33
bcwhite5cb99eb2016-02-01 21:07:5634StatisticsRecorder::HistogramIterator::HistogramIterator(
35 const HistogramMap::iterator& iter, bool include_persistent)
36 : iter_(iter),
37 include_persistent_(include_persistent) {
38}
39
40StatisticsRecorder::HistogramIterator::HistogramIterator(
41 const HistogramIterator& rhs)
42 : iter_(rhs.iter_),
43 include_persistent_(rhs.include_persistent_) {
44}
45
46StatisticsRecorder::HistogramIterator::~HistogramIterator() {}
47
48StatisticsRecorder::HistogramIterator&
49StatisticsRecorder::HistogramIterator::operator++() {
50 const HistogramMap::iterator histograms_end = histograms_->end();
bcwhite373ce212016-02-02 17:57:4151 if (iter_ == histograms_end || lock_ == NULL)
52 return *this;
53
54 base::AutoLock auto_lock(*lock_);
55
56 for (;;) {
bcwhite5cb99eb2016-02-01 21:07:5657 ++iter_;
58 if (iter_ == histograms_end)
59 break;
60 if (!include_persistent_ && (iter_->second->flags() &
61 HistogramBase::kIsPersistent)) {
62 continue;
63 }
64 break;
65 }
bcwhite373ce212016-02-02 17:57:4166
bcwhite5cb99eb2016-02-01 21:07:5667 return *this;
68}
69
[email protected]567d30e2012-07-13 21:48:2970// static
[email protected]fce44c12012-07-19 19:17:3271void StatisticsRecorder::Initialize() {
72 // Ensure that an instance of the StatisticsRecorder object is created.
73 g_statistics_recorder_.Get();
74}
75
[email protected]fce44c12012-07-19 19:17:3276// static
[email protected]567d30e2012-07-13 21:48:2977bool StatisticsRecorder::IsActive() {
78 if (lock_ == NULL)
79 return false;
80 base::AutoLock auto_lock(*lock_);
81 return NULL != histograms_;
82}
83
[email protected]34d062322012-08-01 21:34:0884// static
[email protected]cc7dec212013-03-01 03:53:2585HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
86 HistogramBase* histogram) {
[email protected]567d30e2012-07-13 21:48:2987 // 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]567d30e2012-07-13 21:48:2992 if (lock_ == NULL) {
93 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
94 return histogram;
95 }
[email protected]34d062322012-08-01 21:34:0896
[email protected]cc7dec212013-03-01 03:53:2597 HistogramBase* histogram_to_delete = NULL;
98 HistogramBase* histogram_to_return = NULL;
[email protected]34d062322012-08-01 21:34:0899 {
100 base::AutoLock auto_lock(*lock_);
101 if (histograms_ == NULL) {
[email protected]34d062322012-08-01 21:34:08102 histogram_to_return = histogram;
103 } else {
asvitkine24d3e9a2015-05-27 05:22:14104 const std::string& name = histogram->histogram_name();
bcwhite5cb99eb2016-02-01 21:07:56105 const uint64_t name_hash = histogram->name_hash();
106 DCHECK_NE(0U, name_hash);
bcwhiteb036e4322015-12-10 18:36:34107 HistogramMap::iterator it = histograms_->find(name_hash);
[email protected]34d062322012-08-01 21:34:08108 if (histograms_->end() == it) {
bcwhiteb036e4322015-12-10 18:36:34109 (*histograms_)[name_hash] = histogram;
[email protected]34d062322012-08-01 21:34:08110 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
simonhatchdf5a8142015-07-15 22:22:57111 // 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]34d062322012-08-01 21:34:08120 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.
bcwhite9a970492016-03-08 18:21:02126 DCHECK_EQ(histogram->histogram_name(),
127 it->second->histogram_name()) << "hash collision";
[email protected]34d062322012-08-01 21:34:08128 histogram_to_return = it->second;
129 histogram_to_delete = histogram;
130 }
131 }
[email protected]567d30e2012-07-13 21:48:29132 }
[email protected]34d062322012-08-01 21:34:08133 delete histogram_to_delete;
134 return histogram_to_return;
[email protected]567d30e2012-07-13 21:48:29135}
136
137// static
[email protected]34d062322012-08-01 21:34:08138const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
139 const BucketRanges* ranges) {
140 DCHECK(ranges->HasValidChecksum());
141 scoped_ptr<const BucketRanges> ranges_deleter;
[email protected]567d30e2012-07-13 21:48:29142
[email protected]34d062322012-08-01 21:34:08143 if (lock_ == NULL) {
144 ANNOTATE_LEAKING_OBJECT_PTR(ranges);
145 return ranges;
[email protected]567d30e2012-07-13 21:48:29146 }
147
[email protected]34d062322012-08-01 21:34:08148 base::AutoLock auto_lock(*lock_);
149 if (ranges_ == NULL) {
150 ANNOTATE_LEAKING_OBJECT_PTR(ranges);
151 return ranges;
152 }
153
asvitkine24d3e9a2015-05-27 05:22:14154 std::list<const BucketRanges*>* checksum_matching_list;
[email protected]34d062322012-08-01 21:34:08155 RangesMap::iterator ranges_it = ranges_->find(ranges->checksum());
156 if (ranges_->end() == ranges_it) {
157 // Add a new matching list to map.
asvitkine24d3e9a2015-05-27 05:22:14158 checksum_matching_list = new std::list<const BucketRanges*>();
[email protected]34d062322012-08-01 21:34:08159 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
asvitkine24d3e9a2015-05-27 05:22:14165 for (const BucketRanges* existing_ranges : *checksum_matching_list) {
[email protected]34d062322012-08-01 21:34:08166 if (existing_ranges->Equals(ranges)) {
167 if (existing_ranges == ranges) {
168 return ranges;
169 } else {
[email protected]34d062322012-08-01 21:34:08170 ranges_deleter.reset(ranges);
171 return existing_ranges;
172 }
[email protected]567d30e2012-07-13 21:48:29173 }
174 }
[email protected]4a32f122012-07-25 20:02:48175 // We haven't found a BucketRanges which has the same ranges. Register the
176 // new BucketRanges.
[email protected]34d062322012-08-01 21:34:08177 checksum_matching_list->push_front(ranges);
[email protected]34d062322012-08-01 21:34:08178 return ranges;
[email protected]567d30e2012-07-13 21:48:29179}
180
181// static
[email protected]567d30e2012-07-13 21:48:29182void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
183 std::string* output) {
184 if (!IsActive())
185 return;
186
187 Histograms snapshot;
188 GetSnapshot(query, &snapshot);
bcwhitee497d2d2016-01-22 15:55:09189 std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser);
asvitkine24d3e9a2015-05-27 05:22:14190 for (const HistogramBase* histogram : snapshot) {
191 histogram->WriteHTMLGraph(output);
[email protected]567d30e2012-07-13 21:48:29192 output->append("<br><hr><br>");
193 }
194}
195
196// static
197void 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);
bcwhitee497d2d2016-01-22 15:55:09208 std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser);
asvitkine24d3e9a2015-05-27 05:22:14209 for (const HistogramBase* histogram : snapshot) {
210 histogram->WriteAscii(output);
[email protected]567d30e2012-07-13 21:48:29211 output->append("\n");
212 }
213}
214
215// static
[email protected]38076f12013-11-14 18:33:53216std::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]bbe15712013-12-11 22:10:45223 EscapeJSONString(query, true, &output);
[email protected]38076f12013-11-14 18:33:53224 output += ",";
225 }
226
227 Histograms snapshot;
228 GetSnapshot(query, &snapshot);
229 output += "\"histograms\":[";
[email protected]38076f12013-11-14 18:33:53230 bool first_histogram = true;
asvitkine24d3e9a2015-05-27 05:22:14231 for (const HistogramBase* histogram : snapshot) {
[email protected]38076f12013-11-14 18:33:53232 if (first_histogram)
233 first_histogram = false;
234 else
235 output += ",";
[email protected]75874a82013-11-21 07:48:58236 std::string json;
asvitkine24d3e9a2015-05-27 05:22:14237 histogram->WriteJSON(&json);
[email protected]38076f12013-11-14 18:33:53238 output += json;
239 }
240 output += "]}";
241 return output;
242}
243
244// static
[email protected]567d30e2012-07-13 21:48:29245void StatisticsRecorder::GetHistograms(Histograms* output) {
246 if (lock_ == NULL)
247 return;
248 base::AutoLock auto_lock(*lock_);
[email protected]34d062322012-08-01 21:34:08249 if (histograms_ == NULL)
[email protected]567d30e2012-07-13 21:48:29250 return;
[email protected]34d062322012-08-01 21:34:08251
asvitkine24d3e9a2015-05-27 05:22:14252 for (const auto& entry : *histograms_) {
bcwhiteb036e4322015-12-10 18:36:34253 DCHECK_EQ(entry.first, entry.second->name_hash());
asvitkine24d3e9a2015-05-27 05:22:14254 output->push_back(entry.second);
[email protected]567d30e2012-07-13 21:48:29255 }
256}
257
[email protected]993ae742012-07-18 18:06:30258// static
[email protected]34d062322012-08-01 21:34:08259void 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
asvitkine24d3e9a2015-05-27 05:22:14267 for (const auto& entry : *ranges_) {
268 for (const auto& range_entry : *entry.second) {
269 output->push_back(range_entry);
[email protected]34d062322012-08-01 21:34:08270 }
271 }
272}
273
274// static
bcwhite61e5eff52016-02-19 17:12:37275HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) {
[email protected]567d30e2012-07-13 21:48:29276 if (lock_ == NULL)
[email protected]993ae742012-07-18 18:06:30277 return NULL;
[email protected]567d30e2012-07-13 21:48:29278 base::AutoLock auto_lock(*lock_);
[email protected]34d062322012-08-01 21:34:08279 if (histograms_ == NULL)
[email protected]993ae742012-07-18 18:06:30280 return NULL;
[email protected]34d062322012-08-01 21:34:08281
bcwhite9a970492016-03-08 18:21:02282 HistogramMap::iterator it = histograms_->find(HashMetricName(name));
[email protected]567d30e2012-07-13 21:48:29283 if (histograms_->end() == it)
[email protected]993ae742012-07-18 18:06:30284 return NULL;
bcwhite9a970492016-03-08 18:21:02285 DCHECK_EQ(name, it->second->histogram_name()) << "hash collision";
[email protected]993ae742012-07-18 18:06:30286 return it->second;
[email protected]567d30e2012-07-13 21:48:29287}
288
simonhatchdf5a8142015-07-15 22:22:57289// static
bcwhite008bf4092016-02-17 22:40:35290StatisticsRecorder::HistogramIterator StatisticsRecorder::begin(
291 bool include_persistent) {
292 return HistogramIterator(histograms_->begin(), include_persistent);
293}
294
295// static
296StatisticsRecorder::HistogramIterator StatisticsRecorder::end() {
297 return HistogramIterator(histograms_->end(), true);
298}
299
300// static
301void 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
simonhatchdf5a8142015-07-15 22:22:57316bool 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
bcwhite5cb99eb2016-02-01 21:07:56330 auto it = histograms_->find(HashMetricName(name));
bcwhiteb036e4322015-12-10 18:36:34331 if (it != histograms_->end()) {
332 DCHECK_EQ(name, it->second->histogram_name()) << "hash collision";
333 it->second->SetFlags(HistogramBase::kCallbackExists);
334 }
simonhatchdf5a8142015-07-15 22:22:57335
336 return true;
337}
338
339// static
340void 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).
bcwhite5cb99eb2016-02-01 21:07:56350 auto it = histograms_->find(HashMetricName(name));
bcwhiteb036e4322015-12-10 18:36:34351 if (it != histograms_->end()) {
352 DCHECK_EQ(name, it->second->histogram_name()) << "hash collision";
353 it->second->ClearFlags(HistogramBase::kCallbackExists);
354 }
simonhatchdf5a8142015-07-15 22:22:57355}
356
357// static
358StatisticsRecorder::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
bcwhite5cb99eb2016-02-01 21:07:56371// static
bcwhite008bf4092016-02-17 22:40:35372void StatisticsRecorder::ResetForTesting() {
373 // Just call the private version that is used also by the destructor.
374 Reset();
bcwhite5cb99eb2016-02-01 21:07:56375}
376
377// static
bcwhite008bf4092016-02-17 22:40:35378void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) {
379 if (histograms_)
380 histograms_->erase(HashMetricName(name.as_string()));
[email protected]567d30e2012-07-13 21:48:29381}
382
[email protected]34d062322012-08-01 21:34:08383// 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.
386StatisticsRecorder::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;
simonhatchdf5a8142015-07-15 22:22:57399 callbacks_ = new CallbackMap;
[email protected]34d062322012-08-01 21:34:08400 ranges_ = new RangesMap;
[email protected]08ea1452013-05-02 03:09:25401
402 if (VLOG_IS_ON(1))
403 AtExitManager::RegisterCallback(&DumpHistogramsToVlog, this);
404}
405
[email protected]34d062322012-08-01 21:34:08406StatisticsRecorder::~StatisticsRecorder() {
407 DCHECK(histograms_ && ranges_ && lock_);
[email protected]34d062322012-08-01 21:34:08408
bcwhite008bf4092016-02-17 22:40:35409 // Global clean up.
410 Reset();
411}
412
413// static
414void StatisticsRecorder::Reset() {
415 // If there's no lock then there is nothing to reset.
416 if (!lock_)
417 return;
418
[email protected]34d062322012-08-01 21:34:08419 scoped_ptr<HistogramMap> histograms_deleter;
simonhatchdf5a8142015-07-15 22:22:57420 scoped_ptr<CallbackMap> callbacks_deleter;
[email protected]34d062322012-08-01 21:34:08421 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_);
simonhatchdf5a8142015-07-15 22:22:57427 callbacks_deleter.reset(callbacks_);
[email protected]34d062322012-08-01 21:34:08428 ranges_deleter.reset(ranges_);
429 histograms_ = NULL;
simonhatchdf5a8142015-07-15 22:22:57430 callbacks_ = NULL;
[email protected]34d062322012-08-01 21:34:08431 ranges_ = NULL;
432 }
433 // We are going to leak the histograms and the ranges.
434}
435
bcwhite008bf4092016-02-17 22:40:35436// static
437void StatisticsRecorder::DumpHistogramsToVlog(void* instance) {
438 std::string output;
439 StatisticsRecorder::WriteGraph(std::string(), &output);
440 VLOG(1) << output;
441}
442
[email protected]34d062322012-08-01 21:34:08443
[email protected]567d30e2012-07-13 21:48:29444// static
445StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL;
446// static
simonhatchdf5a8142015-07-15 22:22:57447StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = NULL;
448// static
[email protected]567d30e2012-07-13 21:48:29449StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = NULL;
450// static
451base::Lock* StatisticsRecorder::lock_ = NULL;
[email protected]567d30e2012-07-13 21:48:29452
453} // namespace base