1 // Copyright 2019 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 "testing/perf/perf_result_reporter.h"
6
7 #include <ostream>
8 #include <vector>
9
10 #include "base/check.h"
11 #include "base/no_destructor.h"
12 #include "base/notreached.h"
13 #include "testing/perf/perf_test.h"
14
15 namespace {
16
17 // These characters mess with either the stdout parsing or the dashboard itself.
18 static const base::NoDestructor<std::vector<std::string>> kInvalidCharacters{
19 {"/", ":", "="}};
20
CheckForInvalidCharacters(const std::string & str)21 void CheckForInvalidCharacters(const std::string& str) {
22 for (const auto& invalid : *kInvalidCharacters) {
23 CHECK(str.find(invalid) == std::string::npos)
24 << "Given invalid character for perf names '" << invalid << "'";
25 }
26 }
27
28 } // namespace
29
30 namespace perf_test {
31
PerfResultReporter(const std::string & metric_basename,const std::string & story_name)32 PerfResultReporter::PerfResultReporter(const std::string& metric_basename,
33 const std::string& story_name)
34 : metric_basename_(metric_basename), story_name_(story_name) {
35 CheckForInvalidCharacters(metric_basename_);
36 CheckForInvalidCharacters(story_name_);
37 }
38
39 PerfResultReporter::~PerfResultReporter() = default;
40
RegisterFyiMetric(const std::string & metric_suffix,const std::string & units)41 void PerfResultReporter::RegisterFyiMetric(const std::string& metric_suffix,
42 const std::string& units) {
43 RegisterMetric(metric_suffix, units, false);
44 }
45
RegisterImportantMetric(const std::string & metric_suffix,const std::string & units)46 void PerfResultReporter::RegisterImportantMetric(
47 const std::string& metric_suffix,
48 const std::string& units) {
49 RegisterMetric(metric_suffix, units, true);
50 }
51
AddResult(const std::string & metric_suffix,size_t value) const52 void PerfResultReporter::AddResult(const std::string& metric_suffix,
53 size_t value) const {
54 auto info = GetMetricInfoOrFail(metric_suffix);
55
56 PrintResult(metric_basename_, metric_suffix, story_name_, value, info.units,
57 info.important);
58 }
59
AddResult(const std::string & metric_suffix,double value) const60 void PerfResultReporter::AddResult(const std::string& metric_suffix,
61 double value) const {
62 auto info = GetMetricInfoOrFail(metric_suffix);
63
64 PrintResult(metric_basename_, metric_suffix, story_name_, value, info.units,
65 info.important);
66 }
67
AddResult(const std::string & metric_suffix,const std::string & value) const68 void PerfResultReporter::AddResult(const std::string& metric_suffix,
69 const std::string& value) const {
70 auto info = GetMetricInfoOrFail(metric_suffix);
71
72 PrintResult(metric_basename_, metric_suffix, story_name_, value, info.units,
73 info.important);
74 }
75
AddResult(const std::string & metric_suffix,base::TimeDelta value) const76 void PerfResultReporter::AddResult(const std::string& metric_suffix,
77 base::TimeDelta value) const {
78 auto info = GetMetricInfoOrFail(metric_suffix);
79
80 // Decide what time unit to convert the TimeDelta into. Units are based on
81 // the legacy units in
82 // https://cs.chromium.org/chromium/src/third_party/catapult/tracing/tracing/value/legacy_unit_info.py?q=legacy_unit_info
83 double time = 0;
84 if (info.units == "seconds") {
85 time = value.InSecondsF();
86 } else if (info.units == "ms" || info.units == "milliseconds") {
87 time = value.InMillisecondsF();
88 } else if (info.units == "us") {
89 time = value.InMicrosecondsF();
90 } else if (info.units == "ns") {
91 time = value.InNanoseconds();
92 } else {
93 NOTREACHED() << "Attempted to use AddResult with a TimeDelta when "
94 << "registered unit for metric " << metric_suffix << " is "
95 << info.units;
96 }
97
98 PrintResult(metric_basename_, metric_suffix, story_name_, time, info.units,
99 info.important);
100 }
101
AddResultList(const std::string & metric_suffix,const std::string & values) const102 void PerfResultReporter::AddResultList(const std::string& metric_suffix,
103 const std::string& values) const {
104 auto info = GetMetricInfoOrFail(metric_suffix);
105
106 PrintResultList(metric_basename_, metric_suffix, story_name_, values,
107 info.units, info.important);
108 }
109
AddResultMeanAndError(const std::string & metric_suffix,const std::string & mean_and_error)110 void PerfResultReporter::AddResultMeanAndError(
111 const std::string& metric_suffix,
112 const std::string& mean_and_error) {
113 auto info = GetMetricInfoOrFail(metric_suffix);
114
115 PrintResultMeanAndError(metric_basename_, metric_suffix, story_name_,
116 mean_and_error, info.units, info.important);
117 }
118
GetMetricInfo(const std::string & metric_suffix,MetricInfo * out) const119 bool PerfResultReporter::GetMetricInfo(const std::string& metric_suffix,
120 MetricInfo* out) const {
121 auto iter = metric_map_.find(metric_suffix);
122 if (iter == metric_map_.end()) {
123 return false;
124 }
125
126 *out = iter->second;
127 return true;
128 }
129
RegisterMetric(const std::string & metric_suffix,const std::string & units,bool important)130 void PerfResultReporter::RegisterMetric(const std::string& metric_suffix,
131 const std::string& units,
132 bool important) {
133 CheckForInvalidCharacters(metric_suffix);
134 CHECK(metric_map_.count(metric_suffix) == 0);
135 metric_map_.insert({metric_suffix, {units, important}});
136 }
137
GetMetricInfoOrFail(const std::string & metric_suffix) const138 MetricInfo PerfResultReporter::GetMetricInfoOrFail(
139 const std::string& metric_suffix) const {
140 MetricInfo info;
141 CHECK(GetMetricInfo(metric_suffix, &info))
142 << "Attempted to use unregistered metric " << metric_suffix;
143 return info;
144 }
145
146 } // namespace perf_test
147