1/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 *
7 * Classes for writing out bench results in various formats.
8 */
9
10#ifndef SkResultsWriter_DEFINED
11#define SkResultsWriter_DEFINED
12
13#include "BenchLogger.h"
14#include "SkJSONCPP.h"
15#include "SkStream.h"
16#include "SkString.h"
17#include "SkTArray.h"
18#include "SkTypes.h"
19
20/**
21 * Base class for writing out the bench results.
22 *
23 * Default implementation does nothing.
24 */
25class ResultsWriter : SkNoncopyable {
26public:
27    virtual ~ResultsWriter() {}
28
29    // Record one key value pair that makes up a unique key for this type of run, e.g.
30    // builder name, machine type, Debug/Release, etc.
31    virtual void key(const char name[], const char value[]) {}
32
33    // Record one key value pair that describes the run instance, e.g. git hash, build number.
34    virtual void property(const char name[], const char value[]) {}
35
36    // Denote the start of a specific benchmark. Once bench is called,
37    // then config and timer can be called multiple times to record runs.
38    virtual void bench(const char name[], int32_t x, int32_t y) {}
39
40    // Record the specific configuration a bench is run under, such as "8888".
41    virtual void config(const char name[]) {}
42
43    // Record the options for a configuration, such as "GL_RENDERER".
44    virtual void configOption(const char name[], const char* value) {}
45
46    // Record a single test metric.
47    virtual void timer(const char name[], double ms) {}
48};
49
50/**
51 NanoJSONResultsWriter writes the test results out in the following
52 format:
53
54 {
55    "key": {
56      "arch": "Arm7",
57      "gpu": "SGX540",
58      "os": "Android",
59      "model": "GalaxyNexus",
60    }
61    "gitHash": "d1830323662ae8ae06908b97f15180fd25808894",
62    "build_number": "1234",
63    "results" : {
64        "Xfermode_Luminosity_640_480" : {
65           "8888" : {
66                 "median_ms" : 143.188128906250,
67                 "min_ms" : 143.835957031250,
68                 ...
69              },
70          ...
71*/
72class NanoJSONResultsWriter : public ResultsWriter {
73public:
74    explicit NanoJSONResultsWriter(const char filename[])
75        : fFilename(filename)
76        , fRoot()
77        , fResults(fRoot["results"])
78        , fBench(NULL)
79        , fConfig(NULL) {}
80
81    ~NanoJSONResultsWriter() {
82        SkFILEWStream stream(fFilename.c_str());
83        stream.writeText(Json::StyledWriter().write(fRoot).c_str());
84        stream.flush();
85    }
86
87    // Added under "key".
88    virtual void key(const char name[], const char value[]) {
89        fRoot["key"][name] = value;
90    }
91    // Inserted directly into the root.
92    virtual void property(const char name[], const char value[]) {
93        fRoot[name] = value;
94    }
95    virtual void bench(const char name[], int32_t x, int32_t y) {
96        SkString id = SkStringPrintf( "%s_%d_%d", name, x, y);
97        fResults[id.c_str()] = Json::Value(Json::objectValue);
98        fBench = &fResults[id.c_str()];
99    }
100    virtual void config(const char name[]) {
101        SkASSERT(fBench);
102        fConfig = &(*fBench)[name];
103    }
104    virtual void configOption(const char name[], const char* value) {
105        (*fConfig)["options"][name] = value;
106    }
107    virtual void timer(const char name[], double ms) {
108        // Don't record if nan, or -nan.
109        if (sk_double_isnan(ms)) {
110            return;
111        }
112        SkASSERT(fConfig);
113        (*fConfig)[name] = ms;
114    }
115
116private:
117    SkString fFilename;
118    Json::Value fRoot;
119    Json::Value& fResults;
120    Json::Value* fBench;
121    Json::Value* fConfig;
122};
123
124
125#endif
126