xref: /aosp_15_r20/external/angle/src/tests/test_utils/runner/HistogramWriter.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2020 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // HistogramWriter:
7 //   Helper class for writing histogram-json-set-format files to JSON.
8 
9 #include "HistogramWriter.h"
10 
11 #include "common/debug.h"
12 
13 #include <rapidjson/document.h>
14 
15 #if !ANGLE_HAS_HISTOGRAMS
16 #    error "Must have histograms enabled"
17 #endif  // !ANGLE_HAS_HISTOGRAMS
18 
19 ANGLE_DISABLE_EXTRA_SEMI_WARNING
20 ANGLE_DISABLE_EXTRA_SEMI_STMT_WARNING
21 ANGLE_DISABLE_DESTRUCTOR_OVERRIDE_WARNING
22 ANGLE_DISABLE_SUGGEST_OVERRIDE_WARNINGS
23 #include "tracing/tracing/value/diagnostics/reserved_infos.h"
24 #include "tracing/tracing/value/histogram.h"
25 ANGLE_REENABLE_SUGGEST_OVERRIDE_WARNINGS
26 ANGLE_REENABLE_DESTRUCTOR_OVERRIDE_WARNING
27 ANGLE_REENABLE_EXTRA_SEMI_STMT_WARNING
28 ANGLE_REENABLE_EXTRA_SEMI_WARNING
29 
30 namespace js    = rapidjson;
31 namespace proto = catapult::tracing::tracing::proto;
32 
33 namespace angle
34 {
35 namespace
36 {
UnitAndDirectionToString(proto::UnitAndDirection unit)37 std::string UnitAndDirectionToString(proto::UnitAndDirection unit)
38 {
39     std::stringstream strstr;
40 
41     switch (unit.unit())
42     {
43         case proto::MS_BEST_FIT_FORMAT:
44             strstr << "msBestFitFormat";
45             break;
46         case proto::COUNT:
47             strstr << "count";
48             break;
49         case proto::SIZE_IN_BYTES:
50             strstr << "sizeInBytes";
51             break;
52         default:
53             UNREACHABLE();
54             strstr << "error";
55             break;
56     }
57 
58     switch (unit.improvement_direction())
59     {
60         case proto::NOT_SPECIFIED:
61             break;
62         case proto::SMALLER_IS_BETTER:
63             strstr << "_smallerIsBetter";
64             break;
65         default:
66             UNREACHABLE();
67             break;
68     }
69 
70     return strstr.str();
71 }
72 
StringToUnitAndDirection(const std::string & str)73 proto::UnitAndDirection StringToUnitAndDirection(const std::string &str)
74 {
75     proto::UnitAndDirection unitAndDirection;
76 
77     if (str == "count")
78     {
79         unitAndDirection.set_improvement_direction(proto::NOT_SPECIFIED);
80         unitAndDirection.set_unit(proto::COUNT);
81     }
82     else if (str == "msBestFitFormat_smallerIsBetter")
83     {
84         unitAndDirection.set_improvement_direction(proto::SMALLER_IS_BETTER);
85         unitAndDirection.set_unit(proto::MS_BEST_FIT_FORMAT);
86     }
87     else if (str == "sizeInBytes_smallerIsBetter")
88     {
89         unitAndDirection.set_improvement_direction(proto::SMALLER_IS_BETTER);
90         unitAndDirection.set_unit(proto::SIZE_IN_BYTES);
91     }
92     else
93     {
94         UNREACHABLE();
95     }
96 
97     return unitAndDirection;
98 }
99 }  // namespace
100 
101 HistogramWriter::HistogramWriter() = default;
102 
103 HistogramWriter::~HistogramWriter() = default;
104 
addSample(const std::string & measurement,const std::string & story,double value,const std::string & units)105 void HistogramWriter::addSample(const std::string &measurement,
106                                 const std::string &story,
107                                 double value,
108                                 const std::string &units)
109 {
110     std::string measurementAndStory = measurement + story;
111     if (mHistograms.count(measurementAndStory) == 0)
112     {
113         proto::UnitAndDirection unitAndDirection = StringToUnitAndDirection(units);
114 
115         std::unique_ptr<catapult::HistogramBuilder> builder =
116             std::make_unique<catapult::HistogramBuilder>(measurement, unitAndDirection);
117 
118         // Set all summary options as false - we don't want to generate metric_std, metric_count,
119         // and so on for all metrics.
120         builder->SetSummaryOptions(proto::SummaryOptions());
121         mHistograms[measurementAndStory] = std::move(builder);
122 
123         proto::Diagnostic stories;
124         proto::GenericSet *genericSet = stories.mutable_generic_set();
125         genericSet->add_values(story);
126         mHistograms[measurementAndStory]->AddDiagnostic(catapult::kStoriesDiagnostic, stories);
127     }
128 
129     mHistograms[measurementAndStory]->AddSample(value);
130 }
131 
getAsJSON(js::Document * doc) const132 void HistogramWriter::getAsJSON(js::Document *doc) const
133 {
134     proto::HistogramSet histogramSet;
135 
136     for (const auto &histogram : mHistograms)
137     {
138         std::unique_ptr<proto::Histogram> proto = histogram.second->toProto();
139         histogramSet.mutable_histograms()->AddAllocated(proto.release());
140     }
141 
142     // Custom JSON serialization for histogram-json-set.
143     doc->SetArray();
144 
145     js::Document::AllocatorType &allocator = doc->GetAllocator();
146 
147     for (int histogramIndex = 0; histogramIndex < histogramSet.histograms_size(); ++histogramIndex)
148     {
149         const proto::Histogram &histogram = histogramSet.histograms(histogramIndex);
150 
151         js::Value obj(js::kObjectType);
152 
153         js::Value name(histogram.name(), allocator);
154         obj.AddMember("name", name, allocator);
155 
156         js::Value description(histogram.description(), allocator);
157         obj.AddMember("description", description, allocator);
158 
159         js::Value unitAndDirection(UnitAndDirectionToString(histogram.unit()), allocator);
160         obj.AddMember("unit", unitAndDirection, allocator);
161 
162         if (histogram.has_diagnostics())
163         {
164             js::Value diags(js::kObjectType);
165 
166             for (const auto &mapIter : histogram.diagnostics().diagnostic_map())
167             {
168                 js::Value key(mapIter.first, allocator);
169                 const proto::Diagnostic &diagnostic = mapIter.second;
170 
171                 if (!diagnostic.shared_diagnostic_guid().empty())
172                 {
173                     js::Value guid(diagnostic.shared_diagnostic_guid(), allocator);
174                     diags.AddMember(key, guid, allocator);
175                 }
176                 else if (diagnostic.has_generic_set())
177                 {
178                     const proto::GenericSet genericSet = diagnostic.generic_set();
179 
180                     js::Value setObj(js::kObjectType);
181                     setObj.AddMember("type", "GenericSet", allocator);
182 
183                     js::Value values(js::kArrayType);
184 
185                     for (const std::string &value : genericSet.values())
186                     {
187                         js::Value valueStr(value, allocator);
188                         values.PushBack(valueStr, allocator);
189                     }
190 
191                     setObj.AddMember("values", values, allocator);
192 
193                     diags.AddMember(key, setObj, allocator);
194                 }
195                 else
196                 {
197                     UNREACHABLE();
198                 }
199             }
200 
201             obj.AddMember("diagnostics", diags, allocator);
202         }
203 
204         js::Value sampleValues(js::kArrayType);
205 
206         for (int sampleIndex = 0; sampleIndex < histogram.sample_values_size(); ++sampleIndex)
207         {
208             js::Value sample(histogram.sample_values(sampleIndex));
209             sampleValues.PushBack(sample, allocator);
210         }
211 
212         obj.AddMember("sampleValues", sampleValues, allocator);
213 
214         js::Value maxNumSamplesValues(histogram.max_num_sample_values());
215         obj.AddMember("maxNumSamplesValues", maxNumSamplesValues, allocator);
216 
217         if (histogram.has_bin_boundaries())
218         {
219             js::Value binBoundaries(js::kArrayType);
220 
221             const proto::BinBoundaries &boundaries = histogram.bin_boundaries();
222             for (int binIndex = 0; binIndex < boundaries.bin_specs_size(); ++binIndex)
223             {
224                 js::Value binSpec(boundaries.bin_specs(binIndex).bin_boundary());
225                 binBoundaries.PushBack(binSpec, allocator);
226             }
227 
228             obj.AddMember("binBoundaries", binBoundaries, allocator);
229         }
230 
231         if (histogram.has_summary_options())
232         {
233             const proto::SummaryOptions &options = histogram.summary_options();
234 
235             js::Value summary(js::kObjectType);
236 
237             js::Value avg(options.avg());
238             js::Value count(options.count());
239             js::Value max(options.max());
240             js::Value min(options.min());
241             js::Value std(options.std());
242             js::Value sum(options.sum());
243 
244             summary.AddMember("avg", avg, allocator);
245             summary.AddMember("count", count, allocator);
246             summary.AddMember("max", max, allocator);
247             summary.AddMember("min", min, allocator);
248             summary.AddMember("std", std, allocator);
249             summary.AddMember("sum", sum, allocator);
250 
251             obj.AddMember("summaryOptions", summary, allocator);
252         }
253 
254         if (histogram.has_running())
255         {
256             const proto::RunningStatistics &running = histogram.running();
257 
258             js::Value stats(js::kArrayType);
259 
260             js::Value count(running.count());
261             js::Value max(running.max());
262             js::Value meanlogs(running.meanlogs());
263             js::Value mean(running.mean());
264             js::Value min(running.min());
265             js::Value sum(running.sum());
266             js::Value variance(running.variance());
267 
268             stats.PushBack(count, allocator);
269             stats.PushBack(max, allocator);
270             stats.PushBack(meanlogs, allocator);
271             stats.PushBack(mean, allocator);
272             stats.PushBack(min, allocator);
273             stats.PushBack(sum, allocator);
274             stats.PushBack(variance, allocator);
275 
276             obj.AddMember("running", stats, allocator);
277         }
278 
279         doc->PushBack(obj, allocator);
280     }
281 
282     for (const auto &diagnosticIt : histogramSet.shared_diagnostics())
283     {
284         const proto::Diagnostic &diagnostic = diagnosticIt.second;
285 
286         js::Value obj(js::kObjectType);
287 
288         js::Value name(diagnosticIt.first, allocator);
289         obj.AddMember("name", name, allocator);
290 
291         switch (diagnostic.diagnostic_oneof_case())
292         {
293             case proto::Diagnostic::kGenericSet:
294             {
295                 js::Value type("GenericSet", allocator);
296                 obj.AddMember("type", type, allocator);
297 
298                 const proto::GenericSet &genericSet = diagnostic.generic_set();
299 
300                 js::Value values(js::kArrayType);
301 
302                 for (const std::string &value : genericSet.values())
303                 {
304                     js::Value valueStr(value, allocator);
305                     values.PushBack(valueStr, allocator);
306                 }
307 
308                 obj.AddMember("values", values, allocator);
309                 break;
310             }
311 
312             default:
313                 UNREACHABLE();
314         }
315 
316         doc->PushBack(obj, allocator);
317     }
318 }
319 }  // namespace angle
320