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 "base/at_exit.h"
      8 #include "base/json/string_escape.h"
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/metrics/histogram.h"
     12 #include "base/metrics/metrics_hashes.h"
     13 #include "base/stl_util.h"
     14 #include "base/strings/stringprintf.h"
     15 #include "base/synchronization/lock.h"
     16 #include "base/values.h"
     17 
     18 namespace {
     19 // Initialize histogram statistics gathering system.
     20 base::LazyInstance<base::StatisticsRecorder>::Leaky g_statistics_recorder_ =
     21     LAZY_INSTANCE_INITIALIZER;
     22 }  // namespace
     23 
     24 namespace base {
     25 
     26 // static
     27 void StatisticsRecorder::Initialize() {
     28   // Ensure that an instance of the StatisticsRecorder object is created.
     29   g_statistics_recorder_.Get();
     30 }
     31 
     32 // static
     33 bool StatisticsRecorder::IsActive() {
     34   if (lock_ == NULL)
     35     return false;
     36   base::AutoLock auto_lock(*lock_);
     37   return NULL != histograms_;
     38 }
     39 
     40 // static
     41 HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
     42     HistogramBase* histogram) {
     43   // As per crbug.com/79322 the histograms are intentionally leaked.
     44   if (lock_ == NULL) {
     45     return histogram;
     46   }
     47 
     48   HistogramBase* histogram_to_delete = NULL;
     49   HistogramBase* histogram_to_return = NULL;
     50   {
     51     base::AutoLock auto_lock(*lock_);
     52     if (histograms_ == NULL) {
     53       histogram_to_return = histogram;
     54     } else {
     55       const std::string& name = histogram->histogram_name();
     56       uint64_t name_hash = histogram->name_hash();
     57       HistogramMap::iterator it = histograms_->find(name_hash);
     58       if (histograms_->end() == it) {
     59         (*histograms_)[name_hash] = histogram;
     60         // If there are callbacks for this histogram, we set the kCallbackExists
     61         // flag.
     62         auto callback_iterator = callbacks_->find(name);
     63         if (callback_iterator != callbacks_->end()) {
     64           if (!callback_iterator->second.is_null())
     65             histogram->SetFlags(HistogramBase::kCallbackExists);
     66           else
     67             histogram->ClearFlags(HistogramBase::kCallbackExists);
     68         }
     69         histogram_to_return = histogram;
     70       } else if (histogram == it->second) {
     71         // The histogram was registered before.
     72         histogram_to_return = histogram;
     73       } else {
     74         // We already have one histogram with this name.
     75         DCHECK_EQ(histogram->histogram_name(),
     76                   it->second->histogram_name()) << "hash collision";
     77         histogram_to_return = it->second;
     78         histogram_to_delete = histogram;
     79       }
     80     }
     81   }
     82   delete histogram_to_delete;
     83   return histogram_to_return;
     84 }
     85 
     86 // static
     87 const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
     88     const BucketRanges* ranges) {
     89   DCHECK(ranges->HasValidChecksum());
     90   scoped_ptr<const BucketRanges> ranges_deleter;
     91 
     92   if (lock_ == NULL) {
     93     return ranges;
     94   }
     95 
     96   base::AutoLock auto_lock(*lock_);
     97   if (ranges_ == NULL) {
     98     return ranges;
     99   }
    100 
    101   std::list<const BucketRanges*>* checksum_matching_list;
    102   RangesMap::iterator ranges_it = ranges_->find(ranges->checksum());
    103   if (ranges_->end() == ranges_it) {
    104     // Add a new matching list to map.
    105     checksum_matching_list = new std::list<const BucketRanges*>();
    106     (*ranges_)[ranges->checksum()] = checksum_matching_list;
    107   } else {
    108     checksum_matching_list = ranges_it->second;
    109   }
    110 
    111   for (const BucketRanges* existing_ranges : *checksum_matching_list) {
    112     if (existing_ranges->Equals(ranges)) {
    113       if (existing_ranges == ranges) {
    114         return ranges;
    115       } else {
    116         ranges_deleter.reset(ranges);
    117         return existing_ranges;
    118       }
    119     }
    120   }
    121   // We haven't found a BucketRanges which has the same ranges. Register the
    122   // new BucketRanges.
    123   checksum_matching_list->push_front(ranges);
    124   return ranges;
    125 }
    126 
    127 // static
    128 void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
    129                                         std::string* output) {
    130   if (!IsActive())
    131     return;
    132 
    133   Histograms snapshot;
    134   GetSnapshot(query, &snapshot);
    135   for (const HistogramBase* histogram : snapshot) {
    136     histogram->WriteHTMLGraph(output);
    137     output->append("<br><hr><br>");
    138   }
    139 }
    140 
    141 // static
    142 void StatisticsRecorder::WriteGraph(const std::string& query,
    143                                     std::string* output) {
    144   if (!IsActive())
    145     return;
    146   if (query.length())
    147     StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
    148   else
    149     output->append("Collections of all histograms\n");
    150 
    151   Histograms snapshot;
    152   GetSnapshot(query, &snapshot);
    153   for (const HistogramBase* histogram : snapshot) {
    154     histogram->WriteAscii(output);
    155     output->append("\n");
    156   }
    157 }
    158 
    159 // static
    160 std::string StatisticsRecorder::ToJSON(const std::string& query) {
    161   if (!IsActive())
    162     return std::string();
    163 
    164   std::string output("{");
    165   if (!query.empty()) {
    166     output += "\"query\":";
    167     EscapeJSONString(query, true, &output);
    168     output += ",";
    169   }
    170 
    171   Histograms snapshot;
    172   GetSnapshot(query, &snapshot);
    173   output += "\"histograms\":[";
    174   bool first_histogram = true;
    175   for (const HistogramBase* histogram : snapshot) {
    176     if (first_histogram)
    177       first_histogram = false;
    178     else
    179       output += ",";
    180     std::string json;
    181     histogram->WriteJSON(&json);
    182     output += json;
    183   }
    184   output += "]}";
    185   return output;
    186 }
    187 
    188 // static
    189 void StatisticsRecorder::GetHistograms(Histograms* output) {
    190   if (lock_ == NULL)
    191     return;
    192   base::AutoLock auto_lock(*lock_);
    193   if (histograms_ == NULL)
    194     return;
    195 
    196   for (const auto& entry : *histograms_) {
    197     DCHECK_EQ(entry.first, entry.second->name_hash());
    198     output->push_back(entry.second);
    199   }
    200 }
    201 
    202 // static
    203 void StatisticsRecorder::GetBucketRanges(
    204     std::vector<const BucketRanges*>* output) {
    205   if (lock_ == NULL)
    206     return;
    207   base::AutoLock auto_lock(*lock_);
    208   if (ranges_ == NULL)
    209     return;
    210 
    211   for (const auto& entry : *ranges_) {
    212     for (const auto& range_entry : *entry.second) {
    213       output->push_back(range_entry);
    214     }
    215   }
    216 }
    217 
    218 // static
    219 HistogramBase* StatisticsRecorder::FindHistogram(const std::string& name) {
    220   if (lock_ == NULL)
    221     return NULL;
    222   base::AutoLock auto_lock(*lock_);
    223   if (histograms_ == NULL)
    224     return NULL;
    225 
    226   HistogramMap::iterator it = histograms_->find(HashMetricName(name));
    227   if (histograms_->end() == it)
    228     return NULL;
    229   DCHECK_EQ(name, it->second->histogram_name()) << "hash collision";
    230   return it->second;
    231 }
    232 
    233 // static
    234 bool StatisticsRecorder::SetCallback(
    235     const std::string& name,
    236     const StatisticsRecorder::OnSampleCallback& cb) {
    237   DCHECK(!cb.is_null());
    238   if (lock_ == NULL)
    239     return false;
    240   base::AutoLock auto_lock(*lock_);
    241   if (histograms_ == NULL)
    242     return false;
    243 
    244   if (ContainsKey(*callbacks_, name))
    245     return false;
    246   callbacks_->insert(std::make_pair(name, cb));
    247 
    248   HistogramMap::iterator it = histograms_->find(HashMetricName(name));
    249   if (it != histograms_->end()) {
    250     DCHECK_EQ(name, it->second->histogram_name()) << "hash collision";
    251     it->second->SetFlags(HistogramBase::kCallbackExists);
    252   }
    253 
    254   return true;
    255 }
    256 
    257 // static
    258 void StatisticsRecorder::ClearCallback(const std::string& name) {
    259   if (lock_ == NULL)
    260     return;
    261   base::AutoLock auto_lock(*lock_);
    262   if (histograms_ == NULL)
    263     return;
    264 
    265   callbacks_->erase(name);
    266 
    267   // We also clear the flag from the histogram (if it exists).
    268   HistogramMap::iterator it = histograms_->find(HashMetricName(name));
    269   if (it != histograms_->end()) {
    270     DCHECK_EQ(name, it->second->histogram_name()) << "hash collision";
    271     it->second->ClearFlags(HistogramBase::kCallbackExists);
    272   }
    273 }
    274 
    275 // static
    276 StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback(
    277     const std::string& name) {
    278   if (lock_ == NULL)
    279     return OnSampleCallback();
    280   base::AutoLock auto_lock(*lock_);
    281   if (histograms_ == NULL)
    282     return OnSampleCallback();
    283 
    284   auto callback_iterator = callbacks_->find(name);
    285   return callback_iterator != callbacks_->end() ? callback_iterator->second
    286                                                 : OnSampleCallback();
    287 }
    288 
    289 // private static
    290 void StatisticsRecorder::GetSnapshot(const std::string& query,
    291                                      Histograms* snapshot) {
    292   if (lock_ == NULL)
    293     return;
    294   base::AutoLock auto_lock(*lock_);
    295   if (histograms_ == NULL)
    296     return;
    297 
    298   for (const auto& entry : *histograms_) {
    299     if (entry.second->histogram_name().find(query) != std::string::npos)
    300       snapshot->push_back(entry.second);
    301   }
    302 }
    303 
    304 // This singleton instance should be started during the single threaded portion
    305 // of main(), and hence it is not thread safe.  It initializes globals to
    306 // provide support for all future calls.
    307 StatisticsRecorder::StatisticsRecorder() {
    308   DCHECK(!histograms_);
    309   if (lock_ == NULL) {
    310     // This will leak on purpose. It's the only way to make sure we won't race
    311     // against the static uninitialization of the module while one of our
    312     // static methods relying on the lock get called at an inappropriate time
    313     // during the termination phase. Since it's a static data member, we will
    314     // leak one per process, which would be similar to the instance allocated
    315     // during static initialization and released only on  process termination.
    316     lock_ = new base::Lock;
    317   }
    318   base::AutoLock auto_lock(*lock_);
    319   histograms_ = new HistogramMap;
    320   callbacks_ = new CallbackMap;
    321   ranges_ = new RangesMap;
    322 
    323   if (VLOG_IS_ON(1))
    324     AtExitManager::RegisterCallback(&DumpHistogramsToVlog, this);
    325 }
    326 
    327 // static
    328 void StatisticsRecorder::DumpHistogramsToVlog(void* /* instance */) {
    329   std::string output;
    330   StatisticsRecorder::WriteGraph(std::string(), &output);
    331   VLOG(1) << output;
    332 }
    333 
    334 StatisticsRecorder::~StatisticsRecorder() {
    335   DCHECK(histograms_ && ranges_ && lock_);
    336 
    337   // Clean up.
    338   scoped_ptr<HistogramMap> histograms_deleter;
    339   scoped_ptr<CallbackMap> callbacks_deleter;
    340   scoped_ptr<RangesMap> ranges_deleter;
    341   // We don't delete lock_ on purpose to avoid having to properly protect
    342   // against it going away after we checked for NULL in the static methods.
    343   {
    344     base::AutoLock auto_lock(*lock_);
    345     histograms_deleter.reset(histograms_);
    346     callbacks_deleter.reset(callbacks_);
    347     ranges_deleter.reset(ranges_);
    348     histograms_ = NULL;
    349     callbacks_ = NULL;
    350     ranges_ = NULL;
    351   }
    352   // We are going to leak the histograms and the ranges.
    353 }
    354 
    355 
    356 // static
    357 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL;
    358 // static
    359 StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = NULL;
    360 // static
    361 StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = NULL;
    362 // static
    363 base::Lock* StatisticsRecorder::lock_ = NULL;
    364 
    365 }  // namespace base
    366