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, 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 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 59 static Error fromJson(const json::Value &V, uint32_t &Out) { 60 return intFromJsonTemplate(V, Out); 61 } 62 63 static Error fromJson(const json::Value &V, uint8_t &Out) { 64 return intFromJsonTemplate(V, Out); 65 } 66 67 static Error fromJson(const json::Value &V, int &Out) { 68 return intFromJsonTemplate(V, Out); 69 } 70 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 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 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> 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> 131 static Error fromJson(const json::Value &V, std::vector<T> &Out) { 132 return vectorFromJsonTemplate(V, Out); 133 } 134 135 template <typename T> 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: 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 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 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 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 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 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 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 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 254 static double Seconds(const Duration &D) { 255 return std::chrono::duration<double>(D).count(); 256 } 257 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 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 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 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 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 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 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 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