scc  2022.4.0
SystemC components library
report.cpp
1 /*******************************************************************************
2  * Copyright 2017, 2018 MINRES Technologies GmbH
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *******************************************************************************/
16 /*
17  * report.cpp
18  *
19  * Created on: 19.09.2017
20  * Author: ubuntu
21  */
22 
23 #include "report.h"
24 #include "configurer.h"
25 #include <array>
26 #include <chrono>
27 #include <fstream>
28 #include <mutex>
29 #include <spdlog/async.h>
30 #include <spdlog/sinks/basic_file_sink.h>
31 #include <spdlog/sinks/stdout_color_sinks.h>
32 #include <spdlog/spdlog.h>
33 #include <thread>
34 #include <tuple>
35 #include <unordered_map>
36 #ifdef __GNUC__
37 #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
38 #if GCC_VERSION < 40900
39 #define USE_C_REGEX
40 #endif
41 #define likely(x) __builtin_expect(x, 1)
42 #define unlikely(x) __builtin_expect(x, 0)
43 #else
44 #define likely(x) x
45 #define unlikely(x) x
46 #endif
47 
48 #ifdef USE_C_REGEX
49 #include <regex.h>
50 #else
51 #include <regex>
52 #endif
53 #ifdef ERROR
54 #undef ERROR
55 #endif
56 using namespace std;
57 using namespace sc_core;
58 using namespace scc;
59 
60 namespace {
61 struct char_equal_to : public std::equal_to<char const*> {
62  bool operator()(char const* __x, char const* __y) const { return strcmp(__x, __y) == 0; }
63 };
64 
65 struct char_hash {
66  // BKDR hash algorithm
67  uint64_t operator()(char const* str) const {
68  constexpr unsigned int seed = 131; // 31 131 1313 13131131313 etc//
69  uint64_t hash = 0;
70  while(*str) {
71  hash = (hash * seed) + (*str);
72  str++;
73  }
74  return hash;
75  }
76 };
77 thread_local struct {
78  std::unordered_map<char const*, sc_core::sc_verbosity, char_hash, char_equal_to> table;
79  std::vector<std::string> cache;
80  void insert(char const* key, sc_core::sc_verbosity verb) {
81  cache.push_back(key);
82  table.insert({cache.back().c_str(), verb});
83  }
84  void clear() {
85  table.clear();
86  cache.clear();
87  }
88 } lut;
89 #ifdef MTI_SYSTEMC
90 static const cci::cci_originator originator;
91 #else
92 static const cci::cci_originator originator("reporting");
93 #endif
94 
95 bool& inst_based_logging() {
96  thread_local bool active = getenv("SCC_DISABLE_INSTANCE_BASED_LOGGING") == nullptr;
97  return active;
98 }
99 
100 struct ExtLogConfig : public scc::LogConfig {
101  shared_ptr<spdlog::logger> file_logger;
102  shared_ptr<spdlog::logger> console_logger;
103 #ifdef USE_C_REGEX
104  regex_t start_state{};
105 #else
106  regex reg_ex;
107 #endif
108  sc_time cycle_base{0, SC_NS};
109  auto operator=(const scc::LogConfig& o) -> ExtLogConfig& {
110  scc::LogConfig::operator=(o);
111  return *this;
112  }
113  auto match(const char* type) -> bool {
114 #ifdef USE_C_REGEX
115  return regexec(&start_state, type, 0, nullptr, 0) == 0;
116 #else
117  return regex_search(type, reg_ex);
118 #endif
119  }
120  bool initialized{false};
121 };
122 
123 thread_local ExtLogConfig log_cfg;
124 
125 auto get_tuple(const sc_time& t) -> tuple<sc_time::value_type, sc_time_unit> {
126  auto val = t.value();
127  auto tr = (uint64_t)(sc_time::from_value(1).to_seconds() * 1E15);
128  auto scale = 0U;
129  while((tr % 10) == 0) {
130  tr /= 10;
131  scale++;
132  }
133  sc_assert(tr == 1);
134 
135  auto tu = scale / 3;
136  while(tu < SC_SEC && (val % 10) == 0) {
137  val /= 10;
138  scale++;
139  tu += (0 == (scale % 3));
140  }
141  for(scale %= 3; scale != 0; scale--)
142  val *= 10;
143  return make_tuple(val, static_cast<sc_time_unit>(tu));
144 }
145 
146 auto time2string(const sc_time& t) -> string {
147  const array<const char*, 6> time_units{"fs", "ps", "ns", "us", "ms", "s "};
148  const array<uint64_t, 6> multiplier{
149  1ULL, 1000ULL, 1000ULL * 1000, 1000ULL * 1000 * 1000, 1000ULL * 1000 * 1000 * 1000, 1000ULL * 1000 * 1000 * 1000 * 1000};
150  ostringstream oss;
151  if(!t.value()) {
152  oss << "0 s ";
153  } else {
154  const auto tt = get_tuple(t);
155  const auto val = get<0>(tt);
156  const auto scale = get<1>(tt);
157  const auto fs_val = val * multiplier[scale];
158  for(int j = multiplier.size() - 1; j >= scale; --j) {
159  if(fs_val >= multiplier[j]) {
160  const auto i = val / multiplier[j - scale];
161  const auto f = val % multiplier[j - scale];
162  oss << i << '.' << setw(3 * (j - scale)) << setfill('0') << right << f << ' ' << time_units[j];
163  break;
164  }
165  }
166  }
167  return oss.str();
168 }
169 auto compose_message(const sc_report& rep, const scc::LogConfig& cfg) -> const string {
170  if(rep.get_severity() > SC_INFO || cfg.log_filter_regex.length() == 0 || rep.get_verbosity() == sc_core::SC_MEDIUM ||
171  log_cfg.match(rep.get_msg_type())) {
172  stringstream os;
173  if(likely(cfg.print_sim_time)) {
174  if(unlikely(log_cfg.cycle_base.value())) {
175  if(unlikely(cfg.print_delta))
176  os << "[" << std::setw(7) << std::setfill(' ') << sc_time_stamp().value() / log_cfg.cycle_base.value() << "(" << setw(5)
177  << sc_delta_count() << ")]";
178  else
179  os << "[" << std::setw(7) << std::setfill(' ') << sc_time_stamp().value() / log_cfg.cycle_base.value() << "]";
180  } else {
181  auto t = time2string(sc_time_stamp());
182  if(unlikely(cfg.print_delta))
183  os << "[" << std::setw(20) << std::setfill(' ') << t << "(" << setw(5) << sc_delta_count() << ")]";
184  else
185  os << "[" << std::setw(20) << std::setfill(' ') << t << "]";
186  }
187  }
188  if(unlikely(rep.get_id() >= 0))
189  os << "("
190  << "IWEF"[rep.get_severity()] << rep.get_id() << ") " << rep.get_msg_type() << ": ";
191  else if(cfg.msg_type_field_width) {
192  if(cfg.msg_type_field_width == std::numeric_limits<unsigned>::max())
193  os << rep.get_msg_type() << ": ";
194  else
195  os << util::padded(rep.get_msg_type(), cfg.msg_type_field_width) << ": ";
196  }
197  if(*rep.get_msg())
198  os << rep.get_msg();
199  if(rep.get_severity() > SC_INFO) {
200  if(rep.get_line_number())
201  os << "\n [FILE:" << rep.get_file_name() << ":" << rep.get_line_number() << "]";
202  sc_simcontext* simc = sc_get_curr_simcontext();
203  if(simc && sc_is_running()) {
204  const char* proc_name = rep.get_process_name();
205  if(proc_name)
206  os << "\n [PROCESS:" << proc_name << "]";
207  }
208  }
209  return os.str();
210  } else
211  return "";
212 }
213 
214 inline auto get_verbosity(const sc_report& rep) -> int {
215  return rep.get_verbosity() > sc_core::SC_NONE && rep.get_verbosity() < sc_core::SC_LOW ? rep.get_verbosity() * 10 : rep.get_verbosity();
216 }
217 
218 inline void log2logger(spdlog::logger& logger, const sc_report& rep, const scc::LogConfig& cfg) {
219  auto msg = compose_message(rep, cfg);
220  if(!msg.size())
221  return;
222  switch(rep.get_severity()) {
223  case SC_INFO:
224  switch(get_verbosity(rep)) {
225  case SC_DEBUG:
226  case SC_FULL:
227  logger.trace(msg);
228  break;
229  case SC_HIGH:
230  logger.debug(msg);
231  break;
232  default:
233  logger.info(msg);
234  break;
235  }
236  break;
237  case SC_WARNING:
238  logger.warn(msg);
239  break;
240  case SC_ERROR:
241  logger.error(msg);
242  break;
243  case SC_FATAL:
244  logger.critical(msg);
245  break;
246  default:
247  break;
248  }
249 }
250 
251 inline void log2logger(spdlog::logger& logger, scc::log lvl, const string& msg) {
252  switch(lvl) {
253  case scc::log::DBGTRACE:
254  case scc::log::TRACE:
255  logger.trace(msg);
256  return;
257  case scc::log::DEBUG:
258  logger.debug(msg);
259  return;
260  case scc::log::INFO:
261  logger.info(msg);
262  return;
263  case scc::log::WARNING:
264  logger.warn(msg);
265  return;
266  case scc::log::ERROR:
267  logger.error(msg);
268  return;
269  case scc::log::FATAL:
270  logger.critical(msg);
271  return;
272  default:
273  break;
274  }
275 }
276 
277 void report_handler(const sc_report& rep, const sc_actions& actions) {
278  thread_local bool sc_stop_called = false;
279  if(actions & SC_DO_NOTHING)
280  return;
281  if(rep.get_severity() == sc_core::SC_INFO || !log_cfg.report_only_first_error || sc_report_handler::get_count(SC_ERROR) < 2) {
282  if((actions & SC_DISPLAY) && (!log_cfg.file_logger || get_verbosity(rep) < SC_HIGH))
283  log2logger(*log_cfg.console_logger, rep, log_cfg);
284  if((actions & SC_LOG) && log_cfg.file_logger) {
285  scc::LogConfig lcfg(log_cfg);
286  lcfg.print_sim_time = true;
287  if(!lcfg.msg_type_field_width)
288  lcfg.msg_type_field_width = 24;
289  log2logger(*log_cfg.file_logger, rep, lcfg);
290  }
291  }
292  if(actions & SC_STOP) {
293  this_thread::sleep_for(chrono::milliseconds(static_cast<unsigned>(log_cfg.level) * 10));
294  if(sc_is_running() && !sc_stop_called) {
295  sc_stop();
296  sc_stop_called = true;
297  }
298  }
299  if(actions & SC_ABORT) {
300  this_thread::sleep_for(chrono::milliseconds(static_cast<unsigned>(log_cfg.level) * 20));
301  abort();
302  }
303  if(actions & SC_THROW) {
304  this_thread::sleep_for(chrono::milliseconds(static_cast<unsigned>(log_cfg.level) * 20));
305  throw rep;
306  }
307  if(sc_time_stamp().value() && !sc_is_running()) {
308  log_cfg.console_logger->flush();
309  if(log_cfg.file_logger)
310  log_cfg.file_logger->flush();
311  this_thread::sleep_for(chrono::milliseconds(static_cast<unsigned>(log_cfg.level) * 10));
312  }
313 }
314 } // namespace
315 
317 : os(os)
318 , level(level) {
319  old_buf = os.rdbuf(this); // save and redirect
320 }
321 
323  os.rdbuf(old_buf); // restore
324 }
325 
327  os.rdbuf(old_buf); // restore
328  old_buf = nullptr;
329 }
330 
331 auto scc::stream_redirection::xsputn(const char_type* s, streamsize n) -> streamsize {
332  auto sz = stringbuf::xsputn(s, n);
333  if(s[n - 1] == '\n') {
334  sync();
335  }
336  return sz;
337 }
338 
339 static const array<sc_severity, 8> severity = {SC_FATAL, // scc::log::NONE
340  SC_FATAL, // scc::log::FATAL
341  SC_ERROR, // scc::log::ERROR
342  SC_WARNING, // scc::log::WARNING
343  SC_INFO, // scc::log::INFO
344  SC_INFO, // scc::log::DEBUG
345  SC_INFO, // scc::log::TRACE
346  SC_INFO}; // scc::log::DBGTRACE
347 static const array<sc_verbosity, 8> verbosity = {SC_NONE, // scc::log::NONE
348  SC_LOW, // scc::log::FATAL
349  SC_LOW, // scc::log::ERROR
350  SC_LOW, // scc::log::WARNING
351  SC_MEDIUM, // scc::log::INFO
352  SC_HIGH, // scc::log::DEBUG
353  SC_FULL, // scc::log::TRACE
354  SC_DEBUG}; // scc::log::DBGTRACE
355 
356 auto scc::stream_redirection::sync() -> int {
357  if(level <= log_cfg.level) {
358  auto timestr = time2string(sc_time_stamp());
359  istringstream buf(str());
360  string line;
361  while(getline(buf, line)) {
362  ::sc_report_handler::report(severity[static_cast<unsigned>(level)], "SystemC", line.c_str(),
363  verbosity[static_cast<unsigned>(level)], "", 0);
364  }
365  str(string(""));
366  }
367  return 0; // Success
368 }
369 
370 static std::mutex cfg_guard;
371 static void configure_logging() {
372  std::lock_guard<mutex> lock(cfg_guard);
373  static bool spdlog_initialized = false;
374  if(!log_cfg.dont_create_broker)
375  scc::init_cci("SCCBroker");
376  if(log_cfg.install_handler) {
377  if(!log_cfg.instance_based_log_levels || getenv("SCC_DISABLE_INSTANCE_BASED_LOGGING"))
378  inst_based_logging() = false;
379  sc_report_handler::set_verbosity_level(verbosity[static_cast<unsigned>(log_cfg.level)]);
380  sc_report_handler::set_handler(report_handler);
381  if(!spdlog_initialized) {
382  spdlog::init_thread_pool(1024U,
383  log_cfg.log_file_name.size() ? 2U : 1U); // queue with 8k items and 1 backing thread.
384  log_cfg.console_logger = log_cfg.log_async ? spdlog::stdout_color_mt<spdlog::async_factory>("console_logger")
385  : spdlog::stdout_color_mt("console_logger");
386  auto logger_fmt = log_cfg.print_severity ? "[%L] %v" : "%v";
387  if(log_cfg.colored_output) {
388  std::ostringstream os;
389  os << "%^" << logger_fmt << "%$";
390  log_cfg.console_logger->set_pattern(os.str());
391  } else
392  log_cfg.console_logger->set_pattern("[%L] %v");
393  log_cfg.console_logger->flush_on(spdlog::level::warn);
394  log_cfg.console_logger->set_level(spdlog::level::level_enum::trace);
395  if(log_cfg.log_file_name.size()) {
396  {
397  ofstream ofs;
398  ofs.open(log_cfg.log_file_name, ios::out | ios::trunc);
399  }
400  log_cfg.file_logger = log_cfg.log_async
401  ? spdlog::basic_logger_mt<spdlog::async_factory>("file_logger", log_cfg.log_file_name)
402  : spdlog::basic_logger_mt("file_logger", log_cfg.log_file_name);
403  if(log_cfg.print_severity)
404  log_cfg.file_logger->set_pattern("[%8l] %v");
405  else
406  log_cfg.file_logger->set_pattern("%v");
407  log_cfg.file_logger->flush_on(spdlog::level::warn);
408  log_cfg.file_logger->set_level(spdlog::level::level_enum::trace);
409  }
410  spdlog_initialized = true;
411  } else {
412  log_cfg.console_logger = spdlog::get("console_logger");
413  if(log_cfg.log_file_name.size())
414  log_cfg.file_logger = spdlog::get("file_logger");
415  }
416  if(log_cfg.log_filter_regex.size()) {
417 #ifdef USE_C_REGEX
418  regcomp(&log_cfg.start_state, log_cfg.log_filter_regex.c_str(), REG_EXTENDED);
419 #else
420  log_cfg.reg_ex = regex(log_cfg.log_filter_regex, regex::extended | regex::icase);
421 #endif
422  }
423  }
424 }
425 
426 void scc::reinit_logging(scc::log level) {
427  if(log_cfg.install_handler)
428  sc_report_handler::set_handler(report_handler);
429  log_cfg.level = level;
430  lut.clear();
431  if(!log_cfg.instance_based_log_levels || getenv("SCC_DISABLE_INSTANCE_BASED_LOGGING"))
432  inst_based_logging() = false;
433  log_cfg.initialized = true;
434 }
435 
436 bool scc::is_logging_initialized() { return log_cfg.initialized; }
437 
438 void scc::init_logging(scc::log level, unsigned type_field_width, bool print_time) {
439  log_cfg.msg_type_field_width = type_field_width;
440  log_cfg.print_sys_time = print_time;
441  log_cfg.level = level;
442  configure_logging();
443  log_cfg.initialized = true;
444 }
445 
446 void scc::init_logging(const scc::LogConfig& log_config) {
447  log_cfg = log_config;
448  configure_logging();
449  log_cfg.initialized = true;
450 }
451 
453  log_cfg.level = level;
454  sc_report_handler::set_verbosity_level(verbosity[static_cast<unsigned>(level)]);
455  log_cfg.console_logger->set_level(
456  static_cast<spdlog::level::level_enum>(SPDLOG_LEVEL_OFF - min<int>(SPDLOG_LEVEL_OFF, static_cast<int>(log_cfg.level))));
457  log_cfg.initialized = true;
458 }
459 
460 auto scc::get_logging_level() -> scc::log { return log_cfg.level; }
461 
462 void scc::set_cycle_base(sc_time period) { log_cfg.cycle_base = period; }
463 
465  this->level = level;
466  return *this;
467 }
468 
470  this->msg_type_field_width = width;
471  return *this;
472 }
473 
475  this->print_sys_time = enable;
476  return *this;
477 }
478 
480  this->print_sim_time = enable;
481  return *this;
482 }
483 
485  this->print_delta = enable;
486  return *this;
487 }
488 
490  this->print_severity = enable;
491  return *this;
492 }
493 
494 auto scc::LogConfig::logFileName(string&& name) -> scc::LogConfig& {
495  this->log_file_name = name;
496  return *this;
497 }
498 
499 auto scc::LogConfig::logFileName(const string& name) -> scc::LogConfig& {
500  this->log_file_name = name;
501  return *this;
502 }
503 
505  this->colored_output = enable;
506  return *this;
507 }
508 
509 auto scc::LogConfig::logFilterRegex(string&& expr) -> scc::LogConfig& {
510  this->log_filter_regex = expr;
511  return *this;
512 }
513 
514 auto scc::LogConfig::logFilterRegex(const string& expr) -> scc::LogConfig& {
515  this->log_filter_regex = expr;
516  return *this;
517 }
518 
520  this->log_async = v;
521  return *this;
522 }
523 
525  this->dont_create_broker = v;
526  return *this;
527 }
528 
530  this->report_only_first_error = v;
531  return *this;
532 }
534  this->instance_based_log_levels = v;
535  return *this;
536 }
538  this->install_handler = v;
539  return *this;
540 }
541 
542 auto scc::get_log_verbosity(char const* str) -> sc_core::sc_verbosity {
543  if(inst_based_logging()) {
544  auto it = lut.table.find(str);
545  if(it != lut.table.end())
546  return it->second;
547  if(strchr(str, '.') == nullptr || sc_core::sc_get_current_object()) {
548  string current_name = std::string(str);
549  auto broker = sc_core::sc_get_current_object() ? cci::cci_get_broker() : cci::cci_get_global_broker(originator);
550  while(true) {
551  string param_name = (current_name.empty()) ? SCC_LOG_LEVEL_PARAM_NAME : current_name + "." SCC_LOG_LEVEL_PARAM_NAME;
552  auto h = broker.get_param_handle(param_name);
553  if(h.is_valid()) {
554  sc_core::sc_verbosity ret = verbosity.at(std::min<unsigned>(h.get_cci_value().get_int(), verbosity.size() - 1));
555  lut.insert(str, ret);
556  return ret;
557  } else {
558  auto val = broker.get_preset_cci_value(param_name);
559  if(val.is_int()) {
560  sc_core::sc_verbosity ret = verbosity.at(std::min<unsigned>(val.get_int(), verbosity.size() - 1));
561  lut.insert(str, ret);
562  return ret;
563  } else {
564  if(current_name.empty()) {
565  sc_core::sc_verbosity ret =
566  static_cast<sc_core::sc_verbosity>(::sc_core::sc_report_handler::get_verbosity_level());
567  lut.insert(str, ret);
568  return ret;
569  }
570  auto pos = current_name.rfind(".");
571  if(pos == std::string::npos) {
572  current_name = "";
573  } else {
574  current_name = current_name.substr(0, pos);
575  }
576  }
577  }
578  }
579  }
580  }
581  return static_cast<sc_core::sc_verbosity>(::sc_core::sc_report_handler::get_verbosity_level());
582 }
~stream_redirection()
destructor restoring the output stream buffer
Definition: report.cpp:322
void reset()
reset the stream redirection and restore output buffer of the stream
Definition: report.cpp:326
stream_redirection(std::ostream &os, log level)
constructor redirecting the given stream to a SystemC log message of given llog level
SCC SystemC utilities.
bool is_logging_initialized()
get the state of the SCC logging system
Definition: report.cpp:436
void set_logging_level(log level)
sets the SystemC logging level
Definition: report.cpp:452
void init_logging(log level=log::WARNING, unsigned type_field_width=24, bool print_time=false)
initializes the SystemC logging system with a particular logging level
Definition: report.cpp:438
log get_logging_level()
get the SystemC logging level
Definition: report.cpp:460
void set_cycle_base(sc_core::sc_time period)
sets the cycle base for cycle based logging
sc_core::sc_verbosity get_log_verbosity()
get the global verbosity level
Definition: report.h:308
log
enum defining the log levels
Definition: report.h:84
std::string padded(std::string str, size_t width, bool show_ellipsis=true)
pad a string to a given length by either cutting of the overflow or inserting an ellipsis
Definition: ities.h:324
the configuration class for the logging setup
Definition: report.h:152
LogConfig & printSeverity(bool=true)
Definition: report.cpp:489
LogConfig & printSysTime(bool=true)
Definition: report.cpp:474
LogConfig & logFilterRegex(std::string &&)
LogConfig & reportOnlyFirstError(bool=true)
Definition: report.cpp:529
LogConfig & coloredOutput(bool=true)
Definition: report.cpp:504
LogConfig & dontCreateBroker(bool=true)
Definition: report.cpp:524
LogConfig & logLevel(log)
Definition: report.cpp:464
LogConfig & msgTypeFieldWidth(unsigned)
Definition: report.cpp:469
LogConfig & printSimTime(bool=true)
Definition: report.cpp:479
LogConfig & logAsync(bool=true)
Definition: report.cpp:519
LogConfig & printDelta(bool=true)
Definition: report.cpp:484
LogConfig & instanceBasedLogLevels(bool=true)
Definition: report.cpp:533
LogConfig & installHandler(bool=true)
Definition: report.cpp:537
LogConfig & logFileName(std::string &&)