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 "SkOSFile.h" 16 #include "SkOSPath.h" 17 #include "SkStream.h" 18 #include "SkString.h" 19 #include "SkTypes.h" 20 21 /** 22 * Base class for writing out the bench results. 23 * 24 * Default implementation does nothing. 25 */ 26 class ResultsWriter : SkNoncopyable { 27 public: ~ResultsWriter()28 virtual ~ResultsWriter() {} 29 30 // Record one key value pair that makes up a unique key for this type of run, e.g. 31 // builder name, machine type, Debug/Release, etc. key(const char name[],const char value[])32 virtual void key(const char name[], const char value[]) {} 33 34 // Record one key value pair that describes the run instance, e.g. git hash, build number. property(const char name[],const char value[])35 virtual void property(const char name[], const char value[]) {} 36 37 // Denote the start of a specific benchmark. Once bench is called, 38 // then config and metric can be called multiple times to record runs. bench(const char name[],int32_t x,int32_t y)39 virtual void bench(const char name[], int32_t x, int32_t y) {} 40 41 // Record the specific configuration a bench is run under, such as "8888". config(const char name[])42 virtual void config(const char name[]) {} 43 44 // Record the options for a configuration, such as "GL_RENDERER". configOption(const char name[],const char * value)45 virtual void configOption(const char name[], const char* value) {} 46 47 // Record a single test metric. metric(const char name[],double ms)48 virtual void metric(const char name[], double ms) {} 49 50 // Record a list of test metrics. metrics(const char name[],const SkTArray<double> & array)51 virtual void metrics(const char name[], const SkTArray<double>& array) {} 52 53 // Flush to storage now please. flush()54 virtual void flush() {} 55 }; 56 57 /** 58 NanoJSONResultsWriter writes the test results out in the following 59 format: 60 61 { 62 "key": { 63 "arch": "Arm7", 64 "gpu": "SGX540", 65 "os": "Android", 66 "model": "GalaxyNexus", 67 } 68 "gitHash": "d1830323662ae8ae06908b97f15180fd25808894", 69 "build_number": "1234", 70 "results" : { 71 "Xfermode_Luminosity_640_480" : { 72 "8888" : { 73 "median_ms" : 143.188128906250, 74 "min_ms" : 143.835957031250, 75 ... 76 }, 77 ... 78 */ 79 class NanoJSONResultsWriter : public ResultsWriter { 80 public: NanoJSONResultsWriter(const char filename[])81 explicit NanoJSONResultsWriter(const char filename[]) 82 : fFilename(filename) 83 , fRoot() 84 , fResults(fRoot["results"]) 85 , fBench(nullptr) 86 , fConfig(nullptr) {} 87 ~NanoJSONResultsWriter()88 ~NanoJSONResultsWriter() override { 89 this->flush(); 90 } 91 92 // Added under "key". key(const char name[],const char value[])93 void key(const char name[], const char value[]) override { 94 fRoot["key"][name] = value; 95 } 96 // Inserted directly into the root. property(const char name[],const char value[])97 void property(const char name[], const char value[]) override { 98 fRoot[name] = value; 99 } bench(const char name[],int32_t x,int32_t y)100 void bench(const char name[], int32_t x, int32_t y) override { 101 SkString id = SkStringPrintf( "%s_%d_%d", name, x, y); 102 fResults[id.c_str()] = Json::Value(Json::objectValue); 103 fBench = &fResults[id.c_str()]; 104 } config(const char name[])105 void config(const char name[]) override { 106 SkASSERT(fBench); 107 fConfig = &(*fBench)[name]; 108 } configOption(const char name[],const char * value)109 void configOption(const char name[], const char* value) override { 110 (*fConfig)["options"][name] = value; 111 } metric(const char name[],double ms)112 void metric(const char name[], double ms) override { 113 // Don't record if nan, or -nan. 114 if (sk_double_isnan(ms)) { 115 return; 116 } 117 SkASSERT(fConfig); 118 (*fConfig)[name] = ms; 119 } metrics(const char name[],const SkTArray<double> & array)120 void metrics(const char name[], const SkTArray<double>& array) override { 121 SkASSERT(fConfig); 122 Json::Value value = Json::Value(Json::arrayValue); 123 value.resize(array.count()); 124 for (int i = 0; i < array.count(); i++) { 125 // Don't care about nan-ness. 126 value[i] = array[i]; 127 } 128 (*fConfig)[name] = std::move(value); 129 } 130 131 // Flush to storage now please. flush()132 void flush() override { 133 SkString dirname = SkOSPath::Dirname(fFilename.c_str()); 134 if (!sk_exists(dirname.c_str(), kWrite_SkFILE_Flag)) { 135 if (!sk_mkdir(dirname.c_str())) { 136 SkDebugf("Failed to create directory."); 137 } 138 } 139 SkFILEWStream stream(fFilename.c_str()); 140 stream.writeText(Json::StyledWriter().write(fRoot).c_str()); 141 stream.flush(); 142 } 143 144 private: 145 SkString fFilename; 146 Json::Value fRoot; 147 Json::Value& fResults; 148 Json::Value* fBench; 149 Json::Value* fConfig; 150 }; 151 152 153 #endif 154