xref: /aosp_15_r20/external/cronet/base/metrics/histogram_base.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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   return "UNKNOWN";
50 }
51 
DeserializeHistogramInfo(PickleIterator * iter)52 HistogramBase* DeserializeHistogramInfo(PickleIterator* iter) {
53   int type;
54   if (!iter->ReadInt(&type))
55     return nullptr;
56 
57   switch (type) {
58     case HISTOGRAM:
59       return Histogram::DeserializeInfoImpl(iter);
60     case LINEAR_HISTOGRAM:
61       return LinearHistogram::DeserializeInfoImpl(iter);
62     case BOOLEAN_HISTOGRAM:
63       return BooleanHistogram::DeserializeInfoImpl(iter);
64     case CUSTOM_HISTOGRAM:
65       return CustomHistogram::DeserializeInfoImpl(iter);
66     case SPARSE_HISTOGRAM:
67       return SparseHistogram::DeserializeInfoImpl(iter);
68     default:
69       return nullptr;
70   }
71 }
72 
CountAndBucketData(Count count,int64_t sum,Value::List buckets)73 HistogramBase::CountAndBucketData::CountAndBucketData(Count count,
74                                                       int64_t sum,
75                                                       Value::List buckets)
76     : count(count), sum(sum), buckets(std::move(buckets)) {}
77 
78 HistogramBase::CountAndBucketData::~CountAndBucketData() = default;
79 
80 HistogramBase::CountAndBucketData::CountAndBucketData(
81     CountAndBucketData&& other) = default;
82 
83 HistogramBase::CountAndBucketData& HistogramBase::CountAndBucketData::operator=(
84     CountAndBucketData&& other) = default;
85 
86 const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX;
87 
HistogramBase(const char * name)88 HistogramBase::HistogramBase(const char* name)
89     : histogram_name_(name), flags_(kNoFlags) {}
90 
91 HistogramBase::~HistogramBase() = default;
92 
CheckName(std::string_view name) const93 void HistogramBase::CheckName(std::string_view name) const {
94   DCHECK_EQ(std::string_view(histogram_name()), name)
95       << "Provided histogram name doesn't match instance name. Are you using a "
96          "dynamic string in a macro?";
97 }
98 
SetFlags(int32_t flags)99 void HistogramBase::SetFlags(int32_t flags) {
100   flags_.fetch_or(flags, std::memory_order_relaxed);
101 }
102 
ClearFlags(int32_t flags)103 void HistogramBase::ClearFlags(int32_t flags) {
104   flags_.fetch_and(~flags, std::memory_order_relaxed);
105 }
106 
HasFlags(int32_t flags) const107 bool HistogramBase::HasFlags(int32_t flags) const {
108   // Check this->flags() is a superset of |flags|, i.e. every flag in |flags| is
109   // included.
110   return (this->flags() & flags) == flags;
111 }
112 
AddScaled(Sample value,int count,int scale)113 void HistogramBase::AddScaled(Sample value, int count, int scale) {
114   DCHECK_GT(scale, 0);
115 
116   // Convert raw count and probabilistically round up/down if the remainder
117   // is more than a random number [0, scale). This gives a more accurate
118   // count when there are a large number of records. RandInt is "inclusive",
119   // hence the -1 for the max value.
120   int count_scaled = count / scale;
121   if (count - (count_scaled * scale) > base::RandInt(0, scale - 1))
122     ++count_scaled;
123   if (count_scaled <= 0)
124     return;
125 
126   AddCount(value, count_scaled);
127 }
128 
AddKilo(Sample value,int count)129 void HistogramBase::AddKilo(Sample value, int count) {
130   AddScaled(value, count, 1000);
131 }
132 
AddKiB(Sample value,int count)133 void HistogramBase::AddKiB(Sample value, int count) {
134   AddScaled(value, count, 1024);
135 }
136 
AddTimeMillisecondsGranularity(const TimeDelta & time)137 void HistogramBase::AddTimeMillisecondsGranularity(const TimeDelta& time) {
138   Add(saturated_cast<Sample>(time.InMilliseconds()));
139 }
140 
AddTimeMicrosecondsGranularity(const TimeDelta & time)141 void HistogramBase::AddTimeMicrosecondsGranularity(const TimeDelta& time) {
142   // Intentionally drop high-resolution reports on clients with low-resolution
143   // clocks. High-resolution metrics cannot make use of low-resolution data and
144   // reporting it merely adds noise to the metric. https://crbug.com/807615#c16
145   if (TimeTicks::IsHighResolution())
146     Add(saturated_cast<Sample>(time.InMicroseconds()));
147 }
148 
AddBoolean(bool value)149 void HistogramBase::AddBoolean(bool value) {
150   Add(value ? 1 : 0);
151 }
152 
SerializeInfo(Pickle * pickle) const153 void HistogramBase::SerializeInfo(Pickle* pickle) const {
154   pickle->WriteInt(GetHistogramType());
155   SerializeInfoImpl(pickle);
156 }
157 
FindCorruption(const HistogramSamples & samples) const158 uint32_t HistogramBase::FindCorruption(const HistogramSamples& samples) const {
159   // Not supported by default.
160   return NO_INCONSISTENCIES;
161 }
162 
WriteJSON(std::string * output,JSONVerbosityLevel verbosity_level) const163 void HistogramBase::WriteJSON(std::string* output,
164                               JSONVerbosityLevel verbosity_level) const {
165   CountAndBucketData count_and_bucket_data = GetCountAndBucketData();
166   Value::Dict parameters = GetParameters();
167 
168   JSONStringValueSerializer serializer(output);
169   Value::Dict root;
170   root.Set("name", histogram_name());
171   root.Set("count", count_and_bucket_data.count);
172   root.Set("sum", static_cast<double>(count_and_bucket_data.sum));
173   root.Set("flags", flags());
174   root.Set("params", std::move(parameters));
175   if (verbosity_level != JSON_VERBOSITY_LEVEL_OMIT_BUCKETS)
176     root.Set("buckets", std::move(count_and_bucket_data.buckets));
177   root.Set("pid", static_cast<int>(GetUniqueIdForProcess().GetUnsafeValue()));
178   serializer.Serialize(root);
179 }
180 
FindAndRunCallbacks(HistogramBase::Sample sample) const181 void HistogramBase::FindAndRunCallbacks(HistogramBase::Sample sample) const {
182   StatisticsRecorder::GlobalSampleCallback global_sample_callback =
183       StatisticsRecorder::global_sample_callback();
184   if (global_sample_callback)
185     global_sample_callback(histogram_name(), name_hash(), sample);
186 
187   // We check the flag first since it is very cheap and we can avoid the
188   // function call and lock overhead of FindAndRunHistogramCallbacks().
189   if (!HasFlags(kCallbackExists)) {
190     return;
191   }
192 
193   StatisticsRecorder::FindAndRunHistogramCallbacks(
194       base::PassKey<HistogramBase>(), histogram_name(), name_hash(), sample);
195 }
196 
GetCountAndBucketData() const197 HistogramBase::CountAndBucketData HistogramBase::GetCountAndBucketData() const {
198   std::unique_ptr<HistogramSamples> snapshot = SnapshotSamples();
199   Count count = snapshot->TotalCount();
200   int64_t sum = snapshot->sum();
201   std::unique_ptr<SampleCountIterator> it = snapshot->Iterator();
202 
203   Value::List buckets;
204   while (!it->Done()) {
205     Sample bucket_min;
206     int64_t bucket_max;
207     Count bucket_count;
208     it->Get(&bucket_min, &bucket_max, &bucket_count);
209 
210     Value::Dict bucket_value;
211     bucket_value.Set("low", bucket_min);
212     // TODO(crbug.com/1334256): Make base::Value able to hold int64_t and remove
213     // this cast.
214     bucket_value.Set("high", static_cast<int>(bucket_max));
215     bucket_value.Set("count", bucket_count);
216     buckets.Append(std::move(bucket_value));
217     it->Next();
218   }
219 
220   return CountAndBucketData(count, sum, std::move(buckets));
221 }
222 
WriteAsciiBucketGraph(double x_count,int line_length,std::string * output) const223 void HistogramBase::WriteAsciiBucketGraph(double x_count,
224                                           int line_length,
225                                           std::string* output) const {
226   int x_remainder = line_length - x_count;
227 
228   while (0 < x_count--)
229     output->append("-");
230   output->append("O");
231   while (0 < x_remainder--)
232     output->append(" ");
233 }
234 
GetSimpleAsciiBucketRange(Sample sample) const235 const std::string HistogramBase::GetSimpleAsciiBucketRange(
236     Sample sample) const {
237   return StringPrintf("%d", sample);
238 }
239 
WriteAsciiBucketValue(Count current,double scaled_sum,std::string * output) const240 void HistogramBase::WriteAsciiBucketValue(Count current,
241                                           double scaled_sum,
242                                           std::string* output) const {
243   StringAppendF(output, " (%d = %3.1f%%)", current, current / scaled_sum);
244 }
245 
WriteAscii(std::string * output) const246 void HistogramBase::WriteAscii(std::string* output) const {
247   base::Value::Dict graph_dict = ToGraphDict();
248   output->append(*graph_dict.FindString("header"));
249   output->append("\n");
250   output->append(*graph_dict.FindString("body"));
251 }
252 
253 // static
GetPermanentName(std::string_view name)254 char const* HistogramBase::GetPermanentName(std::string_view name) {
255   // A set of histogram names that provides the "permanent" lifetime required
256   // by histogram objects for those strings that are not already code constants
257   // or held in persistent memory.
258   static base::NoDestructor<std::set<std::string>> permanent_names;
259   static base::NoDestructor<Lock> permanent_names_lock;
260 
261   AutoLock lock(*permanent_names_lock);
262   auto result = permanent_names->insert(std::string(name));
263   return result.first->c_str();
264 }
265 
266 }  // namespace base
267