1 // Copyright 2015 Google Inc. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <algorithm>
16 #include <cstdint>
17 #include <iostream>
18 #include <string>
19 #include <tuple>
20 #include <vector>
21
22 #include "benchmark/benchmark.h"
23 #include "check.h"
24 #include "complexity.h"
25 #include "string_util.h"
26 #include "timers.h"
27
28 // File format reference: http://edoceo.com/utilitas/csv-file-format.
29
30 namespace benchmark {
31
32 namespace {
33 std::vector<std::string> elements = {
34 "name", "iterations", "real_time", "cpu_time",
35 "time_unit", "bytes_per_second", "items_per_second", "label",
36 "error_occurred", "error_message"};
37 } // namespace
38
CsvEscape(const std::string & s)39 std::string CsvEscape(const std::string& s) {
40 std::string tmp;
41 tmp.reserve(s.size() + 2);
42 for (char c : s) {
43 switch (c) {
44 case '"':
45 tmp += "\"\"";
46 break;
47 default:
48 tmp += c;
49 break;
50 }
51 }
52 return '"' + tmp + '"';
53 }
54
ReportContext(const Context & context)55 bool CSVReporter::ReportContext(const Context& context) {
56 PrintBasicContext(&GetErrorStream(), context);
57 return true;
58 }
59
ReportRuns(const std::vector<Run> & reports)60 void CSVReporter::ReportRuns(const std::vector<Run>& reports) {
61 std::ostream& Out = GetOutputStream();
62
63 if (!printed_header_) {
64 // save the names of all the user counters
65 for (const auto& run : reports) {
66 for (const auto& cnt : run.counters) {
67 if (cnt.first == "bytes_per_second" || cnt.first == "items_per_second")
68 continue;
69 user_counter_names_.insert(cnt.first);
70 }
71 }
72
73 // print the header
74 for (auto B = elements.begin(); B != elements.end();) {
75 Out << *B++;
76 if (B != elements.end()) Out << ",";
77 }
78 for (auto B = user_counter_names_.begin();
79 B != user_counter_names_.end();) {
80 Out << ",\"" << *B++ << "\"";
81 }
82 Out << "\n";
83
84 printed_header_ = true;
85 } else {
86 // check that all the current counters are saved in the name set
87 for (const auto& run : reports) {
88 for (const auto& cnt : run.counters) {
89 if (cnt.first == "bytes_per_second" || cnt.first == "items_per_second")
90 continue;
91 BM_CHECK(user_counter_names_.find(cnt.first) !=
92 user_counter_names_.end())
93 << "All counters must be present in each run. "
94 << "Counter named \"" << cnt.first
95 << "\" was not in a run after being added to the header";
96 }
97 }
98 }
99
100 // print results for each run
101 for (const auto& run : reports) {
102 PrintRunData(run);
103 }
104 }
105
PrintRunData(const Run & run)106 void CSVReporter::PrintRunData(const Run& run) {
107 std::ostream& Out = GetOutputStream();
108 Out << CsvEscape(run.benchmark_name()) << ",";
109 if (run.error_occurred) {
110 Out << std::string(elements.size() - 3, ',');
111 Out << "true,";
112 Out << CsvEscape(run.error_message) << "\n";
113 return;
114 }
115
116 // Do not print iteration on bigO and RMS report
117 if (!run.report_big_o && !run.report_rms) {
118 Out << run.iterations;
119 }
120 Out << ",";
121
122 Out << run.GetAdjustedRealTime() << ",";
123 Out << run.GetAdjustedCPUTime() << ",";
124
125 // Do not print timeLabel on bigO and RMS report
126 if (run.report_big_o) {
127 Out << GetBigOString(run.complexity);
128 } else if (!run.report_rms) {
129 Out << GetTimeUnitString(run.time_unit);
130 }
131 Out << ",";
132
133 if (run.counters.find("bytes_per_second") != run.counters.end()) {
134 Out << run.counters.at("bytes_per_second");
135 }
136 Out << ",";
137 if (run.counters.find("items_per_second") != run.counters.end()) {
138 Out << run.counters.at("items_per_second");
139 }
140 Out << ",";
141 if (!run.report_label.empty()) {
142 Out << CsvEscape(run.report_label);
143 }
144 Out << ",,"; // for error_occurred and error_message
145
146 // Print user counters
147 for (const auto& ucn : user_counter_names_) {
148 auto it = run.counters.find(ucn);
149 if (it == run.counters.end()) {
150 Out << ",";
151 } else {
152 Out << "," << it->second;
153 }
154 }
155 Out << '\n';
156 }
157
158 } // end namespace benchmark
159