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/metrics_accumulator.h"
11
12 #include <map>
13 #include <string>
14 #include <utility>
15 #include <vector>
16
17 #include "absl/strings/string_view.h"
18 #include "api/numerics/samples_stats_counter.h"
19 #include "api/test/metrics/metric.h"
20 #include "api/units/timestamp.h"
21 #include "rtc_base/synchronization/mutex.h"
22
23 namespace webrtc {
24 namespace test {
25 namespace {
26
ToStats(const SamplesStatsCounter & values)27 Metric::Stats ToStats(const SamplesStatsCounter& values) {
28 if (values.IsEmpty()) {
29 return Metric::Stats();
30 }
31 return Metric::Stats{.mean = values.GetAverage(),
32 .stddev = values.GetStandardDeviation(),
33 .min = values.GetMin(),
34 .max = values.GetMax()};
35 }
36
SetTimeseries(const Metric & prototype,const SamplesStatsCounter & counter)37 Metric SetTimeseries(const Metric& prototype,
38 const SamplesStatsCounter& counter) {
39 Metric output(prototype);
40 Metric::TimeSeries time_series;
41 for (const SamplesStatsCounter::StatsSample& sample :
42 counter.GetTimedSamples()) {
43 time_series.samples.push_back(
44 Metric::TimeSeries::Sample{.timestamp = sample.time,
45 .value = sample.value,
46 .sample_metadata = sample.metadata});
47 }
48 output.time_series = std::move(time_series);
49 output.stats = ToStats(counter);
50 return output;
51 }
52
53 } // namespace
54
operator <(const MetricsAccumulator::MetricKey & a,const MetricsAccumulator::MetricKey & b)55 bool operator<(const MetricsAccumulator::MetricKey& a,
56 const MetricsAccumulator::MetricKey& b) {
57 if (a.test_case_name < b.test_case_name) {
58 return true;
59 } else if (a.test_case_name > b.test_case_name) {
60 return false;
61 } else {
62 return a.metric_name < b.metric_name;
63 }
64 }
65
AddSample(absl::string_view metric_name,absl::string_view test_case_name,double value,Timestamp timestamp,std::map<std::string,std::string> point_metadata)66 bool MetricsAccumulator::AddSample(
67 absl::string_view metric_name,
68 absl::string_view test_case_name,
69 double value,
70 Timestamp timestamp,
71 std::map<std::string, std::string> point_metadata) {
72 MutexLock lock(&mutex_);
73 bool created;
74 MetricValue* metric_value =
75 GetOrCreateMetric(metric_name, test_case_name, &created);
76 metric_value->counter.AddSample(
77 SamplesStatsCounter::StatsSample{.value = value,
78 .time = timestamp,
79 .metadata = std::move(point_metadata)});
80 return created;
81 }
82
AddMetricMetadata(absl::string_view metric_name,absl::string_view test_case_name,Unit unit,ImprovementDirection improvement_direction,std::map<std::string,std::string> metric_metadata)83 bool MetricsAccumulator::AddMetricMetadata(
84 absl::string_view metric_name,
85 absl::string_view test_case_name,
86 Unit unit,
87 ImprovementDirection improvement_direction,
88 std::map<std::string, std::string> metric_metadata) {
89 MutexLock lock(&mutex_);
90 bool created;
91 MetricValue* metric_value =
92 GetOrCreateMetric(metric_name, test_case_name, &created);
93 metric_value->metric.unit = unit;
94 metric_value->metric.improvement_direction = improvement_direction;
95 metric_value->metric.metric_metadata = std::move(metric_metadata);
96 return created;
97 }
98
GetCollectedMetrics() const99 std::vector<Metric> MetricsAccumulator::GetCollectedMetrics() const {
100 MutexLock lock(&mutex_);
101 std::vector<Metric> out;
102 out.reserve(metrics_.size());
103 for (const auto& [unused_key, metric_value] : metrics_) {
104 out.push_back(SetTimeseries(metric_value.metric, metric_value.counter));
105 }
106 return out;
107 }
108
GetOrCreateMetric(absl::string_view metric_name,absl::string_view test_case_name,bool * created)109 MetricsAccumulator::MetricValue* MetricsAccumulator::GetOrCreateMetric(
110 absl::string_view metric_name,
111 absl::string_view test_case_name,
112 bool* created) {
113 MetricKey key(metric_name, test_case_name);
114 auto it = metrics_.find(key);
115 if (it != metrics_.end()) {
116 *created = false;
117 return &it->second;
118 }
119 *created = true;
120
121 Metric metric{
122 .name = key.metric_name,
123 .unit = Unit::kUnitless,
124 .improvement_direction = ImprovementDirection::kNeitherIsBetter,
125 .test_case = key.test_case_name,
126 };
127 return &metrics_.emplace(key, MetricValue{.metric = std::move(metric)})
128 .first->second;
129 }
130
131 } // namespace test
132 } // namespace webrtc
133