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