forked from OSchip/llvm-project
370 lines
11 KiB
C++
370 lines
11 KiB
C++
//===-- 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 <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, 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, uint8_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 = None;
|
|
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<Optional<libc_benchmarks::BenchmarkLog>>(String)
|
|
.Case("None", libc_benchmarks::BenchmarkLog::None)
|
|
.Case("Last", libc_benchmarks::BenchmarkLog::Last)
|
|
.Case("Full", libc_benchmarks::BenchmarkLog::Full)
|
|
.Default(None);
|
|
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);
|
|
}
|
|
|
|
template <typename T>
|
|
static Error fromJson(const json::Value &V, SmallVectorImpl<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::SizeRange &Out) {
|
|
JsonObjectMapper O(V);
|
|
O.map("From", Out.From);
|
|
O.map("To", Out.To);
|
|
O.map("Step", Out.Step);
|
|
return O.takeError();
|
|
}
|
|
|
|
static Error fromJson(const json::Value &V,
|
|
libc_benchmarks::StudyConfiguration &Out) {
|
|
JsonObjectMapper O(V);
|
|
O.map("Runs", Out.Runs);
|
|
O.map("BufferSize", Out.BufferSize);
|
|
O.map("Size", Out.Size);
|
|
O.map("AddressAlignment", Out.AddressAlignment);
|
|
O.map("MemsetValue", Out.MemsetValue);
|
|
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::FunctionMeasurements &Out) {
|
|
JsonObjectMapper O(V);
|
|
O.map("Name", Out.Name);
|
|
std::vector<uint32_t> Sizes;
|
|
O.map("Sizes", Sizes);
|
|
std::vector<libc_benchmarks::Duration> Runtimes;
|
|
O.map("Runtimes", Runtimes);
|
|
if (Sizes.size() != Runtimes.size())
|
|
return createStringError(errc::io_error,
|
|
"Measurement Size and Runtime mistmatch");
|
|
Out.Measurements.resize(Sizes.size());
|
|
for (size_t I = 0; I < Sizes.size(); ++I) {
|
|
Out.Measurements[I].Size = Sizes[I];
|
|
Out.Measurements[I].Runtime = Runtimes[I];
|
|
}
|
|
return O.takeError();
|
|
}
|
|
|
|
static Error fromJson(const json::Value &V, libc_benchmarks::Study &Out) {
|
|
JsonObjectMapper O(V);
|
|
O.map("Host", Out.Host);
|
|
O.map("Options", Out.Options);
|
|
O.map("Configuration", Out.Configuration);
|
|
O.map("Functions", Out.Functions);
|
|
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.object([&]() {
|
|
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.object([&]() {
|
|
JOS.attribute("Type", CI.Type);
|
|
JOS.attribute("Level", CI.Level);
|
|
JOS.attribute("Size", CI.Size);
|
|
JOS.attribute("NumSharing", CI.NumSharing);
|
|
});
|
|
}
|
|
|
|
static void Serialize(const HostState &HS, json::OStream &JOS) {
|
|
JOS.object([&]() {
|
|
JOS.attribute("CpuName", HS.CpuName);
|
|
JOS.attribute("CpuFrequency", HS.CpuFrequency);
|
|
JOS.attributeArray("Caches", [&]() {
|
|
for (const auto &CI : HS.Caches)
|
|
Serialize(CI, JOS);
|
|
});
|
|
});
|
|
}
|
|
|
|
static void Serialize(const StudyConfiguration &SC, json::OStream &JOS) {
|
|
JOS.object([&]() {
|
|
JOS.attribute("Runs", SC.Runs);
|
|
JOS.attribute("BufferSize", SC.BufferSize);
|
|
JOS.attributeObject("Size", [&]() {
|
|
JOS.attribute("From", SC.Size.From);
|
|
JOS.attribute("To", SC.Size.To);
|
|
JOS.attribute("Step", SC.Size.Step);
|
|
});
|
|
if (SC.AddressAlignment)
|
|
JOS.attribute("AddressAlignment",
|
|
static_cast<int64_t>(SC.AddressAlignment->value()));
|
|
JOS.attribute("MemsetValue", SC.MemsetValue);
|
|
JOS.attribute("MemcmpMismatchAt", SC.MemcmpMismatchAt);
|
|
});
|
|
}
|
|
|
|
static void Serialize(const FunctionMeasurements &FM, json::OStream &JOS) {
|
|
JOS.object([&]() {
|
|
JOS.attribute("Name", FM.Name);
|
|
JOS.attributeArray("Sizes", [&]() {
|
|
for (const auto &M : FM.Measurements)
|
|
JOS.value(M.Size);
|
|
});
|
|
JOS.attributeArray("Runtimes", [&]() {
|
|
for (const auto &M : FM.Measurements)
|
|
JOS.value(Seconds(M.Runtime));
|
|
});
|
|
});
|
|
}
|
|
|
|
void SerializeToJson(const Study &S, json::OStream &JOS) {
|
|
JOS.object([&]() {
|
|
JOS.attributeBegin("Host");
|
|
Serialize(S.Host, JOS);
|
|
JOS.attributeEnd();
|
|
|
|
JOS.attributeBegin("Options");
|
|
Serialize(S.Options, JOS);
|
|
JOS.attributeEnd();
|
|
|
|
JOS.attributeBegin("Configuration");
|
|
Serialize(S.Configuration, JOS);
|
|
JOS.attributeEnd();
|
|
|
|
if (!S.Functions.empty()) {
|
|
JOS.attributeArray("Functions", [&]() {
|
|
for (const auto &FM : S.Functions)
|
|
Serialize(FM, JOS);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
} // namespace libc_benchmarks
|
|
} // namespace llvm
|