|  | /* | 
|  | *  Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  | #include "api/test/metrics/chrome_perf_dashboard_metrics_exporter.h" | 
|  |  | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/memory/memory.h" | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "api/array_view.h" | 
|  | #include "api/test/metrics/metric.h" | 
|  | #include "test/testsupport/file_utils.h" | 
|  | #include "test/testsupport/perf_test_histogram_writer.h" | 
|  | #include "test/testsupport/perf_test_result_writer.h" | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace test { | 
|  | namespace { | 
|  |  | 
|  | std::string ToChromePerfDashboardUnit(Unit unit) { | 
|  | switch (unit) { | 
|  | case Unit::kMilliseconds: | 
|  | return "msBestFitFormat"; | 
|  | case Unit::kPercent: | 
|  | return "n%"; | 
|  | case Unit::kBytes: | 
|  | return "sizeInBytes"; | 
|  | case Unit::kKilobitsPerSecond: | 
|  | // Chrome Perf Dashboard doesn't have kpbs units, so we change the unit | 
|  | // and value accordingly. | 
|  | return "bytesPerSecond"; | 
|  | case Unit::kHertz: | 
|  | return "Hz"; | 
|  | case Unit::kUnitless: | 
|  | return "unitless"; | 
|  | case Unit::kCount: | 
|  | return "count"; | 
|  | } | 
|  | } | 
|  |  | 
|  | double ToChromePerfDashboardValue(double value, Unit unit) { | 
|  | switch (unit) { | 
|  | case Unit::kKilobitsPerSecond: | 
|  | // Chrome Perf Dashboard doesn't have kpbs units, so we change the unit | 
|  | // and value accordingly. | 
|  | return value * 1000 / 8; | 
|  | default: | 
|  | return value; | 
|  | } | 
|  | } | 
|  |  | 
|  | ImproveDirection ToChromePerfDashboardImproveDirection( | 
|  | ImprovementDirection direction) { | 
|  | switch (direction) { | 
|  | case ImprovementDirection::kBiggerIsBetter: | 
|  | return ImproveDirection::kBiggerIsBetter; | 
|  | case ImprovementDirection::kNeitherIsBetter: | 
|  | return ImproveDirection::kNone; | 
|  | case ImprovementDirection::kSmallerIsBetter: | 
|  | return ImproveDirection::kSmallerIsBetter; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool WriteMetricsToFile(const std::string& path, const std::string& data) { | 
|  | CreateDir(DirName(path)); | 
|  | FILE* output = fopen(path.c_str(), "wb"); | 
|  | if (output == NULL) { | 
|  | printf("Failed to write to %s.\n", path.c_str()); | 
|  | return false; | 
|  | } | 
|  | size_t written = fwrite(data.c_str(), sizeof(char), data.size(), output); | 
|  | fclose(output); | 
|  |  | 
|  | if (written != data.size()) { | 
|  | size_t expected = data.size(); | 
|  | printf("Wrote %zu, tried to write %zu\n", written, expected); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool IsEmpty(const Metric::Stats& stats) { | 
|  | return !stats.mean.has_value() && !stats.stddev.has_value() && | 
|  | !stats.min.has_value() && !stats.max.has_value(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | ChromePerfDashboardMetricsExporter::ChromePerfDashboardMetricsExporter( | 
|  | absl::string_view export_file_path) | 
|  | : export_file_path_(export_file_path) {} | 
|  |  | 
|  | bool ChromePerfDashboardMetricsExporter::Export( | 
|  | rtc::ArrayView<const Metric> metrics) { | 
|  | std::unique_ptr<PerfTestResultWriter> writer = | 
|  | absl::WrapUnique<PerfTestResultWriter>(CreateHistogramWriter()); | 
|  | for (const Metric& metric : metrics) { | 
|  | if (metric.time_series.samples.empty() && IsEmpty(metric.stats)) { | 
|  | // If there were no data collected for the metric it is expected that 0 | 
|  | // will be exported, so add 0 to the samples. | 
|  | writer->LogResult( | 
|  | metric.name, metric.test_case, | 
|  | ToChromePerfDashboardValue(0, metric.unit), | 
|  | ToChromePerfDashboardUnit(metric.unit), | 
|  | /*important=*/false, | 
|  | ToChromePerfDashboardImproveDirection(metric.improvement_direction)); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (metric.time_series.samples.empty()) { | 
|  | writer->LogResultMeanAndError( | 
|  | metric.name, metric.test_case, | 
|  | ToChromePerfDashboardValue(*metric.stats.mean, metric.unit), | 
|  | ToChromePerfDashboardValue(*metric.stats.stddev, metric.unit), | 
|  | ToChromePerfDashboardUnit(metric.unit), | 
|  | /*important=*/false, | 
|  | ToChromePerfDashboardImproveDirection(metric.improvement_direction)); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | std::vector<double> samples(metric.time_series.samples.size()); | 
|  | for (size_t i = 0; i < metric.time_series.samples.size(); ++i) { | 
|  | samples[i] = ToChromePerfDashboardValue( | 
|  | metric.time_series.samples[i].value, metric.unit); | 
|  | } | 
|  | writer->LogResultList( | 
|  | metric.name, metric.test_case, samples, | 
|  | ToChromePerfDashboardUnit(metric.unit), | 
|  | /*important=*/false, | 
|  | ToChromePerfDashboardImproveDirection(metric.improvement_direction)); | 
|  | } | 
|  | return WriteMetricsToFile(export_file_path_, writer->Serialize()); | 
|  | } | 
|  |  | 
|  | }  // namespace test | 
|  | }  // namespace webrtc |