HEBench
hebench_report_impl.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 <algorithm>
6 #include <cassert>
7 #include <cstring>
8 #include <limits>
9 #include <sstream>
10 
11 #include "hebench/modules/general/include/hebench_math_utils.h"
12 #include "hebench/modules/general/include/hebench_utilities.h"
13 #include "hebench_report_impl.h"
14 
15 namespace hebench {
16 namespace ReportGen {
17 
18 std::string to_string(const std::string_view &s)
19 {
20  return std::string(s.begin(), s.end());
21 }
22 
24  m_main_event(std::numeric_limits<decltype(m_main_event)>::max())
25 {
26 }
27 
28 void TimingReportImpl::newEventType(std::uint32_t set_id, const std::string &set_header, bool is_main_event)
29 {
30  if (m_event_headers.count(set_id) <= 0)
31  m_event_types.push_back(set_id);
32  m_event_headers[set_id] = set_header;
33  if (is_main_event || m_main_event == std::numeric_limits<decltype(m_main_event)>::max())
34  m_main_event = set_id;
35 }
36 
37 void TimingReportImpl::newEvent(std::shared_ptr<TimingReportEventC> p_event, const std::string &set_header)
38 {
39  if (p_event)
40  {
41  if (m_event_headers.count(p_event->event_type_id) <= 0 || !set_header.empty())
42  newEventType(p_event->event_type_id, set_header);
43  m_events.push_back(p_event);
44  } // end if
45 }
46 
47 void TimingReportImpl::reserveCapacityForEvents(std::size_t new_capacity)
48 {
49  m_events.reserve(new_capacity);
50 }
51 
53 {
54  m_events.clear();
55 }
56 
57 void TimingReportImpl::appendHeader(const std::string &header, bool new_line)
58 {
59  std::stringstream ss;
60  ss << m_header;
61  if (new_line)
62  ss << std::endl;
63  ss << header;
64  m_header = ss.str();
65 }
66 
67 void TimingReportImpl::prependHeader(const std::string &header, bool new_line)
68 {
69  std::stringstream ss;
70  ss << header;
71  if (new_line)
72  ss << std::endl;
73  ss << m_header;
74  m_header = ss.str();
75 }
76 
77 void TimingReportImpl::appendFooter(const std::string &footer, bool new_line)
78 {
79  std::stringstream ss;
80  ss << m_footer;
81  if (new_line)
82  ss << std::endl;
83  ss << footer;
84  m_footer = ss.str();
85 }
86 
87 void TimingReportImpl::prependFooter(const std::string &footer, bool new_line)
88 {
89  std::stringstream ss;
90  ss << footer;
91  if (new_line)
92  ss << std::endl;
93  ss << m_footer;
94  m_footer = ss.str();
95 }
96 
97 std::ostream &TimingReportImpl::convert2CSV(std::ostream &os) const
98 {
99  if (!os)
100  throw std::ios_base::failure("Output stream is in an invalid state.");
101 
102  os << TagVersion << std::endl // report version
103  << "Events recorded," << m_events.size() << std::endl
104  << "Main event," << m_main_event << std::endl
105  << TagReportHeader << std::endl // header start
106  << getHeader() << std::endl;
107  if (!os)
108  throw std::ios_base::failure("Error writing report header to stream.");
109  if (m_events.empty())
110  {
111  os << TagFailedTest << std::endl // this report is of a test that failed validation
112  << "Failed" << std::endl;
113  } // end if
114  else
115  {
116  os << TagReportData << std::endl // start of the report table
117  << ",idx,ID,Event,Description,Time ratio num,Time ratio den,"
118  << "Wall time start,Wall time end,Elapsed wall time,"
119  << "CPU time start,CPU time end,Elapsed CPU time,Input Samples"
120  << std::endl;
121  if (!os)
122  throw std::ios_base::failure("Error writing table header to stream.");
123 
124  for (std::size_t i = 0; i < m_events.size(); ++i)
125  {
126  const TimingReportEventC &timing_event = *m_events[i];
127  os << "," << i << "," << timing_event.event_type_id << ",";
128  if (m_event_headers.count(timing_event.event_type_id) > 0)
129  os << m_event_headers.at(timing_event.event_type_id);
130  os << "," << timing_event.description << ","
131  << timing_event.time_interval_ratio_num << "," << timing_event.time_interval_ratio_den << ","
132  << hebench::Utilities::convertDoubleToStr(timing_event.wall_time_start) << "," << hebench::Utilities::convertDoubleToStr(timing_event.wall_time_end) << ","
133  << hebench::Utilities::convertDoubleToStr(timing_event.wall_time_end - timing_event.wall_time_start) << ","
134  << hebench::Utilities::convertDoubleToStr(timing_event.cpu_time_start) << "," << hebench::Utilities::convertDoubleToStr(timing_event.cpu_time_end) << ","
135  << hebench::Utilities::convertDoubleToStr(timing_event.cpu_time_end - timing_event.cpu_time_start) << ","
136  << timing_event.input_sample_count;
137 
138  os << std::endl;
139 
140  if (!os)
141  throw std::ios_base::failure("Error writing report event to stream.");
142  } // end for
143  } // end else
144 
145  os << TagReportFooter << std::endl // footer start
146  << getFooter() << std::endl;
147 
148  // end of report
149  os << TagReportEnd << std::endl;
150  if (!os)
151  throw std::ios_base::failure("Error writing report event to stream.");
152 
153  return os;
154 }
155 
156 std::string_view TimingReportImpl::findNextValueCSV(std::size_t &out_start_idx,
157  std::size_t &out_count,
158  const char *s)
159 {
160  // ignore blank spaces
161  std::string_view s_view(s);
162  out_start_idx = std::min(s_view.find_first_not_of(" \t\n\r\f\v"), s_view.length());
163  out_count = 0;
164  s_view.remove_prefix(out_start_idx);
165  if (!s_view.empty() && s_view.front() != ',')
166  {
167  // value is not empty
168 
169  // check for quotations
170  if (s_view.front() == '\"')
171  {
172  // find the closing quotations
173  std::size_t value_end_pos = s_view.find_first_of('\"', 1);
174  if (value_end_pos != std::string_view::npos)
175  {
176  value_end_pos = std::min(s_view.find_first_of(',', value_end_pos + 1), s_view.length());
177  out_count = value_end_pos;
178  } // end if
179  } // end if
180  else
181  out_count = std::min(s_view.find_first_of(','), s_view.length());
182  } // end else
183 
184  s_view = std::string_view(s + out_start_idx, out_count);
185  auto last_non_blank_idx = s_view.find_last_not_of(" \t\n\r\f\v");
186  if (last_non_blank_idx != std::string_view::npos)
187  s_view.remove_suffix(s_view.length() - last_non_blank_idx - 1);
188 
189  return s_view;
190 }
191 
192 void TimingReportImpl::parseHeadingValue(std::string &s_out_heading, std::uint64_t &out_value,
193  const std::string &s_line)
194 {
195  std::stringstream ss;
196  std::size_t value_start, value_length;
197  std::string_view s_line_view;
198  std::string s_value;
199 
200  s_line_view = std::string_view(s_line.c_str());
201 
202  // heading
203  s_out_heading = findNextValueCSV(value_start, value_length, s_line_view.data());
204  if (value_start + value_length < s_line_view.length())
205  ++value_length;
206  s_line_view.remove_prefix(value_start + value_length);
207 
208  // value
209  s_value = to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
210  ss = std::stringstream(s_value);
211  if (s_value.empty() || !(ss >> out_value))
212  throw std::runtime_error("Invalid CSV format. Expected type uint64_t for heading \"" + s_out_heading + "\", but read \"" + s_value + "\".");
213 }
214 
215 void TimingReportImpl::parseHeadingValue(std::string &s_out_heading, double &out_value,
216  const std::string &s_line)
217 {
218  std::stringstream ss;
219  std::size_t value_start, value_length;
220  std::string_view s_line_view;
221  std::string s_value;
222 
223  s_line_view = std::string_view(s_line.c_str());
224 
225  // heading
226  s_out_heading = findNextValueCSV(value_start, value_length, s_line_view.data());
227  if (value_start + value_length < s_line_view.length())
228  ++value_length;
229  s_line_view.remove_prefix(value_start + value_length);
230 
231  // value
232  s_value = to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
233  ss = std::stringstream(s_value);
234  if (s_value.empty() || !(ss >> out_value))
235  throw std::runtime_error("Invalid CSV format. Expected type double for heading \"" + s_out_heading + "\", but read \"" + s_value + "\".");
236 }
237 
238 void TimingReportImpl::parseTimingEvent(std::string &s_out_event_header,
239  std::shared_ptr<TimingReportEventC> &p_out_event,
240  const std::string &s_line)
241 {
242  std::stringstream ss;
243  std::size_t value_start, value_length;
244  std::string_view s_line_view;
245  std::string s_value;
246  std::shared_ptr<TimingReportEventC> retval;
247 
248  s_out_event_header.clear();
249  if (!s_line.empty())
250  {
251  std::uint32_t id;
252  std::string event_description;
253  double cpu_start_time, cpu_end_time, wall_start_time, wall_end_time;
254  int64_t time_interval_num, time_interval_den;
255  std::uint64_t input_sample_count;
256 
257  s_line_view = std::string_view(s_line.c_str());
258 
259  // parse order:
260  // ,idx,ID,Event,Description,Time ratio num, Time ratio den,Wall time start,Wall time end,Elapsed wall time,CPU time start,CPU time end,Elapsed CPU time,Input Samples"
261 
262  // skip any empty columns at the start
263  std::string_view sv_tmp;
264  do
265  {
266  sv_tmp = findNextValueCSV(value_start, value_length, s_line_view.data());
267  if (sv_tmp.empty())
268  {
269  if (value_start + value_length < s_line_view.length())
270  ++value_length;
271  s_line_view.remove_prefix(value_start + value_length);
272  } // end if
273  } while (sv_tmp.empty());
274 
275  // idx (skip)
276  findNextValueCSV(value_start, value_length, s_line_view.data());
277  if (value_start + value_length < s_line_view.length())
278  ++value_length;
279  s_line_view.remove_prefix(value_start + value_length);
280 
281  // ID
282  s_value = to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
283  if (value_start + value_length < s_line_view.length())
284  ++value_length;
285  s_line_view.remove_prefix(value_start + value_length);
286  ss = std::stringstream(s_value);
287  if (s_value.empty() || !(ss >> id))
288  {
289  ss = std::stringstream();
290  ss << "Invalid timing event format. Expected type uint32_t for ID, but read value \"" + s_value + "\".";
291  throw std::runtime_error(ss.str());
292  } // end if
293 
294  // Event
295  s_out_event_header = to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
296  if (value_start + value_length < s_line_view.length())
297  ++value_length;
298  s_line_view.remove_prefix(value_start + value_length);
299 
300  // Description
301  event_description = to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
302  if (value_start + value_length < s_line_view.length())
303  ++value_length;
304  s_line_view.remove_prefix(value_start + value_length);
305 
306  // Time ratio num
307  s_value = to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
308  if (value_start + value_length < s_line_view.length())
309  ++value_length;
310  s_line_view.remove_prefix(value_start + value_length);
311  ss = std::stringstream(s_value);
312  if (s_value.empty() || !(ss >> time_interval_num))
313  {
314  ss = std::stringstream();
315  ss << "Invalid timing event format. Expected type int64 for Time ratio num, but read value \"" << s_value << "\".";
316  throw std::runtime_error(ss.str());
317  } // end if
318 
319  // Time ratio den
320  s_value = to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
321  if (value_start + value_length < s_line_view.length())
322  ++value_length;
323  s_line_view.remove_prefix(value_start + value_length);
324  ss = std::stringstream(s_value);
325  if (s_value.empty() || !(ss >> time_interval_den) || time_interval_den == 0)
326  {
327  ss = std::stringstream();
328  ss << "Invalid timing event format. Expected non-zero of type int64 for Time ratio den, but read value \"" << s_value << "\".";
329  throw std::runtime_error(ss.str());
330  } // end if
331 
332  // Wall time start
333  s_value = to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
334  if (value_start + value_length < s_line_view.length())
335  ++value_length;
336  s_line_view.remove_prefix(value_start + value_length);
337  ss = std::stringstream(s_value);
338  if (s_value.empty() || !(ss >> wall_start_time))
339  {
340  ss = std::stringstream();
341  ss << "Invalid timing event format. Expected type double for Wall time start, but read value \"" << s_value << "\".";
342  throw std::runtime_error(ss.str());
343  } // end if
344 
345  // Wall time end
346  s_value = to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
347  if (value_start + value_length < s_line_view.length())
348  ++value_length;
349  s_line_view.remove_prefix(value_start + value_length);
350  ss = std::stringstream(s_value);
351  if (s_value.empty() || !(ss >> wall_end_time))
352  {
353  ss = std::stringstream();
354  ss << "Invalid timing event format. Expected type double for Wall time end, but read value \"" << s_value << "\".";
355  throw std::runtime_error(ss.str());
356  } // end if
357 
358  // Wall time elapsed (skip)
359  findNextValueCSV(value_start, value_length, s_line_view.data());
360  if (value_start + value_length < s_line_view.length())
361  ++value_length;
362  s_line_view.remove_prefix(value_start + value_length);
363 
364  // CPU time start
365  s_value = to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
366  if (value_start + value_length < s_line_view.length())
367  ++value_length;
368  s_line_view.remove_prefix(value_start + value_length);
369  ss = std::stringstream(s_value);
370  if (s_value.empty() || !(ss >> cpu_start_time))
371  {
372  ss = std::stringstream();
373  ss << "Invalid timing event format. Expected type double for CPU time start, but read value \"" << s_value << "\".";
374  throw std::runtime_error(ss.str());
375  } // end if
376 
377  // CPU time end
378  s_value = to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
379  if (value_start + value_length < s_line_view.length())
380  ++value_length;
381  s_line_view.remove_prefix(value_start + value_length);
382  ss = std::stringstream(s_value);
383  if (s_value.empty() || !(ss >> cpu_end_time))
384  {
385  ss = std::stringstream();
386  ss << "Invalid timing event format. Expected type double for CPU time end, but read value \"" << s_value << "\".";
387  throw std::runtime_error(ss.str());
388  } // end if
389 
390  // CPU time elapsed (skip)
391  findNextValueCSV(value_start, value_length, s_line_view.data());
392  if (value_start + value_length < s_line_view.length())
393  ++value_length;
394  s_line_view.remove_prefix(value_start + value_length);
395 
396  // Number of input samples
397  s_value = to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
398  if (value_start + value_length < s_line_view.length())
399  ++value_length;
400  s_line_view.remove_prefix(value_start + value_length);
401  ss = std::stringstream(s_value);
402  if (s_value.empty() || !(ss >> input_sample_count))
403  {
404  ss = std::stringstream();
405  ss << "Invalid timing event format. Expected type uint64_t for number of Input Samples, but read value \"" << s_value << "\".";
406  throw std::runtime_error(ss.str());
407  } // end if
408 
409  retval = std::make_shared<TimingReportEventC>();
410  retval->event_type_id = id;
411  hebench::Utilities::copyString(retval->description, MAX_TIME_REPORT_EVENT_DESCRIPTION_SIZE, event_description);
412  retval->cpu_time_start = cpu_start_time;
413  retval->cpu_time_end = cpu_end_time;
414  retval->wall_time_start = wall_start_time;
415  retval->wall_time_end = wall_end_time;
416  retval->input_sample_count = input_sample_count;
417  retval->time_interval_ratio_num = time_interval_num;
418  retval->time_interval_ratio_den = time_interval_den;
419 
420  } // end if
421 
422  p_out_event = retval;
423 }
424 
425 std::istream &TimingReportImpl::getTrimmedLine(std::istream &is, std::string &s_out, const std::string &extra_trim)
426 {
427  return getTrimmedLine(is, s_out, extra_trim, extra_trim);
428 }
429 
430 std::istream &TimingReportImpl::getTrimmedLine(std::istream &is, std::string &s_out,
431  const std::string &extra_ltrim, const std::string &extra_rtrim)
432 {
433  std::getline(is, s_out);
434  std::string s_ltrim(" \t\n\r\f\v");
435  std::string s_rtrim = s_ltrim;
436  if (!extra_ltrim.empty())
437  s_ltrim.append(extra_ltrim);
438  if (!extra_rtrim.empty())
439  s_rtrim.append(extra_rtrim);
440  s_out.erase(0, s_out.find_first_not_of(s_ltrim));
441  s_out.erase(s_out.find_last_not_of(s_rtrim) + 1);
442  return is;
443 }
444 
445 std::string TimingReportImpl::readTextBlock(std::istream &is, std::string &s_block,
446  const std::vector<std::string> tags)
447 {
448  constexpr const char *ch_trim = " \t\n\r\f\v,";
449  std::string s_line;
450  std::string s_tag;
451  std::stringstream ss;
452  bool b_found = false;
453  bool b_first_block_line = true;
454 
455  while (is && !b_found)
456  {
457  std::getline(is, s_line);
458  s_tag = s_line;
459  s_tag.erase(0, s_tag.find_first_not_of(ch_trim));
460  s_tag.erase(s_tag.find_last_not_of(ch_trim) + 1);
461 
462  for (std::size_t tag_i = 0; !b_found && tag_i < tags.size(); ++tag_i)
463  b_found = s_tag == tags[tag_i];
464  if (!b_found)
465  {
466  if (b_first_block_line)
467  b_first_block_line = false;
468  else
469  ss << std::endl;
470  ss << s_line;
471  } // end if
472  } // end while
473 
474  s_block = ss.str();
475  return b_found ? s_tag : std::string();
476 }
477 
478 TimingReportImpl TimingReportImpl::loadCSV(const std::string &csv_content)
479 {
480  std::istringstream iss(csv_content);
481  return loadCSV(iss);
482 }
483 
485 {
486  std::string s_line;
487  std::string s_block;
488  std::uint64_t events_count, u64_main_event;
489 
490  TimingReportImpl retval;
491 
492  // version
493  getTrimmedLine(is, s_line, ",");
494  if (s_line != TagVersion)
495  {
496  std::stringstream ss;
497  ss << "Invalid CSV report version found. Expected \"" << TagVersion << "\", but read \"" << s_line << "\".";
498  throw std::runtime_error(ss.str());
499  } // end if
500 
501  // read events recorded
502  getTrimmedLine(is, s_line, ",");
503  std::string heading;
504  parseHeadingValue(heading, events_count, s_line);
505 
506  // read main event
507  getTrimmedLine(is, s_line, ",");
508  parseHeadingValue(heading, u64_main_event, s_line);
509 
510  // read until test header found
511  if (TagReportHeader != readTextBlock(is, s_block, { TagReportHeader }))
512  throw std::runtime_error("Report header not found in CSV. End of file reached.");
513 
514  // read header
515  s_line = readTextBlock(is, s_block, { TagReportData, TagFailedTest });
516  retval.setHeader(s_block);
517 
518  if (s_line == TagFailedTest) // report is a validation failed run
519  {
520  // skip content until footer is found
521  while (s_line != TagReportFooter && (is))
522  getTrimmedLine(is, s_line, ",");
523  } // end if
524  else if (s_line == TagReportData) // report is a valid run
525  {
526  // skip table header
527  getTrimmedLine(is, s_line, ",");
528 
529  // add main event
530  retval.newEventType(u64_main_event, "", true);
531 
532  // read each timing event until footer is found
533  while (s_line != TagReportFooter && (is))
534  {
535  getTrimmedLine(is, s_line, ",");
536 
537  if (s_line != TagReportFooter)
538  {
539  std::string s_event_header;
540  std::shared_ptr<TimingReportEventC> p_event;
541  parseTimingEvent(s_event_header, p_event, s_line);
542  retval.newEvent(p_event, s_event_header);
543  } // end if
544  } // end while
545 
546  if (retval.getEvents().size() != events_count)
547  {
548  std::stringstream ss;
549  ss << "Inconsistent number of events read from CSV. Expected " << events_count << ", but read " << retval.getEvents().size() << ".";
550  throw std::runtime_error(ss.str());
551  } // end if
552  } // end else
553  else
554  throw std::runtime_error("Report data not found in CSV. End of file reached.");
555 
556  if (s_line != TagReportFooter)
557  throw std::runtime_error("Report footer not found in CSV. End of file reached.");
558 
559  // read footer
560  s_line = readTextBlock(is, s_block, { TagReportEnd });
561  // if (TagReportEnd != s_line)
562  // throw std::runtime_error("Report end marker not found in CSV. End of file reached.");
563  retval.setFooter(s_block);
564 
565  return retval;
566 }
567 
568 void TimingReportImpl::setTimingPrefix(TimingPrefixedSeconds &prefix, double seconds, char ch_prefix)
569 {
570  switch (ch_prefix)
571  {
572  case 0:
573  computeTimingPrefix(prefix, seconds);
574  break;
575  case 's':
576  prefix.time_interval_ratio_den = 1;
579  break;
580  case 'm':
581  prefix.time_interval_ratio_den = 1e3; //1000;
584  break;
585  case 'u':
586  prefix.time_interval_ratio_den = 1e6; //1000000;
589  break;
590  case 'n':
591  prefix.time_interval_ratio_den = 1e9; //1000000000;
594  break;
595  default:
596  throw std::invalid_argument("Unknown prefix.");
597  break;
598  } // end switch
599 
600  prefix.value = seconds * prefix.time_interval_ratio_den;
601 }
602 
604 {
605  // convert to seconds
606  prefix.value = seconds;
607  prefix.time_interval_ratio_den = 1;
608  std::size_t scale_i;
609  for (scale_i = 0; scale_i < 4 && prefix.value > 0.0 && prefix.value < 1.0; ++scale_i)
610  {
611  prefix.value *= 1000.0;
612  prefix.time_interval_ratio_den *= 1000;
613  } // end for
614 
615  switch (scale_i)
616  {
617  case 0: // seconds
620  break;
621 
622  case 1: // milliseconds
625  break;
626 
627  case 2: // microseconds
630  break;
631 
632  default: // 3 - nanoseconds
635  break;
636  } // end switch
637 }
638 
639 } // namespace ReportGen
640 } // namespace hebench
static constexpr const char * TagReportFooter
void appendHeader(const std::string &header, bool new_line)
void prependHeader(const std::string &header, bool new_line)
std::ostream & convert2CSV(std::ostream &os) const
static void setTimingPrefix(TimingPrefixedSeconds &prefix, double seconds, char ch_prefix)
void setHeader(const std::string &header)
void setFooter(const std::string &footer)
void prependFooter(const std::string &footer, bool new_line)
const std::vector< std::shared_ptr< TimingReportEventC > > & getEvents() const
void appendFooter(const std::string &footer, bool new_line)
static constexpr const char * TagVersion
const std::string & getHeader() const
void newEventType(std::uint32_t set_id, const std::string &set_header, bool is_main_event=false)
static constexpr const char * TagReportEnd
void reserveCapacityForEvents(std::size_t new_capacity)
static constexpr const char * TagReportData
static constexpr const char * TagFailedTest
static constexpr const char * TagReportHeader
static void computeTimingPrefix(TimingPrefixedSeconds &prefix, double seconds)
const std::string & getFooter() const
static TimingReportImpl loadCSV(std::istream &is)
void newEvent(std::shared_ptr< TimingReportEventC > p_event, const std::string &set_header=std::string())
#define MAX_TIME_REPORT_EVENT_DESCRIPTION_SIZE
#define MAX_DESCRIPTION_BUFFER_SIZE
#define MAX_SYMBOL_BUFFER_SIZE
int64_t time_interval_ratio_den
Denominator of timing scale ratio with respect to a unit.
char prefix[MAX_DESCRIPTION_BUFFER_SIZE]
Full name for the prefix (or empty string if no prefix).
std::string to_string(const std::string_view &s)
int64_t time_interval_ratio_num
Scale of time interval used for this event.
char symbol[MAX_SYMBOL_BUFFER_SIZE]
Symbol for the prefix.
double value
Value in the specified unit.
uint32_t event_type_id
ID specifying the event type.
int64_t time_interval_ratio_den
Scale of time interval used for this event.
char description[MAX_TIME_REPORT_EVENT_DESCRIPTION_SIZE]
Description attached to this event.
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