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