1 //===-- JSON serialization routines ---------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "JSON.h"
10 #include "LibcBenchmark.h"
11 #include "llvm/ADT/DenseSet.h"
12 #include "llvm/ADT/SmallVector.h"
13 #include "llvm/ADT/StringRef.h"
14 #include "llvm/ADT/StringSwitch.h"
15 #include "llvm/Support/Errc.h"
16 #include "llvm/Support/Error.h"
17 #include "llvm/Support/ErrorHandling.h"
18 #include "llvm/Support/JSON.h"
19 #include "llvm/Support/MathExtras.h"
20
21 #include <chrono>
22 #include <limits>
23 #include <memory>
24 #include <string>
25 #include <vector>
26
27 namespace llvm {
28 namespace libc_benchmarks {
29
30 template <typename T>
intFromJsonTemplate(const json::Value & V,T & Out)31 static Error intFromJsonTemplate(const json::Value &V, T &Out) {
32 if (const auto &MaybeInt64 = V.getAsInteger()) {
33 int64_t Value = *MaybeInt64;
34 if (Value < std::numeric_limits<T>::min() ||
35 Value > std::numeric_limits<T>::max())
36 return createStringError(errc::io_error, "Out of bound Integer");
37 Out = Value;
38 return Error::success();
39 }
40 return createStringError(errc::io_error, "Can't parse Integer");
41 }
42
fromJson(const json::Value & V,double & Out)43 static Error fromJson(const json::Value &V, double &Out) {
44 if (auto S = V.getAsNumber()) {
45 Out = *S;
46 return Error::success();
47 }
48 return createStringError(errc::io_error, "Can't parse Double");
49 }
50
fromJson(const json::Value & V,std::string & Out)51 static Error fromJson(const json::Value &V, std::string &Out) {
52 if (auto S = V.getAsString()) {
53 Out = std::string(*S);
54 return Error::success();
55 }
56 return createStringError(errc::io_error, "Can't parse String");
57 }
58
fromJson(const json::Value & V,uint32_t & Out)59 static Error fromJson(const json::Value &V, uint32_t &Out) {
60 return intFromJsonTemplate(V, Out);
61 }
62
fromJson(const json::Value & V,uint8_t & Out)63 static Error fromJson(const json::Value &V, uint8_t &Out) {
64 return intFromJsonTemplate(V, Out);
65 }
66
fromJson(const json::Value & V,int & Out)67 static Error fromJson(const json::Value &V, int &Out) {
68 return intFromJsonTemplate(V, Out);
69 }
70
fromJson(const json::Value & V,libc_benchmarks::Duration & D)71 static Error fromJson(const json::Value &V, libc_benchmarks::Duration &D) {
72 if (V.kind() != json::Value::Kind::Number)
73 return createStringError(errc::io_error, "Can't parse Duration");
74 D = libc_benchmarks::Duration(*V.getAsNumber());
75 return Error::success();
76 }
77
fromJson(const json::Value & V,MaybeAlign & Out)78 static Error fromJson(const json::Value &V, MaybeAlign &Out) {
79 const auto MaybeInt = V.getAsInteger();
80 if (!MaybeInt)
81 return createStringError(errc::io_error,
82 "Can't parse Align, not an Integer");
83 const int64_t Value = *MaybeInt;
84 if (!Value) {
85 Out = None;
86 return Error::success();
87 }
88 if (isPowerOf2_64(Value)) {
89 Out = Align(Value);
90 return Error::success();
91 }
92 return createStringError(errc::io_error,
93 "Can't parse Align, not a power of two");
94 }
95
fromJson(const json::Value & V,libc_benchmarks::BenchmarkLog & Out)96 static Error fromJson(const json::Value &V,
97 libc_benchmarks::BenchmarkLog &Out) {
98 if (V.kind() != json::Value::Kind::String)
99 return createStringError(errc::io_error,
100 "Can't parse BenchmarkLog, not a String");
101 const auto String = *V.getAsString();
102 auto Parsed =
103 llvm::StringSwitch<Optional<libc_benchmarks::BenchmarkLog>>(String)
104 .Case("None", libc_benchmarks::BenchmarkLog::None)
105 .Case("Last", libc_benchmarks::BenchmarkLog::Last)
106 .Case("Full", libc_benchmarks::BenchmarkLog::Full)
107 .Default(None);
108 if (!Parsed)
109 return createStringError(errc::io_error,
110 Twine("Can't parse BenchmarkLog, invalid value '")
111 .concat(String)
112 .concat("'"));
113 Out = *Parsed;
114 return Error::success();
115 }
116
117 template <typename C>
vectorFromJsonTemplate(const json::Value & V,C & Out)118 Error vectorFromJsonTemplate(const json::Value &V, C &Out) {
119 auto *A = V.getAsArray();
120 if (!A)
121 return createStringError(errc::io_error, "Can't parse Array");
122 Out.clear();
123 Out.resize(A->size());
124 for (auto InOutPair : llvm::zip(*A, Out))
125 if (auto E = fromJson(std::get<0>(InOutPair), std::get<1>(InOutPair)))
126 return std::move(E);
127 return Error::success();
128 }
129
130 template <typename T>
fromJson(const json::Value & V,std::vector<T> & Out)131 static Error fromJson(const json::Value &V, std::vector<T> &Out) {
132 return vectorFromJsonTemplate(V, Out);
133 }
134
135 template <typename T>
fromJson(const json::Value & V,SmallVectorImpl<T> & Out)136 static Error fromJson(const json::Value &V, SmallVectorImpl<T> &Out) {
137 return vectorFromJsonTemplate(V, Out);
138 }
139
140 // Same as llvm::json::ObjectMapper but adds a finer error reporting mechanism.
141 class JsonObjectMapper {
142 const json::Object *O;
143 Error E;
144 SmallDenseSet<StringRef> SeenFields;
145
146 public:
JsonObjectMapper(const json::Value & V)147 explicit JsonObjectMapper(const json::Value &V)
148 : O(V.getAsObject()),
149 E(O ? Error::success()
150 : createStringError(errc::io_error, "Expected JSON Object")) {}
151
takeError()152 Error takeError() {
153 if (E)
154 return std::move(E);
155 for (const auto &Itr : *O) {
156 const StringRef Key = Itr.getFirst();
157 if (!SeenFields.count(Key))
158 E = createStringError(errc::io_error,
159 Twine("Unknown field: ").concat(Key));
160 }
161 return std::move(E);
162 }
163
map(StringRef Key,T & Out)164 template <typename T> void map(StringRef Key, T &Out) {
165 if (E)
166 return;
167 if (const json::Value *Value = O->get(Key)) {
168 SeenFields.insert(Key);
169 E = fromJson(*Value, Out);
170 }
171 }
172 };
173
fromJson(const json::Value & V,libc_benchmarks::BenchmarkOptions & Out)174 static Error fromJson(const json::Value &V,
175 libc_benchmarks::BenchmarkOptions &Out) {
176 JsonObjectMapper O(V);
177 O.map("MinDuration", Out.MinDuration);
178 O.map("MaxDuration", Out.MaxDuration);
179 O.map("InitialIterations", Out.InitialIterations);
180 O.map("MaxIterations", Out.MaxIterations);
181 O.map("MinSamples", Out.MinSamples);
182 O.map("MaxSamples", Out.MaxSamples);
183 O.map("Epsilon", Out.Epsilon);
184 O.map("ScalingFactor", Out.ScalingFactor);
185 O.map("Log", Out.Log);
186 return O.takeError();
187 }
188
fromJson(const json::Value & V,libc_benchmarks::SizeRange & Out)189 static Error fromJson(const json::Value &V, libc_benchmarks::SizeRange &Out) {
190 JsonObjectMapper O(V);
191 O.map("From", Out.From);
192 O.map("To", Out.To);
193 O.map("Step", Out.Step);
194 return O.takeError();
195 }
196
fromJson(const json::Value & V,libc_benchmarks::StudyConfiguration & Out)197 static Error fromJson(const json::Value &V,
198 libc_benchmarks::StudyConfiguration &Out) {
199 JsonObjectMapper O(V);
200 O.map("Runs", Out.Runs);
201 O.map("BufferSize", Out.BufferSize);
202 O.map("Size", Out.Size);
203 O.map("AddressAlignment", Out.AddressAlignment);
204 O.map("MemsetValue", Out.MemsetValue);
205 O.map("MemcmpMismatchAt", Out.MemcmpMismatchAt);
206 return O.takeError();
207 }
208
fromJson(const json::Value & V,libc_benchmarks::CacheInfo & Out)209 static Error fromJson(const json::Value &V, libc_benchmarks::CacheInfo &Out) {
210 JsonObjectMapper O(V);
211 O.map("Type", Out.Type);
212 O.map("Level", Out.Level);
213 O.map("Size", Out.Size);
214 O.map("NumSharing", Out.NumSharing);
215 return O.takeError();
216 }
217
fromJson(const json::Value & V,libc_benchmarks::HostState & Out)218 static Error fromJson(const json::Value &V, libc_benchmarks::HostState &Out) {
219 JsonObjectMapper O(V);
220 O.map("CpuName", Out.CpuName);
221 O.map("CpuFrequency", Out.CpuFrequency);
222 O.map("Caches", Out.Caches);
223 return O.takeError();
224 }
225
fromJson(const json::Value & V,libc_benchmarks::FunctionMeasurements & Out)226 static Error fromJson(const json::Value &V,
227 libc_benchmarks::FunctionMeasurements &Out) {
228 JsonObjectMapper O(V);
229 O.map("Name", Out.Name);
230 std::vector<uint32_t> Sizes;
231 O.map("Sizes", Sizes);
232 std::vector<libc_benchmarks::Duration> Runtimes;
233 O.map("Runtimes", Runtimes);
234 if (Sizes.size() != Runtimes.size())
235 return createStringError(errc::io_error,
236 "Measurement Size and Runtime mistmatch");
237 Out.Measurements.resize(Sizes.size());
238 for (size_t I = 0; I < Sizes.size(); ++I) {
239 Out.Measurements[I].Size = Sizes[I];
240 Out.Measurements[I].Runtime = Runtimes[I];
241 }
242 return O.takeError();
243 }
244
fromJson(const json::Value & V,libc_benchmarks::Study & Out)245 static Error fromJson(const json::Value &V, libc_benchmarks::Study &Out) {
246 JsonObjectMapper O(V);
247 O.map("Host", Out.Host);
248 O.map("Options", Out.Options);
249 O.map("Configuration", Out.Configuration);
250 O.map("Functions", Out.Functions);
251 return O.takeError();
252 }
253
Seconds(const Duration & D)254 static double Seconds(const Duration &D) {
255 return std::chrono::duration<double>(D).count();
256 }
257
ParseJsonStudy(StringRef Content)258 Expected<Study> ParseJsonStudy(StringRef Content) {
259 Expected<json::Value> EV = json::parse(Content);
260 if (!EV)
261 return EV.takeError();
262 Study S;
263 if (Error E = fromJson(*EV, S))
264 return std::move(E);
265 return S;
266 }
267
Serialize(const BenchmarkLog & L)268 static StringRef Serialize(const BenchmarkLog &L) {
269 switch (L) {
270 case BenchmarkLog::None:
271 return "None";
272 case BenchmarkLog::Last:
273 return "Last";
274 case BenchmarkLog::Full:
275 return "Full";
276 }
277 llvm_unreachable("Unhandled BenchmarkLog value");
278 }
279
Serialize(const BenchmarkOptions & BO,json::OStream & JOS)280 static void Serialize(const BenchmarkOptions &BO, json::OStream &JOS) {
281 JOS.object([&]() {
282 JOS.attribute("MinDuration", Seconds(BO.MinDuration));
283 JOS.attribute("MaxDuration", Seconds(BO.MaxDuration));
284 JOS.attribute("InitialIterations", BO.InitialIterations);
285 JOS.attribute("MaxIterations", BO.MaxIterations);
286 JOS.attribute("MinSamples", BO.MinSamples);
287 JOS.attribute("MaxSamples", BO.MaxSamples);
288 JOS.attribute("Epsilon", BO.Epsilon);
289 JOS.attribute("ScalingFactor", BO.ScalingFactor);
290 JOS.attribute("Log", Serialize(BO.Log));
291 });
292 }
293
Serialize(const CacheInfo & CI,json::OStream & JOS)294 static void Serialize(const CacheInfo &CI, json::OStream &JOS) {
295 JOS.object([&]() {
296 JOS.attribute("Type", CI.Type);
297 JOS.attribute("Level", CI.Level);
298 JOS.attribute("Size", CI.Size);
299 JOS.attribute("NumSharing", CI.NumSharing);
300 });
301 }
302
Serialize(const HostState & HS,json::OStream & JOS)303 static void Serialize(const HostState &HS, json::OStream &JOS) {
304 JOS.object([&]() {
305 JOS.attribute("CpuName", HS.CpuName);
306 JOS.attribute("CpuFrequency", HS.CpuFrequency);
307 JOS.attributeArray("Caches", [&]() {
308 for (const auto &CI : HS.Caches)
309 Serialize(CI, JOS);
310 });
311 });
312 }
313
Serialize(const StudyConfiguration & SC,json::OStream & JOS)314 static void Serialize(const StudyConfiguration &SC, json::OStream &JOS) {
315 JOS.object([&]() {
316 JOS.attribute("Runs", SC.Runs);
317 JOS.attribute("BufferSize", SC.BufferSize);
318 JOS.attributeObject("Size", [&]() {
319 JOS.attribute("From", SC.Size.From);
320 JOS.attribute("To", SC.Size.To);
321 JOS.attribute("Step", SC.Size.Step);
322 });
323 if (SC.AddressAlignment)
324 JOS.attribute("AddressAlignment",
325 static_cast<int64_t>(SC.AddressAlignment->value()));
326 JOS.attribute("MemsetValue", SC.MemsetValue);
327 JOS.attribute("MemcmpMismatchAt", SC.MemcmpMismatchAt);
328 });
329 }
330
Serialize(const FunctionMeasurements & FM,json::OStream & JOS)331 static void Serialize(const FunctionMeasurements &FM, json::OStream &JOS) {
332 JOS.object([&]() {
333 JOS.attribute("Name", FM.Name);
334 JOS.attributeArray("Sizes", [&]() {
335 for (const auto &M : FM.Measurements)
336 JOS.value(M.Size);
337 });
338 JOS.attributeArray("Runtimes", [&]() {
339 for (const auto &M : FM.Measurements)
340 JOS.value(Seconds(M.Runtime));
341 });
342 });
343 }
344
SerializeToJson(const Study & S,json::OStream & JOS)345 void SerializeToJson(const Study &S, json::OStream &JOS) {
346 JOS.object([&]() {
347 JOS.attributeBegin("Host");
348 Serialize(S.Host, JOS);
349 JOS.attributeEnd();
350
351 JOS.attributeBegin("Options");
352 Serialize(S.Options, JOS);
353 JOS.attributeEnd();
354
355 JOS.attributeBegin("Configuration");
356 Serialize(S.Configuration, JOS);
357 JOS.attributeEnd();
358
359 if (!S.Functions.empty()) {
360 JOS.attributeArray("Functions", [&]() {
361 for (const auto &FM : S.Functions)
362 Serialize(FM, JOS);
363 });
364 }
365 });
366 }
367
368 } // namespace libc_benchmarks
369 } // namespace llvm
370