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,bool & Out)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
fromJson(const json::Value & V,double & Out)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
fromJson(const json::Value & V,std::string & Out)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
fromJson(const json::Value & V,uint32_t & Out)67 static Error fromJson(const json::Value &V, uint32_t &Out) {
68 return intFromJsonTemplate(V, Out);
69 }
70
fromJson(const json::Value & V,int & Out)71 static Error fromJson(const json::Value &V, int &Out) {
72 return intFromJsonTemplate(V, Out);
73 }
74
fromJson(const json::Value & V,libc_benchmarks::Duration & D)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
fromJson(const json::Value & V,MaybeAlign & Out)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
fromJson(const json::Value & V,libc_benchmarks::BenchmarkLog & Out)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>
vectorFromJsonTemplate(const json::Value & V,C & Out)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>
fromJson(const json::Value & V,std::vector<T> & Out)135 static Error fromJson(const json::Value &V, std::vector<T> &Out) {
136 return vectorFromJsonTemplate(V, Out);
137 }
138
139 // Same as llvm::json::ObjectMapper but adds a finer error reporting mechanism.
140 class JsonObjectMapper {
141 const json::Object *O;
142 Error E;
143 SmallDenseSet<StringRef> SeenFields;
144
145 public:
JsonObjectMapper(const json::Value & V)146 explicit JsonObjectMapper(const json::Value &V)
147 : O(V.getAsObject()),
148 E(O ? Error::success()
149 : createStringError(errc::io_error, "Expected JSON Object")) {}
150
takeError()151 Error takeError() {
152 if (E)
153 return std::move(E);
154 for (const auto &Itr : *O) {
155 const StringRef Key = Itr.getFirst();
156 if (!SeenFields.count(Key))
157 E = createStringError(errc::io_error,
158 Twine("Unknown field: ").concat(Key));
159 }
160 return std::move(E);
161 }
162
map(StringRef Key,T & Out)163 template <typename T> void map(StringRef Key, T &Out) {
164 if (E)
165 return;
166 if (const json::Value *Value = O->get(Key)) {
167 SeenFields.insert(Key);
168 E = fromJson(*Value, Out);
169 }
170 }
171 };
172
fromJson(const json::Value & V,libc_benchmarks::BenchmarkOptions & Out)173 static Error fromJson(const json::Value &V,
174 libc_benchmarks::BenchmarkOptions &Out) {
175 JsonObjectMapper O(V);
176 O.map("MinDuration", Out.MinDuration);
177 O.map("MaxDuration", Out.MaxDuration);
178 O.map("InitialIterations", Out.InitialIterations);
179 O.map("MaxIterations", Out.MaxIterations);
180 O.map("MinSamples", Out.MinSamples);
181 O.map("MaxSamples", Out.MaxSamples);
182 O.map("Epsilon", Out.Epsilon);
183 O.map("ScalingFactor", Out.ScalingFactor);
184 O.map("Log", Out.Log);
185 return O.takeError();
186 }
187
fromJson(const json::Value & V,libc_benchmarks::StudyConfiguration & Out)188 static Error fromJson(const json::Value &V,
189 libc_benchmarks::StudyConfiguration &Out) {
190 JsonObjectMapper O(V);
191 O.map("Function", Out.Function);
192 O.map("NumTrials", Out.NumTrials);
193 O.map("IsSweepMode", Out.IsSweepMode);
194 O.map("SweepModeMaxSize", Out.SweepModeMaxSize);
195 O.map("SizeDistributionName", Out.SizeDistributionName);
196 O.map("AccessAlignment", Out.AccessAlignment);
197 O.map("MemcmpMismatchAt", Out.MemcmpMismatchAt);
198 return O.takeError();
199 }
200
fromJson(const json::Value & V,libc_benchmarks::CacheInfo & Out)201 static Error fromJson(const json::Value &V, libc_benchmarks::CacheInfo &Out) {
202 JsonObjectMapper O(V);
203 O.map("Type", Out.Type);
204 O.map("Level", Out.Level);
205 O.map("Size", Out.Size);
206 O.map("NumSharing", Out.NumSharing);
207 return O.takeError();
208 }
209
fromJson(const json::Value & V,libc_benchmarks::HostState & Out)210 static Error fromJson(const json::Value &V, libc_benchmarks::HostState &Out) {
211 JsonObjectMapper O(V);
212 O.map("CpuName", Out.CpuName);
213 O.map("CpuFrequency", Out.CpuFrequency);
214 O.map("Caches", Out.Caches);
215 return O.takeError();
216 }
217
fromJson(const json::Value & V,libc_benchmarks::Runtime & Out)218 static Error fromJson(const json::Value &V, libc_benchmarks::Runtime &Out) {
219 JsonObjectMapper O(V);
220 O.map("Host", Out.Host);
221 O.map("BufferSize", Out.BufferSize);
222 O.map("BatchParameterCount", Out.BatchParameterCount);
223 O.map("BenchmarkOptions", Out.BenchmarkOptions);
224 return O.takeError();
225 }
226
fromJson(const json::Value & V,libc_benchmarks::Study & Out)227 static Error fromJson(const json::Value &V, libc_benchmarks::Study &Out) {
228 JsonObjectMapper O(V);
229 O.map("StudyName", Out.StudyName);
230 O.map("Runtime", Out.Runtime);
231 O.map("Configuration", Out.Configuration);
232 O.map("Measurements", Out.Measurements);
233 return O.takeError();
234 }
235
seconds(const Duration & D)236 static double seconds(const Duration &D) {
237 return std::chrono::duration<double>(D).count();
238 }
239
parseJsonStudy(StringRef Content)240 Expected<Study> parseJsonStudy(StringRef Content) {
241 Expected<json::Value> EV = json::parse(Content);
242 if (!EV)
243 return EV.takeError();
244 Study S;
245 if (Error E = fromJson(*EV, S))
246 return std::move(E);
247 return S;
248 }
249
serialize(const BenchmarkLog & L)250 static StringRef serialize(const BenchmarkLog &L) {
251 switch (L) {
252 case BenchmarkLog::None:
253 return "None";
254 case BenchmarkLog::Last:
255 return "Last";
256 case BenchmarkLog::Full:
257 return "Full";
258 }
259 llvm_unreachable("Unhandled BenchmarkLog value");
260 }
261
serialize(const BenchmarkOptions & BO,json::OStream & JOS)262 static void serialize(const BenchmarkOptions &BO, json::OStream &JOS) {
263 JOS.attribute("MinDuration", seconds(BO.MinDuration));
264 JOS.attribute("MaxDuration", seconds(BO.MaxDuration));
265 JOS.attribute("InitialIterations", BO.InitialIterations);
266 JOS.attribute("MaxIterations", BO.MaxIterations);
267 JOS.attribute("MinSamples", BO.MinSamples);
268 JOS.attribute("MaxSamples", BO.MaxSamples);
269 JOS.attribute("Epsilon", BO.Epsilon);
270 JOS.attribute("ScalingFactor", BO.ScalingFactor);
271 JOS.attribute("Log", serialize(BO.Log));
272 }
273
serialize(const CacheInfo & CI,json::OStream & JOS)274 static void serialize(const CacheInfo &CI, json::OStream &JOS) {
275 JOS.attribute("Type", CI.Type);
276 JOS.attribute("Level", CI.Level);
277 JOS.attribute("Size", CI.Size);
278 JOS.attribute("NumSharing", CI.NumSharing);
279 }
280
serialize(const StudyConfiguration & SC,json::OStream & JOS)281 static void serialize(const StudyConfiguration &SC, json::OStream &JOS) {
282 JOS.attribute("Function", SC.Function);
283 JOS.attribute("NumTrials", SC.NumTrials);
284 JOS.attribute("IsSweepMode", SC.IsSweepMode);
285 JOS.attribute("SweepModeMaxSize", SC.SweepModeMaxSize);
286 JOS.attribute("SizeDistributionName", SC.SizeDistributionName);
287 JOS.attribute("AccessAlignment",
288 static_cast<int64_t>(SC.AccessAlignment->value()));
289 JOS.attribute("MemcmpMismatchAt", SC.MemcmpMismatchAt);
290 }
291
serialize(const HostState & HS,json::OStream & JOS)292 static void serialize(const HostState &HS, json::OStream &JOS) {
293 JOS.attribute("CpuName", HS.CpuName);
294 JOS.attribute("CpuFrequency", HS.CpuFrequency);
295 JOS.attributeArray("Caches", [&]() {
296 for (const auto &CI : HS.Caches)
297 JOS.object([&]() { serialize(CI, JOS); });
298 });
299 }
300
serialize(const Runtime & RI,json::OStream & JOS)301 static void serialize(const Runtime &RI, json::OStream &JOS) {
302 JOS.attributeObject("Host", [&]() { serialize(RI.Host, JOS); });
303 JOS.attribute("BufferSize", RI.BufferSize);
304 JOS.attribute("BatchParameterCount", RI.BatchParameterCount);
305 JOS.attributeObject("BenchmarkOptions",
306 [&]() { serialize(RI.BenchmarkOptions, JOS); });
307 }
308
serializeToJson(const Study & S,json::OStream & JOS)309 void serializeToJson(const Study &S, json::OStream &JOS) {
310 JOS.object([&]() {
311 JOS.attribute("StudyName", S.StudyName);
312 JOS.attributeObject("Runtime", [&]() { serialize(S.Runtime, JOS); });
313 JOS.attributeObject("Configuration",
314 [&]() { serialize(S.Configuration, JOS); });
315 if (!S.Measurements.empty()) {
316 JOS.attributeArray("Measurements", [&]() {
317 for (const auto &M : S.Measurements)
318 JOS.value(seconds(M));
319 });
320 }
321 });
322 }
323
324 } // namespace libc_benchmarks
325 } // namespace llvm
326