Home | History | Annotate | Download | only in metrics
      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 
     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 
     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 
     50 StatisticsRecorder::HistogramIterator::HistogramIterator(
     51     const HistogramIterator& rhs)
     52     : iter_(rhs.iter_),
     53       include_persistent_(rhs.include_persistent_) {
     54 }
     55 
     56 StatisticsRecorder::HistogramIterator::~HistogramIterator() {}
     57 
     58 StatisticsRecorder::HistogramIterator&
     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 
     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
     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
    111 bool StatisticsRecorder::IsActive() {
    112   base::AutoLock auto_lock(lock_.Get());
    113   return histograms_ != nullptr;
    114 }
    115 
    116 // static
    117 void StatisticsRecorder::RegisterHistogramProvider(
    118     const WeakPtr<HistogramProvider>& provider) {
    119   providers_->push_back(provider);
    120 }
    121 
    122 // static
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    433 void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) {
    434   if (histograms_)
    435     histograms_->erase(name);
    436 }
    437 
    438 // static
    439 std::unique_ptr<StatisticsRecorder>
    440 StatisticsRecorder::CreateTemporaryForTesting() {
    441   return WrapUnique(new StatisticsRecorder());
    442 }
    443 
    444 // static
    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
    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.
    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 
    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
    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
    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