| //===-- JSON serialization routines ---------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "JSON.h" | 
 | #include "LibcBenchmark.h" | 
 | #include "llvm/ADT/DenseSet.h" | 
 | #include "llvm/ADT/SmallVector.h" | 
 | #include "llvm/ADT/StringRef.h" | 
 | #include "llvm/ADT/StringSwitch.h" | 
 | #include "llvm/Support/Errc.h" | 
 | #include "llvm/Support/Error.h" | 
 | #include "llvm/Support/ErrorHandling.h" | 
 | #include "llvm/Support/JSON.h" | 
 | #include "llvm/Support/MathExtras.h" | 
 |  | 
 | #include <chrono> | 
 | #include <limits> | 
 | #include <memory> | 
 | #include <optional> | 
 | #include <string> | 
 | #include <vector> | 
 |  | 
 | namespace llvm { | 
 | namespace libc_benchmarks { | 
 |  | 
 | template <typename T> | 
 | static Error intFromJsonTemplate(const json::Value &V, T &Out) { | 
 |   if (const auto &MaybeInt64 = V.getAsInteger()) { | 
 |     int64_t Value = *MaybeInt64; | 
 |     if (Value < std::numeric_limits<T>::min() || | 
 |         Value > std::numeric_limits<T>::max()) | 
 |       return createStringError(errc::io_error, "Out of bound Integer"); | 
 |     Out = Value; | 
 |     return Error::success(); | 
 |   } | 
 |   return createStringError(errc::io_error, "Can't parse Integer"); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, bool &Out) { | 
 |   if (auto B = V.getAsBoolean()) { | 
 |     Out = *B; | 
 |     return Error::success(); | 
 |   } | 
 |   return createStringError(errc::io_error, "Can't parse Boolean"); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, double &Out) { | 
 |   if (auto S = V.getAsNumber()) { | 
 |     Out = *S; | 
 |     return Error::success(); | 
 |   } | 
 |   return createStringError(errc::io_error, "Can't parse Double"); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, std::string &Out) { | 
 |   if (auto S = V.getAsString()) { | 
 |     Out = std::string(*S); | 
 |     return Error::success(); | 
 |   } | 
 |   return createStringError(errc::io_error, "Can't parse String"); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, uint32_t &Out) { | 
 |   return intFromJsonTemplate(V, Out); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, int &Out) { | 
 |   return intFromJsonTemplate(V, Out); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, libc_benchmarks::Duration &D) { | 
 |   if (V.kind() != json::Value::Kind::Number) | 
 |     return createStringError(errc::io_error, "Can't parse Duration"); | 
 |   D = libc_benchmarks::Duration(*V.getAsNumber()); | 
 |   return Error::success(); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, MaybeAlign &Out) { | 
 |   const auto MaybeInt = V.getAsInteger(); | 
 |   if (!MaybeInt) | 
 |     return createStringError(errc::io_error, | 
 |                              "Can't parse Align, not an Integer"); | 
 |   const int64_t Value = *MaybeInt; | 
 |   if (!Value) { | 
 |     Out = std::nullopt; | 
 |     return Error::success(); | 
 |   } | 
 |   if (isPowerOf2_64(Value)) { | 
 |     Out = Align(Value); | 
 |     return Error::success(); | 
 |   } | 
 |   return createStringError(errc::io_error, | 
 |                            "Can't parse Align, not a power of two"); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, | 
 |                       libc_benchmarks::BenchmarkLog &Out) { | 
 |   if (V.kind() != json::Value::Kind::String) | 
 |     return createStringError(errc::io_error, | 
 |                              "Can't parse BenchmarkLog, not a String"); | 
 |   const auto String = *V.getAsString(); | 
 |   auto Parsed = | 
 |       llvm::StringSwitch<std::optional<libc_benchmarks::BenchmarkLog>>(String) | 
 |           .Case("None", libc_benchmarks::BenchmarkLog::None) | 
 |           .Case("Last", libc_benchmarks::BenchmarkLog::Last) | 
 |           .Case("Full", libc_benchmarks::BenchmarkLog::Full) | 
 |           .Default(std::nullopt); | 
 |   if (!Parsed) | 
 |     return createStringError(errc::io_error, | 
 |                              Twine("Can't parse BenchmarkLog, invalid value '") | 
 |                                  .concat(String) | 
 |                                  .concat("'")); | 
 |   Out = *Parsed; | 
 |   return Error::success(); | 
 | } | 
 |  | 
 | template <typename C> | 
 | Error vectorFromJsonTemplate(const json::Value &V, C &Out) { | 
 |   auto *A = V.getAsArray(); | 
 |   if (!A) | 
 |     return createStringError(errc::io_error, "Can't parse Array"); | 
 |   Out.clear(); | 
 |   Out.resize(A->size()); | 
 |   for (auto InOutPair : llvm::zip(*A, Out)) | 
 |     if (auto E = fromJson(std::get<0>(InOutPair), std::get<1>(InOutPair))) | 
 |       return std::move(E); | 
 |   return Error::success(); | 
 | } | 
 |  | 
 | template <typename T> | 
 | static Error fromJson(const json::Value &V, std::vector<T> &Out) { | 
 |   return vectorFromJsonTemplate(V, Out); | 
 | } | 
 |  | 
 | // Same as llvm::json::ObjectMapper but adds a finer error reporting mechanism. | 
 | class JsonObjectMapper { | 
 |   const json::Object *O; | 
 |   Error E; | 
 |   SmallDenseSet<StringRef> SeenFields; | 
 |  | 
 | public: | 
 |   explicit JsonObjectMapper(const json::Value &V) | 
 |       : O(V.getAsObject()), | 
 |         E(O ? Error::success() | 
 |             : createStringError(errc::io_error, "Expected JSON Object")) {} | 
 |  | 
 |   Error takeError() { | 
 |     if (E) | 
 |       return std::move(E); | 
 |     for (const auto &Itr : *O) { | 
 |       const StringRef Key = Itr.getFirst(); | 
 |       if (!SeenFields.count(Key)) | 
 |         E = createStringError(errc::io_error, | 
 |                               Twine("Unknown field: ").concat(Key)); | 
 |     } | 
 |     return std::move(E); | 
 |   } | 
 |  | 
 |   template <typename T> void map(StringRef Key, T &Out) { | 
 |     if (E) | 
 |       return; | 
 |     if (const json::Value *Value = O->get(Key)) { | 
 |       SeenFields.insert(Key); | 
 |       E = fromJson(*Value, Out); | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | static Error fromJson(const json::Value &V, | 
 |                       libc_benchmarks::BenchmarkOptions &Out) { | 
 |   JsonObjectMapper O(V); | 
 |   O.map("MinDuration", Out.MinDuration); | 
 |   O.map("MaxDuration", Out.MaxDuration); | 
 |   O.map("InitialIterations", Out.InitialIterations); | 
 |   O.map("MaxIterations", Out.MaxIterations); | 
 |   O.map("MinSamples", Out.MinSamples); | 
 |   O.map("MaxSamples", Out.MaxSamples); | 
 |   O.map("Epsilon", Out.Epsilon); | 
 |   O.map("ScalingFactor", Out.ScalingFactor); | 
 |   O.map("Log", Out.Log); | 
 |   return O.takeError(); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, | 
 |                       libc_benchmarks::StudyConfiguration &Out) { | 
 |   JsonObjectMapper O(V); | 
 |   O.map("Function", Out.Function); | 
 |   O.map("NumTrials", Out.NumTrials); | 
 |   O.map("IsSweepMode", Out.IsSweepMode); | 
 |   O.map("SweepModeMaxSize", Out.SweepModeMaxSize); | 
 |   O.map("SizeDistributionName", Out.SizeDistributionName); | 
 |   O.map("AccessAlignment", Out.AccessAlignment); | 
 |   O.map("MemcmpMismatchAt", Out.MemcmpMismatchAt); | 
 |   return O.takeError(); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, libc_benchmarks::CacheInfo &Out) { | 
 |   JsonObjectMapper O(V); | 
 |   O.map("Type", Out.Type); | 
 |   O.map("Level", Out.Level); | 
 |   O.map("Size", Out.Size); | 
 |   O.map("NumSharing", Out.NumSharing); | 
 |   return O.takeError(); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, libc_benchmarks::HostState &Out) { | 
 |   JsonObjectMapper O(V); | 
 |   O.map("CpuName", Out.CpuName); | 
 |   O.map("CpuFrequency", Out.CpuFrequency); | 
 |   O.map("Caches", Out.Caches); | 
 |   return O.takeError(); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, libc_benchmarks::Runtime &Out) { | 
 |   JsonObjectMapper O(V); | 
 |   O.map("Host", Out.Host); | 
 |   O.map("BufferSize", Out.BufferSize); | 
 |   O.map("BatchParameterCount", Out.BatchParameterCount); | 
 |   O.map("BenchmarkOptions", Out.BenchmarkOptions); | 
 |   return O.takeError(); | 
 | } | 
 |  | 
 | static Error fromJson(const json::Value &V, libc_benchmarks::Study &Out) { | 
 |   JsonObjectMapper O(V); | 
 |   O.map("StudyName", Out.StudyName); | 
 |   O.map("Runtime", Out.Runtime); | 
 |   O.map("Configuration", Out.Configuration); | 
 |   O.map("Measurements", Out.Measurements); | 
 |   return O.takeError(); | 
 | } | 
 |  | 
 | static double seconds(const Duration &D) { | 
 |   return std::chrono::duration<double>(D).count(); | 
 | } | 
 |  | 
 | Expected<Study> parseJsonStudy(StringRef Content) { | 
 |   Expected<json::Value> EV = json::parse(Content); | 
 |   if (!EV) | 
 |     return EV.takeError(); | 
 |   Study S; | 
 |   if (Error E = fromJson(*EV, S)) | 
 |     return std::move(E); | 
 |   return S; | 
 | } | 
 |  | 
 | static StringRef serialize(const BenchmarkLog &L) { | 
 |   switch (L) { | 
 |   case BenchmarkLog::None: | 
 |     return "None"; | 
 |   case BenchmarkLog::Last: | 
 |     return "Last"; | 
 |   case BenchmarkLog::Full: | 
 |     return "Full"; | 
 |   } | 
 |   llvm_unreachable("Unhandled BenchmarkLog value"); | 
 | } | 
 |  | 
 | static void serialize(const BenchmarkOptions &BO, json::OStream &JOS) { | 
 |   JOS.attribute("MinDuration", seconds(BO.MinDuration)); | 
 |   JOS.attribute("MaxDuration", seconds(BO.MaxDuration)); | 
 |   JOS.attribute("InitialIterations", BO.InitialIterations); | 
 |   JOS.attribute("MaxIterations", BO.MaxIterations); | 
 |   JOS.attribute("MinSamples", BO.MinSamples); | 
 |   JOS.attribute("MaxSamples", BO.MaxSamples); | 
 |   JOS.attribute("Epsilon", BO.Epsilon); | 
 |   JOS.attribute("ScalingFactor", BO.ScalingFactor); | 
 |   JOS.attribute("Log", serialize(BO.Log)); | 
 | } | 
 |  | 
 | static void serialize(const CacheInfo &CI, json::OStream &JOS) { | 
 |   JOS.attribute("Type", CI.Type); | 
 |   JOS.attribute("Level", CI.Level); | 
 |   JOS.attribute("Size", CI.Size); | 
 |   JOS.attribute("NumSharing", CI.NumSharing); | 
 | } | 
 |  | 
 | static void serialize(const StudyConfiguration &SC, json::OStream &JOS) { | 
 |   JOS.attribute("Function", SC.Function); | 
 |   JOS.attribute("NumTrials", SC.NumTrials); | 
 |   JOS.attribute("IsSweepMode", SC.IsSweepMode); | 
 |   JOS.attribute("SweepModeMaxSize", SC.SweepModeMaxSize); | 
 |   JOS.attribute("SizeDistributionName", SC.SizeDistributionName); | 
 |   JOS.attribute("AccessAlignment", | 
 |                 static_cast<int64_t>(SC.AccessAlignment->value())); | 
 |   JOS.attribute("MemcmpMismatchAt", SC.MemcmpMismatchAt); | 
 | } | 
 |  | 
 | static void serialize(const HostState &HS, json::OStream &JOS) { | 
 |   JOS.attribute("CpuName", HS.CpuName); | 
 |   JOS.attribute("CpuFrequency", HS.CpuFrequency); | 
 |   JOS.attributeArray("Caches", [&]() { | 
 |     for (const auto &CI : HS.Caches) | 
 |       JOS.object([&]() { serialize(CI, JOS); }); | 
 |   }); | 
 | } | 
 |  | 
 | static void serialize(const Runtime &RI, json::OStream &JOS) { | 
 |   JOS.attributeObject("Host", [&]() { serialize(RI.Host, JOS); }); | 
 |   JOS.attribute("BufferSize", RI.BufferSize); | 
 |   JOS.attribute("BatchParameterCount", RI.BatchParameterCount); | 
 |   JOS.attributeObject("BenchmarkOptions", | 
 |                       [&]() { serialize(RI.BenchmarkOptions, JOS); }); | 
 | } | 
 |  | 
 | void serializeToJson(const Study &S, json::OStream &JOS) { | 
 |   JOS.object([&]() { | 
 |     JOS.attribute("StudyName", S.StudyName); | 
 |     JOS.attributeObject("Runtime", [&]() { serialize(S.Runtime, JOS); }); | 
 |     JOS.attributeObject("Configuration", | 
 |                         [&]() { serialize(S.Configuration, JOS); }); | 
 |     if (!S.Measurements.empty()) { | 
 |       JOS.attributeArray("Measurements", [&]() { | 
 |         for (const auto &M : S.Measurements) | 
 |           JOS.value(seconds(M)); | 
 |       }); | 
 |     } | 
 |   }); | 
 | } | 
 |  | 
 | } // namespace libc_benchmarks | 
 | } // namespace llvm |