xref: /aosp_15_r20/external/cronet/base/test/metrics/histogram_tester.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2014 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/test/metrics/histogram_tester.h"
6 
7 #include <stddef.h>
8 
9 #include <string_view>
10 
11 #include "base/metrics/histogram.h"
12 #include "base/metrics/histogram_samples.h"
13 #include "base/metrics/metrics_hashes.h"
14 #include "base/metrics/sample_map.h"
15 #include "base/metrics/sparse_histogram.h"
16 #include "base/metrics/statistics_recorder.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 
21 namespace base {
22 
HistogramTester()23 HistogramTester::HistogramTester() {
24   // Record any histogram data that exists when the object is created so it can
25   // be subtracted later.
26   for (const auto* const histogram : StatisticsRecorder::GetHistograms()) {
27     histograms_snapshot_[histogram->histogram_name()] =
28         histogram->SnapshotSamples();
29   }
30 }
31 
32 HistogramTester::~HistogramTester() = default;
33 
ExpectUniqueSample(std::string_view name,HistogramBase::Sample sample,HistogramBase::Count expected_bucket_count,const Location & location) const34 void HistogramTester::ExpectUniqueSample(
35     std::string_view name,
36     HistogramBase::Sample sample,
37     HistogramBase::Count expected_bucket_count,
38     const Location& location) const {
39   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
40   if (histogram) {
41     int actual_bucket_count;
42     int actual_total_count;
43     GetBucketCountForSamples(*histogram, sample, &actual_bucket_count,
44                              &actual_total_count);
45 
46     EXPECT_TRUE(expected_bucket_count == actual_bucket_count &&
47                 expected_bucket_count == actual_total_count)
48         << "Histogram \"" << name << "\" did not meet its expectations.\n"
49         << "Bucket " << sample << " should contain " << expected_bucket_count
50         << " samples and contained " << actual_bucket_count << " samples.\n"
51         << "The total count of samples in the histogram should be "
52         << expected_bucket_count << " and was " << actual_total_count << ".\n"
53         << SnapshotToString(*histogram) << "\n"
54         << "(expected at " << location.ToString() << ")";
55   } else {
56     // No histogram means there were zero samples.
57     EXPECT_EQ(0, expected_bucket_count)
58         << "Zero samples found for Histogram \"" << name << "\".\n"
59         << "(expected at " << location.ToString() << ")";
60     return;
61   }
62 }
63 
ExpectUniqueTimeSample(std::string_view name,TimeDelta sample,HistogramBase::Count expected_bucket_count,const Location & location) const64 void HistogramTester::ExpectUniqueTimeSample(
65     std::string_view name,
66     TimeDelta sample,
67     HistogramBase::Count expected_bucket_count,
68     const Location& location) const {
69   ExpectUniqueSample(name, sample.InMilliseconds(), expected_bucket_count,
70                      location);
71 }
72 
ExpectBucketCount(std::string_view name,HistogramBase::Sample sample,HistogramBase::Count expected_count,const Location & location) const73 void HistogramTester::ExpectBucketCount(std::string_view name,
74                                         HistogramBase::Sample sample,
75                                         HistogramBase::Count expected_count,
76                                         const Location& location) const {
77   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
78   if (histogram) {
79     int actual_count;
80     GetBucketCountForSamples(*histogram, sample, &actual_count,
81                              /*total_count=*/nullptr);
82 
83     EXPECT_EQ(expected_count, actual_count)
84         << "Histogram \"" << name
85         << "\" does not have the right number of samples (" << expected_count
86         << ") in the expected bucket (" << sample << "). It has ("
87         << actual_count << ").\n"
88         << SnapshotToString(*histogram) << "\n"
89         << "(expected at " << location.ToString() << ")";
90   } else {
91     // No histogram means there were zero samples.
92     EXPECT_EQ(0, expected_count)
93         << "Histogram \"" << name << "\" does not exist. "
94         << "(expected at " << location.ToString() << ")";
95   }
96 }
97 
ExpectTotalCount(std::string_view name,HistogramBase::Count expected_count,const Location & location) const98 void HistogramTester::ExpectTotalCount(std::string_view name,
99                                        HistogramBase::Count expected_count,
100                                        const Location& location) const {
101   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
102   if (histogram) {
103     int actual_count = GetTotalCountForSamples(*histogram);
104 
105     EXPECT_EQ(expected_count, actual_count)
106         << "Histogram \"" << name
107         << "\" does not have the right total number of samples ("
108         << expected_count << "). It has (" << actual_count << ").\n"
109         << SnapshotToString(*histogram) << "\n"
110         << "(expected at " << location.ToString() << ")";
111   } else {
112     // No histogram means there were zero samples.
113     EXPECT_EQ(0, expected_count)
114         << "Histogram \"" << name << "\" does not exist. "
115         << "(expected at " << location.ToString() << ")";
116   }
117 }
118 
ExpectTimeBucketCount(std::string_view name,TimeDelta sample,HistogramBase::Count count,const Location & location) const119 void HistogramTester::ExpectTimeBucketCount(std::string_view name,
120                                             TimeDelta sample,
121                                             HistogramBase::Count count,
122                                             const Location& location) const {
123   ExpectBucketCount(name, sample.InMilliseconds(), count, location);
124 }
125 
GetTotalSum(std::string_view name) const126 int64_t HistogramTester::GetTotalSum(std::string_view name) const {
127   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
128   if (!histogram)
129     return 0;
130 
131   int64_t original_sum = 0;
132   auto original_samples_it = histograms_snapshot_.find(name);
133   if (original_samples_it != histograms_snapshot_.end())
134     original_sum = original_samples_it->second->sum();
135 
136   std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
137   return samples->sum() - original_sum;
138 }
139 
GetAllSamples(std::string_view name) const140 std::vector<Bucket> HistogramTester::GetAllSamples(
141     std::string_view name) const {
142   std::vector<Bucket> samples;
143   std::unique_ptr<HistogramSamples> snapshot =
144       GetHistogramSamplesSinceCreation(name);
145   if (snapshot) {
146     for (auto it = snapshot->Iterator(); !it->Done(); it->Next()) {
147       HistogramBase::Sample sample;
148       int64_t max;
149       HistogramBase::Count count;
150       it->Get(&sample, &max, &count);
151       samples.emplace_back(sample, count);
152     }
153   }
154   return samples;
155 }
156 
GetBucketCount(std::string_view name,HistogramBase::Sample sample) const157 HistogramBase::Count HistogramTester::GetBucketCount(
158     std::string_view name,
159     HistogramBase::Sample sample) const {
160   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
161   HistogramBase::Count count = 0;
162   if (histogram) {
163     GetBucketCountForSamples(*histogram, sample, &count,
164                              /*total_count=*/nullptr);
165   }
166   return count;
167 }
168 
GetBucketCountForSamples(const HistogramBase & histogram,HistogramBase::Sample sample,HistogramBase::Count * count,HistogramBase::Count * total_count) const169 void HistogramTester::GetBucketCountForSamples(
170     const HistogramBase& histogram,
171     HistogramBase::Sample sample,
172     HistogramBase::Count* count,
173     HistogramBase::Count* total_count) const {
174   std::unique_ptr<HistogramSamples> samples = histogram.SnapshotSamples();
175   *count = samples->GetCount(sample);
176   if (total_count)
177     *total_count = samples->TotalCount();
178   auto histogram_data = histograms_snapshot_.find(histogram.histogram_name());
179   if (histogram_data != histograms_snapshot_.end()) {
180     *count -= histogram_data->second->GetCount(sample);
181     if (total_count)
182       *total_count -= histogram_data->second->TotalCount();
183   }
184 }
185 
GetTotalCountsForPrefix(std::string_view prefix) const186 HistogramTester::CountsMap HistogramTester::GetTotalCountsForPrefix(
187     std::string_view prefix) const {
188   EXPECT_TRUE(prefix.find('.') != StringPiece::npos)
189       << "|prefix| ought to contain at least one period, to avoid matching too"
190       << " many histograms.";
191 
192   CountsMap result;
193 
194   // Find candidate matches by using the logic built into GetSnapshot().
195   for (const HistogramBase* histogram : StatisticsRecorder::GetHistograms()) {
196     if (!StartsWith(histogram->histogram_name(), prefix,
197                     CompareCase::SENSITIVE)) {
198       continue;
199     }
200     std::unique_ptr<HistogramSamples> new_samples =
201         GetHistogramSamplesSinceCreation(histogram->histogram_name());
202     // Omit unchanged histograms from the result.
203     if (new_samples->TotalCount()) {
204       result[histogram->histogram_name()] = new_samples->TotalCount();
205     }
206   }
207   return result;
208 }
209 
210 std::unique_ptr<HistogramSamples>
GetHistogramSamplesSinceCreation(std::string_view histogram_name) const211 HistogramTester::GetHistogramSamplesSinceCreation(
212     std::string_view histogram_name) const {
213   HistogramBase* histogram = StatisticsRecorder::FindHistogram(histogram_name);
214   // Whether the histogram exists or not may not depend on the current test
215   // calling this method, but rather on which tests ran before and possibly
216   // generated a histogram or not (see http://crbug.com/473689). To provide a
217   // response which is independent of the previously run tests, this method
218   // creates empty samples in the absence of the histogram, rather than
219   // returning null.
220   if (!histogram) {
221     return std::unique_ptr<HistogramSamples>(
222         new SampleMap(HashMetricName(histogram_name)));
223   }
224   std::unique_ptr<HistogramSamples> named_samples =
225       histogram->SnapshotSamples();
226   auto original_samples_it = histograms_snapshot_.find(histogram_name);
227   if (original_samples_it != histograms_snapshot_.end())
228     named_samples->Subtract(*original_samples_it->second.get());
229   return named_samples;
230 }
231 
GetAllHistogramsRecorded() const232 std::string HistogramTester::GetAllHistogramsRecorded() const {
233   std::string output;
234 
235   for (const auto* const histogram : StatisticsRecorder::GetHistograms()) {
236     std::unique_ptr<HistogramSamples> named_samples =
237         histogram->SnapshotSamples();
238 
239     for (const auto& histogram_data : histograms_snapshot_) {
240       if (histogram_data.first == histogram->histogram_name())
241         named_samples->Subtract(*histogram_data.second);
242     }
243 
244     if (named_samples->TotalCount()) {
245       auto current_count = histogram->SnapshotSamples()->TotalCount();
246       StringAppendF(&output, "Histogram: %s recorded %d new samples.\n",
247                     histogram->histogram_name(), named_samples->TotalCount());
248       if (current_count != named_samples->TotalCount()) {
249         StringAppendF(&output,
250                       "WARNING: There were samples recorded to this histogram "
251                       "before tester instantiation.\n");
252       }
253       histogram->WriteAscii(&output);
254       StringAppendF(&output, "\n");
255     }
256   }
257 
258   return output;
259 }
260 
GetTotalCountForSamples(const base::HistogramBase & histogram) const261 int HistogramTester::GetTotalCountForSamples(
262     const base::HistogramBase& histogram) const {
263   std::unique_ptr<HistogramSamples> samples = histogram.SnapshotSamples();
264   int actual_count = samples->TotalCount();
265   auto histogram_data = histograms_snapshot_.find(histogram.histogram_name());
266   if (histogram_data != histograms_snapshot_.end())
267     actual_count -= histogram_data->second->TotalCount();
268   return actual_count;
269 }
270 
SnapshotToString(const base::HistogramBase & histogram) const271 std::string HistogramTester::SnapshotToString(
272     const base::HistogramBase& histogram) const {
273   std::unique_ptr<HistogramSamples> snapshot =
274       GetHistogramSamplesSinceCreation(histogram.histogram_name());
275 
276   base::Value::Dict graph_dict =
277       snapshot->ToGraphDict(histogram.histogram_name(), histogram.flags());
278   std::string tmp;
279   // The header message describes this histogram samples (name of the histogram
280   // and median of the samples). The body contains an ASCII art histogram of the
281   // samples.
282   tmp.append(*graph_dict.FindString("header"));
283   tmp.append("\n");
284   tmp.append(*graph_dict.FindString("body"));
285   return tmp;
286 }
287 
PrintTo(const Bucket & bucket,std::ostream * os)288 void PrintTo(const Bucket& bucket, std::ostream* os) {
289   *os << "Bucket " << bucket.min << ": " << bucket.count;
290 }
291 
292 }  // namespace base
293