/* * Copyright 2013 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 SkResultsWriter_DEFINED #define SkResultsWriter_DEFINED #include "include/core/SkString.h" #include "include/core/SkTypes.h" #include "src/core/SkOSFile.h" #include "src/utils/SkJSONWriter.h" /** NanoJSONResultsWriter helps nanobench writes the test results out in the following format: { "key": { "arch": "Arm7", "gpu": "SGX540", "os": "Android", "model": "GalaxyNexus", } "gitHash": "d1830323662ae8ae06908b97f15180fd25808894", "build_number": "1234", "results" : { "Xfermode_Luminosity_640_480" : { "8888" : { "median_ms" : 143.188128906250, "min_ms" : 143.835957031250, ... }, ... */ class NanoJSONResultsWriter : public SkJSONWriter { public: NanoJSONResultsWriter(SkWStream* stream, Mode mode) : SkJSONWriter(stream, mode) {} void beginBench(const char* name, int32_t x, int32_t y) { SkString id = SkStringPrintf("%s_%d_%d", name, x, y); this->beginObject(id.c_str()); } void endBench() { this->endObject(); } void appendMetric(const char* name, double value) { // Don't record if nan, or -nan. if (!sk_double_isnan(value)) { this->appendDoubleDigits(name, value, 16); } } }; /** NanoFILEAppendAndCloseStream: re-open the file, append the data, then close on every write() call. The purpose of this class is to not keep the file handle open between JSON flushes. SkJSONWriter uses a 32k in-memory cache already, so it only flushes occasionally and is well equipped for a steam like this. See: https://b.corp.google.com/issues/143074513 */ class NanoFILEAppendAndCloseStream : public SkWStream { public: NanoFILEAppendAndCloseStream(const char* filePath) : fFilePath(filePath) { // Open the file as "write" to ensure it exists and clear any contents before we begin // appending. FILE* file = sk_fopen(fFilePath.c_str(), kWrite_SkFILE_Flag); if (!file) { SkDebugf("Failed to open file %s for write.\n", fFilePath.c_str()); fFilePath.reset(); return; } sk_fclose(file); } size_t bytesWritten() const override { return fBytesWritten; } bool write(const void* buffer, size_t size) override { if (fFilePath.isEmpty()) { return false; } FILE* file = sk_fopen(fFilePath.c_str(), kAppend_SkFILE_Flag); if (!file) { SkDebugf("Failed to open file %s for append.\n", fFilePath.c_str()); return false; } size_t bytesWritten = sk_fwrite(buffer, size, file); fBytesWritten += bytesWritten; sk_fclose(file); if (bytesWritten != size) { SkDebugf("NanoFILEAppendAndCloseStream failed writing %d bytes (wrote %d instead)\n", size, bytesWritten); return false; } return true; } void flush() override {} private: SkString fFilePath; size_t fBytesWritten = 0; }; #endif