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 // 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: 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 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 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 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 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 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 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 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 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 236 static double seconds(const Duration &D) { 237 return std::chrono::duration<double>(D).count(); 238 } 239 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 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 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 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 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 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 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 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