• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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 <string_view>
12 #include <utility>
13 
14 #include "base/check_op.h"
15 #include "base/json/json_string_value_serializer.h"
16 #include "base/metrics/histogram.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/metrics/histogram_samples.h"
19 #include "base/metrics/sparse_histogram.h"
20 #include "base/metrics/statistics_recorder.h"
21 #include "base/no_destructor.h"
22 #include "base/notreached.h"
23 #include "base/numerics/safe_conversions.h"
24 #include "base/pickle.h"
25 #include "base/process/process_handle.h"
26 #include "base/rand_util.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/synchronization/lock.h"
29 #include "base/values.h"
30 
31 namespace base {
32 
HistogramTypeToString(HistogramType type)33 std::string HistogramTypeToString(HistogramType type) {
34   switch (type) {
35     case HISTOGRAM:
36       return "HISTOGRAM";
37     case LINEAR_HISTOGRAM:
38       return "LINEAR_HISTOGRAM";
39     case BOOLEAN_HISTOGRAM:
40       return "BOOLEAN_HISTOGRAM";
41     case CUSTOM_HISTOGRAM:
42       return "CUSTOM_HISTOGRAM";
43     case SPARSE_HISTOGRAM:
44       return "SPARSE_HISTOGRAM";
45     case DUMMY_HISTOGRAM:
46       return "DUMMY_HISTOGRAM";
47   }
48   NOTREACHED();
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 
CountAndBucketData(Count count,int64_t sum,Value::List buckets)72 HistogramBase::CountAndBucketData::CountAndBucketData(Count count,
73                                                       int64_t sum,
74                                                       Value::List buckets)
75     : count(count), sum(sum), buckets(std::move(buckets)) {}
76 
77 HistogramBase::CountAndBucketData::~CountAndBucketData() = default;
78 
79 HistogramBase::CountAndBucketData::CountAndBucketData(
80     CountAndBucketData&& other) = default;
81 
82 HistogramBase::CountAndBucketData& HistogramBase::CountAndBucketData::operator=(
83     CountAndBucketData&& other) = default;
84 
85 const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX;
86 
HistogramBase(const char * name)87 HistogramBase::HistogramBase(const char* name)
88     : histogram_name_(name), flags_(kNoFlags) {}
89 
90 HistogramBase::~HistogramBase() = default;
91 
CheckName(std::string_view name) const92 void HistogramBase::CheckName(std::string_view name) const {
93   DCHECK_EQ(std::string_view(histogram_name()), name)
94       << "Provided histogram name doesn't match instance name. Are you using a "
95          "dynamic string in a macro?";
96 }
97 
SetFlags(int32_t flags)98 void HistogramBase::SetFlags(int32_t flags) {
99   flags_.fetch_or(flags, std::memory_order_relaxed);
100 }
101 
ClearFlags(int32_t flags)102 void HistogramBase::ClearFlags(int32_t flags) {
103   flags_.fetch_and(~flags, std::memory_order_relaxed);
104 }
105 
HasFlags(int32_t flags) const106 bool HistogramBase::HasFlags(int32_t flags) const {
107   // Check this->flags() is a superset of |flags|, i.e. every flag in |flags| is
108   // included.
109   return (this->flags() & flags) == flags;
110 }
111 
AddScaled(Sample value,int count,int scale)112 void HistogramBase::AddScaled(Sample value, int count, int scale) {
113   DCHECK_GT(scale, 0);
114 
115   // Convert raw count and probabilistically round up/down if the remainder
116   // is more than a random number [0, scale). This gives a more accurate
117   // count when there are a large number of records. RandInt is "inclusive",
118   // hence the -1 for the max value.
119   int count_scaled = count / scale;
120   if (count - (count_scaled * scale) > base::RandInt(0, scale - 1))
121     ++count_scaled;
122   if (count_scaled <= 0)
123     return;
124 
125   AddCount(value, count_scaled);
126 }
127 
AddKilo(Sample value,int count)128 void HistogramBase::AddKilo(Sample value, int count) {
129   AddScaled(value, count, 1000);
130 }
131 
AddKiB(Sample value,int count)132 void HistogramBase::AddKiB(Sample value, int count) {
133   AddScaled(value, count, 1024);
134 }
135 
AddTimeMillisecondsGranularity(const TimeDelta & time)136 void HistogramBase::AddTimeMillisecondsGranularity(const TimeDelta& time) {
137   Add(saturated_cast<Sample>(time.InMilliseconds()));
138 }
139 
AddTimeMicrosecondsGranularity(const TimeDelta & time)140 void HistogramBase::AddTimeMicrosecondsGranularity(const TimeDelta& time) {
141   // Intentionally drop high-resolution reports on clients with low-resolution
142   // clocks. High-resolution metrics cannot make use of low-resolution data and
143   // reporting it merely adds noise to the metric. https://crbug.com/807615#c16
144   if (TimeTicks::IsHighResolution())
145     Add(saturated_cast<Sample>(time.InMicroseconds()));
146 }
147 
AddBoolean(bool value)148 void HistogramBase::AddBoolean(bool value) {
149   Add(value ? 1 : 0);
150 }
151 
SerializeInfo(Pickle * pickle) const152 void HistogramBase::SerializeInfo(Pickle* pickle) const {
153   pickle->WriteInt(GetHistogramType());
154   SerializeInfoImpl(pickle);
155 }
156 
FindCorruption(const HistogramSamples & samples) const157 uint32_t HistogramBase::FindCorruption(const HistogramSamples& samples) const {
158   // Not supported by default.
159   return NO_INCONSISTENCIES;
160 }
161 
WriteJSON(std::string * output,JSONVerbosityLevel verbosity_level) const162 void HistogramBase::WriteJSON(std::string* output,
163                               JSONVerbosityLevel verbosity_level) const {
164   CountAndBucketData count_and_bucket_data = GetCountAndBucketData();
165   Value::Dict parameters = GetParameters();
166 
167   JSONStringValueSerializer serializer(output);
168   Value::Dict root;
169   root.Set("name", histogram_name());
170   root.Set("count", count_and_bucket_data.count);
171   root.Set("sum", static_cast<double>(count_and_bucket_data.sum));
172   root.Set("flags", flags());
173   root.Set("params", std::move(parameters));
174   if (verbosity_level != JSON_VERBOSITY_LEVEL_OMIT_BUCKETS)
175     root.Set("buckets", std::move(count_and_bucket_data.buckets));
176   root.Set("pid", static_cast<int>(GetUniqueIdForProcess().GetUnsafeValue()));
177   serializer.Serialize(root);
178 }
179 
FindAndRunCallbacks(HistogramBase::Sample sample) const180 void HistogramBase::FindAndRunCallbacks(HistogramBase::Sample sample) const {
181   StatisticsRecorder::GlobalSampleCallback global_sample_callback =
182       StatisticsRecorder::global_sample_callback();
183   if (global_sample_callback)
184     global_sample_callback(histogram_name(), name_hash(), sample);
185 
186   // We check the flag first since it is very cheap and we can avoid the
187   // function call and lock overhead of FindAndRunHistogramCallbacks().
188   if (!HasFlags(kCallbackExists)) {
189     return;
190   }
191 
192   StatisticsRecorder::FindAndRunHistogramCallbacks(
193       base::PassKey<HistogramBase>(), histogram_name(), name_hash(), sample);
194 }
195 
GetCountAndBucketData() const196 HistogramBase::CountAndBucketData HistogramBase::GetCountAndBucketData() const {
197   std::unique_ptr<HistogramSamples> snapshot = SnapshotSamples();
198   Count count = snapshot->TotalCount();
199   int64_t sum = snapshot->sum();
200   std::unique_ptr<SampleCountIterator> it = snapshot->Iterator();
201 
202   Value::List buckets;
203   while (!it->Done()) {
204     Sample bucket_min;
205     int64_t bucket_max;
206     Count bucket_count;
207     it->Get(&bucket_min, &bucket_max, &bucket_count);
208 
209     Value::Dict bucket_value;
210     bucket_value.Set("low", bucket_min);
211     // TODO(crbug.com/40228085): Make base::Value able to hold int64_t and
212     // remove this cast.
213     bucket_value.Set("high", static_cast<int>(bucket_max));
214     bucket_value.Set("count", bucket_count);
215     buckets.Append(std::move(bucket_value));
216     it->Next();
217   }
218 
219   return CountAndBucketData(count, sum, std::move(buckets));
220 }
221 
WriteAsciiBucketGraph(double x_count,int line_length,std::string * output) const222 void HistogramBase::WriteAsciiBucketGraph(double x_count,
223                                           int line_length,
224                                           std::string* output) const {
225   int x_remainder = line_length - x_count;
226 
227   while (0 < x_count--)
228     output->append("-");
229   output->append("O");
230   while (0 < x_remainder--)
231     output->append(" ");
232 }
233 
GetSimpleAsciiBucketRange(Sample sample) const234 const std::string HistogramBase::GetSimpleAsciiBucketRange(
235     Sample sample) const {
236   return StringPrintf("%d", sample);
237 }
238 
WriteAsciiBucketValue(Count current,double scaled_sum,std::string * output) const239 void HistogramBase::WriteAsciiBucketValue(Count current,
240                                           double scaled_sum,
241                                           std::string* output) const {
242   StringAppendF(output, " (%d = %3.1f%%)", current, current / scaled_sum);
243 }
244 
WriteAscii(std::string * output) const245 void HistogramBase::WriteAscii(std::string* output) const {
246   base::Value::Dict graph_dict = ToGraphDict();
247   output->append(*graph_dict.FindString("header"));
248   output->append("\n");
249   output->append(*graph_dict.FindString("body"));
250 }
251 
252 // static
GetPermanentName(std::string_view name)253 char const* HistogramBase::GetPermanentName(std::string_view name) {
254   // A set of histogram names that provides the "permanent" lifetime required
255   // by histogram objects for those strings that are not already code constants
256   // or held in persistent memory.
257   static base::NoDestructor<std::set<std::string>> permanent_names;
258   static base::NoDestructor<Lock> permanent_names_lock;
259 
260   AutoLock lock(*permanent_names_lock);
261   auto result = permanent_names->insert(std::string(name));
262   return result.first->c_str();
263 }
264 
265 }  // namespace base
266