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