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