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