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
19 namespace base {
20
21 typedef HistogramBase::Count Count;
22 typedef HistogramBase::Sample Sample;
23
24 // static
FactoryGet(const std::string & name,int32_t flags)25 HistogramBase* 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
PersistentCreate(PersistentHistogramAllocator * allocator,const std::string & name,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)76 std::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
~SparseHistogram()85 SparseHistogram::~SparseHistogram() {}
86
name_hash() const87 uint64_t SparseHistogram::name_hash() const {
88 return samples_->id();
89 }
90
GetHistogramType() const91 HistogramType SparseHistogram::GetHistogramType() const {
92 return SPARSE_HISTOGRAM;
93 }
94
HasConstructionArguments(Sample,Sample,uint32_t) const95 bool 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
Add(Sample value)103 void SparseHistogram::Add(Sample value) {
104 AddCount(value, 1);
105 }
106
AddCount(Sample value,int count)107 void 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
SnapshotSamples() const120 std::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
SnapshotDelta()128 std::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
SnapshotFinalDelta() const141 std::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
AddSamples(const HistogramSamples & samples)154 void SparseHistogram::AddSamples(const HistogramSamples& samples) {
155 base::AutoLock auto_lock(lock_);
156 samples_->Add(samples);
157 }
158
AddSamplesFromPickle(PickleIterator * iter)159 bool SparseHistogram::AddSamplesFromPickle(PickleIterator* iter) {
160 base::AutoLock auto_lock(lock_);
161 return samples_->AddFromPickle(iter);
162 }
163
WriteHTMLGraph(std::string * output) const164 void SparseHistogram::WriteHTMLGraph(std::string* output) const {
165 output->append("<PRE>");
166 WriteAsciiImpl(true, "<br>", output);
167 output->append("</PRE>");
168 }
169
WriteAscii(std::string * output) const170 void SparseHistogram::WriteAscii(std::string* output) const {
171 WriteAsciiImpl(true, "\n", output);
172 }
173
SerializeInfoImpl(Pickle * pickle) const174 bool SparseHistogram::SerializeInfoImpl(Pickle* pickle) const {
175 return pickle->WriteString(histogram_name()) && pickle->WriteInt(flags());
176 }
177
SparseHistogram(const std::string & name)178 SparseHistogram::SparseHistogram(const std::string& name)
179 : HistogramBase(name),
180 samples_(new SampleMap(HashMetricName(name))),
181 logged_samples_(new SampleMap(samples_->id())) {}
182
SparseHistogram(PersistentHistogramAllocator * allocator,const std::string & name,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)183 SparseHistogram::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
DeserializeInfoImpl(PickleIterator * iter)203 HistogramBase* 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
GetParameters(DictionaryValue *) const216 void SparseHistogram::GetParameters(DictionaryValue* /*params*/) const {
217 // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
218 }
219
GetCountAndBucketData(Count *,int64_t *,ListValue *) const220 void SparseHistogram::GetCountAndBucketData(Count* /*count*/,
221 int64_t* /*sum*/,
222 ListValue* /*buckets*/) const {
223 // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
224 }
225
WriteAsciiImpl(bool graph_it,const std::string & newline,std::string * output) const226 void 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
WriteAsciiHeader(const Count total_count,std::string * output) const279 void 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