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
7 #include <memory>
8
9 #include "base/at_exit.h"
10 #include "base/debug/leak_annotations.h"
11 #include "base/json/string_escape.h"
12 #include "base/logging.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/metrics/histogram.h"
15 #include "base/metrics/metrics_hashes.h"
16 #include "base/metrics/persistent_histogram_allocator.h"
17 #include "base/stl_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/values.h"
20
21 namespace {
22
23 // Initialize histogram statistics gathering system.
24 base::LazyInstance<base::StatisticsRecorder>::Leaky g_statistics_recorder_ =
25 LAZY_INSTANCE_INITIALIZER;
26
HistogramNameLesser(const base::HistogramBase * a,const base::HistogramBase * b)27 bool HistogramNameLesser(const base::HistogramBase* a,
28 const base::HistogramBase* b) {
29 return a->histogram_name() < b->histogram_name();
30 }
31
32 } // namespace
33
34 namespace base {
35
HistogramIterator(const HistogramMap::iterator & iter,bool include_persistent)36 StatisticsRecorder::HistogramIterator::HistogramIterator(
37 const HistogramMap::iterator& iter, bool include_persistent)
38 : iter_(iter),
39 include_persistent_(include_persistent) {
40 // The starting location could point to a persistent histogram when such
41 // is not wanted. If so, skip it.
42 if (!include_persistent_ && iter_ != histograms_->end() &&
43 (iter_->second->flags() & HistogramBase::kIsPersistent)) {
44 // This operator will continue to skip until a non-persistent histogram
45 // is found.
46 operator++();
47 }
48 }
49
HistogramIterator(const HistogramIterator & rhs)50 StatisticsRecorder::HistogramIterator::HistogramIterator(
51 const HistogramIterator& rhs)
52 : iter_(rhs.iter_),
53 include_persistent_(rhs.include_persistent_) {
54 }
55
~HistogramIterator()56 StatisticsRecorder::HistogramIterator::~HistogramIterator() {}
57
58 StatisticsRecorder::HistogramIterator&
operator ++()59 StatisticsRecorder::HistogramIterator::operator++() {
60 const HistogramMap::iterator histograms_end = histograms_->end();
61 if (iter_ == histograms_end)
62 return *this;
63
64 base::AutoLock auto_lock(lock_.Get());
65
66 for (;;) {
67 ++iter_;
68 if (iter_ == histograms_end)
69 break;
70 if (!include_persistent_ && (iter_->second->flags() &
71 HistogramBase::kIsPersistent)) {
72 continue;
73 }
74 break;
75 }
76
77 return *this;
78 }
79
~StatisticsRecorder()80 StatisticsRecorder::~StatisticsRecorder() {
81 DCHECK(histograms_);
82 DCHECK(ranges_);
83
84 // Clean out what this object created and then restore what existed before.
85 Reset();
86 base::AutoLock auto_lock(lock_.Get());
87 histograms_ = existing_histograms_.release();
88 callbacks_ = existing_callbacks_.release();
89 ranges_ = existing_ranges_.release();
90 providers_ = existing_providers_.release();
91 }
92
93 // static
Initialize()94 void StatisticsRecorder::Initialize() {
95 // Tests sometimes create local StatisticsRecorders in order to provide a
96 // contained environment of histograms that can be later discarded. If a
97 // true global instance gets created in this environment then it will
98 // eventually get disconnected when the local instance destructs and
99 // restores the previous state, resulting in no StatisticsRecorder at all.
100 // The global lazy instance, however, will remain valid thus ensuring that
101 // another never gets installed via this method. If a |histograms_| map
102 // exists then assume the StatisticsRecorder is already "initialized".
103 if (histograms_)
104 return;
105
106 // Ensure that an instance of the StatisticsRecorder object is created.
107 g_statistics_recorder_.Get();
108 }
109
110 // static
IsActive()111 bool StatisticsRecorder::IsActive() {
112 base::AutoLock auto_lock(lock_.Get());
113 return histograms_ != nullptr;
114 }
115
116 // static
RegisterHistogramProvider(const WeakPtr<HistogramProvider> & provider)117 void StatisticsRecorder::RegisterHistogramProvider(
118 const WeakPtr<HistogramProvider>& provider) {
119 providers_->push_back(provider);
120 }
121
122 // static
RegisterOrDeleteDuplicate(HistogramBase * histogram)123 HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
124 HistogramBase* histogram) {
125 HistogramBase* histogram_to_delete = nullptr;
126 HistogramBase* histogram_to_return = nullptr;
127 {
128 base::AutoLock auto_lock(lock_.Get());
129 if (!histograms_) {
130 histogram_to_return = histogram;
131
132 // As per crbug.com/79322 the histograms are intentionally leaked, so we
133 // need to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used
134 // only once for an object, the duplicates should not be annotated.
135 // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr)
136 // twice |if (!histograms_)|.
137 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
138 } else {
139 const std::string& name = histogram->histogram_name();
140 HistogramMap::iterator it = histograms_->find(name);
141 if (histograms_->end() == it) {
142 // The StringKey references the name within |histogram| rather than
143 // making a copy.
144 (*histograms_)[name] = histogram;
145 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
146 // If there are callbacks for this histogram, we set the kCallbackExists
147 // flag.
148 auto callback_iterator = callbacks_->find(name);
149 if (callback_iterator != callbacks_->end()) {
150 if (!callback_iterator->second.is_null())
151 histogram->SetFlags(HistogramBase::kCallbackExists);
152 else
153 histogram->ClearFlags(HistogramBase::kCallbackExists);
154 }
155 histogram_to_return = histogram;
156 } else if (histogram == it->second) {
157 // The histogram was registered before.
158 histogram_to_return = histogram;
159 } else {
160 // We already have one histogram with this name.
161 DCHECK_EQ(histogram->histogram_name(),
162 it->second->histogram_name()) << "hash collision";
163 histogram_to_return = it->second;
164 histogram_to_delete = histogram;
165 }
166 }
167 }
168 delete histogram_to_delete;
169 return histogram_to_return;
170 }
171
172 // static
RegisterOrDeleteDuplicateRanges(const BucketRanges * ranges)173 const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
174 const BucketRanges* ranges) {
175 DCHECK(ranges->HasValidChecksum());
176 std::unique_ptr<const BucketRanges> ranges_deleter;
177
178 base::AutoLock auto_lock(lock_.Get());
179 if (!ranges_) {
180 ANNOTATE_LEAKING_OBJECT_PTR(ranges);
181 return ranges;
182 }
183
184 std::list<const BucketRanges*>* checksum_matching_list;
185 RangesMap::iterator ranges_it = ranges_->find(ranges->checksum());
186 if (ranges_->end() == ranges_it) {
187 // Add a new matching list to map.
188 checksum_matching_list = new std::list<const BucketRanges*>();
189 ANNOTATE_LEAKING_OBJECT_PTR(checksum_matching_list);
190 (*ranges_)[ranges->checksum()] = checksum_matching_list;
191 } else {
192 checksum_matching_list = ranges_it->second;
193 }
194
195 for (const BucketRanges* existing_ranges : *checksum_matching_list) {
196 if (existing_ranges->Equals(ranges)) {
197 if (existing_ranges == ranges) {
198 return ranges;
199 } else {
200 ranges_deleter.reset(ranges);
201 return existing_ranges;
202 }
203 }
204 }
205 // We haven't found a BucketRanges which has the same ranges. Register the
206 // new BucketRanges.
207 checksum_matching_list->push_front(ranges);
208 return ranges;
209 }
210
211 // static
WriteHTMLGraph(const std::string & query,std::string * output)212 void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
213 std::string* output) {
214 if (!IsActive())
215 return;
216
217 Histograms snapshot;
218 GetSnapshot(query, &snapshot);
219 std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser);
220 for (const HistogramBase* histogram : snapshot) {
221 histogram->WriteHTMLGraph(output);
222 output->append("<br><hr><br>");
223 }
224 }
225
226 // static
WriteGraph(const std::string & query,std::string * output)227 void StatisticsRecorder::WriteGraph(const std::string& query,
228 std::string* output) {
229 if (!IsActive())
230 return;
231 if (query.length())
232 StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
233 else
234 output->append("Collections of all histograms\n");
235
236 Histograms snapshot;
237 GetSnapshot(query, &snapshot);
238 std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser);
239 for (const HistogramBase* histogram : snapshot) {
240 histogram->WriteAscii(output);
241 output->append("\n");
242 }
243 }
244
245 // static
ToJSON(const std::string & query)246 std::string StatisticsRecorder::ToJSON(const std::string& query) {
247 if (!IsActive())
248 return std::string();
249
250 std::string output("{");
251 if (!query.empty()) {
252 output += "\"query\":";
253 EscapeJSONString(query, true, &output);
254 output += ",";
255 }
256
257 Histograms snapshot;
258 GetSnapshot(query, &snapshot);
259 output += "\"histograms\":[";
260 bool first_histogram = true;
261 for (const HistogramBase* histogram : snapshot) {
262 if (first_histogram)
263 first_histogram = false;
264 else
265 output += ",";
266 std::string json;
267 histogram->WriteJSON(&json);
268 output += json;
269 }
270 output += "]}";
271 return output;
272 }
273
274 // static
GetHistograms(Histograms * output)275 void StatisticsRecorder::GetHistograms(Histograms* output) {
276 base::AutoLock auto_lock(lock_.Get());
277 if (!histograms_)
278 return;
279
280 for (const auto& entry : *histograms_) {
281 output->push_back(entry.second);
282 }
283 }
284
285 // static
GetBucketRanges(std::vector<const BucketRanges * > * output)286 void StatisticsRecorder::GetBucketRanges(
287 std::vector<const BucketRanges*>* output) {
288 base::AutoLock auto_lock(lock_.Get());
289 if (!ranges_)
290 return;
291
292 for (const auto& entry : *ranges_) {
293 for (auto* range_entry : *entry.second) {
294 output->push_back(range_entry);
295 }
296 }
297 }
298
299 // static
FindHistogram(base::StringPiece name)300 HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) {
301 // This must be called *before* the lock is acquired below because it will
302 // call back into this object to register histograms. Those called methods
303 // will acquire the lock at that time.
304 ImportGlobalPersistentHistograms();
305
306 base::AutoLock auto_lock(lock_.Get());
307 if (!histograms_)
308 return nullptr;
309
310 HistogramMap::iterator it = histograms_->find(name);
311 if (histograms_->end() == it)
312 return nullptr;
313 return it->second;
314 }
315
316 // static
ImportProvidedHistograms()317 void StatisticsRecorder::ImportProvidedHistograms() {
318 if (!providers_)
319 return;
320
321 // Merge histogram data from each provider in turn.
322 for (const WeakPtr<HistogramProvider>& provider : *providers_) {
323 // Weak-pointer may be invalid if the provider was destructed, though they
324 // generally never are.
325 if (provider)
326 provider->MergeHistogramDeltas();
327 }
328 }
329
330 // static
begin(bool include_persistent)331 StatisticsRecorder::HistogramIterator StatisticsRecorder::begin(
332 bool include_persistent) {
333 DCHECK(histograms_);
334 ImportGlobalPersistentHistograms();
335
336 HistogramMap::iterator iter_begin;
337 {
338 base::AutoLock auto_lock(lock_.Get());
339 iter_begin = histograms_->begin();
340 }
341 return HistogramIterator(iter_begin, include_persistent);
342 }
343
344 // static
end()345 StatisticsRecorder::HistogramIterator StatisticsRecorder::end() {
346 HistogramMap::iterator iter_end;
347 {
348 base::AutoLock auto_lock(lock_.Get());
349 iter_end = histograms_->end();
350 }
351 return HistogramIterator(iter_end, true);
352 }
353
354 // static
InitLogOnShutdown()355 void StatisticsRecorder::InitLogOnShutdown() {
356 if (!histograms_)
357 return;
358
359 base::AutoLock auto_lock(lock_.Get());
360 g_statistics_recorder_.Get().InitLogOnShutdownWithoutLock();
361 }
362
363 // static
GetSnapshot(const std::string & query,Histograms * snapshot)364 void StatisticsRecorder::GetSnapshot(const std::string& query,
365 Histograms* snapshot) {
366 base::AutoLock auto_lock(lock_.Get());
367 if (!histograms_)
368 return;
369
370 ImportGlobalPersistentHistograms();
371
372 for (const auto& entry : *histograms_) {
373 if (entry.second->histogram_name().find(query) != std::string::npos)
374 snapshot->push_back(entry.second);
375 }
376 }
377
378 // static
SetCallback(const std::string & name,const StatisticsRecorder::OnSampleCallback & cb)379 bool StatisticsRecorder::SetCallback(
380 const std::string& name,
381 const StatisticsRecorder::OnSampleCallback& cb) {
382 DCHECK(!cb.is_null());
383 base::AutoLock auto_lock(lock_.Get());
384 if (!histograms_)
385 return false;
386
387 if (ContainsKey(*callbacks_, name))
388 return false;
389 callbacks_->insert(std::make_pair(name, cb));
390
391 auto it = histograms_->find(name);
392 if (it != histograms_->end())
393 it->second->SetFlags(HistogramBase::kCallbackExists);
394
395 return true;
396 }
397
398 // static
ClearCallback(const std::string & name)399 void StatisticsRecorder::ClearCallback(const std::string& name) {
400 base::AutoLock auto_lock(lock_.Get());
401 if (!histograms_)
402 return;
403
404 callbacks_->erase(name);
405
406 // We also clear the flag from the histogram (if it exists).
407 auto it = histograms_->find(name);
408 if (it != histograms_->end())
409 it->second->ClearFlags(HistogramBase::kCallbackExists);
410 }
411
412 // static
FindCallback(const std::string & name)413 StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback(
414 const std::string& name) {
415 base::AutoLock auto_lock(lock_.Get());
416 if (!histograms_)
417 return OnSampleCallback();
418
419 auto callback_iterator = callbacks_->find(name);
420 return callback_iterator != callbacks_->end() ? callback_iterator->second
421 : OnSampleCallback();
422 }
423
424 // static
GetHistogramCount()425 size_t StatisticsRecorder::GetHistogramCount() {
426 base::AutoLock auto_lock(lock_.Get());
427 if (!histograms_)
428 return 0;
429 return histograms_->size();
430 }
431
432 // static
ForgetHistogramForTesting(base::StringPiece name)433 void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) {
434 if (histograms_)
435 histograms_->erase(name);
436 }
437
438 // static
439 std::unique_ptr<StatisticsRecorder>
CreateTemporaryForTesting()440 StatisticsRecorder::CreateTemporaryForTesting() {
441 return WrapUnique(new StatisticsRecorder());
442 }
443
444 // static
UninitializeForTesting()445 void StatisticsRecorder::UninitializeForTesting() {
446 // Stop now if it's never been initialized.
447 if (!histograms_)
448 return;
449
450 // Get the global instance and destruct it. It's held in static memory so
451 // can't "delete" it; call the destructor explicitly.
452 DCHECK(g_statistics_recorder_.private_instance_);
453 g_statistics_recorder_.Get().~StatisticsRecorder();
454
455 // Now the ugly part. There's no official way to release a LazyInstance once
456 // created so it's necessary to clear out an internal variable which
457 // shouldn't be publicly visible but is for initialization reasons.
458 g_statistics_recorder_.private_instance_ = 0;
459 }
460
461 // static
ImportGlobalPersistentHistograms()462 void StatisticsRecorder::ImportGlobalPersistentHistograms() {
463 if (!histograms_)
464 return;
465
466 // Import histograms from known persistent storage. Histograms could have
467 // been added by other processes and they must be fetched and recognized
468 // locally. If the persistent memory segment is not shared between processes,
469 // this call does nothing.
470 GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
471 if (allocator)
472 allocator->ImportHistogramsToStatisticsRecorder();
473 }
474
475 // This singleton instance should be started during the single threaded portion
476 // of main(), and hence it is not thread safe. It initializes globals to
477 // provide support for all future calls.
StatisticsRecorder()478 StatisticsRecorder::StatisticsRecorder() {
479 base::AutoLock auto_lock(lock_.Get());
480
481 existing_histograms_.reset(histograms_);
482 existing_callbacks_.reset(callbacks_);
483 existing_ranges_.reset(ranges_);
484 existing_providers_.reset(providers_);
485
486 histograms_ = new HistogramMap;
487 callbacks_ = new CallbackMap;
488 ranges_ = new RangesMap;
489 providers_ = new HistogramProviders;
490
491 InitLogOnShutdownWithoutLock();
492 }
493
InitLogOnShutdownWithoutLock()494 void StatisticsRecorder::InitLogOnShutdownWithoutLock() {
495 if (!vlog_initialized_ && VLOG_IS_ON(1)) {
496 vlog_initialized_ = true;
497 AtExitManager::RegisterCallback(&DumpHistogramsToVlog, this);
498 }
499 }
500
501 // static
Reset()502 void StatisticsRecorder::Reset() {
503
504 std::unique_ptr<HistogramMap> histograms_deleter;
505 std::unique_ptr<CallbackMap> callbacks_deleter;
506 std::unique_ptr<RangesMap> ranges_deleter;
507 std::unique_ptr<HistogramProviders> providers_deleter;
508 {
509 base::AutoLock auto_lock(lock_.Get());
510 histograms_deleter.reset(histograms_);
511 callbacks_deleter.reset(callbacks_);
512 ranges_deleter.reset(ranges_);
513 providers_deleter.reset(providers_);
514 histograms_ = nullptr;
515 callbacks_ = nullptr;
516 ranges_ = nullptr;
517 providers_ = nullptr;
518 }
519 // We are going to leak the histograms and the ranges.
520 }
521
522 // static
DumpHistogramsToVlog(void * instance)523 void StatisticsRecorder::DumpHistogramsToVlog(void* instance) {
524 std::string output;
525 StatisticsRecorder::WriteGraph(std::string(), &output);
526 VLOG(1) << output;
527 }
528
529
530 // static
531 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = nullptr;
532 // static
533 StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = nullptr;
534 // static
535 StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = nullptr;
536 // static
537 StatisticsRecorder::HistogramProviders* StatisticsRecorder::providers_;
538 // static
539 base::LazyInstance<base::Lock>::Leaky StatisticsRecorder::lock_ =
540 LAZY_INSTANCE_INITIALIZER;
541
542 } // namespace base
543