blob: 6443ff4ca8bab521408ecc6f4a81e1c6b6e472df [file] [log] [blame]
Paula Toth66d00fe2020-04-08 10:16:30 -07001//===-- JSON serialization routines ---------------------------------------===//
Guillaume Chateletaba80d02020-01-06 13:17:04 +01002//
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"
Siva Chandra Reddy1852af12020-04-28 14:00:12 -070020
Guillaume Chateletaba80d02020-01-06 13:17:04 +010021#include <chrono>
22#include <limits>
23#include <memory>
Siva Chandra Reddy8dcb7f62022-12-11 00:20:33 +000024#include <optional>
Siva Chandra Reddy1852af12020-04-28 14:00:12 -070025#include <string>
Guillaume Chateletaba80d02020-01-06 13:17:04 +010026#include <vector>
27
28namespace llvm {
29namespace libc_benchmarks {
30
31template <typename T>
32static Error intFromJsonTemplate(const json::Value &V, T &Out) {
33 if (const auto &MaybeInt64 = V.getAsInteger()) {
34 int64_t Value = *MaybeInt64;
35 if (Value < std::numeric_limits<T>::min() ||
36 Value > std::numeric_limits<T>::max())
37 return createStringError(errc::io_error, "Out of bound Integer");
38 Out = Value;
39 return Error::success();
40 }
41 return createStringError(errc::io_error, "Can't parse Integer");
42}
43
Guillaume Chateletdeae7e92020-12-17 13:16:14 +000044static Error fromJson(const json::Value &V, bool &Out) {
45 if (auto B = V.getAsBoolean()) {
46 Out = *B;
47 return Error::success();
48 }
49 return createStringError(errc::io_error, "Can't parse Boolean");
50}
51
Guillaume Chateletaba80d02020-01-06 13:17:04 +010052static Error fromJson(const json::Value &V, double &Out) {
53 if (auto S = V.getAsNumber()) {
54 Out = *S;
55 return Error::success();
56 }
57 return createStringError(errc::io_error, "Can't parse Double");
58}
59
60static Error fromJson(const json::Value &V, std::string &Out) {
61 if (auto S = V.getAsString()) {
Siva Chandra Reddy1852af12020-04-28 14:00:12 -070062 Out = std::string(*S);
Guillaume Chateletaba80d02020-01-06 13:17:04 +010063 return Error::success();
64 }
65 return createStringError(errc::io_error, "Can't parse String");
66}
67
68static Error fromJson(const json::Value &V, uint32_t &Out) {
69 return intFromJsonTemplate(V, Out);
70}
71
Guillaume Chateletaba80d02020-01-06 13:17:04 +010072static Error fromJson(const json::Value &V, int &Out) {
73 return intFromJsonTemplate(V, Out);
74}
75
76static Error fromJson(const json::Value &V, libc_benchmarks::Duration &D) {
77 if (V.kind() != json::Value::Kind::Number)
78 return createStringError(errc::io_error, "Can't parse Duration");
79 D = libc_benchmarks::Duration(*V.getAsNumber());
80 return Error::success();
81}
82
83static Error fromJson(const json::Value &V, MaybeAlign &Out) {
84 const auto MaybeInt = V.getAsInteger();
85 if (!MaybeInt)
86 return createStringError(errc::io_error,
87 "Can't parse Align, not an Integer");
88 const int64_t Value = *MaybeInt;
89 if (!Value) {
Siva Chandra Reddy8dcb7f62022-12-11 00:20:33 +000090 Out = std::nullopt;
Guillaume Chateletaba80d02020-01-06 13:17:04 +010091 return Error::success();
92 }
93 if (isPowerOf2_64(Value)) {
94 Out = Align(Value);
95 return Error::success();
96 }
97 return createStringError(errc::io_error,
98 "Can't parse Align, not a power of two");
99}
100
101static Error fromJson(const json::Value &V,
102 libc_benchmarks::BenchmarkLog &Out) {
103 if (V.kind() != json::Value::Kind::String)
104 return createStringError(errc::io_error,
105 "Can't parse BenchmarkLog, not a String");
106 const auto String = *V.getAsString();
107 auto Parsed =
Kazu Hirata660c33e2023-01-14 21:10:14 -0800108 llvm::StringSwitch<std::optional<libc_benchmarks::BenchmarkLog>>(String)
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100109 .Case("None", libc_benchmarks::BenchmarkLog::None)
110 .Case("Last", libc_benchmarks::BenchmarkLog::Last)
111 .Case("Full", libc_benchmarks::BenchmarkLog::Full)
Siva Chandra Reddy8dcb7f62022-12-11 00:20:33 +0000112 .Default(std::nullopt);
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100113 if (!Parsed)
114 return createStringError(errc::io_error,
115 Twine("Can't parse BenchmarkLog, invalid value '")
116 .concat(String)
117 .concat("'"));
118 Out = *Parsed;
119 return Error::success();
120}
121
122template <typename C>
123Error vectorFromJsonTemplate(const json::Value &V, C &Out) {
124 auto *A = V.getAsArray();
125 if (!A)
126 return createStringError(errc::io_error, "Can't parse Array");
127 Out.clear();
128 Out.resize(A->size());
129 for (auto InOutPair : llvm::zip(*A, Out))
130 if (auto E = fromJson(std::get<0>(InOutPair), std::get<1>(InOutPair)))
131 return std::move(E);
132 return Error::success();
133}
134
135template <typename T>
136static Error fromJson(const json::Value &V, std::vector<T> &Out) {
137 return vectorFromJsonTemplate(V, Out);
138}
139
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100140// Same as llvm::json::ObjectMapper but adds a finer error reporting mechanism.
141class JsonObjectMapper {
142 const json::Object *O;
143 Error E;
144 SmallDenseSet<StringRef> SeenFields;
145
146public:
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
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
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
174static 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
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100189static Error fromJson(const json::Value &V,
190 libc_benchmarks::StudyConfiguration &Out) {
191 JsonObjectMapper O(V);
Guillaume Chateletdeae7e92020-12-17 13:16:14 +0000192 O.map("Function", Out.Function);
193 O.map("NumTrials", Out.NumTrials);
194 O.map("IsSweepMode", Out.IsSweepMode);
195 O.map("SweepModeMaxSize", Out.SweepModeMaxSize);
196 O.map("SizeDistributionName", Out.SizeDistributionName);
197 O.map("AccessAlignment", Out.AccessAlignment);
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100198 O.map("MemcmpMismatchAt", Out.MemcmpMismatchAt);
199 return O.takeError();
200}
201
202static Error fromJson(const json::Value &V, libc_benchmarks::CacheInfo &Out) {
203 JsonObjectMapper O(V);
204 O.map("Type", Out.Type);
205 O.map("Level", Out.Level);
206 O.map("Size", Out.Size);
207 O.map("NumSharing", Out.NumSharing);
208 return O.takeError();
209}
210
211static Error fromJson(const json::Value &V, libc_benchmarks::HostState &Out) {
212 JsonObjectMapper O(V);
213 O.map("CpuName", Out.CpuName);
214 O.map("CpuFrequency", Out.CpuFrequency);
215 O.map("Caches", Out.Caches);
216 return O.takeError();
217}
218
Guillaume Chateletdeae7e92020-12-17 13:16:14 +0000219static Error fromJson(const json::Value &V, libc_benchmarks::Runtime &Out) {
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100220 JsonObjectMapper O(V);
Guillaume Chateletdeae7e92020-12-17 13:16:14 +0000221 O.map("Host", Out.Host);
222 O.map("BufferSize", Out.BufferSize);
223 O.map("BatchParameterCount", Out.BatchParameterCount);
224 O.map("BenchmarkOptions", Out.BenchmarkOptions);
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100225 return O.takeError();
226}
227
228static Error fromJson(const json::Value &V, libc_benchmarks::Study &Out) {
229 JsonObjectMapper O(V);
Guillaume Chateletdeae7e92020-12-17 13:16:14 +0000230 O.map("StudyName", Out.StudyName);
231 O.map("Runtime", Out.Runtime);
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100232 O.map("Configuration", Out.Configuration);
Guillaume Chateletdeae7e92020-12-17 13:16:14 +0000233 O.map("Measurements", Out.Measurements);
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100234 return O.takeError();
235}
236
Guillaume Chateletdeae7e92020-12-17 13:16:14 +0000237static double seconds(const Duration &D) {
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100238 return std::chrono::duration<double>(D).count();
239}
240
Guillaume Chateletdeae7e92020-12-17 13:16:14 +0000241Expected<Study> parseJsonStudy(StringRef Content) {
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100242 Expected<json::Value> EV = json::parse(Content);
243 if (!EV)
244 return EV.takeError();
245 Study S;
246 if (Error E = fromJson(*EV, S))
247 return std::move(E);
248 return S;
249}
250
Guillaume Chateletdeae7e92020-12-17 13:16:14 +0000251static StringRef serialize(const BenchmarkLog &L) {
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100252 switch (L) {
253 case BenchmarkLog::None:
254 return "None";
255 case BenchmarkLog::Last:
256 return "Last";
257 case BenchmarkLog::Full:
258 return "Full";
259 }
260 llvm_unreachable("Unhandled BenchmarkLog value");
261}
262
Guillaume Chateletdeae7e92020-12-17 13:16:14 +0000263static void serialize(const BenchmarkOptions &BO, json::OStream &JOS) {
264 JOS.attribute("MinDuration", seconds(BO.MinDuration));
265 JOS.attribute("MaxDuration", seconds(BO.MaxDuration));
266 JOS.attribute("InitialIterations", BO.InitialIterations);
267 JOS.attribute("MaxIterations", BO.MaxIterations);
268 JOS.attribute("MinSamples", BO.MinSamples);
269 JOS.attribute("MaxSamples", BO.MaxSamples);
270 JOS.attribute("Epsilon", BO.Epsilon);
271 JOS.attribute("ScalingFactor", BO.ScalingFactor);
272 JOS.attribute("Log", serialize(BO.Log));
273}
274
275static void serialize(const CacheInfo &CI, json::OStream &JOS) {
276 JOS.attribute("Type", CI.Type);
277 JOS.attribute("Level", CI.Level);
278 JOS.attribute("Size", CI.Size);
279 JOS.attribute("NumSharing", CI.NumSharing);
280}
281
282static void serialize(const StudyConfiguration &SC, json::OStream &JOS) {
283 JOS.attribute("Function", SC.Function);
284 JOS.attribute("NumTrials", SC.NumTrials);
285 JOS.attribute("IsSweepMode", SC.IsSweepMode);
286 JOS.attribute("SweepModeMaxSize", SC.SweepModeMaxSize);
287 JOS.attribute("SizeDistributionName", SC.SizeDistributionName);
288 JOS.attribute("AccessAlignment",
289 static_cast<int64_t>(SC.AccessAlignment->value()));
290 JOS.attribute("MemcmpMismatchAt", SC.MemcmpMismatchAt);
291}
292
293static void serialize(const HostState &HS, json::OStream &JOS) {
294 JOS.attribute("CpuName", HS.CpuName);
295 JOS.attribute("CpuFrequency", HS.CpuFrequency);
296 JOS.attributeArray("Caches", [&]() {
297 for (const auto &CI : HS.Caches)
298 JOS.object([&]() { serialize(CI, JOS); });
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100299 });
300}
301
Guillaume Chateletdeae7e92020-12-17 13:16:14 +0000302static void serialize(const Runtime &RI, json::OStream &JOS) {
303 JOS.attributeObject("Host", [&]() { serialize(RI.Host, JOS); });
304 JOS.attribute("BufferSize", RI.BufferSize);
305 JOS.attribute("BatchParameterCount", RI.BatchParameterCount);
306 JOS.attributeObject("BenchmarkOptions",
307 [&]() { serialize(RI.BenchmarkOptions, JOS); });
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100308}
309
Guillaume Chateletdeae7e92020-12-17 13:16:14 +0000310void serializeToJson(const Study &S, json::OStream &JOS) {
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100311 JOS.object([&]() {
Guillaume Chateletdeae7e92020-12-17 13:16:14 +0000312 JOS.attribute("StudyName", S.StudyName);
313 JOS.attributeObject("Runtime", [&]() { serialize(S.Runtime, JOS); });
314 JOS.attributeObject("Configuration",
315 [&]() { serialize(S.Configuration, JOS); });
316 if (!S.Measurements.empty()) {
317 JOS.attributeArray("Measurements", [&]() {
318 for (const auto &M : S.Measurements)
319 JOS.value(seconds(M));
Guillaume Chateletaba80d02020-01-06 13:17:04 +0100320 });
321 }
322 });
323}
324
325} // namespace libc_benchmarks
326} // namespace llvm