// // Copyright 2020 The ANGLE 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. // // HistogramWriter: // Helper class for writing histogram-json-set-format files to JSON. #include "HistogramWriter.h" #include "common/debug.h" #include #if !defined(ANGLE_HAS_HISTOGRAMS) # error "Must have histograms enabled" #endif // !defined(ANGLE_HAS_HISTOGRAMS) ANGLE_DISABLE_EXTRA_SEMI_WARNING ANGLE_DISABLE_EXTRA_SEMI_STMT_WARNING ANGLE_DISABLE_DESTRUCTOR_OVERRIDE_WARNING ANGLE_DISABLE_SUGGEST_OVERRIDE_WARNINGS #include "tracing/tracing/value/diagnostics/reserved_infos.h" #include "tracing/tracing/value/histogram.h" ANGLE_REENABLE_SUGGEST_OVERRIDE_WARNINGS ANGLE_REENABLE_DESTRUCTOR_OVERRIDE_WARNING ANGLE_REENABLE_EXTRA_SEMI_STMT_WARNING ANGLE_REENABLE_EXTRA_SEMI_WARNING namespace js = rapidjson; namespace proto = catapult::tracing::tracing::proto; namespace angle { namespace { std::string AsJsonString(const std::string string) { return "\"" + string + "\""; } std::string GetUnitAndDirection(proto::UnitAndDirection unit) { ASSERT(unit.improvement_direction() == proto::SMALLER_IS_BETTER); ASSERT(unit.unit() == proto::MS_BEST_FIT_FORMAT); return "msBestFitFormat"; } } // namespace HistogramWriter::HistogramWriter() = default; HistogramWriter::~HistogramWriter() = default; void HistogramWriter::addSample(const std::string &measurement, const std::string &story, double value, const std::string &units) { std::string measurementAndStory = measurement + story; if (mHistograms.count(measurementAndStory) == 0) { proto::UnitAndDirection unitAndDirection; unitAndDirection.set_improvement_direction(proto::SMALLER_IS_BETTER); unitAndDirection.set_unit(proto::MS_BEST_FIT_FORMAT); std::unique_ptr builder = std::make_unique(measurement, unitAndDirection); // 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()); mHistograms[measurementAndStory] = std::move(builder); proto::Diagnostic stories; proto::GenericSet *genericSet = stories.mutable_generic_set(); genericSet->add_values(AsJsonString(story)); mHistograms[measurementAndStory]->AddDiagnostic(catapult::kStoriesDiagnostic, stories); } mHistograms[measurementAndStory]->AddSample(value); } void HistogramWriter::getAsJSON(js::Document *doc) const { proto::HistogramSet histogramSet; for (const auto &histogram : mHistograms) { std::unique_ptr proto = histogram.second->toProto(); histogramSet.mutable_histograms()->AddAllocated(proto.release()); } // Custom JSON serialization for histogram-json-set. doc->SetArray(); js::Document::AllocatorType &allocator = doc->GetAllocator(); for (int histogramIndex = 0; histogramIndex < histogramSet.histograms_size(); ++histogramIndex) { const proto::Histogram &histogram = histogramSet.histograms(histogramIndex); js::Value obj(js::kObjectType); js::Value name(histogram.name(), allocator); obj.AddMember("name", name, allocator); js::Value description(histogram.description(), allocator); obj.AddMember("description", description, allocator); js::Value unitAndDirection(GetUnitAndDirection(histogram.unit()), allocator); obj.AddMember("unit", unitAndDirection, allocator); if (histogram.has_diagnostics()) { js::Value diags(js::kObjectType); for (const auto &mapIter : histogram.diagnostics().diagnostic_map()) { js::Value key(mapIter.first, allocator); const proto::Diagnostic &diagnostic = mapIter.second; if (!diagnostic.shared_diagnostic_guid().empty()) { js::Value guid(diagnostic.shared_diagnostic_guid(), allocator); diags.AddMember(key, guid, allocator); } else if (diagnostic.has_generic_set()) { const proto::GenericSet genericSet = diagnostic.generic_set(); js::Value setObj(js::kObjectType); setObj.AddMember("type", "GenericSet", allocator); js::Value values(js::kArrayType); for (const std::string &value : genericSet.values()) { js::Value valueStr(value, allocator); values.PushBack(valueStr, allocator); } setObj.AddMember("values", values, allocator); diags.AddMember(key, setObj, allocator); } else { UNREACHABLE(); } } obj.AddMember("diagnostics", diags, allocator); } js::Value sampleValues(js::kArrayType); for (int sampleIndex = 0; sampleIndex < histogram.sample_values_size(); ++sampleIndex) { js::Value sample(histogram.sample_values(sampleIndex)); sampleValues.PushBack(sample, allocator); } obj.AddMember("sampleValues", sampleValues, allocator); js::Value maxNumSamplesValues(histogram.max_num_sample_values()); obj.AddMember("maxNumSamplesValues", maxNumSamplesValues, allocator); if (histogram.has_bin_boundaries()) { js::Value binBoundaries(js::kArrayType); const proto::BinBoundaries &boundaries = histogram.bin_boundaries(); for (int binIndex = 0; binIndex < boundaries.bin_specs_size(); ++binIndex) { js::Value binSpec(boundaries.bin_specs(binIndex).bin_boundary()); binBoundaries.PushBack(binSpec, allocator); } obj.AddMember("binBoundaries", binBoundaries, allocator); } if (histogram.has_summary_options()) { const proto::SummaryOptions &options = histogram.summary_options(); js::Value summary(js::kObjectType); js::Value avg(options.avg()); js::Value count(options.count()); js::Value max(options.max()); js::Value min(options.min()); js::Value std(options.std()); js::Value sum(options.sum()); summary.AddMember("avg", avg, allocator); summary.AddMember("count", count, allocator); summary.AddMember("max", max, allocator); summary.AddMember("min", min, allocator); summary.AddMember("std", std, allocator); summary.AddMember("sum", sum, allocator); obj.AddMember("summaryOptions", summary, allocator); } if (histogram.has_running()) { const proto::RunningStatistics &running = histogram.running(); js::Value stats(js::kArrayType); js::Value count(running.count()); js::Value max(running.max()); js::Value meanlogs(running.meanlogs()); js::Value mean(running.mean()); js::Value min(running.min()); js::Value sum(running.sum()); js::Value variance(running.variance()); stats.PushBack(count, allocator); stats.PushBack(max, allocator); stats.PushBack(meanlogs, allocator); stats.PushBack(mean, allocator); stats.PushBack(min, allocator); stats.PushBack(sum, allocator); stats.PushBack(variance, allocator); obj.AddMember("running", stats, allocator); } doc->PushBack(obj, allocator); } for (const auto &diagnosticIt : histogramSet.shared_diagnostics()) { const proto::Diagnostic &diagnostic = diagnosticIt.second; js::Value obj(js::kObjectType); js::Value name(diagnosticIt.first, allocator); obj.AddMember("name", name, allocator); switch (diagnostic.diagnostic_oneof_case()) { case proto::Diagnostic::kGenericSet: { js::Value type("GenericSet", allocator); obj.AddMember("type", type, allocator); const proto::GenericSet &genericSet = diagnostic.generic_set(); js::Value values(js::kArrayType); for (const std::string &value : genericSet.values()) { js::Value valueStr(value, allocator); values.PushBack(valueStr, allocator); } obj.AddMember("values", values, allocator); break; } default: UNREACHABLE(); } doc->PushBack(obj, allocator); } } } // namespace angle