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