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