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 <cmath>
17#include <cstdint>
18#include <iomanip> // for setprecision
19#include <iostream>
20#include <limits>
21#include <string>
22#include <tuple>
23#include <vector>
24
25#include "benchmark/benchmark.h"
26#include "complexity.h"
27#include "string_util.h"
28#include "timers.h"
29
30namespace benchmark {
31namespace internal {
32extern std::map<std::string, std::string>* global_context;
33}
34
35namespace {
36
37std::string StrEscape(const std::string& s) {
38 std::string tmp;
39 tmp.reserve(s.size());
40 for (char c : s) {
41 switch (c) {
42 case '\b':
43 tmp += "\\b";
44 break;
45 case '\f':
46 tmp += "\\f";
47 break;
48 case '\n':
49 tmp += "\\n";
50 break;
51 case '\r':
52 tmp += "\\r";
53 break;
54 case '\t':
55 tmp += "\\t";
56 break;
57 case '\\':
58 tmp += "\\\\";
59 break;
60 case '"':
61 tmp += "\\\"";
62 break;
63 default:
64 tmp += c;
65 break;
66 }
67 }
68 return tmp;
69}
70
71std::string FormatKV(std::string const& key, std::string const& value) {
72 return StrFormat("\"%s\": \"%s\"", StrEscape(key).c_str(),
73 StrEscape(value).c_str());
74}
75
76std::string FormatKV(std::string const& key, const char* value) {
77 return StrFormat("\"%s\": \"%s\"", StrEscape(key).c_str(),
78 StrEscape(value).c_str());
79}
80
81std::string FormatKV(std::string const& key, bool value) {
82 return StrFormat("\"%s\": %s", StrEscape(key).c_str(),
83 value ? "true" : "false");
84}
85
86std::string FormatKV(std::string const& key, int64_t value) {
87 std::stringstream ss;
88 ss << '"' << StrEscape(key) << "\": " << value;
89 return ss.str();
90}
91
92std::string FormatKV(std::string const& key, IterationCount value) {
93 std::stringstream ss;
94 ss << '"' << StrEscape(key) << "\": " << value;
95 return ss.str();
96}
97
98std::string FormatKV(std::string const& key, double value) {
99 std::stringstream ss;
100 ss << '"' << StrEscape(key) << "\": ";
101
102 if (std::isnan(value))
103 ss << (value < 0 ? "-" : "") << "NaN";
104 else if (std::isinf(value))
105 ss << (value < 0 ? "-" : "") << "Infinity";
106 else {
107 const auto max_digits10 =
108 std::numeric_limits<decltype(value)>::max_digits10;
109 const auto max_fractional_digits10 = max_digits10 - 1;
110 ss << std::scientific << std::setprecision(max_fractional_digits10)
111 << value;
112 }
113 return ss.str();
114}
115
116int64_t RoundDouble(double v) { return std::lround(v); }
117
118} // end namespace
119
120bool JSONReporter::ReportContext(const Context& context) {
121 std::ostream& out = GetOutputStream();
122
123 out << "{\n";
124 std::string inner_indent(2, ' ');
125
126 // Open context block and print context information.
127 out << inner_indent << "\"context\": {\n";
128 std::string indent(4, ' ');
129
130 std::string walltime_value = LocalDateTimeString();
131 out << indent << FormatKV("date", walltime_value) << ",\n";
132
133 out << indent << FormatKV("host_name", context.sys_info.name) << ",\n";
134
135 if (Context::executable_name) {
136 out << indent << FormatKV("executable", Context::executable_name) << ",\n";
137 }
138
139 CPUInfo const& info = context.cpu_info;
140 out << indent << FormatKV("num_cpus", static_cast<int64_t>(info.num_cpus))
141 << ",\n";
142 out << indent
143 << FormatKV("mhz_per_cpu",
144 RoundDouble(info.cycles_per_second / 1000000.0))
145 << ",\n";
146 if (CPUInfo::Scaling::UNKNOWN != info.scaling) {
147 out << indent
148 << FormatKV("cpu_scaling_enabled",
149 info.scaling == CPUInfo::Scaling::ENABLED ? true : false)
150 << ",\n";
151 }
152
153 out << indent << "\"caches\": [\n";
154 indent = std::string(6, ' ');
155 std::string cache_indent(8, ' ');
156 for (size_t i = 0; i < info.caches.size(); ++i) {
157 auto& CI = info.caches[i];
158 out << indent << "{\n";
159 out << cache_indent << FormatKV("type", CI.type) << ",\n";
160 out << cache_indent << FormatKV("level", static_cast<int64_t>(CI.level))
161 << ",\n";
162 out << cache_indent << FormatKV("size", static_cast<int64_t>(CI.size))
163 << ",\n";
164 out << cache_indent
165 << FormatKV("num_sharing", static_cast<int64_t>(CI.num_sharing))
166 << "\n";
167 out << indent << "}";
168 if (i != info.caches.size() - 1) out << ",";
169 out << "\n";
170 }
171 indent = std::string(4, ' ');
172 out << indent << "],\n";
173 out << indent << "\"load_avg\": [";
174 for (auto it = info.load_avg.begin(); it != info.load_avg.end();) {
175 out << *it++;
176 if (it != info.load_avg.end()) out << ",";
177 }
178 out << "],\n";
179
180#if defined(NDEBUG)
181 const char build_type[] = "release";
182#else
183 const char build_type[] = "debug";
184#endif
185 out << indent << FormatKV("library_build_type", build_type);
186
187 if (internal::global_context != nullptr) {
188 for (const auto& kv : *internal::global_context) {
189 out << ",\n";
190 out << indent << FormatKV(kv.first, kv.second);
191 }
192 }
193 out << "\n";
194
195 // Close context block and open the list of benchmarks.
196 out << inner_indent << "},\n";
197 out << inner_indent << "\"benchmarks\": [\n";
198 return true;
199}
200
201void JSONReporter::ReportRuns(std::vector<Run> const& reports) {
202 if (reports.empty()) {
203 return;
204 }
205 std::string indent(4, ' ');
206 std::ostream& out = GetOutputStream();
207 if (!first_report_) {
208 out << ",\n";
209 }
210 first_report_ = false;
211
212 for (auto it = reports.begin(); it != reports.end(); ++it) {
213 out << indent << "{\n";
214 PrintRunData(*it);
215 out << indent << '}';
216 auto it_cp = it;
217 if (++it_cp != reports.end()) {
218 out << ",\n";
219 }
220 }
221}
222
223void JSONReporter::Finalize() {
224 // Close the list of benchmarks and the top level object.
225 GetOutputStream() << "\n ]\n}\n";
226}
227
228void JSONReporter::PrintRunData(Run const& run) {
229 std::string indent(6, ' ');
230 std::ostream& out = GetOutputStream();
231 out << indent << FormatKV("name", run.benchmark_name()) << ",\n";
232 out << indent << FormatKV("family_index", run.family_index) << ",\n";
233 out << indent
234 << FormatKV("per_family_instance_index", run.per_family_instance_index)
235 << ",\n";
236 out << indent << FormatKV("run_name", run.run_name.str()) << ",\n";
237 out << indent << FormatKV("run_type", [&run]() -> const char* {
238 switch (run.run_type) {
239 case BenchmarkReporter::Run::RT_Iteration:
240 return "iteration";
241 case BenchmarkReporter::Run::RT_Aggregate:
242 return "aggregate";
243 }
244 BENCHMARK_UNREACHABLE();
245 }()) << ",\n";
246 out << indent << FormatKV("repetitions", run.repetitions) << ",\n";
247 if (run.run_type != BenchmarkReporter::Run::RT_Aggregate) {
248 out << indent << FormatKV("repetition_index", run.repetition_index)
249 << ",\n";
250 }
251 out << indent << FormatKV("threads", run.threads) << ",\n";
252 if (run.run_type == BenchmarkReporter::Run::RT_Aggregate) {
253 out << indent << FormatKV("aggregate_name", run.aggregate_name) << ",\n";
254 out << indent << FormatKV("aggregate_unit", [&run]() -> const char* {
255 switch (run.aggregate_unit) {
256 case StatisticUnit::kTime:
257 return "time";
258 case StatisticUnit::kPercentage:
259 return "percentage";
260 }
261 BENCHMARK_UNREACHABLE();
262 }()) << ",\n";
263 }
264 if (run.error_occurred) {
265 out << indent << FormatKV("error_occurred", run.error_occurred) << ",\n";
266 out << indent << FormatKV("error_message", run.error_message) << ",\n";
267 }
268 if (!run.report_big_o && !run.report_rms) {
269 out << indent << FormatKV("iterations", run.iterations) << ",\n";
270 if (run.run_type != Run::RT_Aggregate ||
271 run.aggregate_unit == StatisticUnit::kTime) {
272 out << indent << FormatKV("real_time", run.GetAdjustedRealTime())
273 << ",\n";
274 out << indent << FormatKV("cpu_time", run.GetAdjustedCPUTime());
275 } else {
276 assert(run.aggregate_unit == StatisticUnit::kPercentage);
277 out << indent << FormatKV("real_time", run.real_accumulated_time)
278 << ",\n";
279 out << indent << FormatKV("cpu_time", run.cpu_accumulated_time);
280 }
281 out << ",\n"
282 << indent << FormatKV("time_unit", GetTimeUnitString(run.time_unit));
283 } else if (run.report_big_o) {
284 out << indent << FormatKV("cpu_coefficient", run.GetAdjustedCPUTime())
285 << ",\n";
286 out << indent << FormatKV("real_coefficient", run.GetAdjustedRealTime())
287 << ",\n";
288 out << indent << FormatKV("big_o", GetBigOString(run.complexity)) << ",\n";
289 out << indent << FormatKV("time_unit", GetTimeUnitString(run.time_unit));
290 } else if (run.report_rms) {
291 out << indent << FormatKV("rms", run.GetAdjustedCPUTime());
292 }
293
294 for (auto& c : run.counters) {
295 out << ",\n" << indent << FormatKV(c.first, c.second);
296 }
297
298 if (run.memory_result) {
299 const MemoryManager::Result memory_result = *run.memory_result;
300 out << ",\n" << indent << FormatKV("allocs_per_iter", run.allocs_per_iter);
301 out << ",\n"
302 << indent << FormatKV("max_bytes_used", memory_result.max_bytes_used);
303
304 auto report_if_present = [&out, &indent](const char* label, int64_t val) {
305 if (val != MemoryManager::TombstoneValue)
306 out << ",\n" << indent << FormatKV(label, val);
307 };
308
309 report_if_present("total_allocated_bytes",
310 memory_result.total_allocated_bytes);
311 report_if_present("net_heap_growth", memory_result.net_heap_growth);
312 }
313
314 if (!run.report_label.empty()) {
315 out << ",\n" << indent << FormatKV("label", run.report_label);
316 }
317 out << '\n';
318}
319
320const int64_t MemoryManager::TombstoneValue =
321 std::numeric_limits<int64_t>::max();
322
323} // end namespace benchmark
324