/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Classes for writing out bench results in various formats.
*/
#ifndef SkResultsWriter_DEFINED
#define SkResultsWriter_DEFINED
#include "BenchLogger.h"
#include "SkJSONCPP.h"
#include "SkOSFile.h"
#include "SkOSPath.h"
#include "SkStream.h"
#include "SkString.h"
#include "SkTypes.h"
/**
* Base class for writing out the bench results.
*
* Default implementation does nothing.
*/
class ResultsWriter : SkNoncopyable {
public:
virtual ~ResultsWriter() {}
// Record one key value pair that makes up a unique key for this type of run, e.g.
// builder name, machine type, Debug/Release, etc.
virtual void key(const char name[], const char value[]) {}
// Record one key value pair that describes the run instance, e.g. git hash, build number.
virtual void property(const char name[], const char value[]) {}
// Denote the start of a specific benchmark. Once bench is called,
// then config and metric can be called multiple times to record runs.
virtual void bench(const char name[], int32_t x, int32_t y) {}
// Record the specific configuration a bench is run under, such as "8888".
virtual void config(const char name[]) {}
// Record the options for a configuration, such as "GL_RENDERER".
virtual void configOption(const char name[], const char* value) {}
// Record a single test metric.
virtual void metric(const char name[], double ms) {}
// Record a list of test metrics.
virtual void metrics(const char name[], const SkTArray<double>& array) {}
// Flush to storage now please.
virtual void flush() {}
};
/**
NanoJSONResultsWriter writes the test results out in the following
format:
{
"key": {
"arch": "Arm7",
"gpu": "SGX540",
"os": "Android",
"model": "GalaxyNexus",
}
"gitHash": "d1830323662ae8ae06908b97f15180fd25808894",
"build_number": "1234",
"results" : {
"Xfermode_Luminosity_640_480" : {
"8888" : {
"median_ms" : 143.188128906250,
"min_ms" : 143.835957031250,
...
},
...
*/
class NanoJSONResultsWriter : public ResultsWriter {
public:
explicit NanoJSONResultsWriter(const char filename[])
: fFilename(filename)
, fRoot()
, fResults(fRoot["results"])
, fBench(nullptr)
, fConfig(nullptr) {}
~NanoJSONResultsWriter() override {
this->flush();
}
// Added under "key".
void key(const char name[], const char value[]) override {
fRoot["key"][name] = value;
}
// Inserted directly into the root.
void property(const char name[], const char value[]) override {
fRoot[name] = value;
}
void bench(const char name[], int32_t x, int32_t y) override {
SkString id = SkStringPrintf( "%s_%d_%d", name, x, y);
fResults[id.c_str()] = Json::Value(Json::objectValue);
fBench = &fResults[id.c_str()];
}
void config(const char name[]) override {
SkASSERT(fBench);
fConfig = &(*fBench)[name];
}
void configOption(const char name[], const char* value) override {
(*fConfig)["options"][name] = value;
}
void metric(const char name[], double ms) override {
// Don't record if nan, or -nan.
if (sk_double_isnan(ms)) {
return;
}
SkASSERT(fConfig);
(*fConfig)[name] = ms;
}
void metrics(const char name[], const SkTArray<double>& array) override {
SkASSERT(fConfig);
Json::Value value = Json::Value(Json::arrayValue);
value.resize(array.count());
for (int i = 0; i < array.count(); i++) {
// Don't care about nan-ness.
value[i] = array[i];
}
(*fConfig)[name] = std::move(value);
}
// Flush to storage now please.
void flush() override {
SkString dirname = SkOSPath::Dirname(fFilename.c_str());
if (!sk_exists(dirname.c_str(), kWrite_SkFILE_Flag)) {
if (!sk_mkdir(dirname.c_str())) {
SkDebugf("Failed to create directory.");
}
}
SkFILEWStream stream(fFilename.c_str());
stream.writeText(Json::StyledWriter().write(fRoot).c_str());
stream.flush();
}
private:
SkString fFilename;
Json::Value fRoot;
Json::Value& fResults;
Json::Value* fBench;
Json::Value* fConfig;
};
#endif