1 /*
2 * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "test/testsupport/perf_test_histogram_writer.h"
12
13 #include <stdlib.h>
14
15 #include <map>
16 #include <memory>
17
18 #include "rtc_base/logging.h"
19 #include "rtc_base/synchronization/mutex.h"
20 #include "third_party/catapult/tracing/tracing/value/diagnostics/reserved_infos.h"
21 #include "third_party/catapult/tracing/tracing/value/histogram.h"
22
23 namespace webrtc {
24 namespace test {
25
26 namespace {
27
28 namespace proto = catapult::tracing::tracing::proto;
29
AsJsonString(const std::string string)30 std::string AsJsonString(const std::string string) {
31 return "\"" + string + "\"";
32 }
33
34 class PerfTestHistogramWriter : public PerfTestResultWriter {
35 public:
PerfTestHistogramWriter()36 PerfTestHistogramWriter() : mutex_() {}
ClearResults()37 void ClearResults() override {
38 MutexLock lock(&mutex_);
39 histograms_.clear();
40 }
41
LogResult(const std::string & graph_name,const std::string & trace_name,const double value,const std::string & units,const bool important,ImproveDirection improve_direction)42 void LogResult(const std::string& graph_name,
43 const std::string& trace_name,
44 const double value,
45 const std::string& units,
46 const bool important,
47 ImproveDirection improve_direction) override {
48 (void)important;
49 AddSample(graph_name, trace_name, value, units, improve_direction);
50 }
LogResultMeanAndError(const std::string & graph_name,const std::string & trace_name,const double mean,const double error,const std::string & units,const bool important,ImproveDirection improve_direction)51 void LogResultMeanAndError(const std::string& graph_name,
52 const std::string& trace_name,
53 const double mean,
54 const double error,
55 const std::string& units,
56 const bool important,
57 ImproveDirection improve_direction) override {
58 RTC_LOG(LS_WARNING) << "Discarding stddev, not supported by histograms";
59 (void)error;
60 (void)important;
61
62 AddSample(graph_name, trace_name, mean, units, improve_direction);
63 }
LogResultList(const std::string & graph_name,const std::string & trace_name,const rtc::ArrayView<const double> values,const std::string & units,const bool important,ImproveDirection improve_direction)64 void LogResultList(const std::string& graph_name,
65 const std::string& trace_name,
66 const rtc::ArrayView<const double> values,
67 const std::string& units,
68 const bool important,
69 ImproveDirection improve_direction) override {
70 (void)important;
71 for (double value : values) {
72 AddSample(graph_name, trace_name, value, units, improve_direction);
73 }
74 }
Serialize() const75 std::string Serialize() const override {
76 proto::HistogramSet histogram_set;
77
78 MutexLock lock(&mutex_);
79 for (const auto& histogram : histograms_) {
80 std::unique_ptr<proto::Histogram> proto = histogram.second->toProto();
81 histogram_set.mutable_histograms()->AddAllocated(proto.release());
82 }
83
84 std::string output;
85 bool ok = histogram_set.SerializeToString(&output);
86 RTC_DCHECK(ok) << "Failed to serialize histogram set to string";
87 return output;
88 }
89
90 private:
AddSample(const std::string & original_graph_name,const std::string & trace_name,const double value,const std::string & units,ImproveDirection improve_direction)91 void AddSample(const std::string& original_graph_name,
92 const std::string& trace_name,
93 const double value,
94 const std::string& units,
95 ImproveDirection improve_direction) {
96 // WebRTC annotates the units into the metric name when they are not
97 // supported by the Histogram API.
98 std::string graph_name = original_graph_name;
99 if (units == "dB") {
100 graph_name += "_dB";
101 } else if (units == "fps") {
102 graph_name += "_fps";
103 } else if (units == "%") {
104 graph_name += "_%";
105 }
106
107 // Lookup on graph name + trace name (or measurement + story in catapult
108 // parlance). There should be several histograms with the same measurement
109 // if they're for different stories.
110 std::string measurement_and_story = graph_name + trace_name;
111 MutexLock lock(&mutex_);
112 if (histograms_.count(measurement_and_story) == 0) {
113 proto::UnitAndDirection unit = ParseUnit(units, improve_direction);
114 std::unique_ptr<catapult::HistogramBuilder> builder =
115 std::make_unique<catapult::HistogramBuilder>(graph_name, unit);
116
117 // Set all summary options as false - we don't want to generate
118 // metric_std, metric_count, and so on for all metrics.
119 builder->SetSummaryOptions(proto::SummaryOptions());
120 histograms_[measurement_and_story] = std::move(builder);
121
122 proto::Diagnostic stories;
123 proto::GenericSet* generic_set = stories.mutable_generic_set();
124 generic_set->add_values(AsJsonString(trace_name));
125 histograms_[measurement_and_story]->AddDiagnostic(
126 catapult::kStoriesDiagnostic, stories);
127 }
128
129 if (units == "bps") {
130 // Bps has been interpreted as bits per second in WebRTC tests.
131 histograms_[measurement_and_story]->AddSample(value / 8);
132 } else {
133 histograms_[measurement_and_story]->AddSample(value);
134 }
135 }
136
ParseUnit(const std::string & units,ImproveDirection improve_direction)137 proto::UnitAndDirection ParseUnit(const std::string& units,
138 ImproveDirection improve_direction) {
139 RTC_DCHECK(units.find('_') == std::string::npos)
140 << "The unit_bigger|smallerIsBetter syntax isn't supported in WebRTC, "
141 "use the enum instead.";
142
143 proto::UnitAndDirection result;
144 result.set_improvement_direction(ParseDirection(improve_direction));
145 if (units == "bps") {
146 result.set_unit(proto::BYTES_PER_SECOND);
147 } else if (units == "dB") {
148 result.set_unit(proto::UNITLESS);
149 } else if (units == "fps") {
150 result.set_unit(proto::HERTZ);
151 } else if (units == "frames") {
152 result.set_unit(proto::COUNT);
153 } else if (units == "ms") {
154 result.set_unit(proto::MS_BEST_FIT_FORMAT);
155 } else if (units == "%") {
156 result.set_unit(proto::UNITLESS);
157 } else {
158 proto::Unit unit = catapult::UnitFromJsonUnit(units);
159
160 // UnitFromJsonUnit returns UNITLESS if it doesn't recognize the unit.
161 if (unit == proto::UNITLESS && units != "unitless") {
162 RTC_LOG(LS_WARNING) << "Unit " << units << " is unsupported.";
163 }
164
165 result.set_unit(unit);
166 }
167 return result;
168 }
169
ParseDirection(ImproveDirection improve_direction)170 proto::ImprovementDirection ParseDirection(
171 ImproveDirection improve_direction) {
172 switch (improve_direction) {
173 case ImproveDirection::kNone:
174 return proto::NOT_SPECIFIED;
175 case ImproveDirection::kSmallerIsBetter:
176 return proto::SMALLER_IS_BETTER;
177 case ImproveDirection::kBiggerIsBetter:
178 return proto::BIGGER_IS_BETTER;
179 default:
180 RTC_NOTREACHED() << "Invalid enum value " << improve_direction;
181 }
182 }
183
184 private:
185 mutable Mutex mutex_;
186 std::map<std::string, std::unique_ptr<catapult::HistogramBuilder>> histograms_
187 RTC_GUARDED_BY(&mutex_);
188 };
189
190 } // namespace
191
CreateHistogramWriter()192 PerfTestResultWriter* CreateHistogramWriter() {
193 return new PerfTestHistogramWriter();
194 }
195
196 } // namespace test
197 } // namespace webrtc
198