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/histogram_snapshot_manager.h"
     16 #include "base/metrics/metrics_hashes.h"
     17 #include "base/metrics/persistent_histogram_allocator.h"
     18 #include "base/metrics/record_histogram_checker.h"
     19 #include "base/stl_util.h"
     20 #include "base/strings/stringprintf.h"
     21 #include "base/values.h"
     22 
     23 namespace base {
     24 namespace {
     25 
     26 bool HistogramNameLesser(const base::HistogramBase* a,
     27                          const base::HistogramBase* b) {
     28   return strcmp(a->histogram_name(), b->histogram_name()) < 0;
     29 }
     30 
     31 }  // namespace
     32 
     33 // static
     34 LazyInstance<Lock>::Leaky StatisticsRecorder::lock_;
     35 
     36 // static
     37 StatisticsRecorder* StatisticsRecorder::top_ = nullptr;
     38 
     39 // static
     40 bool StatisticsRecorder::is_vlog_initialized_ = false;
     41 
     42 size_t StatisticsRecorder::BucketRangesHash::operator()(
     43     const BucketRanges* const a) const {
     44   return a->checksum();
     45 }
     46 
     47 bool StatisticsRecorder::BucketRangesEqual::operator()(
     48     const BucketRanges* const a,
     49     const BucketRanges* const b) const {
     50   return a->Equals(b);
     51 }
     52 
     53 StatisticsRecorder::~StatisticsRecorder() {
     54   const AutoLock auto_lock(lock_.Get());
     55   DCHECK_EQ(this, top_);
     56   top_ = previous_;
     57 }
     58 
     59 // static
     60 void StatisticsRecorder::EnsureGlobalRecorderWhileLocked() {
     61   lock_.Get().AssertAcquired();
     62   if (top_)
     63     return;
     64 
     65   const StatisticsRecorder* const p = new StatisticsRecorder;
     66   // The global recorder is never deleted.
     67   ANNOTATE_LEAKING_OBJECT_PTR(p);
     68   DCHECK_EQ(p, top_);
     69 }
     70 
     71 // static
     72 void StatisticsRecorder::RegisterHistogramProvider(
     73     const WeakPtr<HistogramProvider>& provider) {
     74   const AutoLock auto_lock(lock_.Get());
     75   EnsureGlobalRecorderWhileLocked();
     76   top_->providers_.push_back(provider);
     77 }
     78 
     79 // static
     80 HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
     81     HistogramBase* histogram) {
     82   // Declared before |auto_lock| to ensure correct destruction order.
     83   std::unique_ptr<HistogramBase> histogram_deleter;
     84   const AutoLock auto_lock(lock_.Get());
     85   EnsureGlobalRecorderWhileLocked();
     86 
     87   const char* const name = histogram->histogram_name();
     88   HistogramBase*& registered = top_->histograms_[name];
     89 
     90   if (!registered) {
     91     // |name| is guaranteed to never change or be deallocated so long
     92     // as the histogram is alive (which is forever).
     93     registered = histogram;
     94     ANNOTATE_LEAKING_OBJECT_PTR(histogram);  // see crbug.com/79322
     95     // If there are callbacks for this histogram, we set the kCallbackExists
     96     // flag.
     97     const auto callback_iterator = top_->callbacks_.find(name);
     98     if (callback_iterator != top_->callbacks_.end()) {
     99       if (!callback_iterator->second.is_null())
    100         histogram->SetFlags(HistogramBase::kCallbackExists);
    101       else
    102         histogram->ClearFlags(HistogramBase::kCallbackExists);
    103     }
    104     return histogram;
    105   }
    106 
    107   if (histogram == registered) {
    108     // The histogram was registered before.
    109     return histogram;
    110   }
    111 
    112   // We already have one histogram with this name.
    113   histogram_deleter.reset(histogram);
    114   return registered;
    115 }
    116 
    117 // static
    118 const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
    119     const BucketRanges* ranges) {
    120   DCHECK(ranges->HasValidChecksum());
    121 
    122   // Declared before |auto_lock| to ensure correct destruction order.
    123   std::unique_ptr<const BucketRanges> ranges_deleter;
    124   const AutoLock auto_lock(lock_.Get());
    125   EnsureGlobalRecorderWhileLocked();
    126 
    127   const BucketRanges* const registered = *top_->ranges_.insert(ranges).first;
    128   if (registered == ranges) {
    129     ANNOTATE_LEAKING_OBJECT_PTR(ranges);
    130   } else {
    131     ranges_deleter.reset(ranges);
    132   }
    133 
    134   return registered;
    135 }
    136 
    137 // static
    138 void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
    139                                         std::string* output) {
    140   for (const HistogramBase* const histogram :
    141        Sort(WithName(GetHistograms(), query))) {
    142     histogram->WriteHTMLGraph(output);
    143     *output += "<br><hr><br>";
    144   }
    145 }
    146 
    147 // static
    148 void StatisticsRecorder::WriteGraph(const std::string& query,
    149                                     std::string* output) {
    150   if (query.length())
    151     StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
    152   else
    153     output->append("Collections of all histograms\n");
    154 
    155   for (const HistogramBase* const histogram :
    156        Sort(WithName(GetHistograms(), query))) {
    157     histogram->WriteAscii(output);
    158     output->append("\n");
    159   }
    160 }
    161 
    162 // static
    163 std::string StatisticsRecorder::ToJSON(JSONVerbosityLevel verbosity_level) {
    164   std::string output = "{\"histograms\":[";
    165   const char* sep = "";
    166   for (const HistogramBase* const histogram : Sort(GetHistograms())) {
    167     output += sep;
    168     sep = ",";
    169     std::string json;
    170     histogram->WriteJSON(&json, verbosity_level);
    171     output += json;
    172   }
    173   output += "]}";
    174   return output;
    175 }
    176 
    177 // static
    178 std::vector<const BucketRanges*> StatisticsRecorder::GetBucketRanges() {
    179   std::vector<const BucketRanges*> out;
    180   const AutoLock auto_lock(lock_.Get());
    181   EnsureGlobalRecorderWhileLocked();
    182   out.reserve(top_->ranges_.size());
    183   out.assign(top_->ranges_.begin(), top_->ranges_.end());
    184   return out;
    185 }
    186 
    187 // static
    188 HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) {
    189   // This must be called *before* the lock is acquired below because it will
    190   // call back into this object to register histograms. Those called methods
    191   // will acquire the lock at that time.
    192   ImportGlobalPersistentHistograms();
    193 
    194   const AutoLock auto_lock(lock_.Get());
    195   EnsureGlobalRecorderWhileLocked();
    196 
    197   const HistogramMap::const_iterator it = top_->histograms_.find(name);
    198   return it != top_->histograms_.end() ? it->second : nullptr;
    199 }
    200 
    201 // static
    202 StatisticsRecorder::HistogramProviders
    203 StatisticsRecorder::GetHistogramProviders() {
    204   const AutoLock auto_lock(lock_.Get());
    205   EnsureGlobalRecorderWhileLocked();
    206   return top_->providers_;
    207 }
    208 
    209 // static
    210 void StatisticsRecorder::ImportProvidedHistograms() {
    211   // Merge histogram data from each provider in turn.
    212   for (const WeakPtr<HistogramProvider>& provider : GetHistogramProviders()) {
    213     // Weak-pointer may be invalid if the provider was destructed, though they
    214     // generally never are.
    215     if (provider)
    216       provider->MergeHistogramDeltas();
    217   }
    218 }
    219 
    220 // static
    221 void StatisticsRecorder::PrepareDeltas(
    222     bool include_persistent,
    223     HistogramBase::Flags flags_to_set,
    224     HistogramBase::Flags required_flags,
    225     HistogramSnapshotManager* snapshot_manager) {
    226   Histograms histograms = GetHistograms();
    227   if (!include_persistent)
    228     histograms = NonPersistent(std::move(histograms));
    229   snapshot_manager->PrepareDeltas(Sort(std::move(histograms)), flags_to_set,
    230                                   required_flags);
    231 }
    232 
    233 // static
    234 void StatisticsRecorder::InitLogOnShutdown() {
    235   const AutoLock auto_lock(lock_.Get());
    236   InitLogOnShutdownWhileLocked();
    237 }
    238 
    239 // static
    240 bool StatisticsRecorder::SetCallback(
    241     const std::string& name,
    242     const StatisticsRecorder::OnSampleCallback& cb) {
    243   DCHECK(!cb.is_null());
    244   const AutoLock auto_lock(lock_.Get());
    245   EnsureGlobalRecorderWhileLocked();
    246 
    247   if (!top_->callbacks_.insert({name, cb}).second)
    248     return false;
    249 
    250   const HistogramMap::const_iterator it = top_->histograms_.find(name);
    251   if (it != top_->histograms_.end())
    252     it->second->SetFlags(HistogramBase::kCallbackExists);
    253 
    254   return true;
    255 }
    256 
    257 // static
    258 void StatisticsRecorder::ClearCallback(const std::string& name) {
    259   const AutoLock auto_lock(lock_.Get());
    260   EnsureGlobalRecorderWhileLocked();
    261 
    262   top_->callbacks_.erase(name);
    263 
    264   // We also clear the flag from the histogram (if it exists).
    265   const HistogramMap::const_iterator it = top_->histograms_.find(name);
    266   if (it != top_->histograms_.end())
    267     it->second->ClearFlags(HistogramBase::kCallbackExists);
    268 }
    269 
    270 // static
    271 StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback(
    272     const std::string& name) {
    273   const AutoLock auto_lock(lock_.Get());
    274   EnsureGlobalRecorderWhileLocked();
    275   const auto it = top_->callbacks_.find(name);
    276   return it != top_->callbacks_.end() ? it->second : OnSampleCallback();
    277 }
    278 
    279 // static
    280 size_t StatisticsRecorder::GetHistogramCount() {
    281   const AutoLock auto_lock(lock_.Get());
    282   EnsureGlobalRecorderWhileLocked();
    283   return top_->histograms_.size();
    284 }
    285 
    286 // static
    287 void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) {
    288   const AutoLock auto_lock(lock_.Get());
    289   EnsureGlobalRecorderWhileLocked();
    290 
    291   const HistogramMap::iterator found = top_->histograms_.find(name);
    292   if (found == top_->histograms_.end())
    293     return;
    294 
    295   HistogramBase* const base = found->second;
    296   if (base->GetHistogramType() != SPARSE_HISTOGRAM) {
    297     // When forgetting a histogram, it's likely that other information is
    298     // also becoming invalid. Clear the persistent reference that may no
    299     // longer be valid. There's no danger in this as, at worst, duplicates
    300     // will be created in persistent memory.
    301     static_cast<Histogram*>(base)->bucket_ranges()->set_persistent_reference(0);
    302   }
    303 
    304   top_->histograms_.erase(found);
    305 }
    306 
    307 // static
    308 std::unique_ptr<StatisticsRecorder>
    309 StatisticsRecorder::CreateTemporaryForTesting() {
    310   const AutoLock auto_lock(lock_.Get());
    311   return WrapUnique(new StatisticsRecorder());
    312 }
    313 
    314 // static
    315 void StatisticsRecorder::SetRecordChecker(
    316     std::unique_ptr<RecordHistogramChecker> record_checker) {
    317   const AutoLock auto_lock(lock_.Get());
    318   EnsureGlobalRecorderWhileLocked();
    319   top_->record_checker_ = std::move(record_checker);
    320 }
    321 
    322 // static
    323 bool StatisticsRecorder::ShouldRecordHistogram(uint64_t histogram_hash) {
    324   const AutoLock auto_lock(lock_.Get());
    325   EnsureGlobalRecorderWhileLocked();
    326   return !top_->record_checker_ ||
    327          top_->record_checker_->ShouldRecord(histogram_hash);
    328 }
    329 
    330 // static
    331 StatisticsRecorder::Histograms StatisticsRecorder::GetHistograms() {
    332   // This must be called *before* the lock is acquired below because it will
    333   // call back into this object to register histograms. Those called methods
    334   // will acquire the lock at that time.
    335   ImportGlobalPersistentHistograms();
    336 
    337   Histograms out;
    338 
    339   const AutoLock auto_lock(lock_.Get());
    340   EnsureGlobalRecorderWhileLocked();
    341 
    342   out.reserve(top_->histograms_.size());
    343   for (const auto& entry : top_->histograms_)
    344     out.push_back(entry.second);
    345 
    346   return out;
    347 }
    348 
    349 // static
    350 StatisticsRecorder::Histograms StatisticsRecorder::Sort(Histograms histograms) {
    351   std::sort(histograms.begin(), histograms.end(), &HistogramNameLesser);
    352   return histograms;
    353 }
    354 
    355 // static
    356 StatisticsRecorder::Histograms StatisticsRecorder::WithName(
    357     Histograms histograms,
    358     const std::string& query) {
    359   // Need a C-string query for comparisons against C-string histogram name.
    360   const char* const query_string = query.c_str();
    361   histograms.erase(std::remove_if(histograms.begin(), histograms.end(),
    362                                   [query_string](const HistogramBase* const h) {
    363                                     return !strstr(h->histogram_name(),
    364                                                    query_string);
    365                                   }),
    366                    histograms.end());
    367   return histograms;
    368 }
    369 
    370 // static
    371 StatisticsRecorder::Histograms StatisticsRecorder::NonPersistent(
    372     Histograms histograms) {
    373   histograms.erase(
    374       std::remove_if(histograms.begin(), histograms.end(),
    375                      [](const HistogramBase* const h) {
    376                        return (h->flags() & HistogramBase::kIsPersistent) != 0;
    377                      }),
    378       histograms.end());
    379   return histograms;
    380 }
    381 
    382 // static
    383 void StatisticsRecorder::ImportGlobalPersistentHistograms() {
    384   // Import histograms from known persistent storage. Histograms could have been
    385   // added by other processes and they must be fetched and recognized locally.
    386   // If the persistent memory segment is not shared between processes, this call
    387   // does nothing.
    388   if (GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get())
    389     allocator->ImportHistogramsToStatisticsRecorder();
    390 }
    391 
    392 // This singleton instance should be started during the single threaded portion
    393 // of main(), and hence it is not thread safe. It initializes globals to provide
    394 // support for all future calls.
    395 StatisticsRecorder::StatisticsRecorder() {
    396   lock_.Get().AssertAcquired();
    397   previous_ = top_;
    398   top_ = this;
    399   InitLogOnShutdownWhileLocked();
    400 }
    401 
    402 // static
    403 void StatisticsRecorder::InitLogOnShutdownWhileLocked() {
    404   lock_.Get().AssertAcquired();
    405   if (!is_vlog_initialized_ && VLOG_IS_ON(1)) {
    406     is_vlog_initialized_ = true;
    407     const auto dump_to_vlog = [](void*) {
    408       std::string output;
    409       WriteGraph("", &output);
    410       VLOG(1) << output;
    411     };
    412     AtExitManager::RegisterCallback(dump_to_vlog, nullptr);
    413   }
    414 }
    415 
    416 }  // namespace base
    417