327 lines · cpp
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-exception6//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 <optional>25#include <string>26#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 44static 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 52static 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()) {62 Out = std::string(*S);63 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 72static 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) {90 Out = std::nullopt;91 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 =108 llvm::StringSwitch<std::optional<libc_benchmarks::BenchmarkLog>>(String)109 .Case("None", libc_benchmarks::BenchmarkLog::None)110 .Case("Last", libc_benchmarks::BenchmarkLog::Last)111 .Case("Full", libc_benchmarks::BenchmarkLog::Full)112 .Default(std::nullopt);113 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 140// 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 189static Error fromJson(const json::Value &V,190 libc_benchmarks::StudyConfiguration &Out) {191 JsonObjectMapper O(V);192 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);198 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 219static Error fromJson(const json::Value &V, libc_benchmarks::Runtime &Out) {220 JsonObjectMapper O(V);221 O.map("Host", Out.Host);222 O.map("BufferSize", Out.BufferSize);223 O.map("BatchParameterCount", Out.BatchParameterCount);224 O.map("BenchmarkOptions", Out.BenchmarkOptions);225 return O.takeError();226}227 228static Error fromJson(const json::Value &V, libc_benchmarks::Study &Out) {229 JsonObjectMapper O(V);230 O.map("StudyName", Out.StudyName);231 O.map("Runtime", Out.Runtime);232 O.map("Configuration", Out.Configuration);233 O.map("Measurements", Out.Measurements);234 return O.takeError();235}236 237static double seconds(const Duration &D) {238 return std::chrono::duration<double>(D).count();239}240 241Expected<Study> parseJsonStudy(StringRef Content) {242 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 251static StringRef serialize(const BenchmarkLog &L) {252 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 263static 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); });299 });300}301 302static 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); });308}309 310void serializeToJson(const Study &S, json::OStream &JOS) {311 JOS.object([&]() {312 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));320 });321 }322 });323}324 325} // namespace libc_benchmarks326} // namespace llvm327