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 * TODO(jcgregorio) Add info if tests fail to converge?
24 */
25class ResultsWriter : SkNoncopyable {
26public:
27    virtual ~ResultsWriter() {};
28
29    // Records one option set for this run. All options must be set before
30    // calling bench().
31    virtual void option(const char name[], const char value[]) = 0;
32
33    // Denotes the start of a specific benchmark. Once bench is called,
34    // then config and timer can be called multiple times to record runs.
35    virtual void bench(const char name[], int32_t x, int32_t y) = 0;
36
37    // Records the specific configuration a bench is run under, such as "8888".
38    virtual void config(const char name[]) = 0;
39
40    // Records a single test metric.
41    virtual void timer(const char name[], double ms) = 0;
42
43    // Call when all results are finished.
44    virtual void end() = 0;
45};
46
47/**
48 * This ResultsWriter handles writing out the human readable format of the
49 * bench results.
50 */
51class LoggerResultsWriter : public ResultsWriter {
52public:
53    explicit LoggerResultsWriter(BenchLogger& logger, const char* timeFormat)
54        : fLogger(logger)
55        , fTimeFormat(timeFormat) {
56        fLogger.logProgress("skia bench:");
57    }
58    virtual void option(const char name[], const char value[]) {
59        fLogger.logProgress(SkStringPrintf(" %s=%s", name, value));
60    }
61    virtual void bench(const char name[], int32_t x, int32_t y) {
62        fLogger.logProgress(SkStringPrintf(
63            "\nrunning bench [%3d %3d] %40s", x, y, name));
64    }
65    virtual void config(const char name[]) {
66        fLogger.logProgress(SkStringPrintf("   %s:", name));
67    }
68    virtual void timer(const char name[], double ms) {
69        fLogger.logProgress(SkStringPrintf("  %s = ", name));
70        fLogger.logProgress(SkStringPrintf(fTimeFormat, ms));
71    }
72    virtual void end() {
73        fLogger.logProgress("\n");
74    }
75private:
76    BenchLogger& fLogger;
77    const char* fTimeFormat;
78};
79
80/**
81 * This ResultsWriter handles writing out the results in JSON.
82 *
83 * The output looks like (except compressed to a single line):
84 *
85 *  {
86 *   "options" : {
87 *      "alpha" : "0xFF",
88 *      "scale" : "0",
89 *      ...
90 *      "system" : "UNIX"
91 *   },
92 *   "results" : [
93 *      {
94 *      "name" : "Xfermode_Luminosity_640_480",
95 *      "results" : [
96 *         {
97 *            "name": "565",
98 *            "cmsecs" : 143.188128906250,
99 *            "msecs" : 143.835957031250
100 *         },
101 *         ...
102 */
103
104Json::Value* SkFindNamedNode(Json::Value* root, const char name[]);
105class JSONResultsWriter : public ResultsWriter {
106public:
107    explicit JSONResultsWriter(const char filename[])
108        : fFilename(filename)
109        , fRoot()
110        , fResults(fRoot["results"])
111        , fBench(NULL)
112        , fConfig(NULL) {
113    }
114    virtual void option(const char name[], const char value[]) {
115        fRoot["options"][name] = value;
116    }
117    virtual void bench(const char name[], int32_t x, int32_t y) {
118        SkString sk_name(name);
119        sk_name.append("_");
120        sk_name.appendS32(x);
121        sk_name.append("_");
122        sk_name.appendS32(y);
123        Json::Value* bench_node = SkFindNamedNode(&fResults, sk_name.c_str());
124        fBench = &(*bench_node)["results"];
125    }
126    virtual void config(const char name[]) {
127        SkASSERT(NULL != fBench);
128        fConfig = SkFindNamedNode(fBench, name);
129    }
130    virtual void timer(const char name[], double ms) {
131        SkASSERT(NULL != fConfig);
132        (*fConfig)[name] = ms;
133    }
134    virtual void end() {
135        SkFILEWStream stream(fFilename.c_str());
136        stream.writeText(Json::FastWriter().write(fRoot).c_str());
137        stream.flush();
138    }
139private:
140
141    SkString fFilename;
142    Json::Value fRoot;
143    Json::Value& fResults;
144    Json::Value* fBench;
145    Json::Value* fConfig;
146};
147
148/**
149 * This ResultsWriter writes out to multiple ResultsWriters.
150 */
151class MultiResultsWriter : public ResultsWriter {
152public:
153    MultiResultsWriter() : writers() {
154    };
155    void add(ResultsWriter* writer) {
156      writers.push_back(writer);
157    }
158    virtual void option(const char name[], const char value[]) {
159        for (int i = 0; i < writers.count(); ++i) {
160            writers[i]->option(name, value);
161        }
162    }
163    virtual void bench(const char name[], int32_t x, int32_t y) {
164        for (int i = 0; i < writers.count(); ++i) {
165            writers[i]->bench(name, x, y);
166        }
167    }
168    virtual void config(const char name[]) {
169        for (int i = 0; i < writers.count(); ++i) {
170            writers[i]->config(name);
171        }
172    }
173    virtual void timer(const char name[], double ms) {
174        for (int i = 0; i < writers.count(); ++i) {
175            writers[i]->timer(name, ms);
176        }
177    }
178    virtual void end() {
179        for (int i = 0; i < writers.count(); ++i) {
180            writers[i]->end();
181        }
182    }
183private:
184    SkTArray<ResultsWriter *> writers;
185};
186
187/**
188 * Calls the end() method of T on destruction.
189 */
190template <typename T> class CallEnd : SkNoncopyable {
191public:
192    CallEnd(T& obj) : fObj(obj) {}
193    ~CallEnd() { fObj.end(); }
194private:
195    T&  fObj;
196};
197
198#endif
199