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/dummy_histogram.h"
11 #include "base/metrics/metrics_hashes.h"
12 #include "base/metrics/persistent_histogram_allocator.h"
13 #include "base/metrics/persistent_sample_map.h"
14 #include "base/metrics/sample_map.h"
15 #include "base/metrics/statistics_recorder.h"
16 #include "base/pickle.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/synchronization/lock.h"
19
20 namespace base {
21
22 typedef HistogramBase::Count Count;
23 typedef HistogramBase::Sample Sample;
24
25 // static
FactoryGet(const std::string & name,int32_t flags)26 HistogramBase* SparseHistogram::FactoryGet(const std::string& name,
27 int32_t flags) {
28 HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
29 if (!histogram) {
30 // TODO(gayane): |HashMetricName| is called again in Histogram constructor.
31 // Refactor code to avoid the additional call.
32 bool should_record =
33 StatisticsRecorder::ShouldRecordHistogram(HashMetricName(name));
34 if (!should_record)
35 return DummyHistogram::GetInstance();
36 // Try to create the histogram using a "persistent" allocator. As of
37 // 2016-02-25, the availability of such is controlled by a base::Feature
38 // that is off by default. If the allocator doesn't exist or if
39 // allocating from it fails, code below will allocate the histogram from
40 // the process heap.
41 PersistentMemoryAllocator::Reference histogram_ref = 0;
42 std::unique_ptr<HistogramBase> tentative_histogram;
43 PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
44 if (allocator) {
45 tentative_histogram = allocator->AllocateHistogram(
46 SPARSE_HISTOGRAM, name, 0, 0, nullptr, flags, &histogram_ref);
47 }
48
49 // Handle the case where no persistent allocator is present or the
50 // persistent allocation fails (perhaps because it is full).
51 if (!tentative_histogram) {
52 DCHECK(!histogram_ref); // Should never have been set.
53 DCHECK(!allocator); // Shouldn't have failed.
54 flags &= ~HistogramBase::kIsPersistent;
55 tentative_histogram.reset(new SparseHistogram(GetPermanentName(name)));
56 tentative_histogram->SetFlags(flags);
57 }
58
59 // Register this histogram with the StatisticsRecorder. Keep a copy of
60 // the pointer value to tell later whether the locally created histogram
61 // was registered or deleted. The type is "void" because it could point
62 // to released memory after the following line.
63 const void* tentative_histogram_ptr = tentative_histogram.get();
64 histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(
65 tentative_histogram.release());
66
67 // Persistent histograms need some follow-up processing.
68 if (histogram_ref) {
69 allocator->FinalizeHistogram(histogram_ref,
70 histogram == tentative_histogram_ptr);
71 }
72 }
73
74 CHECK_EQ(SPARSE_HISTOGRAM, histogram->GetHistogramType());
75 return histogram;
76 }
77
78 // static
PersistentCreate(PersistentHistogramAllocator * allocator,const char * name,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)79 std::unique_ptr<HistogramBase> SparseHistogram::PersistentCreate(
80 PersistentHistogramAllocator* allocator,
81 const char* name,
82 HistogramSamples::Metadata* meta,
83 HistogramSamples::Metadata* logged_meta) {
84 return WrapUnique(
85 new SparseHistogram(allocator, name, meta, logged_meta));
86 }
87
88 SparseHistogram::~SparseHistogram() = default;
89
name_hash() const90 uint64_t SparseHistogram::name_hash() const {
91 return unlogged_samples_->id();
92 }
93
GetHistogramType() const94 HistogramType SparseHistogram::GetHistogramType() const {
95 return SPARSE_HISTOGRAM;
96 }
97
HasConstructionArguments(Sample expected_minimum,Sample expected_maximum,uint32_t expected_bucket_count) const98 bool SparseHistogram::HasConstructionArguments(
99 Sample expected_minimum,
100 Sample expected_maximum,
101 uint32_t expected_bucket_count) const {
102 // SparseHistogram never has min/max/bucket_count limit.
103 return false;
104 }
105
Add(Sample value)106 void SparseHistogram::Add(Sample value) {
107 AddCount(value, 1);
108 }
109
AddCount(Sample value,int count)110 void SparseHistogram::AddCount(Sample value, int count) {
111 if (count <= 0) {
112 NOTREACHED();
113 return;
114 }
115 {
116 base::AutoLock auto_lock(lock_);
117 unlogged_samples_->Accumulate(value, count);
118 }
119
120 FindAndRunCallback(value);
121 }
122
SnapshotSamples() const123 std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotSamples() const {
124 std::unique_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
125
126 base::AutoLock auto_lock(lock_);
127 snapshot->Add(*unlogged_samples_);
128 snapshot->Add(*logged_samples_);
129 return std::move(snapshot);
130 }
131
SnapshotDelta()132 std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotDelta() {
133 DCHECK(!final_delta_created_);
134
135 std::unique_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
136 base::AutoLock auto_lock(lock_);
137 snapshot->Add(*unlogged_samples_);
138
139 unlogged_samples_->Subtract(*snapshot);
140 logged_samples_->Add(*snapshot);
141 return std::move(snapshot);
142 }
143
SnapshotFinalDelta() const144 std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotFinalDelta() const {
145 DCHECK(!final_delta_created_);
146 final_delta_created_ = true;
147
148 std::unique_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
149 base::AutoLock auto_lock(lock_);
150 snapshot->Add(*unlogged_samples_);
151
152 return std::move(snapshot);
153 }
154
AddSamples(const HistogramSamples & samples)155 void SparseHistogram::AddSamples(const HistogramSamples& samples) {
156 base::AutoLock auto_lock(lock_);
157 unlogged_samples_->Add(samples);
158 }
159
AddSamplesFromPickle(PickleIterator * iter)160 bool SparseHistogram::AddSamplesFromPickle(PickleIterator* iter) {
161 base::AutoLock auto_lock(lock_);
162 return unlogged_samples_->AddFromPickle(iter);
163 }
164
WriteHTMLGraph(std::string * output) const165 void SparseHistogram::WriteHTMLGraph(std::string* output) const {
166 output->append("<PRE>");
167 WriteAsciiImpl(true, "<br>", output);
168 output->append("</PRE>");
169 }
170
WriteAscii(std::string * output) const171 void SparseHistogram::WriteAscii(std::string* output) const {
172 WriteAsciiImpl(true, "\n", output);
173 }
174
SerializeInfoImpl(Pickle * pickle) const175 void SparseHistogram::SerializeInfoImpl(Pickle* pickle) const {
176 pickle->WriteString(histogram_name());
177 pickle->WriteInt(flags());
178 }
179
SparseHistogram(const char * name)180 SparseHistogram::SparseHistogram(const char* name)
181 : HistogramBase(name),
182 unlogged_samples_(new SampleMap(HashMetricName(name))),
183 logged_samples_(new SampleMap(unlogged_samples_->id())) {}
184
SparseHistogram(PersistentHistogramAllocator * allocator,const char * name,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)185 SparseHistogram::SparseHistogram(PersistentHistogramAllocator* allocator,
186 const char* name,
187 HistogramSamples::Metadata* meta,
188 HistogramSamples::Metadata* logged_meta)
189 : HistogramBase(name),
190 // While other histogram types maintain a static vector of values with
191 // sufficient space for both "active" and "logged" samples, with each
192 // SampleVector being given the appropriate half, sparse histograms
193 // have no such initial allocation. Each sample has its own record
194 // attached to a single PersistentSampleMap by a common 64-bit identifier.
195 // Since a sparse histogram has two sample maps (active and logged),
196 // there must be two sets of sample records with diffent IDs. The
197 // "active" samples use, for convenience purposes, an ID matching
198 // that of the histogram while the "logged" samples use that number
199 // plus 1.
200 unlogged_samples_(
201 new PersistentSampleMap(HashMetricName(name), allocator, meta)),
202 logged_samples_(new PersistentSampleMap(unlogged_samples_->id() + 1,
203 allocator,
204 logged_meta)) {}
205
DeserializeInfoImpl(PickleIterator * iter)206 HistogramBase* SparseHistogram::DeserializeInfoImpl(PickleIterator* iter) {
207 std::string histogram_name;
208 int flags;
209 if (!iter->ReadString(&histogram_name) || !iter->ReadInt(&flags)) {
210 DLOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name;
211 return nullptr;
212 }
213
214 flags &= ~HistogramBase::kIPCSerializationSourceFlag;
215
216 return SparseHistogram::FactoryGet(histogram_name, flags);
217 }
218
GetParameters(DictionaryValue * params) const219 void SparseHistogram::GetParameters(DictionaryValue* params) const {
220 // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
221 }
222
GetCountAndBucketData(Count * count,int64_t * sum,ListValue * buckets) const223 void SparseHistogram::GetCountAndBucketData(Count* count,
224 int64_t* sum,
225 ListValue* buckets) const {
226 // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
227 }
228
WriteAsciiImpl(bool graph_it,const std::string & newline,std::string * output) const229 void SparseHistogram::WriteAsciiImpl(bool graph_it,
230 const std::string& newline,
231 std::string* output) const {
232 // Get a local copy of the data so we are consistent.
233 std::unique_ptr<HistogramSamples> snapshot = SnapshotSamples();
234 Count total_count = snapshot->TotalCount();
235 double scaled_total_count = total_count / 100.0;
236
237 WriteAsciiHeader(total_count, output);
238 output->append(newline);
239
240 // Determine how wide the largest bucket range is (how many digits to print),
241 // so that we'll be able to right-align starts for the graphical bars.
242 // Determine which bucket has the largest sample count so that we can
243 // normalize the graphical bar-width relative to that sample count.
244 Count largest_count = 0;
245 Sample largest_sample = 0;
246 std::unique_ptr<SampleCountIterator> it = snapshot->Iterator();
247 while (!it->Done()) {
248 Sample min;
249 int64_t max;
250 Count count;
251 it->Get(&min, &max, &count);
252 if (min > largest_sample)
253 largest_sample = min;
254 if (count > largest_count)
255 largest_count = count;
256 it->Next();
257 }
258 size_t print_width = GetSimpleAsciiBucketRange(largest_sample).size() + 1;
259
260 // iterate over each item and display them
261 it = snapshot->Iterator();
262 while (!it->Done()) {
263 Sample min;
264 int64_t max;
265 Count count;
266 it->Get(&min, &max, &count);
267
268 // value is min, so display it
269 std::string range = GetSimpleAsciiBucketRange(min);
270 output->append(range);
271 for (size_t j = 0; range.size() + j < print_width + 1; ++j)
272 output->push_back(' ');
273
274 if (graph_it)
275 WriteAsciiBucketGraph(count, largest_count, output);
276 WriteAsciiBucketValue(count, scaled_total_count, output);
277 output->append(newline);
278 it->Next();
279 }
280 }
281
WriteAsciiHeader(const Count total_count,std::string * output) const282 void SparseHistogram::WriteAsciiHeader(const Count total_count,
283 std::string* output) const {
284 StringAppendF(output, "Histogram: %s recorded %d samples", histogram_name(),
285 total_count);
286 if (flags())
287 StringAppendF(output, " (flags = 0x%x)", flags());
288 }
289
290 } // namespace base
291