• 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/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