24 #include "configurer.h"
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>
35 #include <unordered_map>
37 #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
38 #if GCC_VERSION < 40900
41 #define likely(x) __builtin_expect(x, 1)
42 #define unlikely(x) __builtin_expect(x, 0)
57 using namespace sc_core;
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; }
67 uint64_t operator()(
char const* str)
const {
68 constexpr
unsigned int seed = 131;
71 hash = (hash * seed) + (*str);
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) {
82 table.insert({cache.back().c_str(), verb});
90 static const cci::cci_originator originator;
92 static const cci::cci_originator originator(
"reporting");
95 bool& inst_based_logging() {
96 thread_local
bool active = getenv(
"SCC_DISABLE_INSTANCE_BASED_LOGGING") ==
nullptr;
101 shared_ptr<spdlog::logger> file_logger;
102 shared_ptr<spdlog::logger> console_logger;
104 regex_t start_state{};
108 sc_time cycle_base{0, SC_NS};
110 scc::LogConfig::operator=(o);
113 auto match(
const char* type) ->
bool {
115 return regexec(&start_state, type, 0,
nullptr, 0) == 0;
117 return regex_search(type, reg_ex);
120 bool initialized{
false};
123 thread_local ExtLogConfig log_cfg;
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);
129 while((tr % 10) == 0) {
136 while(tu < SC_SEC && (val % 10) == 0) {
139 tu += (0 == (scale % 3));
141 for(scale %= 3; scale != 0; scale--)
143 return make_tuple(val,
static_cast<sc_time_unit
>(tu));
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};
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];
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())) {
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() <<
")]";
179 os <<
"[" << std::setw(7) << std::setfill(
' ') << sc_time_stamp().value() / log_cfg.cycle_base.value() <<
"]";
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() <<
")]";
185 os <<
"[" << std::setw(20) << std::setfill(
' ') << t <<
"]";
188 if(unlikely(rep.get_id() >= 0))
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() <<
": ";
195 os <<
util::padded(rep.get_msg_type(), cfg.msg_type_field_width) <<
": ";
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();
206 os <<
"\n [PROCESS:" << proc_name <<
"]";
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();
218 inline void log2logger(spdlog::logger& logger,
const sc_report& rep,
const scc::LogConfig& cfg) {
219 auto msg = compose_message(rep, cfg);
222 switch(rep.get_severity()) {
224 switch(get_verbosity(rep)) {
244 logger.critical(msg);
251 inline void log2logger(spdlog::logger& logger,
scc::log lvl,
const string& msg) {
253 case scc::log::DBGTRACE:
254 case scc::log::TRACE:
257 case scc::log::DEBUG:
263 case scc::log::WARNING:
266 case scc::log::ERROR:
269 case scc::log::FATAL:
270 logger.critical(msg);
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)
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) {
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);
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) {
296 sc_stop_called =
true;
299 if(actions & SC_ABORT) {
300 this_thread::sleep_for(chrono::milliseconds(
static_cast<unsigned>(log_cfg.level) * 20));
303 if(actions & SC_THROW) {
304 this_thread::sleep_for(chrono::milliseconds(
static_cast<unsigned>(log_cfg.level) * 20));
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));
319 old_buf = os.rdbuf(
this);
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') {
339 static const array<sc_severity, 8> severity = {SC_FATAL,
347 static const array<sc_verbosity, 8> verbosity = {SC_NONE,
356 auto scc::stream_redirection::sync() ->
int {
357 if(level <= log_cfg.level) {
358 auto timestr = time2string(sc_time_stamp());
359 istringstream buf(str());
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);
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);
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());
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()) {
398 ofs.open(log_cfg.log_file_name, ios::out | ios::trunc);
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");
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);
410 spdlog_initialized =
true;
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");
416 if(log_cfg.log_filter_regex.size()) {
418 regcomp(&log_cfg.start_state, log_cfg.log_filter_regex.c_str(), REG_EXTENDED);
420 log_cfg.reg_ex = regex(log_cfg.log_filter_regex, regex::extended | regex::icase);
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;
431 if(!log_cfg.instance_based_log_levels || getenv(
"SCC_DISABLE_INSTANCE_BASED_LOGGING"))
432 inst_based_logging() =
false;
433 log_cfg.initialized =
true;
439 log_cfg.msg_type_field_width = type_field_width;
440 log_cfg.print_sys_time = print_time;
441 log_cfg.level = level;
443 log_cfg.initialized =
true;
447 log_cfg = log_config;
449 log_cfg.initialized =
true;
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;
470 this->msg_type_field_width = width;
475 this->print_sys_time = enable;
480 this->print_sim_time = enable;
485 this->print_delta = enable;
490 this->print_severity = enable;
495 this->log_file_name = name;
500 this->log_file_name = name;
505 this->colored_output = enable;
510 this->log_filter_regex = expr;
515 this->log_filter_regex = expr;
525 this->dont_create_broker = v;
530 this->report_only_first_error = v;
534 this->instance_based_log_levels = v;
538 this->install_handler = v;
543 if(inst_based_logging()) {
544 auto it = lut.table.find(str);
545 if(it != lut.table.end())
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);
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);
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);
558 auto val = broker.get_preset_cci_value(param_name);
560 sc_core::sc_verbosity ret = verbosity.at(std::min<unsigned>(val.get_int(), verbosity.size() - 1));
561 lut.insert(str, ret);
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);
570 auto pos = current_name.rfind(
".");
571 if(pos == std::string::npos) {
574 current_name = current_name.substr(0, pos);
581 return static_cast<sc_core::sc_verbosity
>(::sc_core::sc_report_handler::get_verbosity_level());
~stream_redirection()
destructor restoring the output stream buffer
void reset()
reset the stream redirection and restore output buffer of the stream
stream_redirection(std::ostream &os, log level)
constructor redirecting the given stream to a SystemC log message of given llog level
bool is_logging_initialized()
get the state of the SCC logging system
void set_logging_level(log level)
sets the SystemC logging level
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
log get_logging_level()
get the SystemC logging level
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
log
enum defining the log levels
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
the configuration class for the logging setup
LogConfig & printSeverity(bool=true)
LogConfig & printSysTime(bool=true)
LogConfig & logFilterRegex(std::string &&)
LogConfig & reportOnlyFirstError(bool=true)
LogConfig & coloredOutput(bool=true)
LogConfig & dontCreateBroker(bool=true)
LogConfig & logLevel(log)
LogConfig & msgTypeFieldWidth(unsigned)
LogConfig & printSimTime(bool=true)
LogConfig & logAsync(bool=true)
LogConfig & printDelta(bool=true)
LogConfig & instanceBasedLogLevels(bool=true)
LogConfig & installHandler(bool=true)
LogConfig & logFileName(std::string &&)