blob: f2a9d8b27f48238dce539389e2eb3f9e71460fe1 [file] [log] [blame]
//===-- JSON serialization routines ---------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See 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, 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 = 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 =
.Case("None", libc_benchmarks::BenchmarkLog::None)
.Case("Last", libc_benchmarks::BenchmarkLog::Last)
.Case("Full", libc_benchmarks::BenchmarkLog::Full)
if (!Parsed)
return createStringError(errc::io_error,
Twine("Can't parse BenchmarkLog, invalid value '")
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");
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;
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)
if (const json::Value *Value = O->get(Key)) {
E = fromJson(*Value, Out);
static Error fromJson(const json::Value &V,
libc_benchmarks::BenchmarkOptions &Out) {
JsonObjectMapper O(V);"MinDuration", Out.MinDuration);"MaxDuration", Out.MaxDuration);"InitialIterations", Out.InitialIterations);"MaxIterations", Out.MaxIterations);"MinSamples", Out.MinSamples);"MaxSamples", Out.MaxSamples);"Epsilon", Out.Epsilon);"ScalingFactor", Out.ScalingFactor);"Log", Out.Log);
return O.takeError();
static Error fromJson(const json::Value &V,
libc_benchmarks::StudyConfiguration &Out) {
JsonObjectMapper O(V);"Function", Out.Function);"NumTrials", Out.NumTrials);"IsSweepMode", Out.IsSweepMode);"SweepModeMaxSize", Out.SweepModeMaxSize);"SizeDistributionName", Out.SizeDistributionName);"AccessAlignment", Out.AccessAlignment);"MemcmpMismatchAt", Out.MemcmpMismatchAt);
return O.takeError();
static Error fromJson(const json::Value &V, libc_benchmarks::CacheInfo &Out) {
JsonObjectMapper O(V);"Type", Out.Type);"Level", Out.Level);"Size", Out.Size);"NumSharing", Out.NumSharing);
return O.takeError();
static Error fromJson(const json::Value &V, libc_benchmarks::HostState &Out) {
JsonObjectMapper O(V);"CpuName", Out.CpuName);"CpuFrequency", Out.CpuFrequency);"Caches", Out.Caches);
return O.takeError();
static Error fromJson(const json::Value &V, libc_benchmarks::Runtime &Out) {
JsonObjectMapper O(V);"Host", Out.Host);"BufferSize", Out.BufferSize);"BatchParameterCount", Out.BatchParameterCount);"BenchmarkOptions", Out.BenchmarkOptions);
return O.takeError();
static Error fromJson(const json::Value &V, libc_benchmarks::Study &Out) {
JsonObjectMapper O(V);"StudyName", Out.StudyName);"Runtime", Out.Runtime);"Configuration", Out.Configuration);"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("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);
[&]() { 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); });
[&]() { serialize(S.Configuration, JOS); });
if (!S.Measurements.empty()) {
JOS.attributeArray("Measurements", [&]() {
for (const auto &M : S.Measurements)
} // namespace libc_benchmarks
} // namespace llvm