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 !defined(ANGLE_HAS_HISTOGRAMS)
16 # error "Must have histograms enabled"
17 #endif // !defined(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 {
GetUnitAndDirection(proto::UnitAndDirection unit)37 std::string GetUnitAndDirection(proto::UnitAndDirection unit)
38 {
39 ASSERT(unit.improvement_direction() == proto::SMALLER_IS_BETTER);
40 ASSERT(unit.unit() == proto::MS_BEST_FIT_FORMAT);
41 return "msBestFitFormat_smallerIsBetter";
42 }
43 } // namespace
44
45 HistogramWriter::HistogramWriter() = default;
46
47 HistogramWriter::~HistogramWriter() = default;
48
addSample(const std::string & measurement,const std::string & story,double value,const std::string & units)49 void HistogramWriter::addSample(const std::string &measurement,
50 const std::string &story,
51 double value,
52 const std::string &units)
53 {
54 std::string measurementAndStory = measurement + story;
55 if (mHistograms.count(measurementAndStory) == 0)
56 {
57 proto::UnitAndDirection unitAndDirection;
58 unitAndDirection.set_improvement_direction(proto::SMALLER_IS_BETTER);
59 unitAndDirection.set_unit(proto::MS_BEST_FIT_FORMAT);
60
61 std::unique_ptr<catapult::HistogramBuilder> builder =
62 std::make_unique<catapult::HistogramBuilder>(measurement, unitAndDirection);
63
64 // Set all summary options as false - we don't want to generate metric_std, metric_count,
65 // and so on for all metrics.
66 builder->SetSummaryOptions(proto::SummaryOptions());
67 mHistograms[measurementAndStory] = std::move(builder);
68
69 proto::Diagnostic stories;
70 proto::GenericSet *genericSet = stories.mutable_generic_set();
71 genericSet->add_values(story);
72 mHistograms[measurementAndStory]->AddDiagnostic(catapult::kStoriesDiagnostic, stories);
73 }
74
75 mHistograms[measurementAndStory]->AddSample(value);
76 }
77
getAsJSON(js::Document * doc) const78 void HistogramWriter::getAsJSON(js::Document *doc) const
79 {
80 proto::HistogramSet histogramSet;
81
82 for (const auto &histogram : mHistograms)
83 {
84 std::unique_ptr<proto::Histogram> proto = histogram.second->toProto();
85 histogramSet.mutable_histograms()->AddAllocated(proto.release());
86 }
87
88 // Custom JSON serialization for histogram-json-set.
89 doc->SetArray();
90
91 js::Document::AllocatorType &allocator = doc->GetAllocator();
92
93 for (int histogramIndex = 0; histogramIndex < histogramSet.histograms_size(); ++histogramIndex)
94 {
95 const proto::Histogram &histogram = histogramSet.histograms(histogramIndex);
96
97 js::Value obj(js::kObjectType);
98
99 js::Value name(histogram.name(), allocator);
100 obj.AddMember("name", name, allocator);
101
102 js::Value description(histogram.description(), allocator);
103 obj.AddMember("description", description, allocator);
104
105 js::Value unitAndDirection(GetUnitAndDirection(histogram.unit()), allocator);
106 obj.AddMember("unit", unitAndDirection, allocator);
107
108 if (histogram.has_diagnostics())
109 {
110 js::Value diags(js::kObjectType);
111
112 for (const auto &mapIter : histogram.diagnostics().diagnostic_map())
113 {
114 js::Value key(mapIter.first, allocator);
115 const proto::Diagnostic &diagnostic = mapIter.second;
116
117 if (!diagnostic.shared_diagnostic_guid().empty())
118 {
119 js::Value guid(diagnostic.shared_diagnostic_guid(), allocator);
120 diags.AddMember(key, guid, allocator);
121 }
122 else if (diagnostic.has_generic_set())
123 {
124 const proto::GenericSet genericSet = diagnostic.generic_set();
125
126 js::Value setObj(js::kObjectType);
127 setObj.AddMember("type", "GenericSet", allocator);
128
129 js::Value values(js::kArrayType);
130
131 for (const std::string &value : genericSet.values())
132 {
133 js::Value valueStr(value, allocator);
134 values.PushBack(valueStr, allocator);
135 }
136
137 setObj.AddMember("values", values, allocator);
138
139 diags.AddMember(key, setObj, allocator);
140 }
141 else
142 {
143 UNREACHABLE();
144 }
145 }
146
147 obj.AddMember("diagnostics", diags, allocator);
148 }
149
150 js::Value sampleValues(js::kArrayType);
151
152 for (int sampleIndex = 0; sampleIndex < histogram.sample_values_size(); ++sampleIndex)
153 {
154 js::Value sample(histogram.sample_values(sampleIndex));
155 sampleValues.PushBack(sample, allocator);
156 }
157
158 obj.AddMember("sampleValues", sampleValues, allocator);
159
160 js::Value maxNumSamplesValues(histogram.max_num_sample_values());
161 obj.AddMember("maxNumSamplesValues", maxNumSamplesValues, allocator);
162
163 if (histogram.has_bin_boundaries())
164 {
165 js::Value binBoundaries(js::kArrayType);
166
167 const proto::BinBoundaries &boundaries = histogram.bin_boundaries();
168 for (int binIndex = 0; binIndex < boundaries.bin_specs_size(); ++binIndex)
169 {
170 js::Value binSpec(boundaries.bin_specs(binIndex).bin_boundary());
171 binBoundaries.PushBack(binSpec, allocator);
172 }
173
174 obj.AddMember("binBoundaries", binBoundaries, allocator);
175 }
176
177 if (histogram.has_summary_options())
178 {
179 const proto::SummaryOptions &options = histogram.summary_options();
180
181 js::Value summary(js::kObjectType);
182
183 js::Value avg(options.avg());
184 js::Value count(options.count());
185 js::Value max(options.max());
186 js::Value min(options.min());
187 js::Value std(options.std());
188 js::Value sum(options.sum());
189
190 summary.AddMember("avg", avg, allocator);
191 summary.AddMember("count", count, allocator);
192 summary.AddMember("max", max, allocator);
193 summary.AddMember("min", min, allocator);
194 summary.AddMember("std", std, allocator);
195 summary.AddMember("sum", sum, allocator);
196
197 obj.AddMember("summaryOptions", summary, allocator);
198 }
199
200 if (histogram.has_running())
201 {
202 const proto::RunningStatistics &running = histogram.running();
203
204 js::Value stats(js::kArrayType);
205
206 js::Value count(running.count());
207 js::Value max(running.max());
208 js::Value meanlogs(running.meanlogs());
209 js::Value mean(running.mean());
210 js::Value min(running.min());
211 js::Value sum(running.sum());
212 js::Value variance(running.variance());
213
214 stats.PushBack(count, allocator);
215 stats.PushBack(max, allocator);
216 stats.PushBack(meanlogs, allocator);
217 stats.PushBack(mean, allocator);
218 stats.PushBack(min, allocator);
219 stats.PushBack(sum, allocator);
220 stats.PushBack(variance, allocator);
221
222 obj.AddMember("running", stats, allocator);
223 }
224
225 doc->PushBack(obj, allocator);
226 }
227
228 for (const auto &diagnosticIt : histogramSet.shared_diagnostics())
229 {
230 const proto::Diagnostic &diagnostic = diagnosticIt.second;
231
232 js::Value obj(js::kObjectType);
233
234 js::Value name(diagnosticIt.first, allocator);
235 obj.AddMember("name", name, allocator);
236
237 switch (diagnostic.diagnostic_oneof_case())
238 {
239 case proto::Diagnostic::kGenericSet:
240 {
241 js::Value type("GenericSet", allocator);
242 obj.AddMember("type", type, allocator);
243
244 const proto::GenericSet &genericSet = diagnostic.generic_set();
245
246 js::Value values(js::kArrayType);
247
248 for (const std::string &value : genericSet.values())
249 {
250 js::Value valueStr(value, allocator);
251 values.PushBack(valueStr, allocator);
252 }
253
254 obj.AddMember("values", values, allocator);
255 break;
256 }
257
258 default:
259 UNREACHABLE();
260 }
261
262 doc->PushBack(obj, allocator);
263 }
264 }
265 } // namespace angle
266