/* * Copyright (c) 2020 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 "test/testsupport/perf_test_histogram_writer.h" #include #include #include #include "absl/strings/string_view.h" #include "api/numerics/samples_stats_counter.h" #include "rtc_base/logging.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/synchronization/mutex.h" #include "third_party/catapult/tracing/tracing/value/diagnostics/reserved_infos.h" #include "third_party/catapult/tracing/tracing/value/histogram.h" namespace webrtc { namespace test { namespace { namespace proto = catapult::tracing::tracing::proto; std::string AsJsonString(const std::string string) { return "\"" + string + "\""; } class PerfTestHistogramWriter : public PerfTestResultWriter { public: PerfTestHistogramWriter() : mutex_() {} void ClearResults() override { MutexLock lock(&mutex_); histograms_.clear(); } void LogResult(absl::string_view graph_name, absl::string_view trace_name, const double value, absl::string_view units, const bool important, ImproveDirection improve_direction) override { (void)important; AddSample(graph_name, trace_name, value, units, improve_direction); } void LogResultMeanAndError(absl::string_view graph_name, absl::string_view trace_name, const double mean, const double error, absl::string_view units, const bool important, ImproveDirection improve_direction) override { RTC_LOG(LS_WARNING) << "Discarding stddev, not supported by histograms"; (void)error; (void)important; AddSample(graph_name, trace_name, mean, units, improve_direction); } void LogResultList(absl::string_view graph_name, absl::string_view trace_name, const rtc::ArrayView values, absl::string_view units, const bool important, ImproveDirection improve_direction) override { (void)important; for (double value : values) { AddSample(graph_name, trace_name, value, units, improve_direction); } } std::string Serialize() const override { proto::HistogramSet histogram_set; MutexLock lock(&mutex_); for (const auto& histogram : histograms_) { std::unique_ptr proto = histogram.second->toProto(); histogram_set.mutable_histograms()->AddAllocated(proto.release()); } std::string output; bool ok = histogram_set.SerializeToString(&output); RTC_DCHECK(ok) << "Failed to serialize histogram set to string"; return output; } private: void AddSample(absl::string_view original_graph_name, absl::string_view trace_name, const double value, absl::string_view units, ImproveDirection improve_direction) { // WebRTC annotates the units into the metric name when they are not // supported by the Histogram API. std::string graph_name(original_graph_name); if (units == "dB") { graph_name += "_dB"; } else if (units == "fps") { graph_name += "_fps"; } else if (units == "%") { graph_name += "_%"; } // Lookup on graph name + trace name (or measurement + story in catapult // parlance). There should be several histograms with the same measurement // if they're for different stories. rtc::StringBuilder measurement_and_story; measurement_and_story << graph_name << trace_name; MutexLock lock(&mutex_); if (histograms_.count(measurement_and_story.str()) == 0) { proto::UnitAndDirection unit = ParseUnit(units, improve_direction); std::unique_ptr builder = std::make_unique(graph_name, unit); // Set all summary options as false - we don't want to generate // metric_std, metric_count, and so on for all metrics. builder->SetSummaryOptions(proto::SummaryOptions()); histograms_[measurement_and_story.str()] = std::move(builder); proto::Diagnostic stories; proto::GenericSet* generic_set = stories.mutable_generic_set(); generic_set->add_values(AsJsonString(std::string(trace_name))); histograms_[measurement_and_story.str()]->AddDiagnostic( catapult::kStoriesDiagnostic, stories); } if (units == "bps") { // Bps has been interpreted as bits per second in WebRTC tests. histograms_[measurement_and_story.str()]->AddSample(value / 8); } else { histograms_[measurement_and_story.str()]->AddSample(value); } } proto::UnitAndDirection ParseUnit(absl::string_view units, ImproveDirection improve_direction) { RTC_DCHECK(units.find('_') == std::string::npos) << "The unit_bigger|smallerIsBetter syntax isn't supported in WebRTC, " "use the enum instead."; proto::UnitAndDirection result; result.set_improvement_direction(ParseDirection(improve_direction)); if (units == "bps") { result.set_unit(proto::BYTES_PER_SECOND); } else if (units == "dB") { result.set_unit(proto::UNITLESS); } else if (units == "fps") { result.set_unit(proto::HERTZ); } else if (units == "frames") { result.set_unit(proto::COUNT); } else if (units == "ms") { result.set_unit(proto::MS_BEST_FIT_FORMAT); } else if (units == "%") { result.set_unit(proto::UNITLESS); } else { proto::Unit unit = catapult::UnitFromJsonUnit(std::string(units)); // UnitFromJsonUnit returns UNITLESS if it doesn't recognize the unit. if (unit == proto::UNITLESS && units != "unitless") { RTC_LOG(LS_WARNING) << "Unit " << units << " is unsupported."; } result.set_unit(unit); } return result; } proto::ImprovementDirection ParseDirection( ImproveDirection improve_direction) { switch (improve_direction) { case ImproveDirection::kNone: return proto::NOT_SPECIFIED; case ImproveDirection::kSmallerIsBetter: return proto::SMALLER_IS_BETTER; case ImproveDirection::kBiggerIsBetter: return proto::BIGGER_IS_BETTER; default: RTC_DCHECK_NOTREACHED() << "Invalid enum value " << improve_direction; } } private: mutable Mutex mutex_; std::map> histograms_ RTC_GUARDED_BY(&mutex_); }; } // namespace PerfTestResultWriter* CreateHistogramWriter() { return new PerfTestHistogramWriter(); } } // namespace test } // namespace webrtc