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
19namespace base {
20
21typedef HistogramBase::Count Count;
22typedef HistogramBase::Sample Sample;
23
24// static
25HistogramBase* 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  DCHECK_EQ(SPARSE_HISTOGRAM, histogram->GetHistogramType());
72  return histogram;
73}
74
75// static
76std::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
85SparseHistogram::~SparseHistogram() {}
86
87uint64_t SparseHistogram::name_hash() const {
88  return samples_->id();
89}
90
91HistogramType SparseHistogram::GetHistogramType() const {
92  return SPARSE_HISTOGRAM;
93}
94
95bool 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
103void SparseHistogram::Add(Sample value) {
104  AddCount(value, 1);
105}
106
107void 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
120std::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
128std::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
141std::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
154void SparseHistogram::AddSamples(const HistogramSamples& samples) {
155  base::AutoLock auto_lock(lock_);
156  samples_->Add(samples);
157}
158
159bool SparseHistogram::AddSamplesFromPickle(PickleIterator* iter) {
160  base::AutoLock auto_lock(lock_);
161  return samples_->AddFromPickle(iter);
162}
163
164void SparseHistogram::WriteHTMLGraph(std::string* output) const {
165  output->append("<PRE>");
166  WriteAsciiImpl(true, "<br>", output);
167  output->append("</PRE>");
168}
169
170void SparseHistogram::WriteAscii(std::string* output) const {
171  WriteAsciiImpl(true, "\n", output);
172}
173
174bool SparseHistogram::SerializeInfoImpl(Pickle* pickle) const {
175  return pickle->WriteString(histogram_name()) && pickle->WriteInt(flags());
176}
177
178SparseHistogram::SparseHistogram(const std::string& name)
179    : HistogramBase(name),
180      samples_(new SampleMap(HashMetricName(name))),
181      logged_samples_(new SampleMap(samples_->id())) {}
182
183SparseHistogram::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
203HistogramBase* 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
216void SparseHistogram::GetParameters(DictionaryValue* /*params*/) const {
217  // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
218}
219
220void SparseHistogram::GetCountAndBucketData(Count* /*count*/,
221                                            int64_t* /*sum*/,
222                                            ListValue* /*buckets*/) const {
223  // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
224}
225
226void 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
279void 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() & ~kHexRangePrintingFlag)
286    StringAppendF(output, " (flags = 0x%x)", flags() & ~kHexRangePrintingFlag);
287}
288
289}  // namespace base
290