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/histogram_base.h"
6 
7 #include <limits.h>
8 
9 #include <memory>
10 #include <set>
11 #include <utility>
12 
13 #include "base/check_op.h"
14 #include "base/json/json_string_value_serializer.h"
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/metrics/histogram_samples.h"
18 #include "base/metrics/sparse_histogram.h"
19 #include "base/metrics/statistics_recorder.h"
20 #include "base/no_destructor.h"
21 #include "base/notreached.h"
22 #include "base/numerics/safe_conversions.h"
23 #include "base/pickle.h"
24 #include "base/process/process_handle.h"
25 #include "base/rand_util.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/synchronization/lock.h"
28 #include "base/values.h"
29 
30 namespace base {
31 
HistogramTypeToString(HistogramType type)32 std::string HistogramTypeToString(HistogramType type) {
33   switch (type) {
34     case HISTOGRAM:
35       return "HISTOGRAM";
36     case LINEAR_HISTOGRAM:
37       return "LINEAR_HISTOGRAM";
38     case BOOLEAN_HISTOGRAM:
39       return "BOOLEAN_HISTOGRAM";
40     case CUSTOM_HISTOGRAM:
41       return "CUSTOM_HISTOGRAM";
42     case SPARSE_HISTOGRAM:
43       return "SPARSE_HISTOGRAM";
44     case DUMMY_HISTOGRAM:
45       return "DUMMY_HISTOGRAM";
46   }
47   NOTREACHED();
48   return "UNKNOWN";
49 }
50 
DeserializeHistogramInfo(PickleIterator * iter)51 HistogramBase* DeserializeHistogramInfo(PickleIterator* iter) {
52   int type;
53   if (!iter->ReadInt(&type))
54     return nullptr;
55 
56   switch (type) {
57     case HISTOGRAM:
58       return Histogram::DeserializeInfoImpl(iter);
59     case LINEAR_HISTOGRAM:
60       return LinearHistogram::DeserializeInfoImpl(iter);
61     case BOOLEAN_HISTOGRAM:
62       return BooleanHistogram::DeserializeInfoImpl(iter);
63     case CUSTOM_HISTOGRAM:
64       return CustomHistogram::DeserializeInfoImpl(iter);
65     case SPARSE_HISTOGRAM:
66       return SparseHistogram::DeserializeInfoImpl(iter);
67     default:
68       return nullptr;
69   }
70 }
71 
72 const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX;
73 
HistogramBase(const char * name)74 HistogramBase::HistogramBase(const char* name)
75     : histogram_name_(name), flags_(kNoFlags) {}
76 
77 HistogramBase::~HistogramBase() = default;
78 
CheckName(const StringPiece & name) const79 void HistogramBase::CheckName(const StringPiece& name) const {
80   DCHECK_EQ(StringPiece(histogram_name()), name);
81 }
82 
SetFlags(int32_t flags)83 void HistogramBase::SetFlags(int32_t flags) {
84   HistogramBase::Count old_flags = subtle::NoBarrier_Load(&flags_);
85   subtle::NoBarrier_Store(&flags_, old_flags | flags);
86 }
87 
ClearFlags(int32_t flags)88 void HistogramBase::ClearFlags(int32_t flags) {
89   HistogramBase::Count old_flags = subtle::NoBarrier_Load(&flags_);
90   subtle::NoBarrier_Store(&flags_, old_flags & ~flags);
91 }
92 
AddScaled(Sample value,int count,int scale)93 void HistogramBase::AddScaled(Sample value, int count, int scale) {
94   DCHECK_LT(0, scale);
95 
96   // Convert raw count and probabilistically round up/down if the remainder
97   // is more than a random number [0, scale). This gives a more accurate
98   // count when there are a large number of records. RandInt is "inclusive",
99   // hence the -1 for the max value.
100   int64_t count_scaled = count / scale;
101   if (count - (count_scaled * scale) > base::RandInt(0, scale - 1))
102     count_scaled += 1;
103   if (count_scaled == 0)
104     return;
105 
106   AddCount(value, count_scaled);
107 }
108 
AddKilo(Sample value,int count)109 void HistogramBase::AddKilo(Sample value, int count) {
110   AddScaled(value, count, 1000);
111 }
112 
AddKiB(Sample value,int count)113 void HistogramBase::AddKiB(Sample value, int count) {
114   AddScaled(value, count, 1024);
115 }
116 
AddTimeMillisecondsGranularity(const TimeDelta & time)117 void HistogramBase::AddTimeMillisecondsGranularity(const TimeDelta& time) {
118   Add(saturated_cast<Sample>(time.InMilliseconds()));
119 }
120 
AddTimeMicrosecondsGranularity(const TimeDelta & time)121 void HistogramBase::AddTimeMicrosecondsGranularity(const TimeDelta& time) {
122   // Intentionally drop high-resolution reports on clients with low-resolution
123   // clocks. High-resolution metrics cannot make use of low-resolution data and
124   // reporting it merely adds noise to the metric. https://crbug.com/807615#c16
125   if (TimeTicks::IsHighResolution())
126     Add(saturated_cast<Sample>(time.InMicroseconds()));
127 }
128 
AddBoolean(bool value)129 void HistogramBase::AddBoolean(bool value) {
130   Add(value ? 1 : 0);
131 }
132 
SerializeInfo(Pickle * pickle) const133 void HistogramBase::SerializeInfo(Pickle* pickle) const {
134   pickle->WriteInt(GetHistogramType());
135   SerializeInfoImpl(pickle);
136 }
137 
FindCorruption(const HistogramSamples & samples) const138 uint32_t HistogramBase::FindCorruption(const HistogramSamples& samples) const {
139   // Not supported by default.
140   return NO_INCONSISTENCIES;
141 }
142 
ValidateHistogramContents() const143 void HistogramBase::ValidateHistogramContents() const {}
144 
WriteJSON(std::string * output,JSONVerbosityLevel verbosity_level) const145 void HistogramBase::WriteJSON(std::string* output,
146                               JSONVerbosityLevel verbosity_level) const {
147   Count count = 0;
148   int64_t sum = 0;
149   std::unique_ptr<ListValue> buckets(new ListValue());
150   GetCountAndBucketData(&count, &sum, buckets.get());
151   std::unique_ptr<DictionaryValue> parameters(new DictionaryValue());
152   GetParameters(parameters.get());
153 
154   JSONStringValueSerializer serializer(output);
155   DictionaryValue root;
156   root.SetStringKey("name", histogram_name());
157   root.SetIntKey("count", count);
158   root.SetDoubleKey("sum", static_cast<double>(sum));
159   root.SetIntKey("flags", flags());
160   root.Set("params", std::move(parameters));
161   if (verbosity_level != JSON_VERBOSITY_LEVEL_OMIT_BUCKETS)
162     root.Set("buckets", std::move(buckets));
163   root.SetIntKey("pid", GetUniqueIdForProcess().GetUnsafeValue());
164   serializer.Serialize(root);
165 }
166 
FindAndRunCallback(HistogramBase::Sample sample) const167 void HistogramBase::FindAndRunCallback(HistogramBase::Sample sample) const {
168   StatisticsRecorder::GlobalSampleCallback global_sample_callback =
169       StatisticsRecorder::global_sample_callback();
170   if (global_sample_callback)
171     global_sample_callback(histogram_name(), name_hash(), sample);
172 
173   if ((flags() & kCallbackExists) == 0)
174     return;
175 
176   StatisticsRecorder::OnSampleCallback cb =
177       StatisticsRecorder::FindCallback(histogram_name());
178   if (!cb.is_null())
179     cb.Run(histogram_name(), name_hash(), sample);
180 }
181 
GetCountAndBucketData(Count * count,int64_t * sum,ListValue * buckets) const182 void HistogramBase::GetCountAndBucketData(Count* count,
183                                           int64_t* sum,
184                                           ListValue* buckets) const {
185   std::unique_ptr<HistogramSamples> snapshot = SnapshotSamples();
186   *count = snapshot->TotalCount();
187   *sum = snapshot->sum();
188   std::unique_ptr<SampleCountIterator> it = snapshot->Iterator();
189   uint32_t index = 0;
190   while (!it->Done()) {
191     std::unique_ptr<DictionaryValue> bucket_value(new DictionaryValue());
192     Sample bucket_min;
193     int64_t bucket_max;
194     Count bucket_count;
195     it->Get(&bucket_min, &bucket_max, &bucket_count);
196 
197     bucket_value->SetIntKey("low", bucket_min);
198     bucket_value->SetIntKey("high", bucket_max);
199     bucket_value->SetIntKey("count", bucket_count);
200     buckets->Set(index, std::move(bucket_value));
201     it->Next();
202     ++index;
203   }
204 }
205 
WriteAsciiBucketGraph(double current_size,double max_size,std::string * output) const206 void HistogramBase::WriteAsciiBucketGraph(double current_size,
207                                           double max_size,
208                                           std::string* output) const {
209   const int k_line_length = 72;  // Maximal horizontal width of graph.
210   int x_count = static_cast<int>(k_line_length * (current_size / max_size)
211                                  + 0.5);
212   int x_remainder = k_line_length - x_count;
213 
214   while (0 < x_count--)
215     output->append("-");
216   output->append("O");
217   while (0 < x_remainder--)
218     output->append(" ");
219 }
220 
GetSimpleAsciiBucketRange(Sample sample) const221 const std::string HistogramBase::GetSimpleAsciiBucketRange(
222     Sample sample) const {
223   return StringPrintf("%d", sample);
224 }
225 
WriteAsciiBucketValue(Count current,double scaled_sum,std::string * output) const226 void HistogramBase::WriteAsciiBucketValue(Count current,
227                                           double scaled_sum,
228                                           std::string* output) const {
229   StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum);
230 }
231 
232 // static
GetPermanentName(const std::string & name)233 char const* HistogramBase::GetPermanentName(const std::string& name) {
234   // A set of histogram names that provides the "permanent" lifetime required
235   // by histogram objects for those strings that are not already code constants
236   // or held in persistent memory.
237   static base::NoDestructor<std::set<std::string>> permanent_names;
238   static base::NoDestructor<Lock> permanent_names_lock;
239 
240   AutoLock lock(*permanent_names_lock);
241   auto result = permanent_names->insert(name);
242   return result.first->c_str();
243 }
244 
245 }  // namespace base
246