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/histogram_samples.h"
      6 
      7 #include <limits>
      8 
      9 #include "base/compiler_specific.h"
     10 #include "base/metrics/histogram_functions.h"
     11 #include "base/metrics/histogram_macros.h"
     12 #include "base/numerics/safe_conversions.h"
     13 #include "base/numerics/safe_math.h"
     14 #include "base/pickle.h"
     15 
     16 namespace base {
     17 
     18 namespace {
     19 
     20 // A shorthand constant for the max value of size_t.
     21 constexpr size_t kSizeMax = std::numeric_limits<size_t>::max();
     22 
     23 // A constant stored in an AtomicSingleSample (as_atomic) to indicate that the
     24 // sample is "disabled" and no further accumulation should be done with it. The
     25 // value is chosen such that it will be MAX_UINT16 for both |bucket| & |count|,
     26 // and thus less likely to conflict with real use. Conflicts are explicitly
     27 // handled in the code but it's worth making them as unlikely as possible.
     28 constexpr int32_t kDisabledSingleSample = -1;
     29 
     30 class SampleCountPickleIterator : public SampleCountIterator {
     31  public:
     32   explicit SampleCountPickleIterator(PickleIterator* iter);
     33 
     34   bool Done() const override;
     35   void Next() override;
     36   void Get(HistogramBase::Sample* min,
     37            int64_t* max,
     38            HistogramBase::Count* count) const override;
     39 
     40  private:
     41   PickleIterator* const iter_;
     42 
     43   HistogramBase::Sample min_;
     44   int64_t max_;
     45   HistogramBase::Count count_;
     46   bool is_done_;
     47 };
     48 
     49 SampleCountPickleIterator::SampleCountPickleIterator(PickleIterator* iter)
     50     : iter_(iter),
     51       is_done_(false) {
     52   Next();
     53 }
     54 
     55 bool SampleCountPickleIterator::Done() const {
     56   return is_done_;
     57 }
     58 
     59 void SampleCountPickleIterator::Next() {
     60   DCHECK(!Done());
     61   if (!iter_->ReadInt(&min_) || !iter_->ReadInt64(&max_) ||
     62       !iter_->ReadInt(&count_)) {
     63     is_done_ = true;
     64   }
     65 }
     66 
     67 void SampleCountPickleIterator::Get(HistogramBase::Sample* min,
     68                                     int64_t* max,
     69                                     HistogramBase::Count* count) const {
     70   DCHECK(!Done());
     71   *min = min_;
     72   *max = max_;
     73   *count = count_;
     74 }
     75 
     76 }  // namespace
     77 
     78 static_assert(sizeof(HistogramSamples::AtomicSingleSample) ==
     79                   sizeof(subtle::Atomic32),
     80               "AtomicSingleSample isn't 32 bits");
     81 
     82 HistogramSamples::SingleSample HistogramSamples::AtomicSingleSample::Load()
     83     const {
     84   AtomicSingleSample single_sample = subtle::Acquire_Load(&as_atomic);
     85 
     86   // If the sample was extracted/disabled, it's still zero to the outside.
     87   if (single_sample.as_atomic == kDisabledSingleSample)
     88     single_sample.as_atomic = 0;
     89 
     90   return single_sample.as_parts;
     91 }
     92 
     93 HistogramSamples::SingleSample HistogramSamples::AtomicSingleSample::Extract(
     94     bool disable) {
     95   AtomicSingleSample single_sample = subtle::NoBarrier_AtomicExchange(
     96       &as_atomic, disable ? kDisabledSingleSample : 0);
     97   if (single_sample.as_atomic == kDisabledSingleSample)
     98     single_sample.as_atomic = 0;
     99   return single_sample.as_parts;
    100 }
    101 
    102 bool HistogramSamples::AtomicSingleSample::Accumulate(
    103     size_t bucket,
    104     HistogramBase::Count count) {
    105   if (count == 0)
    106     return true;
    107 
    108   // Convert the parameters to 16-bit variables because it's all 16-bit below.
    109   // To support decrements/subtractions, divide the |count| into sign/value and
    110   // do the proper operation below. The alternative is to change the single-
    111   // sample's count to be a signed integer (int16_t) and just add an int16_t
    112   // |count16| but that is somewhat wasteful given that the single-sample is
    113   // never expected to have a count less than zero.
    114   if (count < -std::numeric_limits<uint16_t>::max() ||
    115       count > std::numeric_limits<uint16_t>::max() ||
    116       bucket > std::numeric_limits<uint16_t>::max()) {
    117     return false;
    118   }
    119   bool count_is_negative = count < 0;
    120   uint16_t count16 = static_cast<uint16_t>(count_is_negative ? -count : count);
    121   uint16_t bucket16 = static_cast<uint16_t>(bucket);
    122 
    123   // A local, unshared copy of the single-sample is necessary so the parts
    124   // can be manipulated without worrying about atomicity.
    125   AtomicSingleSample single_sample;
    126 
    127   bool sample_updated;
    128   do {
    129     subtle::Atomic32 original = subtle::Acquire_Load(&as_atomic);
    130     if (original == kDisabledSingleSample)
    131       return false;
    132     single_sample.as_atomic = original;
    133     if (single_sample.as_atomic != 0) {
    134       // Only the same bucket (parameter and stored) can be counted multiple
    135       // times.
    136       if (single_sample.as_parts.bucket != bucket16)
    137         return false;
    138     } else {
    139       // The |single_ sample| was zero so becomes the |bucket| parameter, the
    140       // contents of which were checked above to fit in 16 bits.
    141       single_sample.as_parts.bucket = bucket16;
    142     }
    143 
    144     // Update count, making sure that it doesn't overflow.
    145     CheckedNumeric<uint16_t> new_count(single_sample.as_parts.count);
    146     if (count_is_negative)
    147       new_count -= count16;
    148     else
    149       new_count += count16;
    150     if (!new_count.AssignIfValid(&single_sample.as_parts.count))
    151       return false;
    152 
    153     // Don't let this become equivalent to the "disabled" value.
    154     if (single_sample.as_atomic == kDisabledSingleSample)
    155       return false;
    156 
    157     // Store the updated single-sample back into memory. |existing| is what
    158     // was in that memory location at the time of the call; if it doesn't
    159     // match |original| then the swap didn't happen so loop again.
    160     subtle::Atomic32 existing = subtle::Release_CompareAndSwap(
    161         &as_atomic, original, single_sample.as_atomic);
    162     sample_updated = (existing == original);
    163   } while (!sample_updated);
    164 
    165   return true;
    166 }
    167 
    168 bool HistogramSamples::AtomicSingleSample::IsDisabled() const {
    169   return subtle::Acquire_Load(&as_atomic) == kDisabledSingleSample;
    170 }
    171 
    172 HistogramSamples::LocalMetadata::LocalMetadata() {
    173   // This is the same way it's done for persistent metadata since no ctor
    174   // is called for the data members in that case.
    175   memset(this, 0, sizeof(*this));
    176 }
    177 
    178 HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta)
    179     : meta_(meta) {
    180   DCHECK(meta_->id == 0 || meta_->id == id);
    181 
    182   // It's possible that |meta| is contained in initialized, read-only memory
    183   // so it's essential that no write be done in that case.
    184   if (!meta_->id)
    185     meta_->id = id;
    186 }
    187 
    188 // This mustn't do anything with |meta_|. It was passed to the ctor and may
    189 // be invalid by the time this dtor gets called.
    190 HistogramSamples::~HistogramSamples() = default;
    191 
    192 void HistogramSamples::Add(const HistogramSamples& other) {
    193   IncreaseSumAndCount(other.sum(), other.redundant_count());
    194   std::unique_ptr<SampleCountIterator> it = other.Iterator();
    195   bool success = AddSubtractImpl(it.get(), ADD);
    196   DCHECK(success);
    197 }
    198 
    199 bool HistogramSamples::AddFromPickle(PickleIterator* iter) {
    200   int64_t sum;
    201   HistogramBase::Count redundant_count;
    202 
    203   if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count))
    204     return false;
    205 
    206   IncreaseSumAndCount(sum, redundant_count);
    207 
    208   SampleCountPickleIterator pickle_iter(iter);
    209   return AddSubtractImpl(&pickle_iter, ADD);
    210 }
    211 
    212 void HistogramSamples::Subtract(const HistogramSamples& other) {
    213   IncreaseSumAndCount(-other.sum(), -other.redundant_count());
    214   std::unique_ptr<SampleCountIterator> it = other.Iterator();
    215   bool success = AddSubtractImpl(it.get(), SUBTRACT);
    216   DCHECK(success);
    217 }
    218 
    219 void HistogramSamples::Serialize(Pickle* pickle) const {
    220   pickle->WriteInt64(sum());
    221   pickle->WriteInt(redundant_count());
    222 
    223   HistogramBase::Sample min;
    224   int64_t max;
    225   HistogramBase::Count count;
    226   for (std::unique_ptr<SampleCountIterator> it = Iterator(); !it->Done();
    227        it->Next()) {
    228     it->Get(&min, &max, &count);
    229     pickle->WriteInt(min);
    230     pickle->WriteInt64(max);
    231     pickle->WriteInt(count);
    232   }
    233 }
    234 
    235 bool HistogramSamples::AccumulateSingleSample(HistogramBase::Sample value,
    236                                               HistogramBase::Count count,
    237                                               size_t bucket) {
    238   if (single_sample().Accumulate(bucket, count)) {
    239     // Success. Update the (separate) sum and redundant-count.
    240     IncreaseSumAndCount(strict_cast<int64_t>(value) * count, count);
    241     return true;
    242   }
    243   return false;
    244 }
    245 
    246 void HistogramSamples::IncreaseSumAndCount(int64_t sum,
    247                                            HistogramBase::Count count) {
    248 #ifdef ARCH_CPU_64_BITS
    249   subtle::NoBarrier_AtomicIncrement(&meta_->sum, sum);
    250 #else
    251   meta_->sum += sum;
    252 #endif
    253   subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, count);
    254 }
    255 
    256 void HistogramSamples::RecordNegativeSample(NegativeSampleReason reason,
    257                                             HistogramBase::Count increment) {
    258   UMA_HISTOGRAM_ENUMERATION("UMA.NegativeSamples.Reason", reason,
    259                             MAX_NEGATIVE_SAMPLE_REASONS);
    260   UMA_HISTOGRAM_CUSTOM_COUNTS("UMA.NegativeSamples.Increment", increment, 1,
    261                               1 << 30, 100);
    262   UmaHistogramSparse("UMA.NegativeSamples.Histogram",
    263                      static_cast<int32_t>(id()));
    264 }
    265 
    266 SampleCountIterator::~SampleCountIterator() = default;
    267 
    268 bool SampleCountIterator::GetBucketIndex(size_t* index) const {
    269   DCHECK(!Done());
    270   return false;
    271 }
    272 
    273 SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min,
    274                                            int64_t max,
    275                                            HistogramBase::Count count)
    276     : SingleSampleIterator(min, max, count, kSizeMax) {}
    277 
    278 SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min,
    279                                            int64_t max,
    280                                            HistogramBase::Count count,
    281                                            size_t bucket_index)
    282     : min_(min), max_(max), bucket_index_(bucket_index), count_(count) {}
    283 
    284 SingleSampleIterator::~SingleSampleIterator() = default;
    285 
    286 bool SingleSampleIterator::Done() const {
    287   return count_ == 0;
    288 }
    289 
    290 void SingleSampleIterator::Next() {
    291   DCHECK(!Done());
    292   count_ = 0;
    293 }
    294 
    295 void SingleSampleIterator::Get(HistogramBase::Sample* min,
    296                                int64_t* max,
    297                                HistogramBase::Count* count) const {
    298   DCHECK(!Done());
    299   if (min != nullptr)
    300     *min = min_;
    301   if (max != nullptr)
    302     *max = max_;
    303   if (count != nullptr)
    304     *count = count_;
    305 }
    306 
    307 bool SingleSampleIterator::GetBucketIndex(size_t* index) const {
    308   DCHECK(!Done());
    309   if (bucket_index_ == kSizeMax)
    310     return false;
    311   *index = bucket_index_;
    312   return true;
    313 }
    314 
    315 }  // namespace base
    316