/* * Copyright 2014 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Classes for writing out bench results in various formats. */ #ifndef SkPictureResultsWriter_DEFINED #define SkPictureResultsWriter_DEFINED #include "PictureRenderer.h" #include "BenchLogger.h" #include "ResultsWriter.h" #include "SkJSONCPP.h" #include "SkStream.h" #include "SkString.h" #include "SkTArray.h" #include "TimerData.h" /** * Base class for writing picture bench results. */ class PictureResultsWriter : SkNoncopyable { public: enum TileFlags {kPurging, kAvg}; PictureResultsWriter() {} virtual ~PictureResultsWriter() {} virtual void bench(const char name[], int32_t x, int32_t y) = 0; virtual void logRenderer(sk_tools::PictureRenderer *pr) = 0; virtual void tileMeta(int x, int y, int tx, int ty) = 0; virtual void addTileFlag(PictureResultsWriter::TileFlags flag) = 0; virtual void tileData( TimerData* data, const char format[], const TimerData::Result result, uint32_t timerTypes, int numInnerLoops = 1) = 0; virtual void end() = 0; }; /** * This class allows bench data to be piped into multiple * PictureResultWriter classes. It does not own any classes * passed to it, so the owner is required to manage any classes * passed to PictureResultsMultiWriter */ class PictureResultsMultiWriter : public PictureResultsWriter { public: PictureResultsMultiWriter() : fWriters() {} void add(PictureResultsWriter* newWriter) { fWriters.push_back(newWriter); } virtual ~PictureResultsMultiWriter() {} virtual void bench(const char name[], int32_t x, int32_t y) SK_OVERRIDE { for(int i=0; ibench(name, x, y); } } virtual void logRenderer(sk_tools::PictureRenderer *pr) SK_OVERRIDE { for(int i=0; ilogRenderer(pr); } } virtual void tileMeta(int x, int y, int tx, int ty) SK_OVERRIDE { for(int i=0; itileMeta(x, y, tx, ty); } } virtual void addTileFlag(PictureResultsWriter::TileFlags flag) SK_OVERRIDE { for(int i=0; iaddTileFlag(flag); } } virtual void tileData( TimerData* data, const char format[], const TimerData::Result result, uint32_t timerTypes, int numInnerLoops = 1) SK_OVERRIDE { for(int i=0; itileData(data, format, result, timerTypes, numInnerLoops); } } virtual void end() SK_OVERRIDE { for(int i=0; iend(); } } private: SkTArray fWriters; }; /** * Writes to BenchLogger to mimic original behavior */ class PictureResultsLoggerWriter : public PictureResultsWriter { private: void logProgress(const char str[]) { if(fLogger != NULL) { fLogger->logProgress(str); } } public: PictureResultsLoggerWriter(BenchLogger* log) : fLogger(log), fCurrentLine() {} virtual void bench(const char name[], int32_t x, int32_t y) SK_OVERRIDE { SkString result; result.printf("running bench [%i %i] %s ", x, y, name); this->logProgress(result.c_str()); } virtual void logRenderer(sk_tools::PictureRenderer* renderer) SK_OVERRIDE { fCurrentLine = renderer->getConfigName(); } virtual void tileMeta(int x, int y, int tx, int ty) SK_OVERRIDE { fCurrentLine.appendf(": tile [%i,%i] out of [%i,%i]", x, y, tx, ty); } virtual void addTileFlag(PictureResultsWriter::TileFlags flag) SK_OVERRIDE { if(flag == PictureResultsWriter::kPurging) { fCurrentLine.append(" "); } else if(flag == PictureResultsWriter::kAvg) { fCurrentLine.append(" "); } } virtual void tileData( TimerData* data, const char format[], const TimerData::Result result, uint32_t timerTypes, int numInnerLoops = 1) SK_OVERRIDE { SkString results = data->getResult(format, result, fCurrentLine.c_str(), timerTypes, numInnerLoops); results.append("\n"); this->logProgress(results.c_str()); } virtual void end() {} private: BenchLogger* fLogger; SkString fCurrentLine; }; /** * This PictureResultsWriter collects data in a JSON node * * The format is something like * { * benches: [ * { * name: "Name_of_test" * tilesets: [ * { * name: "Name of the configuration" * tiles: [ * { * flags: { * purging: true //Flags for the current tile * // are put here * } * data: { * wsecs: [....] //Actual data ends up here * } * } * ] * } * ] * } * ] * }*/ class PictureJSONResultsWriter : public PictureResultsWriter { public: PictureJSONResultsWriter(const char filename[], const char builderName[], int buildNumber, int timestamp, const char gitHash[], int gitNumber) : fStream(filename) { fBuilderName = SkString(builderName); fBuildNumber = buildNumber; fTimestamp = timestamp; fGitHash = SkString(gitHash); fGitNumber = gitNumber; fBuilderData = this->makeBuilderJson(); } virtual void bench(const char name[], int32_t x, int32_t y) SK_OVERRIDE { fBenchName = SkString(name); } virtual void logRenderer(sk_tools::PictureRenderer* pr) SK_OVERRIDE { fParams = pr->getJSONConfig(); fConfigString = pr->getConfigName(); } // Apparently tiles aren't used, so tileMeta is empty virtual void tileMeta(int x, int y, int tx, int ty) SK_OVERRIDE {} // Flags aren't used, so addTileFlag is empty virtual void addTileFlag(PictureResultsWriter::TileFlags flag) SK_OVERRIDE {} virtual void tileData( TimerData* data, const char format[], const TimerData::Result result, uint32_t timerTypes, int numInnerLoops = 1) SK_OVERRIDE { Json::Value newData = data->getJSON(timerTypes, result, numInnerLoops); Json::Value combinedParams(fBuilderData); for(Json::ValueIterator iter = fParams.begin(); iter != fParams.end(); iter++) { combinedParams[iter.key().asString()]= *iter; } // For each set of timer data for(Json::ValueIterator iter = newData.begin(); iter != newData.end(); iter++) { Json::Value data; data["buildNumber"] = fBuildNumber; data["timestamp"] = fTimestamp; data["gitHash"] = fGitHash.c_str(); data["gitNumber"] = fGitNumber; data["isTrybot"] = fBuilderName.endsWith("Trybot"); data["params"] = combinedParams; data["params"]["benchName"] = fBenchName.c_str(); // Not including skpSize because that's deprecated? data["key"] = this->makeKey(iter.key().asString().c_str()).c_str(); // Get the data SkTArray times; Json::Value val = *iter; for(Json::ValueIterator vals = val.begin(); vals != val.end(); vals++) { times.push_back((*vals).asDouble()); } qsort(static_cast(times.begin()), times.count(), sizeof(double), PictureJSONResultsWriter::CompareDoubles); data["value"] = times[static_cast(times.count() * 0.25f)]; data["params"]["measurementType"] = iter.key().asString(); fStream.writeText(Json::FastWriter().write(data).c_str()); } } virtual void end() SK_OVERRIDE { fStream.flush(); } private: Json::Value makeBuilderJson() const { static const int kNumKeys = 6; static const char* kKeys[kNumKeys] = { "role", "os", "model", "gpu", "arch", "configuration"}; Json::Value builderData; if (!fBuilderName.isEmpty()) { SkTArray splitBuilder; SkStrSplit(fBuilderName.c_str(), "-", &splitBuilder); SkASSERT(splitBuilder.count() >= kNumKeys); for (int i = 0; i < kNumKeys && i < splitBuilder.count(); ++i) { builderData[kKeys[i]] = splitBuilder[i].c_str(); } builderData["builderName"] = fBuilderName.c_str(); if (kNumKeys < splitBuilder.count()) { SkString extras; for (int i = kNumKeys; i < splitBuilder.count(); ++i) { extras.append(splitBuilder[i]); if (i != splitBuilder.count() - 1) { extras.append("-"); } } builderData["badParams"] = extras.c_str(); } } return builderData; } static int CompareDoubles(const void* p1, const void* p2) { if(*static_cast(p1) < *static_cast(p2)) { return -1; } else if(*static_cast(p1) == *static_cast(p2)) { return 0; } else { return 1; } } SkString makeKey(const char measurementType[]) const { SkString tmp(fBuilderName); tmp.append("_"); tmp.append(fBenchName); tmp.append("_"); tmp.append(fConfigString); tmp.append("_"); tmp.append(measurementType); return tmp; } SkFILEWStream fStream; Json::Value fBuilderData; SkString fBenchName; Json::Value fParams; SkString fConfigString; SkString fBuilderName; int fBuildNumber; int fTimestamp; SkString fGitHash; int fGitNumber; }; #endif