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