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 "include/core/SkString.h"
14 #include "include/core/SkTypes.h"
15 #include "src/core/SkOSFile.h"
16 #include "src/utils/SkJSONWriter.h"
17 
18 /**
19  NanoJSONResultsWriter helps nanobench writes the test results out in the following format:
20 
21  {
22     "key": {
23       "arch": "Arm7",
24       "gpu": "SGX540",
25       "os": "Android",
26       "model": "GalaxyNexus",
27     }
28     "gitHash": "d1830323662ae8ae06908b97f15180fd25808894",
29     "build_number": "1234",
30     "results" : {
31         "Xfermode_Luminosity_640_480" : {
32            "8888" : {
33                  "median_ms" : 143.188128906250,
34                  "min_ms" : 143.835957031250,
35                  ...
36               },
37           ...
38 */
39 class NanoJSONResultsWriter : public SkJSONWriter {
40 public:
NanoJSONResultsWriter(SkWStream * stream,Mode mode)41     NanoJSONResultsWriter(SkWStream* stream, Mode mode) : SkJSONWriter(stream, mode) {}
42 
beginBench(const char * name,int32_t x,int32_t y)43     void beginBench(const char* name, int32_t x, int32_t y) {
44         SkString id = SkStringPrintf("%s_%d_%d", name, x, y);
45         this->beginObject(id.c_str());
46     }
47 
endBench()48     void endBench() { this->endObject(); }
49 
appendMetric(const char * name,double value)50     void appendMetric(const char* name, double value) {
51         // Don't record if nan, or -nan.
52         if (!sk_double_isnan(value)) {
53             this->appendDoubleDigits(name, value, 16);
54         }
55     }
56 };
57 
58 /**
59  NanoFILEAppendAndCloseStream: re-open the file, append the data, then close on every write() call.
60 
61  The purpose of this class is to not keep the file handle open between JSON flushes. SkJSONWriter
62  uses a 32k in-memory cache already, so it only flushes occasionally and is well equipped for a
63  steam like this.
64 
65  See: https://b.corp.google.com/issues/143074513
66 */
67 class NanoFILEAppendAndCloseStream : public SkWStream {
68 public:
NanoFILEAppendAndCloseStream(const char * filePath)69     NanoFILEAppendAndCloseStream(const char* filePath) : fFilePath(filePath) {
70         // Open the file as "write" to ensure it exists and clear any contents before we begin
71         // appending.
72         FILE* file = sk_fopen(fFilePath.c_str(), kWrite_SkFILE_Flag);
73         if (!file) {
74             SkDebugf("Failed to open file %s for write.\n", fFilePath.c_str());
75             fFilePath.reset();
76             return;
77         }
78         sk_fclose(file);
79     }
80 
bytesWritten()81     size_t bytesWritten() const override { return fBytesWritten; }
82 
write(const void * buffer,size_t size)83     bool write(const void* buffer, size_t size) override {
84         if (fFilePath.isEmpty()) {
85             return false;
86         }
87 
88         FILE* file = sk_fopen(fFilePath.c_str(), kAppend_SkFILE_Flag);
89         if (!file) {
90             SkDebugf("Failed to open file %s for append.\n", fFilePath.c_str());
91             return false;
92         }
93 
94         size_t bytesWritten = sk_fwrite(buffer, size, file);
95         fBytesWritten += bytesWritten;
96         sk_fclose(file);
97 
98         if (bytesWritten != size) {
99             SkDebugf("NanoFILEAppendAndCloseStream failed writing %d bytes (wrote %d instead)\n",
100                      size, bytesWritten);
101             return false;
102         }
103 
104         return true;
105     }
106 
flush()107     void flush() override {}
108 
109 private:
110     SkString fFilePath;
111     size_t fBytesWritten = 0;
112 };
113 
114 #endif
115