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