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/metrics/metrics_hashes.h"
10#include "base/metrics/sample_map.h"
11#include "base/metrics/statistics_recorder.h"
12#include "base/pickle.h"
13#include "base/strings/stringprintf.h"
14#include "base/synchronization/lock.h"
15
16namespace base {
17
18typedef HistogramBase::Count Count;
19typedef HistogramBase::Sample Sample;
20
21// static
22HistogramBase* SparseHistogram::FactoryGet(const std::string& name,
23                                           int32_t flags) {
24  HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
25
26  if (!histogram) {
27    // To avoid racy destruction at shutdown, the following will be leaked.
28    HistogramBase* tentative_histogram = new SparseHistogram(name);
29    tentative_histogram->SetFlags(flags);
30    histogram =
31        StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
32  }
33  DCHECK_EQ(SPARSE_HISTOGRAM, histogram->GetHistogramType());
34  return histogram;
35}
36
37SparseHistogram::~SparseHistogram() {}
38
39uint64_t SparseHistogram::name_hash() const {
40  return samples_.id();
41}
42
43HistogramType SparseHistogram::GetHistogramType() const {
44  return SPARSE_HISTOGRAM;
45}
46
47bool SparseHistogram::HasConstructionArguments(
48    Sample /* expected_minimum */,
49    Sample /* expected_maximum */,
50    size_t /* expected_bucket_count */) const {
51  // SparseHistogram never has min/max/bucket_count limit.
52  return false;
53}
54
55void SparseHistogram::Add(Sample value) {
56  AddCount(value, 1);
57}
58
59void SparseHistogram::AddCount(Sample value, int count) {
60  if (count <= 0) {
61    NOTREACHED();
62    return;
63  }
64  {
65    base::AutoLock auto_lock(lock_);
66    samples_.Accumulate(value, count);
67  }
68
69  FindAndRunCallback(value);
70}
71
72scoped_ptr<HistogramSamples> SparseHistogram::SnapshotSamples() const {
73  scoped_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
74
75  base::AutoLock auto_lock(lock_);
76  snapshot->Add(samples_);
77  return std::move(snapshot);
78}
79
80void SparseHistogram::AddSamples(const HistogramSamples& samples) {
81  base::AutoLock auto_lock(lock_);
82  samples_.Add(samples);
83}
84
85bool SparseHistogram::AddSamplesFromPickle(PickleIterator* iter) {
86  base::AutoLock auto_lock(lock_);
87  return samples_.AddFromPickle(iter);
88}
89
90void SparseHistogram::WriteHTMLGraph(std::string* output) const {
91  output->append("<PRE>");
92  WriteAsciiImpl(true, "<br>", output);
93  output->append("</PRE>");
94}
95
96void SparseHistogram::WriteAscii(std::string* output) const {
97  WriteAsciiImpl(true, "\n", output);
98}
99
100bool SparseHistogram::SerializeInfoImpl(Pickle* pickle) const {
101  return pickle->WriteString(histogram_name()) && pickle->WriteInt(flags());
102}
103
104SparseHistogram::SparseHistogram(const std::string& name)
105    : HistogramBase(name),
106      samples_(HashMetricName(name)) {}
107
108HistogramBase* SparseHistogram::DeserializeInfoImpl(PickleIterator* iter) {
109  std::string histogram_name;
110  int flags;
111  if (!iter->ReadString(&histogram_name) || !iter->ReadInt(&flags)) {
112    DLOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name;
113    return NULL;
114  }
115
116  DCHECK(flags & HistogramBase::kIPCSerializationSourceFlag);
117  flags &= ~HistogramBase::kIPCSerializationSourceFlag;
118
119  return SparseHistogram::FactoryGet(histogram_name, flags);
120}
121
122void SparseHistogram::GetParameters(DictionaryValue* /* params */) const {
123  // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
124}
125
126void SparseHistogram::GetCountAndBucketData(Count* /* count */,
127                                            int64_t* /* sum */,
128                                            ListValue* /* buckets */) const {
129  // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
130}
131
132void SparseHistogram::WriteAsciiImpl(bool graph_it,
133                                     const std::string& newline,
134                                     std::string* output) const {
135  // Get a local copy of the data so we are consistent.
136  scoped_ptr<HistogramSamples> snapshot = SnapshotSamples();
137  Count total_count = snapshot->TotalCount();
138  double scaled_total_count = total_count / 100.0;
139
140  WriteAsciiHeader(total_count, output);
141  output->append(newline);
142
143  // Determine how wide the largest bucket range is (how many digits to print),
144  // so that we'll be able to right-align starts for the graphical bars.
145  // Determine which bucket has the largest sample count so that we can
146  // normalize the graphical bar-width relative to that sample count.
147  Count largest_count = 0;
148  Sample largest_sample = 0;
149  scoped_ptr<SampleCountIterator> it = snapshot->Iterator();
150  while (!it->Done()) {
151    Sample min;
152    Sample max;
153    Count count;
154    it->Get(&min, &max, &count);
155    if (min > largest_sample)
156      largest_sample = min;
157    if (count > largest_count)
158      largest_count = count;
159    it->Next();
160  }
161  size_t print_width = GetSimpleAsciiBucketRange(largest_sample).size() + 1;
162
163  // iterate over each item and display them
164  it = snapshot->Iterator();
165  while (!it->Done()) {
166    Sample min;
167    Sample max;
168    Count count;
169    it->Get(&min, &max, &count);
170
171    // value is min, so display it
172    std::string range = GetSimpleAsciiBucketRange(min);
173    output->append(range);
174    for (size_t j = 0; range.size() + j < print_width + 1; ++j)
175      output->push_back(' ');
176
177    if (graph_it)
178      WriteAsciiBucketGraph(count, largest_count, output);
179    WriteAsciiBucketValue(count, scaled_total_count, output);
180    output->append(newline);
181    it->Next();
182  }
183}
184
185void SparseHistogram::WriteAsciiHeader(const Count total_count,
186                                       std::string* output) const {
187  StringAppendF(output,
188                "Histogram: %s recorded %d samples",
189                histogram_name().c_str(),
190                total_count);
191  if (flags() & ~kHexRangePrintingFlag)
192    StringAppendF(output, " (flags = 0x%x)", flags() & ~kHexRangePrintingFlag);
193}
194
195}  // namespace base
196