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 "SkStream.h" 17 #include "SkString.h" 18 #include "SkTypes.h" 19 20 /** 21 * Base class for writing out the bench results. 22 * 23 * Default implementation does nothing. 24 */ 25 class ResultsWriter : SkNoncopyable { 26 public: 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 metric 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 metric(const char name[], double ms) {} 48 49 // Flush to storage now please. 50 virtual void flush() {} 51 }; 52 53 /** 54 NanoJSONResultsWriter writes the test results out in the following 55 format: 56 57 { 58 "key": { 59 "arch": "Arm7", 60 "gpu": "SGX540", 61 "os": "Android", 62 "model": "GalaxyNexus", 63 } 64 "gitHash": "d1830323662ae8ae06908b97f15180fd25808894", 65 "build_number": "1234", 66 "results" : { 67 "Xfermode_Luminosity_640_480" : { 68 "8888" : { 69 "median_ms" : 143.188128906250, 70 "min_ms" : 143.835957031250, 71 ... 72 }, 73 ... 74 */ 75 class NanoJSONResultsWriter : public ResultsWriter { 76 public: 77 explicit NanoJSONResultsWriter(const char filename[]) 78 : fFilename(filename) 79 , fRoot() 80 , fResults(fRoot["results"]) 81 , fBench(nullptr) 82 , fConfig(nullptr) {} 83 84 ~NanoJSONResultsWriter() { 85 this->flush(); 86 } 87 88 // Added under "key". 89 void key(const char name[], const char value[]) override { 90 fRoot["key"][name] = value; 91 } 92 // Inserted directly into the root. 93 void property(const char name[], const char value[]) override { 94 fRoot[name] = value; 95 } 96 void bench(const char name[], int32_t x, int32_t y) override { 97 SkString id = SkStringPrintf( "%s_%d_%d", name, x, y); 98 fResults[id.c_str()] = Json::Value(Json::objectValue); 99 fBench = &fResults[id.c_str()]; 100 } 101 void config(const char name[]) override { 102 SkASSERT(fBench); 103 fConfig = &(*fBench)[name]; 104 } 105 void configOption(const char name[], const char* value) override { 106 (*fConfig)["options"][name] = value; 107 } 108 void metric(const char name[], double ms) override { 109 // Don't record if nan, or -nan. 110 if (sk_double_isnan(ms)) { 111 return; 112 } 113 SkASSERT(fConfig); 114 (*fConfig)[name] = ms; 115 } 116 117 // Flush to storage now please. 118 void flush() override { 119 SkString dirname = SkOSPath::Dirname(fFilename.c_str()); 120 if (!sk_exists(dirname.c_str(), kWrite_SkFILE_Flag)) { 121 if (!sk_mkdir(dirname.c_str())) { 122 SkDebugf("Failed to create directory."); 123 } 124 } 125 SkFILEWStream stream(fFilename.c_str()); 126 stream.writeText(Json::StyledWriter().write(fRoot).c_str()); 127 stream.flush(); 128 } 129 130 private: 131 SkString fFilename; 132 Json::Value fRoot; 133 Json::Value& fResults; 134 Json::Value* fBench; 135 Json::Value* fConfig; 136 }; 137 138 139 #endif 140