HEBench
hebench_report_compiler.cpp
Go to the documentation of this file.
1 
2 // Copyright (C) 2021 Intel Corporation
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #include <filesystem>
6 #include <fstream>
7 #include <iostream>
8 #include <sstream>
9 #include <stdexcept>
10 #include <string>
11 #include <string_view>
12 #include <unordered_set>
13 #include <utility>
14 #include <vector>
15 
16 #include "hebench/modules/args_parser/include/args_parser.h"
17 #include "hebench/modules/general/include/hebench_utilities.h"
18 
20 #include "hebench_report_cpp.h"
22 #include "hebench_report_stats.h"
23 
24 namespace hebench {
25 namespace ReportGen {
26 namespace Compiler {
27 
28 using namespace std::literals;
29 using namespace hebench::ReportGen::cpp;
30 
32 {
33  std::filesystem::path input_file;
35  bool b_silent;
36  char time_unit;
40 
41  void showConfig(std::ostream &os) const;
42  static void showVersion(std::ostream &os);
43  static std::string getTimeUnitName(char time_unit, const std::string &default_name = "(default)");
44 
45 private:
46  static const std::unordered_set<std::string> m_allowed_time_units;
47 };
48 
49 const std::unordered_set<std::string> ReportCompilerConfig::m_allowed_time_units({ "s", "ms", "us", "ns" });
50 
51 std::string ReportCompilerConfig::getTimeUnitName(char time_unit, const std::string &default_name)
52 {
53  std::string retval;
54  switch (time_unit)
55  {
56  case 'm':
57  retval = "milliseconds";
58  break;
59  case 'u':
60  retval = "microseconds";
61  break;
62  case 'n':
63  retval = "nanoseconds";
64  break;
65  default:
66  retval = default_name;
67  break;
68  } // end switch
69  return retval;
70 }
71 
72 void ReportCompilerConfig::showVersion(std::ostream &os)
73 {
74  (void)os;
75 }
76 
77 std::vector<std::filesystem::path> extractInputFiles(const std::string &filename)
78 {
79  std::vector<std::filesystem::path> retval;
80 
81  // Open file and see if each line contains a valid, existing filename.
82  // Throw exception if a file does not exist after the first one.
83 
84  std::ifstream fnum;
85  fnum.open(filename, std::ios_base::in);
86  if (!fnum.is_open())
87  throw std::ios_base::failure("Could not open file \"" + filename + "\"");
88 
89  std::filesystem::path root_path = std::filesystem::canonical(filename).parent_path();
90 
91  bool b_done = false;
92  std::string s_line;
93  std::size_t line_num = 0;
94  while (!b_done && std::getline(fnum, s_line))
95  {
96  ++line_num;
97  hebench::Utilities::trim(s_line);
98  if (!s_line.empty())
99  {
100  std::filesystem::path next_filename = s_line;
101  if (next_filename.is_relative())
102  // relative paths are relative to input file
103  next_filename = root_path / next_filename;
104 
105  if (std::filesystem::is_regular_file(next_filename))
106  {
107  retval.emplace_back(next_filename.string());
108  } // end if
109  else // line is not a file
110  {
111  if (!retval.empty())
112  {
113  // other lines were already added as files, so,
114  // error out because this line points to non-existing file
115  std::stringstream ss;
116  ss << filename << ":" << line_num << ": file specified in line not found: " << next_filename;
117  throw std::invalid_argument(ss.str());
118  } // end if
119 
120  // first non-empty line is not an existing file, so,
121  // this filename itself could be the file to read
122  b_done = true;
123  } // end if
124  } // end if
125  } // end while
126 
127  if (retval.empty())
128  retval.emplace_back(filename);
129 
130  return retval;
131 }
132 
133 extern "C"
134 {
135 
136  int32_t compile(const ReportCompilerConfigC *p_config, char *s_error, size_t s_error_size)
137  {
138  static const std::string ReportSuffix = "report";
139 
140  int retval = 1;
141 
142  std::stringstream ss_err;
143  ReportCompilerConfig config;
144 
145  try
146  {
147  if (!p_config || !p_config->input_file)
148  throw std::runtime_error("Invalid null compiler configuration values.");
149 
150  config.input_file = p_config->input_file;
151  config.b_show_overview = p_config->b_show_overview;
152  config.b_silent = p_config->b_silent;
153  config.time_unit = p_config->time_unit;
154  config.time_unit_stats = p_config->time_unit_stats;
155  config.time_unit_overview = p_config->time_unit_overview;
156  config.time_unit_summary = p_config->time_unit_summary;
157 
158  if (!config.b_silent)
159  {
160  std::cout << "Extracting input file names..." << std::endl
161  << std::endl;
162  } // end if
163 
164  std::size_t max_w_params = 0;
165  std::stringstream ss_overview_header;
166  std::stringstream ss_overview;
167  std::filesystem::path overview_filename = config.input_file;
168  overview_filename.replace_filename(overview_filename.stem().string() + "_overview.csv");
169  std::vector<std::filesystem::path> csv_filenames = extractInputFiles(config.input_file);
170 
171  if (!config.b_silent)
172  {
173  std::cout << "Input files:" << std::endl;
174  for (std::size_t i = 0; i < csv_filenames.size(); ++i)
175  std::cout << " " << i << ", " << csv_filenames[i] << std::endl;
176  std::cout << std::endl
177  << "Overview file (output):" << std::endl
178  << " " << overview_filename << std::endl
179  << std::endl;
180  } // end if
181 
182  ss_overview_header << ",,,,,,,,,,,,,,Wall Time,,,,,,,,,,,,,CPU Time" << std::endl
183  << "Workload,End State,Filename,Category,Data type,Cipher text,Scheme,Security,Extra,"
184  << "ID,Event,Total Wall Time,Samples per sec,Samples per sec trimmed,"
185  // wall
186  << "Average,Standard Deviation,Time Unit,Time Factor,Min,Max,Median,Trimmed Average,Trimmed Standard Deviation,1-th percentile,10-th percentile,90-th percentile,99-th percentile,"
187  // cpu
188  << "Average,Standard Deviation,Time Unit,Time Factor,Min,Max,Median,Trimmed Average,Trimmed Standard Deviation,1-th percentile,10-th percentile,90-th percentile,99-th percentile,Input Samples";
189 
190  for (std::size_t csv_file_i = 0; csv_file_i < csv_filenames.size(); ++csv_file_i)
191  {
192  if (!config.b_silent)
193  {
194  std::cout << "=====================" << std::endl
195  << " Progress: " << csv_file_i << "/" << csv_filenames.size() << std::endl
196  << " " << hebench::Utilities::convertDoubleToStr(csv_file_i * 100.0 / csv_filenames.size(), 2) << "%" << std::endl
197  << "=====================" << std::endl
198  << std::endl;
199 
200  std::cout << "Report file:" << std::endl
201  << " ";
202  } // end if
203  std::cerr << csv_filenames[csv_file_i] << std::endl;
204 
205  if (!config.b_silent)
206  {
207  std::cout << "Loading report..." << std::endl;
208  } // end if
209 
210  std::shared_ptr<TimingReport> p_report;
211 
212  try
213  {
214  p_report = std::make_shared<TimingReport>(TimingReport::loadReportFromCSVFile(csv_filenames[csv_file_i]));
215  }
216  catch (...)
217  {
218  }
219 
220  if (p_report)
221  {
222  TimingReport &report = *p_report;
223 
224  if (!config.b_silent)
225  {
226  std::cout << "Parsing report header..." << std::endl;
227  } // end if
228 
229  hebench::ReportGen::OverviewHeader overview_header;
230  if (report.getEventCount() <= 0)
231  {
232  std::cerr << "WARNING: The loaded report belongs to a failed benchmark." << std::endl;
233  overview_header.parseHeader(csv_filenames[csv_file_i], report.getHeader(), hebench::ReportGen::OverviewHeader::EndStateGeneralFailure);
234  overview_header.outputHeader(ss_overview, false);
235  } // end if
236  else
237  {
238  overview_header.parseHeader(csv_filenames[csv_file_i], report.getHeader(), hebench::ReportGen::OverviewHeader::EndStateOK);
239  // make sure we keep track of the workload parameters
240  if (overview_header.w_params.size() > max_w_params)
241  {
242  for (std::size_t i = max_w_params; i < overview_header.w_params.size(); ++i)
243  ss_overview_header << ",wp" << i;
244  max_w_params = overview_header.w_params.size();
245  } // end if
246  overview_header.outputHeader(ss_overview, false);
247  ss_overview << ",";
248 
249  char tmp_time_unit;
250  std::filesystem::path stem_filename = csv_filenames[csv_file_i];
251  std::filesystem::path summary_filename;
252  std::filesystem::path stats_filename;
253 
254  // remove "report" from input file name
255  std::string s_tmp = stem_filename.stem();
256  if (s_tmp.length() >= ReportSuffix.length() && s_tmp.substr(s_tmp.length() - ReportSuffix.length()) == ReportSuffix)
257  stem_filename.replace_filename(s_tmp.substr(0, s_tmp.length() - ReportSuffix.length()));
258  summary_filename = stem_filename;
259  summary_filename += "summary.csv";
260  stats_filename = stem_filename;
261  stats_filename += "stats.csv";
262 
263  if (!config.b_silent)
264  {
265  std::cout << "Computing statistics..." << std::endl;
266  } // end if
267 
268  hebench::ReportGen::ReportStats report_stats(report);
269 
270  if (!config.b_silent)
271  {
272  std::cout << "Writing summary to:" << std::endl
273  << " " << summary_filename << std::endl;
274  } // end if
275 
276  tmp_time_unit = config.time_unit_summary;
277  hebench::Utilities::writeToFile(
278  summary_filename,
279  [&report_stats, tmp_time_unit](std::ostream &os) -> void {
280  report_stats.generateSummaryCSV(os, tmp_time_unit);
281  },
282  false);
283 
284  if (!config.b_silent)
285  {
286  std::cout << "Writing statistics to:" << std::endl
287  << " " << stats_filename << std::endl;
288  } // end if
289 
290  tmp_time_unit = config.time_unit_stats;
291  hebench::Utilities::writeToFile(
292  stats_filename,
293  [&report_stats, tmp_time_unit](std::ostream &os) -> void {
294  report_stats.generateCSV(os, tmp_time_unit);
295  },
296  false);
297 
298  if (!config.b_silent)
299  {
300  std::cout << "Adding to report overview..." << std::endl;
301  } // end if
302 
303  report_stats.generateCSV(ss_overview, report_stats.getMainEventTypeStats(), config.time_unit_overview, false);
304  // add the workload parameters if any was found
305  if (overview_header.w_params.empty())
306  {
307  // on file name
308  std::cerr << "WARNING: No workload parameters found while parsing." << std::endl;
309  } // end if
310  for (std::size_t i = 0; i < overview_header.w_params.size(); ++i)
311  {
312  if (overview_header.w_params[i].find_first_of(',') == std::string::npos)
313  ss_overview << "," << overview_header.w_params[i];
314  else
315  ss_overview << ",\"" << overview_header.w_params[i] << "\"";
316  } // end for
317  } // end else
318  } // end if
319  else
320  {
321  std::cerr << "WARNING: Failed to load report from file." << std::endl;
322  ss_overview << "Failed," << csv_filenames[csv_file_i] << ",Load";
323  } // end else
324 
325  if (!config.b_silent)
326  std::cout << std::endl;
327 
328  ss_overview << std::endl;
329  } // end for
330 
331  ss_overview_header << std::endl
332  << ss_overview.str();
333  ss_overview = std::stringstream();
334  std::string s_overview = ss_overview_header.str();
335  ss_overview_header = std::stringstream();
336  hebench::Utilities::writeToFile(overview_filename, s_overview.c_str(), s_overview.size() * sizeof(char), false);
337 
338  if (!config.b_silent)
339  {
340  std::cout << "=====================" << std::endl
341  << " Progress: " << csv_filenames.size() << "/" << csv_filenames.size() << std::endl
342  << " 100%" << std::endl
343  << "=====================" << std::endl
344  << std::endl;
345  } // end if
346 
347  if (config.b_show_overview)
348  {
349  if (!config.b_silent)
350  std::cout << "Overview:" << std::endl;
351  std::cout << std::endl
352  << s_overview << std::endl;
353  } // end if
354  }
355  catch (std::exception &ex)
356  {
357  ss_err << ex.what();
358  retval = 0;
359  }
360  catch (...)
361  {
362  ss_err << "Unexpected error occurred.";
363  retval = 0;
364  }
365 
366  if (!retval)
367  // report error message
368  hebench::Utilities::copyString(s_error, s_error_size, ss_err.str());
369 
370  return retval;
371  }
372 }
373 
374 } // namespace Compiler
375 } // namespace ReportGen
376 } // namespace hebench
void generateCSV(std::ostream &os, char ch_prefix)
Generates complete CSV stats for this report.
void generateSummaryCSV(std::ostream &os, char ch_prefix)
Generates summary CSV for this report.
const ReportEventTypeStats & getMainEventTypeStats() const
static TimingReport loadReportFromCSVFile(const std::string &filename)
std::vector< std::filesystem::path > extractInputFiles(const std::string &filename)
char time_unit_overview
Time unit for report overview. If 0, the fallback time_unit will be used.
int32_t b_silent
If non-zero, the run details will be omited. Any warning, error, or important messages are directed t...
const char * input_file
C-string containing the input file name.
char time_unit_stats
Time unit for report statistics. If 0, the fallback time_unit will be used.
int32_t compile(const ReportCompilerConfigC *p_config, char *s_error, size_t s_error_size)
Runs the compiler using the specified configuration.
char time_unit_summary
Time unit for report summaries. If 0, the fallback time_unit will be used.
int32_t b_show_overview
If non-zero, the compiled overview will be output to stdout.
char time_unit
Fallback time unit when no time unit is specified for a specific output.
std::uint64_t copyString(char *dst, std::uint64_t size, const std::string &src)
Copies a C++ string object into a C-style string.
Definition: utilities.cpp:13
static std::string getTimeUnitName(char time_unit, const std::string &default_name="(default)")
void outputHeader(std::ostream &os, bool new_line=true)
void parseHeader(const std::string &filename, const std::string &s_header, const std::string &s_end_state)
static constexpr const char * EndStateGeneralFailure