• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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