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/sparse_histogram.h"
      6 
      7 #include <utility>
      8 
      9 #include "base/memory/ptr_util.h"
     10 #include "base/metrics/metrics_hashes.h"
     11 #include "base/metrics/persistent_histogram_allocator.h"
     12 #include "base/metrics/persistent_sample_map.h"
     13 #include "base/metrics/sample_map.h"
     14 #include "base/metrics/statistics_recorder.h"
     15 #include "base/pickle.h"
     16 #include "base/strings/stringprintf.h"
     17 #include "base/synchronization/lock.h"
     18 
     19 namespace base {
     20 
     21 typedef HistogramBase::Count Count;
     22 typedef HistogramBase::Sample Sample;
     23 
     24 // static
     25 HistogramBase* SparseHistogram::FactoryGet(const std::string& name,
     26                                            int32_t flags) {
     27   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
     28   if (!histogram) {
     29     // Try to create the histogram using a "persistent" allocator. As of
     30     // 2016-02-25, the availability of such is controlled by a base::Feature
     31     // that is off by default. If the allocator doesn't exist or if
     32     // allocating from it fails, code below will allocate the histogram from
     33     // the process heap.
     34     PersistentMemoryAllocator::Reference histogram_ref = 0;
     35     std::unique_ptr<HistogramBase> tentative_histogram;
     36     PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
     37     if (allocator) {
     38       tentative_histogram = allocator->AllocateHistogram(
     39           SPARSE_HISTOGRAM, name, 0, 0, nullptr, flags, &histogram_ref);
     40     }
     41 
     42     // Handle the case where no persistent allocator is present or the
     43     // persistent allocation fails (perhaps because it is full).
     44     if (!tentative_histogram) {
     45       DCHECK(!histogram_ref);  // Should never have been set.
     46       DCHECK(!allocator);      // Shouldn't have failed.
     47       flags &= ~HistogramBase::kIsPersistent;
     48       tentative_histogram.reset(new SparseHistogram(name));
     49       tentative_histogram->SetFlags(flags);
     50     }
     51 
     52     // Register this histogram with the StatisticsRecorder. Keep a copy of
     53     // the pointer value to tell later whether the locally created histogram
     54     // was registered or deleted. The type is "void" because it could point
     55     // to released memory after the following line.
     56     const void* tentative_histogram_ptr = tentative_histogram.get();
     57     histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(
     58         tentative_histogram.release());
     59 
     60     // Persistent histograms need some follow-up processing.
     61     if (histogram_ref) {
     62       allocator->FinalizeHistogram(histogram_ref,
     63                                    histogram == tentative_histogram_ptr);
     64     }
     65 
     66     ReportHistogramActivity(*histogram, HISTOGRAM_CREATED);
     67   } else {
     68     ReportHistogramActivity(*histogram, HISTOGRAM_LOOKUP);
     69   }
     70 
     71   CHECK_EQ(SPARSE_HISTOGRAM, histogram->GetHistogramType());
     72   return histogram;
     73 }
     74 
     75 // static
     76 std::unique_ptr<HistogramBase> SparseHistogram::PersistentCreate(
     77     PersistentHistogramAllocator* allocator,
     78     const std::string& name,
     79     HistogramSamples::Metadata* meta,
     80     HistogramSamples::Metadata* logged_meta) {
     81   return WrapUnique(
     82       new SparseHistogram(allocator, name, meta, logged_meta));
     83 }
     84 
     85 SparseHistogram::~SparseHistogram() {}
     86 
     87 uint64_t SparseHistogram::name_hash() const {
     88   return samples_->id();
     89 }
     90 
     91 HistogramType SparseHistogram::GetHistogramType() const {
     92   return SPARSE_HISTOGRAM;
     93 }
     94 
     95 bool SparseHistogram::HasConstructionArguments(
     96     Sample expected_minimum,
     97     Sample expected_maximum,
     98     uint32_t expected_bucket_count) const {
     99   // SparseHistogram never has min/max/bucket_count limit.
    100   return false;
    101 }
    102 
    103 void SparseHistogram::Add(Sample value) {
    104   AddCount(value, 1);
    105 }
    106 
    107 void SparseHistogram::AddCount(Sample value, int count) {
    108   if (count <= 0) {
    109     NOTREACHED();
    110     return;
    111   }
    112   {
    113     base::AutoLock auto_lock(lock_);
    114     samples_->Accumulate(value, count);
    115   }
    116 
    117   FindAndRunCallback(value);
    118 }
    119 
    120 std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotSamples() const {
    121   std::unique_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
    122 
    123   base::AutoLock auto_lock(lock_);
    124   snapshot->Add(*samples_);
    125   return std::move(snapshot);
    126 }
    127 
    128 std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotDelta() {
    129   DCHECK(!final_delta_created_);
    130 
    131   std::unique_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
    132   base::AutoLock auto_lock(lock_);
    133   snapshot->Add(*samples_);
    134 
    135   // Subtract what was previously logged and update that information.
    136   snapshot->Subtract(*logged_samples_);
    137   logged_samples_->Add(*snapshot);
    138   return std::move(snapshot);
    139 }
    140 
    141 std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotFinalDelta() const {
    142   DCHECK(!final_delta_created_);
    143   final_delta_created_ = true;
    144 
    145   std::unique_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
    146   base::AutoLock auto_lock(lock_);
    147   snapshot->Add(*samples_);
    148 
    149   // Subtract what was previously logged and then return.
    150   snapshot->Subtract(*logged_samples_);
    151   return std::move(snapshot);
    152 }
    153 
    154 void SparseHistogram::AddSamples(const HistogramSamples& samples) {
    155   base::AutoLock auto_lock(lock_);
    156   samples_->Add(samples);
    157 }
    158 
    159 bool SparseHistogram::AddSamplesFromPickle(PickleIterator* iter) {
    160   base::AutoLock auto_lock(lock_);
    161   return samples_->AddFromPickle(iter);
    162 }
    163 
    164 void SparseHistogram::WriteHTMLGraph(std::string* output) const {
    165   output->append("<PRE>");
    166   WriteAsciiImpl(true, "<br>", output);
    167   output->append("</PRE>");
    168 }
    169 
    170 void SparseHistogram::WriteAscii(std::string* output) const {
    171   WriteAsciiImpl(true, "\n", output);
    172 }
    173 
    174 bool SparseHistogram::SerializeInfoImpl(Pickle* pickle) const {
    175   return pickle->WriteString(histogram_name()) && pickle->WriteInt(flags());
    176 }
    177 
    178 SparseHistogram::SparseHistogram(const std::string& name)
    179     : HistogramBase(name),
    180       samples_(new SampleMap(HashMetricName(name))),
    181       logged_samples_(new SampleMap(samples_->id())) {}
    182 
    183 SparseHistogram::SparseHistogram(PersistentHistogramAllocator* allocator,
    184                                  const std::string& name,
    185                                  HistogramSamples::Metadata* meta,
    186                                  HistogramSamples::Metadata* logged_meta)
    187     : HistogramBase(name),
    188       // While other histogram types maintain a static vector of values with
    189       // sufficient space for both "active" and "logged" samples, with each
    190       // SampleVector being given the appropriate half, sparse histograms
    191       // have no such initial allocation. Each sample has its own record
    192       // attached to a single PersistentSampleMap by a common 64-bit identifier.
    193       // Since a sparse histogram has two sample maps (active and logged),
    194       // there must be two sets of sample records with diffent IDs. The
    195       // "active" samples use, for convenience purposes, an ID matching
    196       // that of the histogram while the "logged" samples use that number
    197       // plus 1.
    198       samples_(new PersistentSampleMap(HashMetricName(name), allocator, meta)),
    199       logged_samples_(
    200           new PersistentSampleMap(samples_->id() + 1, allocator, logged_meta)) {
    201 }
    202 
    203 HistogramBase* SparseHistogram::DeserializeInfoImpl(PickleIterator* iter) {
    204   std::string histogram_name;
    205   int flags;
    206   if (!iter->ReadString(&histogram_name) || !iter->ReadInt(&flags)) {
    207     DLOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name;
    208     return NULL;
    209   }
    210 
    211   flags &= ~HistogramBase::kIPCSerializationSourceFlag;
    212 
    213   return SparseHistogram::FactoryGet(histogram_name, flags);
    214 }
    215 
    216 void SparseHistogram::GetParameters(DictionaryValue* params) const {
    217   // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
    218 }
    219 
    220 void SparseHistogram::GetCountAndBucketData(Count* count,
    221                                             int64_t* sum,
    222                                             ListValue* buckets) const {
    223   // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
    224 }
    225 
    226 void SparseHistogram::WriteAsciiImpl(bool graph_it,
    227                                      const std::string& newline,
    228                                      std::string* output) const {
    229   // Get a local copy of the data so we are consistent.
    230   std::unique_ptr<HistogramSamples> snapshot = SnapshotSamples();
    231   Count total_count = snapshot->TotalCount();
    232   double scaled_total_count = total_count / 100.0;
    233 
    234   WriteAsciiHeader(total_count, output);
    235   output->append(newline);
    236 
    237   // Determine how wide the largest bucket range is (how many digits to print),
    238   // so that we'll be able to right-align starts for the graphical bars.
    239   // Determine which bucket has the largest sample count so that we can
    240   // normalize the graphical bar-width relative to that sample count.
    241   Count largest_count = 0;
    242   Sample largest_sample = 0;
    243   std::unique_ptr<SampleCountIterator> it = snapshot->Iterator();
    244   while (!it->Done()) {
    245     Sample min;
    246     Sample max;
    247     Count count;
    248     it->Get(&min, &max, &count);
    249     if (min > largest_sample)
    250       largest_sample = min;
    251     if (count > largest_count)
    252       largest_count = count;
    253     it->Next();
    254   }
    255   size_t print_width = GetSimpleAsciiBucketRange(largest_sample).size() + 1;
    256 
    257   // iterate over each item and display them
    258   it = snapshot->Iterator();
    259   while (!it->Done()) {
    260     Sample min;
    261     Sample max;
    262     Count count;
    263     it->Get(&min, &max, &count);
    264 
    265     // value is min, so display it
    266     std::string range = GetSimpleAsciiBucketRange(min);
    267     output->append(range);
    268     for (size_t j = 0; range.size() + j < print_width + 1; ++j)
    269       output->push_back(' ');
    270 
    271     if (graph_it)
    272       WriteAsciiBucketGraph(count, largest_count, output);
    273     WriteAsciiBucketValue(count, scaled_total_count, output);
    274     output->append(newline);
    275     it->Next();
    276   }
    277 }
    278 
    279 void SparseHistogram::WriteAsciiHeader(const Count total_count,
    280                                        std::string* output) const {
    281   StringAppendF(output,
    282                 "Histogram: %s recorded %d samples",
    283                 histogram_name().c_str(),
    284                 total_count);
    285   if (flags())
    286     StringAppendF(output, " (flags = 0x%x)", flags());
    287 }
    288 
    289 }  // namespace base
    290