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