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