1 /*
2 * Copyright (c) 2022 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 #include "api/test/metrics/print_result_proxy_metrics_exporter.h"
11
12 #include <string>
13 #include <unordered_set>
14
15 #include "api/array_view.h"
16 #include "api/test/metrics/metric.h"
17 #include "test/testsupport/perf_test.h"
18
19 namespace webrtc {
20 namespace test {
21 namespace {
22
ToPrintResultUnit(Unit unit)23 std::string ToPrintResultUnit(Unit unit) {
24 switch (unit) {
25 case Unit::kMilliseconds:
26 return "msBestFitFormat";
27 case Unit::kPercent:
28 return "n%";
29 case Unit::kBytes:
30 return "sizeInBytes";
31 case Unit::kKilobitsPerSecond:
32 // PrintResults prefer Chrome Perf Dashboard units, which doesn't have
33 // kpbs units, so we change the unit and value accordingly.
34 return "bytesPerSecond";
35 case Unit::kHertz:
36 return "Hz";
37 case Unit::kUnitless:
38 return "unitless";
39 case Unit::kCount:
40 return "count";
41 }
42 }
43
ToPrintResultValue(double value,Unit unit)44 double ToPrintResultValue(double value, Unit unit) {
45 switch (unit) {
46 case Unit::kKilobitsPerSecond:
47 // PrintResults prefer Chrome Perf Dashboard units, which doesn't have
48 // kpbs units, so we change the unit and value accordingly.
49 return value * 1000 / 8;
50 default:
51 return value;
52 }
53 }
54
ToPrintResultImproveDirection(ImprovementDirection direction)55 ImproveDirection ToPrintResultImproveDirection(ImprovementDirection direction) {
56 switch (direction) {
57 case ImprovementDirection::kBiggerIsBetter:
58 return ImproveDirection::kBiggerIsBetter;
59 case ImprovementDirection::kNeitherIsBetter:
60 return ImproveDirection::kNone;
61 case ImprovementDirection::kSmallerIsBetter:
62 return ImproveDirection::kSmallerIsBetter;
63 }
64 }
65
IsEmpty(const Metric::Stats & stats)66 bool IsEmpty(const Metric::Stats& stats) {
67 return !stats.mean.has_value() && !stats.stddev.has_value() &&
68 !stats.min.has_value() && !stats.max.has_value();
69 }
70
NameEndsWithConnected(const std::string & name)71 bool NameEndsWithConnected(const std::string& name) {
72 static const std::string suffix = "_connected";
73 return name.size() >= suffix.size() &&
74 0 == name.compare(name.size() - suffix.size(), suffix.size(), suffix);
75 }
76
77 } // namespace
78
Export(rtc::ArrayView<const Metric> metrics)79 bool PrintResultProxyMetricsExporter::Export(
80 rtc::ArrayView<const Metric> metrics) {
81 static const std::unordered_set<std::string> per_call_metrics{
82 "actual_encode_bitrate",
83 "encode_frame_rate",
84 "harmonic_framerate",
85 "max_skipped",
86 "min_psnr_dB",
87 "retransmission_bitrate",
88 "sent_packets_loss",
89 "transmission_bitrate",
90 "dropped_frames",
91 "frames_in_flight",
92 "rendered_frames",
93 "average_receive_rate",
94 "average_send_rate",
95 "bytes_discarded_no_receiver",
96 "bytes_received",
97 "bytes_sent",
98 "packets_discarded_no_receiver",
99 "packets_received",
100 "packets_sent",
101 "payload_bytes_received",
102 "payload_bytes_sent",
103 "cpu_usage"};
104
105 for (const Metric& metric : metrics) {
106 if (metric.time_series.samples.empty() && IsEmpty(metric.stats)) {
107 // If there were no data collected for the metric it is expected that 0
108 // will be exported, so add 0 to the samples.
109 PrintResult(metric.name, /*modifier=*/"", metric.test_case,
110 ToPrintResultValue(0, metric.unit),
111 ToPrintResultUnit(metric.unit), /*important=*/false,
112 ToPrintResultImproveDirection(metric.improvement_direction));
113 continue;
114 }
115
116 if (metric.time_series.samples.empty()) {
117 PrintResultMeanAndError(
118 metric.name, /*modifier=*/"", metric.test_case,
119 ToPrintResultValue(*metric.stats.mean, metric.unit),
120 ToPrintResultValue(*metric.stats.stddev, metric.unit),
121 ToPrintResultUnit(metric.unit),
122 /*important=*/false,
123 ToPrintResultImproveDirection(metric.improvement_direction));
124 continue;
125 }
126
127 if (metric.time_series.samples.size() == 1lu &&
128 (per_call_metrics.count(metric.name) > 0 ||
129 NameEndsWithConnected(metric.name))) {
130 // Increase backwards compatibility for 1 value use case.
131 PrintResult(
132 metric.name, /*modifier=*/"", metric.test_case,
133 ToPrintResultValue(metric.time_series.samples[0].value, metric.unit),
134 ToPrintResultUnit(metric.unit), /*important=*/false,
135 ToPrintResultImproveDirection(metric.improvement_direction));
136 continue;
137 }
138
139 SamplesStatsCounter counter;
140 for (size_t i = 0; i < metric.time_series.samples.size(); ++i) {
141 counter.AddSample(SamplesStatsCounter::StatsSample{
142 .value = ToPrintResultValue(metric.time_series.samples[i].value,
143 metric.unit),
144 .time = metric.time_series.samples[i].timestamp,
145 .metadata = metric.time_series.samples[i].sample_metadata});
146 }
147
148 PrintResult(metric.name, /*modifier=*/"", metric.test_case, counter,
149 ToPrintResultUnit(metric.unit),
150 /*important=*/false,
151 ToPrintResultImproveDirection(metric.improvement_direction));
152 }
153 return true;
154 }
155
156 } // namespace test
157 } // namespace webrtc
158