1a8c51b3fSopenharmony_ci#include <cstdio> 2a8c51b3fSopenharmony_ci#include <cstring> 3a8c51b3fSopenharmony_ci#include <fstream> 4a8c51b3fSopenharmony_ci#include <iostream> 5a8c51b3fSopenharmony_ci#include <map> 6a8c51b3fSopenharmony_ci#include <memory> 7a8c51b3fSopenharmony_ci#include <random> 8a8c51b3fSopenharmony_ci#include <sstream> 9a8c51b3fSopenharmony_ci#include <streambuf> 10a8c51b3fSopenharmony_ci 11a8c51b3fSopenharmony_ci#include "../src/benchmark_api_internal.h" 12a8c51b3fSopenharmony_ci#include "../src/check.h" // NOTE: check.h is for internal use only! 13a8c51b3fSopenharmony_ci#include "../src/log.h" // NOTE: log.h is for internal use only 14a8c51b3fSopenharmony_ci#include "../src/re.h" // NOTE: re.h is for internal use only 15a8c51b3fSopenharmony_ci#include "output_test.h" 16a8c51b3fSopenharmony_ci 17a8c51b3fSopenharmony_ci// ========================================================================= // 18a8c51b3fSopenharmony_ci// ------------------------------ Internals -------------------------------- // 19a8c51b3fSopenharmony_ci// ========================================================================= // 20a8c51b3fSopenharmony_cinamespace internal { 21a8c51b3fSopenharmony_cinamespace { 22a8c51b3fSopenharmony_ci 23a8c51b3fSopenharmony_ciusing TestCaseList = std::vector<TestCase>; 24a8c51b3fSopenharmony_ci 25a8c51b3fSopenharmony_ci// Use a vector because the order elements are added matters during iteration. 26a8c51b3fSopenharmony_ci// std::map/unordered_map don't guarantee that. 27a8c51b3fSopenharmony_ci// For example: 28a8c51b3fSopenharmony_ci// SetSubstitutions({{"%HelloWorld", "Hello"}, {"%Hello", "Hi"}}); 29a8c51b3fSopenharmony_ci// Substitute("%HelloWorld") // Always expands to Hello. 30a8c51b3fSopenharmony_ciusing SubMap = std::vector<std::pair<std::string, std::string>>; 31a8c51b3fSopenharmony_ci 32a8c51b3fSopenharmony_ciTestCaseList& GetTestCaseList(TestCaseID ID) { 33a8c51b3fSopenharmony_ci // Uses function-local statics to ensure initialization occurs 34a8c51b3fSopenharmony_ci // before first use. 35a8c51b3fSopenharmony_ci static TestCaseList lists[TC_NumID]; 36a8c51b3fSopenharmony_ci return lists[ID]; 37a8c51b3fSopenharmony_ci} 38a8c51b3fSopenharmony_ci 39a8c51b3fSopenharmony_ciSubMap& GetSubstitutions() { 40a8c51b3fSopenharmony_ci // Don't use 'dec_re' from header because it may not yet be initialized. 41a8c51b3fSopenharmony_ci // clang-format off 42a8c51b3fSopenharmony_ci static std::string safe_dec_re = "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?"; 43a8c51b3fSopenharmony_ci static std::string time_re = "([0-9]+[.])?[0-9]+"; 44a8c51b3fSopenharmony_ci static std::string percentage_re = "[0-9]+[.][0-9]{2}"; 45a8c51b3fSopenharmony_ci static SubMap map = { 46a8c51b3fSopenharmony_ci {"%float", "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?"}, 47a8c51b3fSopenharmony_ci // human-readable float 48a8c51b3fSopenharmony_ci {"%hrfloat", "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?[kKMGTPEZYmunpfazy]?i?"}, 49a8c51b3fSopenharmony_ci {"%percentage", percentage_re}, 50a8c51b3fSopenharmony_ci {"%int", "[ ]*[0-9]+"}, 51a8c51b3fSopenharmony_ci {" %s ", "[ ]+"}, 52a8c51b3fSopenharmony_ci {"%time", "[ ]*" + time_re + "[ ]+ns"}, 53a8c51b3fSopenharmony_ci {"%console_report", "[ ]*" + time_re + "[ ]+ns [ ]*" + time_re + "[ ]+ns [ ]*[0-9]+"}, 54a8c51b3fSopenharmony_ci {"%console_percentage_report", "[ ]*" + percentage_re + "[ ]+% [ ]*" + percentage_re + "[ ]+% [ ]*[0-9]+"}, 55a8c51b3fSopenharmony_ci {"%console_us_report", "[ ]*" + time_re + "[ ]+us [ ]*" + time_re + "[ ]+us [ ]*[0-9]+"}, 56a8c51b3fSopenharmony_ci {"%console_ms_report", "[ ]*" + time_re + "[ ]+ms [ ]*" + time_re + "[ ]+ms [ ]*[0-9]+"}, 57a8c51b3fSopenharmony_ci {"%console_s_report", "[ ]*" + time_re + "[ ]+s [ ]*" + time_re + "[ ]+s [ ]*[0-9]+"}, 58a8c51b3fSopenharmony_ci {"%console_time_only_report", "[ ]*" + time_re + "[ ]+ns [ ]*" + time_re + "[ ]+ns"}, 59a8c51b3fSopenharmony_ci {"%console_us_report", "[ ]*" + time_re + "[ ]+us [ ]*" + time_re + "[ ]+us [ ]*[0-9]+"}, 60a8c51b3fSopenharmony_ci {"%console_us_time_only_report", "[ ]*" + time_re + "[ ]+us [ ]*" + time_re + "[ ]+us"}, 61a8c51b3fSopenharmony_ci {"%csv_header", 62a8c51b3fSopenharmony_ci "name,iterations,real_time,cpu_time,time_unit,bytes_per_second," 63a8c51b3fSopenharmony_ci "items_per_second,label,error_occurred,error_message"}, 64a8c51b3fSopenharmony_ci {"%csv_report", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns,,,,,"}, 65a8c51b3fSopenharmony_ci {"%csv_us_report", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",us,,,,,"}, 66a8c51b3fSopenharmony_ci {"%csv_ms_report", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ms,,,,,"}, 67a8c51b3fSopenharmony_ci {"%csv_s_report", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",s,,,,,"}, 68a8c51b3fSopenharmony_ci {"%csv_bytes_report", 69a8c51b3fSopenharmony_ci "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns," + safe_dec_re + ",,,,"}, 70a8c51b3fSopenharmony_ci {"%csv_items_report", 71a8c51b3fSopenharmony_ci "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns,," + safe_dec_re + ",,,"}, 72a8c51b3fSopenharmony_ci {"%csv_bytes_items_report", 73a8c51b3fSopenharmony_ci "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns," + safe_dec_re + 74a8c51b3fSopenharmony_ci "," + safe_dec_re + ",,,"}, 75a8c51b3fSopenharmony_ci {"%csv_label_report_begin", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns,,,"}, 76a8c51b3fSopenharmony_ci {"%csv_label_report_end", ",,"}}; 77a8c51b3fSopenharmony_ci // clang-format on 78a8c51b3fSopenharmony_ci return map; 79a8c51b3fSopenharmony_ci} 80a8c51b3fSopenharmony_ci 81a8c51b3fSopenharmony_cistd::string PerformSubstitutions(std::string source) { 82a8c51b3fSopenharmony_ci SubMap const& subs = GetSubstitutions(); 83a8c51b3fSopenharmony_ci using SizeT = std::string::size_type; 84a8c51b3fSopenharmony_ci for (auto const& KV : subs) { 85a8c51b3fSopenharmony_ci SizeT pos; 86a8c51b3fSopenharmony_ci SizeT next_start = 0; 87a8c51b3fSopenharmony_ci while ((pos = source.find(KV.first, next_start)) != std::string::npos) { 88a8c51b3fSopenharmony_ci next_start = pos + KV.second.size(); 89a8c51b3fSopenharmony_ci source.replace(pos, KV.first.size(), KV.second); 90a8c51b3fSopenharmony_ci } 91a8c51b3fSopenharmony_ci } 92a8c51b3fSopenharmony_ci return source; 93a8c51b3fSopenharmony_ci} 94a8c51b3fSopenharmony_ci 95a8c51b3fSopenharmony_civoid CheckCase(std::stringstream& remaining_output, TestCase const& TC, 96a8c51b3fSopenharmony_ci TestCaseList const& not_checks) { 97a8c51b3fSopenharmony_ci std::string first_line; 98a8c51b3fSopenharmony_ci bool on_first = true; 99a8c51b3fSopenharmony_ci std::string line; 100a8c51b3fSopenharmony_ci while (remaining_output.eof() == false) { 101a8c51b3fSopenharmony_ci BM_CHECK(remaining_output.good()); 102a8c51b3fSopenharmony_ci std::getline(remaining_output, line); 103a8c51b3fSopenharmony_ci if (on_first) { 104a8c51b3fSopenharmony_ci first_line = line; 105a8c51b3fSopenharmony_ci on_first = false; 106a8c51b3fSopenharmony_ci } 107a8c51b3fSopenharmony_ci for (const auto& NC : not_checks) { 108a8c51b3fSopenharmony_ci BM_CHECK(!NC.regex->Match(line)) 109a8c51b3fSopenharmony_ci << "Unexpected match for line \"" << line << "\" for MR_Not regex \"" 110a8c51b3fSopenharmony_ci << NC.regex_str << "\"" 111a8c51b3fSopenharmony_ci << "\n actual regex string \"" << TC.substituted_regex << "\"" 112a8c51b3fSopenharmony_ci << "\n started matching near: " << first_line; 113a8c51b3fSopenharmony_ci } 114a8c51b3fSopenharmony_ci if (TC.regex->Match(line)) return; 115a8c51b3fSopenharmony_ci BM_CHECK(TC.match_rule != MR_Next) 116a8c51b3fSopenharmony_ci << "Expected line \"" << line << "\" to match regex \"" << TC.regex_str 117a8c51b3fSopenharmony_ci << "\"" 118a8c51b3fSopenharmony_ci << "\n actual regex string \"" << TC.substituted_regex << "\"" 119a8c51b3fSopenharmony_ci << "\n started matching near: " << first_line; 120a8c51b3fSopenharmony_ci } 121a8c51b3fSopenharmony_ci BM_CHECK(remaining_output.eof() == false) 122a8c51b3fSopenharmony_ci << "End of output reached before match for regex \"" << TC.regex_str 123a8c51b3fSopenharmony_ci << "\" was found" 124a8c51b3fSopenharmony_ci << "\n actual regex string \"" << TC.substituted_regex << "\"" 125a8c51b3fSopenharmony_ci << "\n started matching near: " << first_line; 126a8c51b3fSopenharmony_ci} 127a8c51b3fSopenharmony_ci 128a8c51b3fSopenharmony_civoid CheckCases(TestCaseList const& checks, std::stringstream& output) { 129a8c51b3fSopenharmony_ci std::vector<TestCase> not_checks; 130a8c51b3fSopenharmony_ci for (size_t i = 0; i < checks.size(); ++i) { 131a8c51b3fSopenharmony_ci const auto& TC = checks[i]; 132a8c51b3fSopenharmony_ci if (TC.match_rule == MR_Not) { 133a8c51b3fSopenharmony_ci not_checks.push_back(TC); 134a8c51b3fSopenharmony_ci continue; 135a8c51b3fSopenharmony_ci } 136a8c51b3fSopenharmony_ci CheckCase(output, TC, not_checks); 137a8c51b3fSopenharmony_ci not_checks.clear(); 138a8c51b3fSopenharmony_ci } 139a8c51b3fSopenharmony_ci} 140a8c51b3fSopenharmony_ci 141a8c51b3fSopenharmony_ciclass TestReporter : public benchmark::BenchmarkReporter { 142a8c51b3fSopenharmony_ci public: 143a8c51b3fSopenharmony_ci TestReporter(std::vector<benchmark::BenchmarkReporter*> reps) 144a8c51b3fSopenharmony_ci : reporters_(std::move(reps)) {} 145a8c51b3fSopenharmony_ci 146a8c51b3fSopenharmony_ci bool ReportContext(const Context& context) override { 147a8c51b3fSopenharmony_ci bool last_ret = false; 148a8c51b3fSopenharmony_ci bool first = true; 149a8c51b3fSopenharmony_ci for (auto rep : reporters_) { 150a8c51b3fSopenharmony_ci bool new_ret = rep->ReportContext(context); 151a8c51b3fSopenharmony_ci BM_CHECK(first || new_ret == last_ret) 152a8c51b3fSopenharmony_ci << "Reports return different values for ReportContext"; 153a8c51b3fSopenharmony_ci first = false; 154a8c51b3fSopenharmony_ci last_ret = new_ret; 155a8c51b3fSopenharmony_ci } 156a8c51b3fSopenharmony_ci (void)first; 157a8c51b3fSopenharmony_ci return last_ret; 158a8c51b3fSopenharmony_ci } 159a8c51b3fSopenharmony_ci 160a8c51b3fSopenharmony_ci void ReportRuns(const std::vector<Run>& report) override { 161a8c51b3fSopenharmony_ci for (auto rep : reporters_) rep->ReportRuns(report); 162a8c51b3fSopenharmony_ci } 163a8c51b3fSopenharmony_ci void Finalize() override { 164a8c51b3fSopenharmony_ci for (auto rep : reporters_) rep->Finalize(); 165a8c51b3fSopenharmony_ci } 166a8c51b3fSopenharmony_ci 167a8c51b3fSopenharmony_ci private: 168a8c51b3fSopenharmony_ci std::vector<benchmark::BenchmarkReporter*> reporters_; 169a8c51b3fSopenharmony_ci}; 170a8c51b3fSopenharmony_ci} // namespace 171a8c51b3fSopenharmony_ci 172a8c51b3fSopenharmony_ci} // end namespace internal 173a8c51b3fSopenharmony_ci 174a8c51b3fSopenharmony_ci// ========================================================================= // 175a8c51b3fSopenharmony_ci// -------------------------- Results checking ----------------------------- // 176a8c51b3fSopenharmony_ci// ========================================================================= // 177a8c51b3fSopenharmony_ci 178a8c51b3fSopenharmony_cinamespace internal { 179a8c51b3fSopenharmony_ci 180a8c51b3fSopenharmony_ci// Utility class to manage subscribers for checking benchmark results. 181a8c51b3fSopenharmony_ci// It works by parsing the CSV output to read the results. 182a8c51b3fSopenharmony_ciclass ResultsChecker { 183a8c51b3fSopenharmony_ci public: 184a8c51b3fSopenharmony_ci struct PatternAndFn : public TestCase { // reusing TestCase for its regexes 185a8c51b3fSopenharmony_ci PatternAndFn(const std::string& rx, ResultsCheckFn fn_) 186a8c51b3fSopenharmony_ci : TestCase(rx), fn(std::move(fn_)) {} 187a8c51b3fSopenharmony_ci ResultsCheckFn fn; 188a8c51b3fSopenharmony_ci }; 189a8c51b3fSopenharmony_ci 190a8c51b3fSopenharmony_ci std::vector<PatternAndFn> check_patterns; 191a8c51b3fSopenharmony_ci std::vector<Results> results; 192a8c51b3fSopenharmony_ci std::vector<std::string> field_names; 193a8c51b3fSopenharmony_ci 194a8c51b3fSopenharmony_ci void Add(const std::string& entry_pattern, const ResultsCheckFn& fn); 195a8c51b3fSopenharmony_ci 196a8c51b3fSopenharmony_ci void CheckResults(std::stringstream& output); 197a8c51b3fSopenharmony_ci 198a8c51b3fSopenharmony_ci private: 199a8c51b3fSopenharmony_ci void SetHeader_(const std::string& csv_header); 200a8c51b3fSopenharmony_ci void SetValues_(const std::string& entry_csv_line); 201a8c51b3fSopenharmony_ci 202a8c51b3fSopenharmony_ci std::vector<std::string> SplitCsv_(const std::string& line); 203a8c51b3fSopenharmony_ci}; 204a8c51b3fSopenharmony_ci 205a8c51b3fSopenharmony_ci// store the static ResultsChecker in a function to prevent initialization 206a8c51b3fSopenharmony_ci// order problems 207a8c51b3fSopenharmony_ciResultsChecker& GetResultsChecker() { 208a8c51b3fSopenharmony_ci static ResultsChecker rc; 209a8c51b3fSopenharmony_ci return rc; 210a8c51b3fSopenharmony_ci} 211a8c51b3fSopenharmony_ci 212a8c51b3fSopenharmony_ci// add a results checker for a benchmark 213a8c51b3fSopenharmony_civoid ResultsChecker::Add(const std::string& entry_pattern, 214a8c51b3fSopenharmony_ci const ResultsCheckFn& fn) { 215a8c51b3fSopenharmony_ci check_patterns.emplace_back(entry_pattern, fn); 216a8c51b3fSopenharmony_ci} 217a8c51b3fSopenharmony_ci 218a8c51b3fSopenharmony_ci// check the results of all subscribed benchmarks 219a8c51b3fSopenharmony_civoid ResultsChecker::CheckResults(std::stringstream& output) { 220a8c51b3fSopenharmony_ci // first reset the stream to the start 221a8c51b3fSopenharmony_ci { 222a8c51b3fSopenharmony_ci auto start = std::stringstream::pos_type(0); 223a8c51b3fSopenharmony_ci // clear before calling tellg() 224a8c51b3fSopenharmony_ci output.clear(); 225a8c51b3fSopenharmony_ci // seek to zero only when needed 226a8c51b3fSopenharmony_ci if (output.tellg() > start) output.seekg(start); 227a8c51b3fSopenharmony_ci // and just in case 228a8c51b3fSopenharmony_ci output.clear(); 229a8c51b3fSopenharmony_ci } 230a8c51b3fSopenharmony_ci // now go over every line and publish it to the ResultsChecker 231a8c51b3fSopenharmony_ci std::string line; 232a8c51b3fSopenharmony_ci bool on_first = true; 233a8c51b3fSopenharmony_ci while (output.eof() == false) { 234a8c51b3fSopenharmony_ci BM_CHECK(output.good()); 235a8c51b3fSopenharmony_ci std::getline(output, line); 236a8c51b3fSopenharmony_ci if (on_first) { 237a8c51b3fSopenharmony_ci SetHeader_(line); // this is important 238a8c51b3fSopenharmony_ci on_first = false; 239a8c51b3fSopenharmony_ci continue; 240a8c51b3fSopenharmony_ci } 241a8c51b3fSopenharmony_ci SetValues_(line); 242a8c51b3fSopenharmony_ci } 243a8c51b3fSopenharmony_ci // finally we can call the subscribed check functions 244a8c51b3fSopenharmony_ci for (const auto& p : check_patterns) { 245a8c51b3fSopenharmony_ci BM_VLOG(2) << "--------------------------------\n"; 246a8c51b3fSopenharmony_ci BM_VLOG(2) << "checking for benchmarks matching " << p.regex_str << "...\n"; 247a8c51b3fSopenharmony_ci for (const auto& r : results) { 248a8c51b3fSopenharmony_ci if (!p.regex->Match(r.name)) { 249a8c51b3fSopenharmony_ci BM_VLOG(2) << p.regex_str << " is not matched by " << r.name << "\n"; 250a8c51b3fSopenharmony_ci continue; 251a8c51b3fSopenharmony_ci } 252a8c51b3fSopenharmony_ci BM_VLOG(2) << p.regex_str << " is matched by " << r.name << "\n"; 253a8c51b3fSopenharmony_ci BM_VLOG(1) << "Checking results of " << r.name << ": ... \n"; 254a8c51b3fSopenharmony_ci p.fn(r); 255a8c51b3fSopenharmony_ci BM_VLOG(1) << "Checking results of " << r.name << ": OK.\n"; 256a8c51b3fSopenharmony_ci } 257a8c51b3fSopenharmony_ci } 258a8c51b3fSopenharmony_ci} 259a8c51b3fSopenharmony_ci 260a8c51b3fSopenharmony_ci// prepare for the names in this header 261a8c51b3fSopenharmony_civoid ResultsChecker::SetHeader_(const std::string& csv_header) { 262a8c51b3fSopenharmony_ci field_names = SplitCsv_(csv_header); 263a8c51b3fSopenharmony_ci} 264a8c51b3fSopenharmony_ci 265a8c51b3fSopenharmony_ci// set the values for a benchmark 266a8c51b3fSopenharmony_civoid ResultsChecker::SetValues_(const std::string& entry_csv_line) { 267a8c51b3fSopenharmony_ci if (entry_csv_line.empty()) return; // some lines are empty 268a8c51b3fSopenharmony_ci BM_CHECK(!field_names.empty()); 269a8c51b3fSopenharmony_ci auto vals = SplitCsv_(entry_csv_line); 270a8c51b3fSopenharmony_ci BM_CHECK_EQ(vals.size(), field_names.size()); 271a8c51b3fSopenharmony_ci results.emplace_back(vals[0]); // vals[0] is the benchmark name 272a8c51b3fSopenharmony_ci auto& entry = results.back(); 273a8c51b3fSopenharmony_ci for (size_t i = 1, e = vals.size(); i < e; ++i) { 274a8c51b3fSopenharmony_ci entry.values[field_names[i]] = vals[i]; 275a8c51b3fSopenharmony_ci } 276a8c51b3fSopenharmony_ci} 277a8c51b3fSopenharmony_ci 278a8c51b3fSopenharmony_ci// a quick'n'dirty csv splitter (eliminating quotes) 279a8c51b3fSopenharmony_cistd::vector<std::string> ResultsChecker::SplitCsv_(const std::string& line) { 280a8c51b3fSopenharmony_ci std::vector<std::string> out; 281a8c51b3fSopenharmony_ci if (line.empty()) return out; 282a8c51b3fSopenharmony_ci if (!field_names.empty()) out.reserve(field_names.size()); 283a8c51b3fSopenharmony_ci size_t prev = 0, pos = line.find_first_of(','), curr = pos; 284a8c51b3fSopenharmony_ci while (pos != line.npos) { 285a8c51b3fSopenharmony_ci BM_CHECK(curr > 0); 286a8c51b3fSopenharmony_ci if (line[prev] == '"') ++prev; 287a8c51b3fSopenharmony_ci if (line[curr - 1] == '"') --curr; 288a8c51b3fSopenharmony_ci out.push_back(line.substr(prev, curr - prev)); 289a8c51b3fSopenharmony_ci prev = pos + 1; 290a8c51b3fSopenharmony_ci pos = line.find_first_of(',', pos + 1); 291a8c51b3fSopenharmony_ci curr = pos; 292a8c51b3fSopenharmony_ci } 293a8c51b3fSopenharmony_ci curr = line.size(); 294a8c51b3fSopenharmony_ci if (line[prev] == '"') ++prev; 295a8c51b3fSopenharmony_ci if (line[curr - 1] == '"') --curr; 296a8c51b3fSopenharmony_ci out.push_back(line.substr(prev, curr - prev)); 297a8c51b3fSopenharmony_ci return out; 298a8c51b3fSopenharmony_ci} 299a8c51b3fSopenharmony_ci 300a8c51b3fSopenharmony_ci} // end namespace internal 301a8c51b3fSopenharmony_ci 302a8c51b3fSopenharmony_cisize_t AddChecker(const std::string& bm_name, const ResultsCheckFn& fn) { 303a8c51b3fSopenharmony_ci auto& rc = internal::GetResultsChecker(); 304a8c51b3fSopenharmony_ci rc.Add(bm_name, fn); 305a8c51b3fSopenharmony_ci return rc.results.size(); 306a8c51b3fSopenharmony_ci} 307a8c51b3fSopenharmony_ci 308a8c51b3fSopenharmony_ciint Results::NumThreads() const { 309a8c51b3fSopenharmony_ci auto pos = name.find("/threads:"); 310a8c51b3fSopenharmony_ci if (pos == name.npos) return 1; 311a8c51b3fSopenharmony_ci auto end = name.find('/', pos + 9); 312a8c51b3fSopenharmony_ci std::stringstream ss; 313a8c51b3fSopenharmony_ci ss << name.substr(pos + 9, end); 314a8c51b3fSopenharmony_ci int num = 1; 315a8c51b3fSopenharmony_ci ss >> num; 316a8c51b3fSopenharmony_ci BM_CHECK(!ss.fail()); 317a8c51b3fSopenharmony_ci return num; 318a8c51b3fSopenharmony_ci} 319a8c51b3fSopenharmony_ci 320a8c51b3fSopenharmony_cidouble Results::NumIterations() const { return GetAs<double>("iterations"); } 321a8c51b3fSopenharmony_ci 322a8c51b3fSopenharmony_cidouble Results::GetTime(BenchmarkTime which) const { 323a8c51b3fSopenharmony_ci BM_CHECK(which == kCpuTime || which == kRealTime); 324a8c51b3fSopenharmony_ci const char* which_str = which == kCpuTime ? "cpu_time" : "real_time"; 325a8c51b3fSopenharmony_ci double val = GetAs<double>(which_str); 326a8c51b3fSopenharmony_ci auto unit = Get("time_unit"); 327a8c51b3fSopenharmony_ci BM_CHECK(unit); 328a8c51b3fSopenharmony_ci if (*unit == "ns") { 329a8c51b3fSopenharmony_ci return val * 1.e-9; 330a8c51b3fSopenharmony_ci } 331a8c51b3fSopenharmony_ci if (*unit == "us") { 332a8c51b3fSopenharmony_ci return val * 1.e-6; 333a8c51b3fSopenharmony_ci } 334a8c51b3fSopenharmony_ci if (*unit == "ms") { 335a8c51b3fSopenharmony_ci return val * 1.e-3; 336a8c51b3fSopenharmony_ci } 337a8c51b3fSopenharmony_ci if (*unit == "s") { 338a8c51b3fSopenharmony_ci return val; 339a8c51b3fSopenharmony_ci } 340a8c51b3fSopenharmony_ci BM_CHECK(1 == 0) << "unknown time unit: " << *unit; 341a8c51b3fSopenharmony_ci return 0; 342a8c51b3fSopenharmony_ci} 343a8c51b3fSopenharmony_ci 344a8c51b3fSopenharmony_ci// ========================================================================= // 345a8c51b3fSopenharmony_ci// -------------------------- Public API Definitions------------------------ // 346a8c51b3fSopenharmony_ci// ========================================================================= // 347a8c51b3fSopenharmony_ci 348a8c51b3fSopenharmony_ciTestCase::TestCase(std::string re, int rule) 349a8c51b3fSopenharmony_ci : regex_str(std::move(re)), 350a8c51b3fSopenharmony_ci match_rule(rule), 351a8c51b3fSopenharmony_ci substituted_regex(internal::PerformSubstitutions(regex_str)), 352a8c51b3fSopenharmony_ci regex(std::make_shared<benchmark::Regex>()) { 353a8c51b3fSopenharmony_ci std::string err_str; 354a8c51b3fSopenharmony_ci regex->Init(substituted_regex, &err_str); 355a8c51b3fSopenharmony_ci BM_CHECK(err_str.empty()) 356a8c51b3fSopenharmony_ci << "Could not construct regex \"" << substituted_regex << "\"" 357a8c51b3fSopenharmony_ci << "\n originally \"" << regex_str << "\"" 358a8c51b3fSopenharmony_ci << "\n got error: " << err_str; 359a8c51b3fSopenharmony_ci} 360a8c51b3fSopenharmony_ci 361a8c51b3fSopenharmony_ciint AddCases(TestCaseID ID, std::initializer_list<TestCase> il) { 362a8c51b3fSopenharmony_ci auto& L = internal::GetTestCaseList(ID); 363a8c51b3fSopenharmony_ci L.insert(L.end(), il); 364a8c51b3fSopenharmony_ci return 0; 365a8c51b3fSopenharmony_ci} 366a8c51b3fSopenharmony_ci 367a8c51b3fSopenharmony_ciint SetSubstitutions( 368a8c51b3fSopenharmony_ci std::initializer_list<std::pair<std::string, std::string>> il) { 369a8c51b3fSopenharmony_ci auto& subs = internal::GetSubstitutions(); 370a8c51b3fSopenharmony_ci for (auto KV : il) { 371a8c51b3fSopenharmony_ci bool exists = false; 372a8c51b3fSopenharmony_ci KV.second = internal::PerformSubstitutions(KV.second); 373a8c51b3fSopenharmony_ci for (auto& EKV : subs) { 374a8c51b3fSopenharmony_ci if (EKV.first == KV.first) { 375a8c51b3fSopenharmony_ci EKV.second = std::move(KV.second); 376a8c51b3fSopenharmony_ci exists = true; 377a8c51b3fSopenharmony_ci break; 378a8c51b3fSopenharmony_ci } 379a8c51b3fSopenharmony_ci } 380a8c51b3fSopenharmony_ci if (!exists) subs.push_back(std::move(KV)); 381a8c51b3fSopenharmony_ci } 382a8c51b3fSopenharmony_ci return 0; 383a8c51b3fSopenharmony_ci} 384a8c51b3fSopenharmony_ci 385a8c51b3fSopenharmony_ci// Disable deprecated warnings temporarily because we need to reference 386a8c51b3fSopenharmony_ci// CSVReporter but don't want to trigger -Werror=-Wdeprecated-declarations 387a8c51b3fSopenharmony_ciBENCHMARK_DISABLE_DEPRECATED_WARNING 388a8c51b3fSopenharmony_ci 389a8c51b3fSopenharmony_civoid RunOutputTests(int argc, char* argv[]) { 390a8c51b3fSopenharmony_ci using internal::GetTestCaseList; 391a8c51b3fSopenharmony_ci benchmark::Initialize(&argc, argv); 392a8c51b3fSopenharmony_ci auto options = benchmark::internal::GetOutputOptions(/*force_no_color*/ true); 393a8c51b3fSopenharmony_ci benchmark::ConsoleReporter CR(options); 394a8c51b3fSopenharmony_ci benchmark::JSONReporter JR; 395a8c51b3fSopenharmony_ci benchmark::CSVReporter CSVR; 396a8c51b3fSopenharmony_ci struct ReporterTest { 397a8c51b3fSopenharmony_ci std::string name; 398a8c51b3fSopenharmony_ci std::vector<TestCase>& output_cases; 399a8c51b3fSopenharmony_ci std::vector<TestCase>& error_cases; 400a8c51b3fSopenharmony_ci benchmark::BenchmarkReporter& reporter; 401a8c51b3fSopenharmony_ci std::stringstream out_stream; 402a8c51b3fSopenharmony_ci std::stringstream err_stream; 403a8c51b3fSopenharmony_ci 404a8c51b3fSopenharmony_ci ReporterTest(const std::string& n, std::vector<TestCase>& out_tc, 405a8c51b3fSopenharmony_ci std::vector<TestCase>& err_tc, 406a8c51b3fSopenharmony_ci benchmark::BenchmarkReporter& br) 407a8c51b3fSopenharmony_ci : name(n), output_cases(out_tc), error_cases(err_tc), reporter(br) { 408a8c51b3fSopenharmony_ci reporter.SetOutputStream(&out_stream); 409a8c51b3fSopenharmony_ci reporter.SetErrorStream(&err_stream); 410a8c51b3fSopenharmony_ci } 411a8c51b3fSopenharmony_ci } TestCases[] = { 412a8c51b3fSopenharmony_ci {std::string("ConsoleReporter"), GetTestCaseList(TC_ConsoleOut), 413a8c51b3fSopenharmony_ci GetTestCaseList(TC_ConsoleErr), CR}, 414a8c51b3fSopenharmony_ci {std::string("JSONReporter"), GetTestCaseList(TC_JSONOut), 415a8c51b3fSopenharmony_ci GetTestCaseList(TC_JSONErr), JR}, 416a8c51b3fSopenharmony_ci {std::string("CSVReporter"), GetTestCaseList(TC_CSVOut), 417a8c51b3fSopenharmony_ci GetTestCaseList(TC_CSVErr), CSVR}, 418a8c51b3fSopenharmony_ci }; 419a8c51b3fSopenharmony_ci 420a8c51b3fSopenharmony_ci // Create the test reporter and run the benchmarks. 421a8c51b3fSopenharmony_ci std::cout << "Running benchmarks...\n"; 422a8c51b3fSopenharmony_ci internal::TestReporter test_rep({&CR, &JR, &CSVR}); 423a8c51b3fSopenharmony_ci benchmark::RunSpecifiedBenchmarks(&test_rep); 424a8c51b3fSopenharmony_ci 425a8c51b3fSopenharmony_ci for (auto& rep_test : TestCases) { 426a8c51b3fSopenharmony_ci std::string msg = 427a8c51b3fSopenharmony_ci std::string("\nTesting ") + rep_test.name + std::string(" Output\n"); 428a8c51b3fSopenharmony_ci std::string banner(msg.size() - 1, '-'); 429a8c51b3fSopenharmony_ci std::cout << banner << msg << banner << "\n"; 430a8c51b3fSopenharmony_ci 431a8c51b3fSopenharmony_ci std::cerr << rep_test.err_stream.str(); 432a8c51b3fSopenharmony_ci std::cout << rep_test.out_stream.str(); 433a8c51b3fSopenharmony_ci 434a8c51b3fSopenharmony_ci internal::CheckCases(rep_test.error_cases, rep_test.err_stream); 435a8c51b3fSopenharmony_ci internal::CheckCases(rep_test.output_cases, rep_test.out_stream); 436a8c51b3fSopenharmony_ci 437a8c51b3fSopenharmony_ci std::cout << "\n"; 438a8c51b3fSopenharmony_ci } 439a8c51b3fSopenharmony_ci 440a8c51b3fSopenharmony_ci // now that we know the output is as expected, we can dispatch 441a8c51b3fSopenharmony_ci // the checks to subscribees. 442a8c51b3fSopenharmony_ci auto& csv = TestCases[2]; 443a8c51b3fSopenharmony_ci // would use == but gcc spits a warning 444a8c51b3fSopenharmony_ci BM_CHECK(csv.name == std::string("CSVReporter")); 445a8c51b3fSopenharmony_ci internal::GetResultsChecker().CheckResults(csv.out_stream); 446a8c51b3fSopenharmony_ci} 447a8c51b3fSopenharmony_ci 448a8c51b3fSopenharmony_ciBENCHMARK_RESTORE_DEPRECATED_WARNING 449a8c51b3fSopenharmony_ci 450a8c51b3fSopenharmony_ciint SubstrCnt(const std::string& haystack, const std::string& pat) { 451a8c51b3fSopenharmony_ci if (pat.length() == 0) return 0; 452a8c51b3fSopenharmony_ci int count = 0; 453a8c51b3fSopenharmony_ci for (size_t offset = haystack.find(pat); offset != std::string::npos; 454a8c51b3fSopenharmony_ci offset = haystack.find(pat, offset + pat.length())) 455a8c51b3fSopenharmony_ci ++count; 456a8c51b3fSopenharmony_ci return count; 457a8c51b3fSopenharmony_ci} 458a8c51b3fSopenharmony_ci 459a8c51b3fSopenharmony_cistatic char ToHex(int ch) { 460a8c51b3fSopenharmony_ci return ch < 10 ? static_cast<char>('0' + ch) 461a8c51b3fSopenharmony_ci : static_cast<char>('a' + (ch - 10)); 462a8c51b3fSopenharmony_ci} 463a8c51b3fSopenharmony_ci 464a8c51b3fSopenharmony_cistatic char RandomHexChar() { 465a8c51b3fSopenharmony_ci static std::mt19937 rd{std::random_device{}()}; 466a8c51b3fSopenharmony_ci static std::uniform_int_distribution<int> mrand{0, 15}; 467a8c51b3fSopenharmony_ci return ToHex(mrand(rd)); 468a8c51b3fSopenharmony_ci} 469a8c51b3fSopenharmony_ci 470a8c51b3fSopenharmony_cistatic std::string GetRandomFileName() { 471a8c51b3fSopenharmony_ci std::string model = "test.%%%%%%"; 472a8c51b3fSopenharmony_ci for (auto& ch : model) { 473a8c51b3fSopenharmony_ci if (ch == '%') ch = RandomHexChar(); 474a8c51b3fSopenharmony_ci } 475a8c51b3fSopenharmony_ci return model; 476a8c51b3fSopenharmony_ci} 477a8c51b3fSopenharmony_ci 478a8c51b3fSopenharmony_cistatic bool FileExists(std::string const& name) { 479a8c51b3fSopenharmony_ci std::ifstream in(name.c_str()); 480a8c51b3fSopenharmony_ci return in.good(); 481a8c51b3fSopenharmony_ci} 482a8c51b3fSopenharmony_ci 483a8c51b3fSopenharmony_cistatic std::string GetTempFileName() { 484a8c51b3fSopenharmony_ci // This function attempts to avoid race conditions where two tests 485a8c51b3fSopenharmony_ci // create the same file at the same time. However, it still introduces races 486a8c51b3fSopenharmony_ci // similar to tmpnam. 487a8c51b3fSopenharmony_ci int retries = 3; 488a8c51b3fSopenharmony_ci while (--retries) { 489a8c51b3fSopenharmony_ci std::string name = GetRandomFileName(); 490a8c51b3fSopenharmony_ci if (!FileExists(name)) return name; 491a8c51b3fSopenharmony_ci } 492a8c51b3fSopenharmony_ci std::cerr << "Failed to create unique temporary file name" << std::endl; 493a8c51b3fSopenharmony_ci std::abort(); 494a8c51b3fSopenharmony_ci} 495a8c51b3fSopenharmony_ci 496a8c51b3fSopenharmony_cistd::string GetFileReporterOutput(int argc, char* argv[]) { 497a8c51b3fSopenharmony_ci std::vector<char*> new_argv(argv, argv + argc); 498a8c51b3fSopenharmony_ci assert(static_cast<decltype(new_argv)::size_type>(argc) == new_argv.size()); 499a8c51b3fSopenharmony_ci 500a8c51b3fSopenharmony_ci std::string tmp_file_name = GetTempFileName(); 501a8c51b3fSopenharmony_ci std::cout << "Will be using this as the tmp file: " << tmp_file_name << '\n'; 502a8c51b3fSopenharmony_ci 503a8c51b3fSopenharmony_ci std::string tmp = "--benchmark_out="; 504a8c51b3fSopenharmony_ci tmp += tmp_file_name; 505a8c51b3fSopenharmony_ci new_argv.emplace_back(const_cast<char*>(tmp.c_str())); 506a8c51b3fSopenharmony_ci 507a8c51b3fSopenharmony_ci argc = int(new_argv.size()); 508a8c51b3fSopenharmony_ci 509a8c51b3fSopenharmony_ci benchmark::Initialize(&argc, new_argv.data()); 510a8c51b3fSopenharmony_ci benchmark::RunSpecifiedBenchmarks(); 511a8c51b3fSopenharmony_ci 512a8c51b3fSopenharmony_ci // Read the output back from the file, and delete the file. 513a8c51b3fSopenharmony_ci std::ifstream tmp_stream(tmp_file_name); 514a8c51b3fSopenharmony_ci std::string output = std::string((std::istreambuf_iterator<char>(tmp_stream)), 515a8c51b3fSopenharmony_ci std::istreambuf_iterator<char>()); 516a8c51b3fSopenharmony_ci std::remove(tmp_file_name.c_str()); 517a8c51b3fSopenharmony_ci 518a8c51b3fSopenharmony_ci return output; 519a8c51b3fSopenharmony_ci} 520