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>
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 
43 static Error fromJson(const json::Value &V, bool &Out) {
44   if (auto B = V.getAsBoolean()) {
45     Out = *B;
46     return Error::success();
47   }
48   return createStringError(errc::io_error, "Can't parse Boolean");
49 }
50 
51 static Error fromJson(const json::Value &V, double &Out) {
52   if (auto S = V.getAsNumber()) {
53     Out = *S;
54     return Error::success();
55   }
56   return createStringError(errc::io_error, "Can't parse Double");
57 }
58 
59 static Error fromJson(const json::Value &V, std::string &Out) {
60   if (auto S = V.getAsString()) {
61     Out = std::string(*S);
62     return Error::success();
63   }
64   return createStringError(errc::io_error, "Can't parse String");
65 }
66 
67 static Error fromJson(const json::Value &V, uint32_t &Out) {
68   return intFromJsonTemplate(V, Out);
69 }
70 
71 static Error fromJson(const json::Value &V, int &Out) {
72   return intFromJsonTemplate(V, Out);
73 }
74 
75 static Error fromJson(const json::Value &V, libc_benchmarks::Duration &D) {
76   if (V.kind() != json::Value::Kind::Number)
77     return createStringError(errc::io_error, "Can't parse Duration");
78   D = libc_benchmarks::Duration(*V.getAsNumber());
79   return Error::success();
80 }
81 
82 static Error fromJson(const json::Value &V, MaybeAlign &Out) {
83   const auto MaybeInt = V.getAsInteger();
84   if (!MaybeInt)
85     return createStringError(errc::io_error,
86                              "Can't parse Align, not an Integer");
87   const int64_t Value = *MaybeInt;
88   if (!Value) {
89     Out = None;
90     return Error::success();
91   }
92   if (isPowerOf2_64(Value)) {
93     Out = Align(Value);
94     return Error::success();
95   }
96   return createStringError(errc::io_error,
97                            "Can't parse Align, not a power of two");
98 }
99 
100 static Error fromJson(const json::Value &V,
101                       libc_benchmarks::BenchmarkLog &Out) {
102   if (V.kind() != json::Value::Kind::String)
103     return createStringError(errc::io_error,
104                              "Can't parse BenchmarkLog, not a String");
105   const auto String = *V.getAsString();
106   auto Parsed =
107       llvm::StringSwitch<Optional<libc_benchmarks::BenchmarkLog>>(String)
108           .Case("None", libc_benchmarks::BenchmarkLog::None)
109           .Case("Last", libc_benchmarks::BenchmarkLog::Last)
110           .Case("Full", libc_benchmarks::BenchmarkLog::Full)
111           .Default(None);
112   if (!Parsed)
113     return createStringError(errc::io_error,
114                              Twine("Can't parse BenchmarkLog, invalid value '")
115                                  .concat(String)
116                                  .concat("'"));
117   Out = *Parsed;
118   return Error::success();
119 }
120 
121 template <typename C>
122 Error vectorFromJsonTemplate(const json::Value &V, C &Out) {
123   auto *A = V.getAsArray();
124   if (!A)
125     return createStringError(errc::io_error, "Can't parse Array");
126   Out.clear();
127   Out.resize(A->size());
128   for (auto InOutPair : llvm::zip(*A, Out))
129     if (auto E = fromJson(std::get<0>(InOutPair), std::get<1>(InOutPair)))
130       return std::move(E);
131   return Error::success();
132 }
133 
134 template <typename T>
135 static Error fromJson(const json::Value &V, std::vector<T> &Out) {
136   return vectorFromJsonTemplate(V, Out);
137 }
138 
139 template <typename T>
140 static Error fromJson(const json::Value &V, SmallVectorImpl<T> &Out) {
141   return vectorFromJsonTemplate(V, Out);
142 }
143 
144 // Same as llvm::json::ObjectMapper but adds a finer error reporting mechanism.
145 class JsonObjectMapper {
146   const json::Object *O;
147   Error E;
148   SmallDenseSet<StringRef> SeenFields;
149 
150 public:
151   explicit JsonObjectMapper(const json::Value &V)
152       : O(V.getAsObject()),
153         E(O ? Error::success()
154             : createStringError(errc::io_error, "Expected JSON Object")) {}
155 
156   Error takeError() {
157     if (E)
158       return std::move(E);
159     for (const auto &Itr : *O) {
160       const StringRef Key = Itr.getFirst();
161       if (!SeenFields.count(Key))
162         E = createStringError(errc::io_error,
163                               Twine("Unknown field: ").concat(Key));
164     }
165     return std::move(E);
166   }
167 
168   template <typename T> void map(StringRef Key, T &Out) {
169     if (E)
170       return;
171     if (const json::Value *Value = O->get(Key)) {
172       SeenFields.insert(Key);
173       E = fromJson(*Value, Out);
174     }
175   }
176 };
177 
178 static Error fromJson(const json::Value &V,
179                       libc_benchmarks::BenchmarkOptions &Out) {
180   JsonObjectMapper O(V);
181   O.map("MinDuration", Out.MinDuration);
182   O.map("MaxDuration", Out.MaxDuration);
183   O.map("InitialIterations", Out.InitialIterations);
184   O.map("MaxIterations", Out.MaxIterations);
185   O.map("MinSamples", Out.MinSamples);
186   O.map("MaxSamples", Out.MaxSamples);
187   O.map("Epsilon", Out.Epsilon);
188   O.map("ScalingFactor", Out.ScalingFactor);
189   O.map("Log", Out.Log);
190   return O.takeError();
191 }
192 
193 static Error fromJson(const json::Value &V,
194                       libc_benchmarks::StudyConfiguration &Out) {
195   JsonObjectMapper O(V);
196   O.map("Function", Out.Function);
197   O.map("NumTrials", Out.NumTrials);
198   O.map("IsSweepMode", Out.IsSweepMode);
199   O.map("SweepModeMaxSize", Out.SweepModeMaxSize);
200   O.map("SizeDistributionName", Out.SizeDistributionName);
201   O.map("AccessAlignment", Out.AccessAlignment);
202   O.map("MemcmpMismatchAt", Out.MemcmpMismatchAt);
203   return O.takeError();
204 }
205 
206 static Error fromJson(const json::Value &V, libc_benchmarks::CacheInfo &Out) {
207   JsonObjectMapper O(V);
208   O.map("Type", Out.Type);
209   O.map("Level", Out.Level);
210   O.map("Size", Out.Size);
211   O.map("NumSharing", Out.NumSharing);
212   return O.takeError();
213 }
214 
215 static Error fromJson(const json::Value &V, libc_benchmarks::HostState &Out) {
216   JsonObjectMapper O(V);
217   O.map("CpuName", Out.CpuName);
218   O.map("CpuFrequency", Out.CpuFrequency);
219   O.map("Caches", Out.Caches);
220   return O.takeError();
221 }
222 
223 static Error fromJson(const json::Value &V, libc_benchmarks::Runtime &Out) {
224   JsonObjectMapper O(V);
225   O.map("Host", Out.Host);
226   O.map("BufferSize", Out.BufferSize);
227   O.map("BatchParameterCount", Out.BatchParameterCount);
228   O.map("BenchmarkOptions", Out.BenchmarkOptions);
229   return O.takeError();
230 }
231 
232 static Error fromJson(const json::Value &V, libc_benchmarks::Study &Out) {
233   JsonObjectMapper O(V);
234   O.map("StudyName", Out.StudyName);
235   O.map("Runtime", Out.Runtime);
236   O.map("Configuration", Out.Configuration);
237   O.map("Measurements", Out.Measurements);
238   return O.takeError();
239 }
240 
241 static double seconds(const Duration &D) {
242   return std::chrono::duration<double>(D).count();
243 }
244 
245 Expected<Study> parseJsonStudy(StringRef Content) {
246   Expected<json::Value> EV = json::parse(Content);
247   if (!EV)
248     return EV.takeError();
249   Study S;
250   if (Error E = fromJson(*EV, S))
251     return std::move(E);
252   return S;
253 }
254 
255 static StringRef serialize(const BenchmarkLog &L) {
256   switch (L) {
257   case BenchmarkLog::None:
258     return "None";
259   case BenchmarkLog::Last:
260     return "Last";
261   case BenchmarkLog::Full:
262     return "Full";
263   }
264   llvm_unreachable("Unhandled BenchmarkLog value");
265 }
266 
267 static void serialize(const BenchmarkOptions &BO, json::OStream &JOS) {
268   JOS.attribute("MinDuration", seconds(BO.MinDuration));
269   JOS.attribute("MaxDuration", seconds(BO.MaxDuration));
270   JOS.attribute("InitialIterations", BO.InitialIterations);
271   JOS.attribute("MaxIterations", BO.MaxIterations);
272   JOS.attribute("MinSamples", BO.MinSamples);
273   JOS.attribute("MaxSamples", BO.MaxSamples);
274   JOS.attribute("Epsilon", BO.Epsilon);
275   JOS.attribute("ScalingFactor", BO.ScalingFactor);
276   JOS.attribute("Log", serialize(BO.Log));
277 }
278 
279 static void serialize(const CacheInfo &CI, json::OStream &JOS) {
280   JOS.attribute("Type", CI.Type);
281   JOS.attribute("Level", CI.Level);
282   JOS.attribute("Size", CI.Size);
283   JOS.attribute("NumSharing", CI.NumSharing);
284 }
285 
286 static void serialize(const StudyConfiguration &SC, json::OStream &JOS) {
287   JOS.attribute("Function", SC.Function);
288   JOS.attribute("NumTrials", SC.NumTrials);
289   JOS.attribute("IsSweepMode", SC.IsSweepMode);
290   JOS.attribute("SweepModeMaxSize", SC.SweepModeMaxSize);
291   JOS.attribute("SizeDistributionName", SC.SizeDistributionName);
292   JOS.attribute("AccessAlignment",
293                 static_cast<int64_t>(SC.AccessAlignment->value()));
294   JOS.attribute("MemcmpMismatchAt", SC.MemcmpMismatchAt);
295 }
296 
297 static void serialize(const HostState &HS, json::OStream &JOS) {
298   JOS.attribute("CpuName", HS.CpuName);
299   JOS.attribute("CpuFrequency", HS.CpuFrequency);
300   JOS.attributeArray("Caches", [&]() {
301     for (const auto &CI : HS.Caches)
302       JOS.object([&]() { serialize(CI, JOS); });
303   });
304 }
305 
306 static void serialize(const Runtime &RI, json::OStream &JOS) {
307   JOS.attributeObject("Host", [&]() { serialize(RI.Host, JOS); });
308   JOS.attribute("BufferSize", RI.BufferSize);
309   JOS.attribute("BatchParameterCount", RI.BatchParameterCount);
310   JOS.attributeObject("BenchmarkOptions",
311                       [&]() { serialize(RI.BenchmarkOptions, JOS); });
312 }
313 
314 void serializeToJson(const Study &S, json::OStream &JOS) {
315   JOS.object([&]() {
316     JOS.attribute("StudyName", S.StudyName);
317     JOS.attributeObject("Runtime", [&]() { serialize(S.Runtime, JOS); });
318     JOS.attributeObject("Configuration",
319                         [&]() { serialize(S.Configuration, JOS); });
320     if (!S.Measurements.empty()) {
321       JOS.attributeArray("Measurements", [&]() {
322         for (const auto &M : S.Measurements)
323           JOS.value(seconds(M));
324       });
325     }
326   });
327 }
328 
329 } // namespace libc_benchmarks
330 } // namespace llvm
331