1 /* 2 * Copyright 2014 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 SkPictureResultsWriter_DEFINED 11 #define SkPictureResultsWriter_DEFINED 12 13 14 #include "PictureRenderer.h" 15 #include "BenchLogger.h" 16 #include "ResultsWriter.h" 17 #include "SkJSONCPP.h" 18 #include "SkStream.h" 19 #include "SkString.h" 20 #include "SkTArray.h" 21 #include "TimerData.h" 22 23 /** 24 * Base class for writing picture bench results. 25 */ 26 class PictureResultsWriter : SkNoncopyable { 27 public: 28 enum TileFlags {kPurging, kAvg}; 29 PictureResultsWriter()30 PictureResultsWriter() {} ~PictureResultsWriter()31 virtual ~PictureResultsWriter() {} 32 33 virtual void bench(const char name[], int32_t x, int32_t y) = 0; 34 virtual void logRenderer(sk_tools::PictureRenderer *pr) = 0; 35 virtual void tileMeta(int x, int y, int tx, int ty) = 0; 36 virtual void addTileFlag(PictureResultsWriter::TileFlags flag) = 0; 37 virtual void tileData( 38 TimerData* data, 39 const char format[], 40 const TimerData::Result result, 41 uint32_t timerTypes, 42 int numInnerLoops = 1) = 0; 43 virtual void end() = 0; 44 }; 45 46 /** 47 * This class allows bench data to be piped into multiple 48 * PictureResultWriter classes. It does not own any classes 49 * passed to it, so the owner is required to manage any classes 50 * passed to PictureResultsMultiWriter */ 51 class PictureResultsMultiWriter : public PictureResultsWriter { 52 public: PictureResultsMultiWriter()53 PictureResultsMultiWriter() 54 : fWriters() {} add(PictureResultsWriter * newWriter)55 void add(PictureResultsWriter* newWriter) { 56 fWriters.push_back(newWriter); 57 } ~PictureResultsMultiWriter()58 virtual ~PictureResultsMultiWriter() {} bench(const char name[],int32_t x,int32_t y)59 void bench(const char name[], int32_t x, int32_t y) override { 60 for(int i=0; i<fWriters.count(); ++i) { 61 fWriters[i]->bench(name, x, y); 62 } 63 } logRenderer(sk_tools::PictureRenderer * pr)64 void logRenderer(sk_tools::PictureRenderer *pr) override { 65 for(int i=0; i<fWriters.count(); ++i) { 66 fWriters[i]->logRenderer(pr); 67 } 68 } tileMeta(int x,int y,int tx,int ty)69 void tileMeta(int x, int y, int tx, int ty) override { 70 for(int i=0; i<fWriters.count(); ++i) { 71 fWriters[i]->tileMeta(x, y, tx, ty); 72 } 73 } addTileFlag(PictureResultsWriter::TileFlags flag)74 void addTileFlag(PictureResultsWriter::TileFlags flag) override { 75 for(int i=0; i<fWriters.count(); ++i) { 76 fWriters[i]->addTileFlag(flag); 77 } 78 } 79 virtual void tileData( 80 TimerData* data, 81 const char format[], 82 const TimerData::Result result, 83 uint32_t timerTypes, 84 int numInnerLoops = 1) override { 85 for(int i=0; i<fWriters.count(); ++i) { 86 fWriters[i]->tileData(data, format, result, timerTypes, 87 numInnerLoops); 88 } 89 } end()90 void end() override { 91 for(int i=0; i<fWriters.count(); ++i) { 92 fWriters[i]->end(); 93 } 94 } 95 private: 96 SkTArray<PictureResultsWriter*> fWriters; 97 }; 98 99 /** 100 * Writes to BenchLogger to mimic original behavior 101 */ 102 class PictureResultsLoggerWriter : public PictureResultsWriter { 103 private: logProgress(const char str[])104 void logProgress(const char str[]) { 105 if(fLogger != NULL) { 106 fLogger->logProgress(str); 107 } 108 } 109 public: PictureResultsLoggerWriter(BenchLogger * log)110 PictureResultsLoggerWriter(BenchLogger* log) 111 : fLogger(log), fCurrentLine() {} bench(const char name[],int32_t x,int32_t y)112 void bench(const char name[], int32_t x, int32_t y) override { 113 SkString result; 114 result.printf("running bench [%i %i] %s ", x, y, name); 115 this->logProgress(result.c_str()); 116 } logRenderer(sk_tools::PictureRenderer * renderer)117 void logRenderer(sk_tools::PictureRenderer* renderer) override { 118 fCurrentLine = renderer->getConfigName(); 119 } tileMeta(int x,int y,int tx,int ty)120 void tileMeta(int x, int y, int tx, int ty) override { 121 fCurrentLine.appendf(": tile [%i,%i] out of [%i,%i]", x, y, tx, ty); 122 } addTileFlag(PictureResultsWriter::TileFlags flag)123 void addTileFlag(PictureResultsWriter::TileFlags flag) override { 124 if(flag == PictureResultsWriter::kPurging) { 125 fCurrentLine.append(" <withPurging>"); 126 } else if(flag == PictureResultsWriter::kAvg) { 127 fCurrentLine.append(" <averaged>"); 128 } 129 } 130 virtual void tileData( 131 TimerData* data, 132 const char format[], 133 const TimerData::Result result, 134 uint32_t timerTypes, 135 int numInnerLoops = 1) override { 136 SkString results = data->getResult(format, result, 137 fCurrentLine.c_str(), timerTypes, numInnerLoops); 138 results.append("\n"); 139 this->logProgress(results.c_str()); 140 } end()141 void end() override {} 142 private: 143 BenchLogger* fLogger; 144 SkString fCurrentLine; 145 }; 146 147 /** 148 * This PictureResultsWriter collects data in a JSON node 149 * 150 * The format is something like 151 * { 152 * benches: [ 153 * { 154 * name: "Name_of_test" 155 * tilesets: [ 156 * { 157 * name: "Name of the configuration" 158 * tiles: [ 159 * { 160 * flags: { 161 * purging: true //Flags for the current tile 162 * // are put here 163 * } 164 * data: { 165 * wsecs: [....] //Actual data ends up here 166 * } 167 * } 168 * ] 169 * } 170 * ] 171 * } 172 * ] 173 * }*/ 174 175 class PictureJSONResultsWriter : public PictureResultsWriter { 176 public: PictureJSONResultsWriter(const char filename[],const char builderName[],int buildNumber,int timestamp,const char gitHash[],int gitNumber)177 PictureJSONResultsWriter(const char filename[], 178 const char builderName[], 179 int buildNumber, 180 int timestamp, 181 const char gitHash[], 182 int gitNumber) 183 : fStream(filename) { 184 fBuilderName = SkString(builderName); 185 fBuildNumber = buildNumber; 186 fTimestamp = timestamp; 187 fGitHash = SkString(gitHash); 188 fGitNumber = gitNumber; 189 fBuilderData = this->makeBuilderJson(); 190 } 191 bench(const char name[],int32_t x,int32_t y)192 void bench(const char name[], int32_t x, int32_t y) override { 193 fBenchName = SkString(name); 194 } logRenderer(sk_tools::PictureRenderer * pr)195 void logRenderer(sk_tools::PictureRenderer* pr) override { 196 fParams = pr->getJSONConfig(); 197 fConfigString = pr->getConfigName(); 198 } 199 // Apparently tiles aren't used, so tileMeta is empty tileMeta(int x,int y,int tx,int ty)200 void tileMeta(int x, int y, int tx, int ty) override {} 201 // Flags aren't used, so addTileFlag is empty addTileFlag(PictureResultsWriter::TileFlags flag)202 void addTileFlag(PictureResultsWriter::TileFlags flag) override {} 203 virtual void tileData( 204 TimerData* data, 205 const char format[], 206 const TimerData::Result result, 207 uint32_t timerTypes, 208 int numInnerLoops = 1) override { 209 Json::Value newData = data->getJSON(timerTypes, result, numInnerLoops); 210 Json::Value combinedParams(fBuilderData); 211 for(Json::ValueIterator iter = fParams.begin(); iter != fParams.end(); 212 iter++) { 213 combinedParams[iter.key().asString()]= *iter; 214 } 215 // For each set of timer data 216 for(Json::ValueIterator iter = newData.begin(); iter != newData.end(); 217 iter++) { 218 Json::Value data; 219 data["buildNumber"] = fBuildNumber; 220 data["timestamp"] = fTimestamp; 221 data["gitHash"] = fGitHash.c_str(); 222 data["gitNumber"] = fGitNumber; 223 data["isTrybot"] = fBuilderName.endsWith("Trybot"); 224 225 data["params"] = combinedParams; 226 data["params"]["benchName"] = fBenchName.c_str(); 227 228 // Not including skpSize because that's deprecated? 229 data["key"] = this->makeKey(iter.key().asString().c_str()).c_str(); 230 // Get the data 231 SkTArray<double> times; 232 Json::Value val = *iter; 233 for(Json::ValueIterator vals = val.begin(); vals != val.end(); 234 vals++) { 235 times.push_back((*vals).asDouble()); 236 } 237 qsort(static_cast<void*>(times.begin()), times.count(), 238 sizeof(double), PictureJSONResultsWriter::CompareDoubles); 239 data["value"] = times[static_cast<int>(times.count() * 0.25f)]; 240 data["params"]["measurementType"] = iter.key().asString(); 241 fStream.writeText(Json::FastWriter().write(data).c_str()); 242 } 243 } end()244 void end() override { 245 fStream.flush(); 246 } 247 private: makeBuilderJson()248 Json::Value makeBuilderJson() const { 249 static const int kNumKeys = 6; 250 static const char* kKeys[kNumKeys] = { 251 "role", "os", "model", "gpu", "arch", "configuration"}; 252 Json::Value builderData; 253 254 if (!fBuilderName.isEmpty()) { 255 SkTArray<SkString> splitBuilder; 256 SkStrSplit(fBuilderName.c_str(), "-", &splitBuilder); 257 SkASSERT(splitBuilder.count() >= kNumKeys); 258 for (int i = 0; i < kNumKeys && i < splitBuilder.count(); ++i) { 259 builderData[kKeys[i]] = splitBuilder[i].c_str(); 260 } 261 builderData["builderName"] = fBuilderName.c_str(); 262 if (kNumKeys < splitBuilder.count()) { 263 SkString extras; 264 for (int i = kNumKeys; i < splitBuilder.count(); ++i) { 265 extras.append(splitBuilder[i]); 266 if (i != splitBuilder.count() - 1) { 267 extras.append("-"); 268 } 269 } 270 builderData["badParams"] = extras.c_str(); 271 } 272 } 273 return builderData; 274 } 275 CompareDoubles(const void * p1,const void * p2)276 static int CompareDoubles(const void* p1, const void* p2) { 277 if(*static_cast<const double*>(p1) < *static_cast<const double*>(p2)) { 278 return -1; 279 } else if(*static_cast<const double*>(p1) == 280 *static_cast<const double*>(p2)) { 281 return 0; 282 } else { 283 return 1; 284 } 285 } makeKey(const char measurementType[])286 SkString makeKey(const char measurementType[]) const { 287 SkString tmp(fBuilderName); 288 tmp.append("_"); 289 tmp.append(fBenchName); 290 tmp.append("_"); 291 tmp.append(fConfigString); 292 tmp.append("_"); 293 tmp.append(measurementType); 294 return tmp; 295 } 296 297 SkFILEWStream fStream; 298 Json::Value fBuilderData; 299 SkString fBenchName; 300 Json::Value fParams; 301 302 SkString fConfigString; 303 SkString fBuilderName; 304 int fBuildNumber; 305 int fTimestamp; 306 SkString fGitHash; 307 int fGitNumber; 308 }; 309 310 #endif 311