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 "benchmark/benchmark.h"
16 #include "complexity.h"
17
18 #include <algorithm>
19 #include <cstdint>
20 #include <iostream>
21 #include <string>
22 #include <tuple>
23 #include <vector>
24
25 #include "check.h"
26 #include "string_util.h"
27 #include "timers.h"
28
29 // File format reference: http://edoceo.com/utilitas/csv-file-format.
30
31 namespace benchmark {
32
33 namespace {
34 std::vector<std::string> elements = {
35 "name", "iterations", "real_time", "cpu_time",
36 "time_unit", "bytes_per_second", "items_per_second", "label",
37 "error_occurred", "error_message"};
38 } // namespace
39
CsvEscape(const std::string & s)40 std::string CsvEscape(const std::string & s) {
41 std::string tmp;
42 tmp.reserve(s.size() + 2);
43 for (char c : s) {
44 switch (c) {
45 case '"' : tmp += "\"\""; break;
46 default : tmp += c; break;
47 }
48 }
49 return '"' + tmp + '"';
50 }
51
ReportContext(const Context & context)52 bool CSVReporter::ReportContext(const Context& context) {
53 PrintBasicContext(&GetErrorStream(), context);
54 return true;
55 }
56
ReportRuns(const std::vector<Run> & reports)57 void CSVReporter::ReportRuns(const std::vector<Run>& reports) {
58 std::ostream& Out = GetOutputStream();
59
60 if (!printed_header_) {
61 // save the names of all the user counters
62 for (const auto& run : reports) {
63 for (const auto& cnt : run.counters) {
64 if (cnt.first == "bytes_per_second" || cnt.first == "items_per_second")
65 continue;
66 user_counter_names_.insert(cnt.first);
67 }
68 }
69
70 // print the header
71 for (auto B = elements.begin(); B != elements.end();) {
72 Out << *B++;
73 if (B != elements.end()) Out << ",";
74 }
75 for (auto B = user_counter_names_.begin();
76 B != user_counter_names_.end();) {
77 Out << ",\"" << *B++ << "\"";
78 }
79 Out << "\n";
80
81 printed_header_ = true;
82 } else {
83 // check that all the current counters are saved in the name set
84 for (const auto& run : reports) {
85 for (const auto& cnt : run.counters) {
86 if (cnt.first == "bytes_per_second" || cnt.first == "items_per_second")
87 continue;
88 CHECK(user_counter_names_.find(cnt.first) != user_counter_names_.end())
89 << "All counters must be present in each run. "
90 << "Counter named \"" << cnt.first
91 << "\" was not in a run after being added to the header";
92 }
93 }
94 }
95
96 // print results for each run
97 for (const auto& run : reports) {
98 PrintRunData(run);
99 }
100 }
101
PrintRunData(const Run & run)102 void CSVReporter::PrintRunData(const Run& run) {
103 std::ostream& Out = GetOutputStream();
104 Out << CsvEscape(run.benchmark_name()) << ",";
105 if (run.error_occurred) {
106 Out << std::string(elements.size() - 3, ',');
107 Out << "true,";
108 Out << CsvEscape(run.error_message) << "\n";
109 return;
110 }
111
112 // Do not print iteration on bigO and RMS report
113 if (!run.report_big_o && !run.report_rms) {
114 Out << run.iterations;
115 }
116 Out << ",";
117
118 Out << run.GetAdjustedRealTime() << ",";
119 Out << run.GetAdjustedCPUTime() << ",";
120
121 // Do not print timeLabel on bigO and RMS report
122 if (run.report_big_o) {
123 Out << GetBigOString(run.complexity);
124 } else if (!run.report_rms) {
125 Out << GetTimeUnitString(run.time_unit);
126 }
127 Out << ",";
128
129 if (run.counters.find("bytes_per_second") != run.counters.end()) {
130 Out << run.counters.at("bytes_per_second");
131 }
132 Out << ",";
133 if (run.counters.find("items_per_second") != run.counters.end()) {
134 Out << run.counters.at("items_per_second");
135 }
136 Out << ",";
137 if (!run.report_label.empty()) {
138 Out << CsvEscape(run.report_label);
139 }
140 Out << ",,"; // for error_occurred and error_message
141
142 // Print user counters
143 for (const auto& ucn : user_counter_names_) {
144 auto it = run.counters.find(ucn);
145 if (it == run.counters.end()) {
146 Out << ",";
147 } else {
148 Out << "," << it->second;
149 }
150 }
151 Out << '\n';
152 }
153
154 } // end namespace benchmark
155