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 "base/compiler_specific.h"
8#include "base/pickle.h"
9
10namespace base {
11
12namespace {
13
14class SampleCountPickleIterator : public SampleCountIterator {
15 public:
16  explicit SampleCountPickleIterator(PickleIterator* iter);
17
18  bool Done() const override;
19  void Next() override;
20  void Get(HistogramBase::Sample* min,
21           HistogramBase::Sample* max,
22           HistogramBase::Count* count) const override;
23
24 private:
25  PickleIterator* const iter_;
26
27  HistogramBase::Sample min_;
28  HistogramBase::Sample max_;
29  HistogramBase::Count count_;
30  bool is_done_;
31};
32
33SampleCountPickleIterator::SampleCountPickleIterator(PickleIterator* iter)
34    : iter_(iter),
35      is_done_(false) {
36  Next();
37}
38
39bool SampleCountPickleIterator::Done() const {
40  return is_done_;
41}
42
43void SampleCountPickleIterator::Next() {
44  DCHECK(!Done());
45  if (!iter_->ReadInt(&min_) ||
46      !iter_->ReadInt(&max_) ||
47      !iter_->ReadInt(&count_))
48    is_done_ = true;
49}
50
51void SampleCountPickleIterator::Get(HistogramBase::Sample* min,
52                                    HistogramBase::Sample* max,
53                                    HistogramBase::Count* count) const {
54  DCHECK(!Done());
55  *min = min_;
56  *max = max_;
57  *count = count_;
58}
59
60}  // namespace
61
62// Don't try to delegate behavior to the constructor below that accepts a
63// Matadata pointer by passing &local_meta_. Such cannot be reliably passed
64// because it has not yet been constructed -- no member variables have; the
65// class itself is in the middle of being constructed. Using it to
66// initialize meta_ is okay because the object now exists and local_meta_
67// is before meta_ in the construction order.
68HistogramSamples::HistogramSamples(uint64_t id)
69    : meta_(&local_meta_) {
70  meta_->id = id;
71}
72
73HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta)
74    : meta_(meta) {
75  DCHECK(meta_->id == 0 || meta_->id == id);
76  meta_->id = id;
77}
78
79HistogramSamples::~HistogramSamples() {}
80
81// Despite using atomic operations, the increment/add actions below are *not*
82// atomic! Race conditions may cause loss of samples or even completely corrupt
83// the 64-bit sum on 32-bit machines. This is done intentionally to reduce the
84// cost of these operations that could be executed in performance-significant
85//  points of the code.
86//
87// TODO(bcwhite): Gather quantitative information as to the cost of using
88// proper atomic increments and improve either globally or for those histograms
89// that really need it.
90
91void HistogramSamples::Add(const HistogramSamples& other) {
92  meta_->sum += other.sum();
93
94  HistogramBase::Count old_redundant_count =
95      subtle::NoBarrier_Load(&meta_->redundant_count);
96  subtle::NoBarrier_Store(&meta_->redundant_count,
97      old_redundant_count + other.redundant_count());
98  bool success = AddSubtractImpl(other.Iterator().get(), ADD);
99  DCHECK(success);
100}
101
102bool HistogramSamples::AddFromPickle(PickleIterator* iter) {
103  int64_t sum;
104  HistogramBase::Count redundant_count;
105
106  if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count))
107    return false;
108
109  meta_->sum += sum;
110
111  HistogramBase::Count old_redundant_count =
112      subtle::NoBarrier_Load(&meta_->redundant_count);
113  subtle::NoBarrier_Store(&meta_->redundant_count,
114                          old_redundant_count + redundant_count);
115
116  SampleCountPickleIterator pickle_iter(iter);
117  return AddSubtractImpl(&pickle_iter, ADD);
118}
119
120void HistogramSamples::Subtract(const HistogramSamples& other) {
121  meta_->sum -= other.sum();
122
123  HistogramBase::Count old_redundant_count =
124      subtle::NoBarrier_Load(&meta_->redundant_count);
125  subtle::NoBarrier_Store(&meta_->redundant_count,
126                          old_redundant_count - other.redundant_count());
127  bool success = AddSubtractImpl(other.Iterator().get(), SUBTRACT);
128  DCHECK(success);
129}
130
131bool HistogramSamples::Serialize(Pickle* pickle) const {
132  if (!pickle->WriteInt64(meta_->sum))
133    return false;
134  if (!pickle->WriteInt(subtle::NoBarrier_Load(&meta_->redundant_count)))
135    return false;
136
137  HistogramBase::Sample min;
138  HistogramBase::Sample max;
139  HistogramBase::Count count;
140  for (scoped_ptr<SampleCountIterator> it = Iterator();
141       !it->Done();
142       it->Next()) {
143    it->Get(&min, &max, &count);
144    if (!pickle->WriteInt(min) ||
145        !pickle->WriteInt(max) ||
146        !pickle->WriteInt(count))
147      return false;
148  }
149  return true;
150}
151
152void HistogramSamples::IncreaseSum(int64_t diff) {
153  meta_->sum += diff;
154}
155
156void HistogramSamples::IncreaseRedundantCount(HistogramBase::Count diff) {
157  subtle::NoBarrier_Store(&meta_->redundant_count,
158      subtle::NoBarrier_Load(&meta_->redundant_count) + diff);
159}
160
161SampleCountIterator::~SampleCountIterator() {}
162
163bool SampleCountIterator::GetBucketIndex(size_t* /* index */) const {
164  DCHECK(!Done());
165  return false;
166}
167
168}  // namespace base
169