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 <cstdio> 18#include <cstring> 19#include <iostream> 20#include <string> 21#include <tuple> 22#include <vector> 23 24#include "benchmark/benchmark.h" 25#include "check.h" 26#include "colorprint.h" 27#include "commandlineflags.h" 28#include "complexity.h" 29#include "counter.h" 30#include "internal_macros.h" 31#include "string_util.h" 32#include "timers.h" 33 34namespace benchmark { 35 36BENCHMARK_EXPORT 37bool ConsoleReporter::ReportContext(const Context& context) { 38 name_field_width_ = context.name_field_width; 39 printed_header_ = false; 40 prev_counters_.clear(); 41 42 PrintBasicContext(&GetErrorStream(), context); 43 44#ifdef BENCHMARK_OS_WINDOWS 45 if ((output_options_ & OO_Color) && &std::cout != &GetOutputStream()) { 46 GetErrorStream() 47 << "Color printing is only supported for stdout on windows." 48 " Disabling color printing\n"; 49 output_options_ = static_cast<OutputOptions>(output_options_ & ~OO_Color); 50 } 51#endif 52 53 return true; 54} 55 56BENCHMARK_EXPORT 57void ConsoleReporter::PrintHeader(const Run& run) { 58 std::string str = 59 FormatString("%-*s %13s %15s %12s", static_cast<int>(name_field_width_), 60 "Benchmark", "Time", "CPU", "Iterations"); 61 if (!run.counters.empty()) { 62 if (output_options_ & OO_Tabular) { 63 for (auto const& c : run.counters) { 64 str += FormatString(" %10s", c.first.c_str()); 65 } 66 } else { 67 str += " UserCounters..."; 68 } 69 } 70 std::string line = std::string(str.length(), '-'); 71 GetOutputStream() << line << "\n" << str << "\n" << line << "\n"; 72} 73 74BENCHMARK_EXPORT 75void ConsoleReporter::ReportRuns(const std::vector<Run>& reports) { 76 for (const auto& run : reports) { 77 // print the header: 78 // --- if none was printed yet 79 bool print_header = !printed_header_; 80 // --- or if the format is tabular and this run 81 // has different fields from the prev header 82 print_header |= (output_options_ & OO_Tabular) && 83 (!internal::SameNames(run.counters, prev_counters_)); 84 if (print_header) { 85 printed_header_ = true; 86 prev_counters_ = run.counters; 87 PrintHeader(run); 88 } 89 // As an alternative to printing the headers like this, we could sort 90 // the benchmarks by header and then print. But this would require 91 // waiting for the full results before printing, or printing twice. 92 PrintRunData(run); 93 } 94} 95 96static void IgnoreColorPrint(std::ostream& out, LogColor, const char* fmt, 97 ...) { 98 va_list args; 99 va_start(args, fmt); 100 out << FormatString(fmt, args); 101 va_end(args); 102} 103 104static std::string FormatTime(double time) { 105 // For the time columns of the console printer 13 digits are reserved. One of 106 // them is a space and max two of them are the time unit (e.g ns). That puts 107 // us at 10 digits usable for the number. 108 // Align decimal places... 109 if (time < 1.0) { 110 return FormatString("%10.3f", time); 111 } 112 if (time < 10.0) { 113 return FormatString("%10.2f", time); 114 } 115 if (time < 100.0) { 116 return FormatString("%10.1f", time); 117 } 118 // Assuming the time is at max 9.9999e+99 and we have 10 digits for the 119 // number, we get 10-1(.)-1(e)-1(sign)-2(exponent) = 5 digits to print. 120 if (time > 9999999999 /*max 10 digit number*/) { 121 return FormatString("%1.4e", time); 122 } 123 return FormatString("%10.0f", time); 124} 125 126BENCHMARK_EXPORT 127void ConsoleReporter::PrintRunData(const Run& result) { 128 typedef void(PrinterFn)(std::ostream&, LogColor, const char*, ...); 129 auto& Out = GetOutputStream(); 130 PrinterFn* printer = (output_options_ & OO_Color) 131 ? static_cast<PrinterFn*>(ColorPrintf) 132 : IgnoreColorPrint; 133 auto name_color = 134 (result.report_big_o || result.report_rms) ? COLOR_BLUE : COLOR_GREEN; 135 printer(Out, name_color, "%-*s ", name_field_width_, 136 result.benchmark_name().c_str()); 137 138 if (internal::SkippedWithError == result.skipped) { 139 printer(Out, COLOR_RED, "ERROR OCCURRED: \'%s\'", 140 result.skip_message.c_str()); 141 printer(Out, COLOR_DEFAULT, "\n"); 142 return; 143 } else if (internal::SkippedWithMessage == result.skipped) { 144 printer(Out, COLOR_WHITE, "SKIPPED: \'%s\'", result.skip_message.c_str()); 145 printer(Out, COLOR_DEFAULT, "\n"); 146 return; 147 } 148 149 const double real_time = result.GetAdjustedRealTime(); 150 const double cpu_time = result.GetAdjustedCPUTime(); 151 const std::string real_time_str = FormatTime(real_time); 152 const std::string cpu_time_str = FormatTime(cpu_time); 153 154 if (result.report_big_o) { 155 std::string big_o = GetBigOString(result.complexity); 156 printer(Out, COLOR_YELLOW, "%10.2f %-4s %10.2f %-4s ", real_time, 157 big_o.c_str(), cpu_time, big_o.c_str()); 158 } else if (result.report_rms) { 159 printer(Out, COLOR_YELLOW, "%10.0f %-4s %10.0f %-4s ", real_time * 100, "%", 160 cpu_time * 100, "%"); 161 } else if (result.run_type != Run::RT_Aggregate || 162 result.aggregate_unit == StatisticUnit::kTime) { 163 const char* timeLabel = GetTimeUnitString(result.time_unit); 164 printer(Out, COLOR_YELLOW, "%s %-4s %s %-4s ", real_time_str.c_str(), 165 timeLabel, cpu_time_str.c_str(), timeLabel); 166 } else { 167 assert(result.aggregate_unit == StatisticUnit::kPercentage); 168 printer(Out, COLOR_YELLOW, "%10.2f %-4s %10.2f %-4s ", 169 (100. * result.real_accumulated_time), "%", 170 (100. * result.cpu_accumulated_time), "%"); 171 } 172 173 if (!result.report_big_o && !result.report_rms) { 174 printer(Out, COLOR_CYAN, "%10lld", result.iterations); 175 } 176 177 for (auto& c : result.counters) { 178 const std::size_t cNameLen = 179 std::max(std::string::size_type(10), c.first.length()); 180 std::string s; 181 const char* unit = ""; 182 if (result.run_type == Run::RT_Aggregate && 183 result.aggregate_unit == StatisticUnit::kPercentage) { 184 s = StrFormat("%.2f", 100. * c.second.value); 185 unit = "%"; 186 } else { 187 s = HumanReadableNumber(c.second.value, c.second.oneK); 188 if (c.second.flags & Counter::kIsRate) 189 unit = (c.second.flags & Counter::kInvert) ? "s" : "/s"; 190 } 191 if (output_options_ & OO_Tabular) { 192 printer(Out, COLOR_DEFAULT, " %*s%s", cNameLen - strlen(unit), s.c_str(), 193 unit); 194 } else { 195 printer(Out, COLOR_DEFAULT, " %s=%s%s", c.first.c_str(), s.c_str(), unit); 196 } 197 } 198 199 if (!result.report_label.empty()) { 200 printer(Out, COLOR_DEFAULT, " %s", result.report_label.c_str()); 201 } 202 203 printer(Out, COLOR_DEFAULT, "\n"); 204} 205 206} // end namespace benchmark 207