| // Copyright 2015 Google Inc. All rights reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include <iostream> | |
| #include <string> | |
| #include <vector> | |
| #include "benchmark/benchmark.h" | |
| #include "check.h" | |
| #include "complexity.h" | |
| // File format reference: http://edoceo.com/utilitas/csv-file-format. | |
| namespace benchmark { | |
| namespace { | |
| const std::vector<const char*> elements = { | |
| "name", "iterations", "real_time", "cpu_time", | |
| "time_unit", "bytes_per_second", "items_per_second", "label", | |
| "error_occurred", "error_message"}; | |
| std::string CsvEscape(const std::string& s) { | |
| std::string tmp; | |
| tmp.reserve(s.size() + 2); | |
| for (char c : s) { | |
| switch (c) { | |
| case '"': | |
| tmp += "\"\""; | |
| break; | |
| default: | |
| tmp += c; | |
| break; | |
| } | |
| } | |
| return '"' + tmp + '"'; | |
| } | |
| } // namespace | |
| BENCHMARK_EXPORT | |
| bool CSVReporter::ReportContext(const Context& context) { | |
| PrintBasicContext(&GetErrorStream(), context); | |
| return true; | |
| } | |
| BENCHMARK_EXPORT | |
| void CSVReporter::ReportRuns(const std::vector<Run>& reports) { | |
| std::ostream& Out = GetOutputStream(); | |
| if (!printed_header_) { | |
| // save the names of all the user counters | |
| for (const auto& run : reports) { | |
| for (const auto& cnt : run.counters) { | |
| if (cnt.first == "bytes_per_second" || | |
| cnt.first == "items_per_second") { | |
| continue; | |
| } | |
| user_counter_names_.insert(cnt.first); | |
| } | |
| } | |
| // print the header | |
| for (auto B = elements.begin(); B != elements.end();) { | |
| Out << *B++; | |
| if (B != elements.end()) { | |
| Out << ","; | |
| } | |
| } | |
| for (auto B = user_counter_names_.begin(); | |
| B != user_counter_names_.end();) { | |
| Out << ",\"" << *B++ << "\""; | |
| } | |
| Out << "\n"; | |
| printed_header_ = true; | |
| } else { | |
| // check that all the current counters are saved in the name set | |
| for (const auto& run : reports) { | |
| for (const auto& cnt : run.counters) { | |
| if (cnt.first == "bytes_per_second" || | |
| cnt.first == "items_per_second") { | |
| continue; | |
| } | |
| BM_CHECK(user_counter_names_.find(cnt.first) != | |
| user_counter_names_.end()) | |
| << "All counters must be present in each run. " | |
| << "Counter named \"" << cnt.first | |
| << "\" was not in a run after being added to the header"; | |
| } | |
| } | |
| } | |
| // print results for each run | |
| for (const auto& run : reports) { | |
| PrintRunData(run); | |
| } | |
| } | |
| BENCHMARK_EXPORT | |
| void CSVReporter::PrintRunData(const Run& run) { | |
| std::ostream& Out = GetOutputStream(); | |
| Out << CsvEscape(run.benchmark_name()) << ","; | |
| if (run.skipped != 0u) { | |
| Out << std::string(elements.size() - 3, ','); | |
| Out << std::boolalpha << (internal::SkippedWithError == run.skipped) << ","; | |
| Out << CsvEscape(run.skip_message) << "\n"; | |
| return; | |
| } | |
| // Do not print iteration on bigO and RMS report | |
| if (!run.report_big_o && !run.report_rms) { | |
| Out << run.iterations; | |
| } | |
| Out << ","; | |
| if (run.run_type != Run::RT_Aggregate || | |
| run.aggregate_unit == StatisticUnit::kTime) { | |
| Out << run.GetAdjustedRealTime() << ","; | |
| Out << run.GetAdjustedCPUTime() << ","; | |
| } else { | |
| assert(run.aggregate_unit == StatisticUnit::kPercentage); | |
| Out << run.real_accumulated_time << ","; | |
| Out << run.cpu_accumulated_time << ","; | |
| } | |
| // Do not print timeLabel on bigO and RMS report | |
| if (run.report_big_o) { | |
| Out << GetBigOString(run.complexity); | |
| } else if (!run.report_rms && | |
| run.aggregate_unit != StatisticUnit::kPercentage) { | |
| Out << GetTimeUnitString(run.time_unit); | |
| } | |
| Out << ","; | |
| if (run.counters.find("bytes_per_second") != run.counters.end()) { | |
| Out << run.counters.at("bytes_per_second"); | |
| } | |
| Out << ","; | |
| if (run.counters.find("items_per_second") != run.counters.end()) { | |
| Out << run.counters.at("items_per_second"); | |
| } | |
| Out << ","; | |
| if (!run.report_label.empty()) { | |
| Out << CsvEscape(run.report_label); | |
| } | |
| Out << ",,"; // for error_occurred and error_message | |
| // Print user counters | |
| for (const auto& ucn : user_counter_names_) { | |
| auto it = run.counters.find(ucn); | |
| if (it == run.counters.end()) { | |
| Out << ","; | |
| } else { | |
| Out << "," << it->second; | |
| } | |
| } | |
| Out << '\n'; | |
| } | |
| } // end namespace benchmark |