diff --git a/report/CMakeLists.txt b/report/CMakeLists.txt index abd7a7e..e386ad2 100644 --- a/report/CMakeLists.txt +++ b/report/CMakeLists.txt @@ -8,15 +8,13 @@ set(GITHUB "https://github.com/" CACHE STRING "github base url") include(FetchContent) include(CTest) -FetchContent_Declare( - cpm-cmake - GIT_REPOSITORY ${GITHUB}cpm-cmake/CPM.cmake.git - GIT_SHALLOW True - GIT_TAG v0.31.1 +# download CPM.cmake +file( + DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v0.40.2/CPM.cmake + ${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake ) - -FetchContent_MakeAvailable(cpm-cmake) -include(${cpm-cmake_SOURCE_DIR}/cmake/CPM.cmake) +include(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake) cpmaddpackage("${GITHUB}TheLartians/PackageProject.cmake.git@1.4.1") @@ -27,6 +25,15 @@ cpmaddpackage( GIT_TAG main ) +# upstream set INSTALL_NAME_DIR wrong ! +if (APPLE) + set_target_properties( + systemc + PROPERTIES + INSTALL_NAME_DIR "@rpath" + ) +endif() + cpmaddpackage( NAME SystemCCCI GIT_REPOSITORY ${GITHUB}accellera-official/cci.git @@ -34,6 +41,16 @@ cpmaddpackage( GIT_TAG main ) +# upstream set INSTALL_NAME_DIR wrong ! +if (APPLE) + set_target_properties( + cci + PROPERTIES + INSTALL_NAME_DIR "@rpath" + ) +endif() + + set(WITH_FMT "true" CACHE STRING "Include FMT library") if(WITH_FMT) @@ -41,7 +58,7 @@ if(WITH_FMT) NAME fmt GIT_REPOSITORY ${GITHUB}fmtlib/fmt.git GIT_SHALLOW True - GIT_TAG 9.1.0 + GIT_TAG 11.0.2 OPTIONS FMT_INSTALL "" ON ) endif() @@ -49,17 +66,17 @@ endif() FetchContent_Declare( spdlog_git GIT_REPOSITORY "https://github.com/gabime/spdlog.git" - GIT_TAG "v1.9.2" + GIT_TAG "v1.15.0" GIT_SHALLOW ON ) -FetchContent_Populate(spdlog_git) +FetchContent_MakeAvailable(spdlog_git) FetchContent_GetProperties( spdlog_git SOURCE_DIR spdlog_git_SRC_DIR POPULATED spdlog_git_FOUND ) -add_library(${PROJECT_NAME} src/report.cpp src/logger.cpp) +add_library(${PROJECT_NAME} src/sc_report.cpp src/logger.cpp) target_include_directories( ${PROJECT_NAME} PUBLIC $ @@ -73,7 +90,6 @@ endif() target_include_directories(${PROJECT_NAME} PRIVATE ${spdlog_git_SRC_DIR}/include) if(TARGET SystemC::cci) - target_compile_definitions(${PROJECT_NAME} PRIVATE HAS_CCI) target_link_libraries(${PROJECT_NAME} PUBLIC SystemC::cci) endif() diff --git a/report/include/scp/logger.h b/report/include/scp/logger.h index bfcb4ac..2a8c63f 100644 --- a/report/include/scp/logger.h +++ b/report/include/scp/logger.h @@ -17,7 +17,7 @@ #ifndef _SCP_LOGGER_H_ #define _SCP_LOGGER_H_ -#include "report.h" +#include "sc_report.h" /** \ingroup scp-report * @{ diff --git a/report/include/scp/report.h b/report/include/scp/report.h index 56f3303..3200349 100644 --- a/report/include/scp/report.h +++ b/report/include/scp/report.h @@ -1,435 +1,13 @@ -/******************************************************************************* - * Copyright 2016-2022 MINRES Technologies GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *******************************************************************************/ +/* this is merely a convenience to maintain the old API's */ -#ifndef _SCP_REPORT_H_ -#define _SCP_REPORT_H_ +#include "scp/sc_report.h" +#include "scp/helpers.h" +#include "scp/logger.h" -#include -#include -#include -#include -#include - -#ifdef __GNUG__ -#include -#include -#endif - -#include -#include -#include - -#ifdef FMT_SHARED -#include +#ifdef HAS_CCI +#ifndef _GLOBAL_SCP_LOGGER_FROM_CCI_DEFINED +#define _GLOBAL_SCP_LOGGER_FROM_CCI_DEFINED +#include "scp/report_cci_setter.h" +static scp::scp_logger_from_cci _global_scp_logger_from_cci; +#endif // _GLOBAL_SCP_LOGGER_FROM_CCI_DEFINED #endif - -#if defined(_MSC_VER) && defined(ERROR) -#undef ERROR -#endif - -namespace sc_core { -const sc_core::sc_verbosity SC_UNSET = (sc_core::sc_verbosity)INT_MAX; -} - -//! the name of the CCI property to attach to modules to control logging of -//! this module -#define SCP_LOG_LEVEL_PARAM_NAME "log_level" - -// must be global for macro to work. -static const char* _SCP_FMT_EMPTY_STR = ""; - -/** \ingroup scp-report - * @{ - */ -/**@{*/ -//! @brief reporting utilities -namespace scp { -//! \brief array holding string representations of log levels -static std::array buffer = { - { "NONE", "FATAL", "ERROR", "WARNING", "INFO", "DEBUG", "TRACE", - "TRACEALL" } -}; -//! \brief enum defining the log levels -enum class log { - NONE, - FATAL, - ERROR, - WARNING, - INFO, - DEBUG, - TRACE, - TRACEALL, - DBGTRACE = TRACEALL -}; - -/** - * @fn log as_log(int) - * @brief safely convert an integer into a log level - * - * @param logLevel the logging level - * @return the log level - */ -inline log as_log(int logLevel) { - assert(logLevel >= static_cast(log::NONE) && - logLevel <= static_cast(log::TRACEALL)); - std::array m = { { log::NONE, log::FATAL, log::ERROR, - log::WARNING, log::INFO, log::DEBUG, - log::TRACE, log::TRACEALL } }; - return m[logLevel]; -} -/** - * @fn std::istream& operator >>(std::istream&, log&) - * @brief read a log level from input stream e.g. used by boost::lexical_cast - * - * @param is input stream holding the string representation - * @param val the value holding the resulting value - * @return the input stream - */ -inline std::istream& operator>>(std::istream& is, log& val) { - std::string buf; - is >> buf; - for (auto i = 0U; i <= static_cast(log::TRACEALL); ++i) { - if (std::strcmp(buf.c_str(), buffer[i]) == 0) { - val = as_log(i); - return is; - } - } - return is; -} -/** - * @fn std::ostream& operator <<(std::ostream&, const log&) - * @brief output the textual representation of the log level - * - * @param os output stream - * @param val logging level - * @return reference to the stream for chaining - */ -inline std::ostream& operator<<(std::ostream& os, log const& val) { - os << buffer[static_cast(val)]; - return os; -} - -/** - * @brief cached logging information used in the (logger) form. - * - */ -struct scp_logger_cache { - sc_core::sc_verbosity level = sc_core::SC_UNSET; - std::string type; - std::vector features; - - /** - * @brief Initialize the verbosity cache and/or return the cached value. - * - * @return sc_core::sc_verbosity - */ - sc_core::sc_verbosity get_log_verbosity_cached(const char*, const char*); -}; - -inline sc_core::sc_verbosity get_log_verbosity() { - return static_cast( - ::sc_core::sc_report_handler::get_verbosity_level()); -} -/** - * @fn sc_core::sc_verbosity get_log_verbosity(const char*) - * @brief get the scope-based verbosity level - * - * The function returns a scope specific verbosity level if defined (e.g. by - * using a CCI param named "log_level"). Otherwise the global verbosity level - * is being returned - * - * @param t the SystemC hierarchy scope name - * @return the verbosity level - */ -sc_core::sc_verbosity get_log_verbosity(char const* t); -/** - * @fn sc_core::sc_verbosity get_log_verbosity(const char*) - * @brief get the scope-based verbosity level - * - * The function returns a scope specific verbosity level if defined (e.g. by - * using a CCI param named "log_level"). Otherwise the global verbosity level - * is being returned - * - * @param t the SystemC hierarchy scope name - * @return the verbosity level - */ -inline sc_core::sc_verbosity get_log_verbosity(std::string const& t) { - return get_log_verbosity(t.c_str()); -} - -/** - * @brief Return list of logging parameters that have been used - * - */ -std::vector get_logging_parameters(); - -/** - * @struct ScLogger - * @brief the logger class - * - * The ScLogger creates a RTTI based output stream to be used similar to - * std::cout - * - * @tparam SEVERITY - */ -template -struct ScLogger { - /** - * @fn ScLogger(const char*, int, int=sc_core::SC_MEDIUM) - * @brief - * - * @param file where the log entry originates - * @param line number where the log entry originates - * @param verbosity the log level - */ - ScLogger(const char* file, int line, int verbosity = sc_core::SC_MEDIUM): - t(nullptr), file(file), line(line), level(verbosity){}; - - ScLogger() = delete; - - ScLogger(const ScLogger&) = delete; - - ScLogger(ScLogger&&) = delete; - - ScLogger& operator=(const ScLogger&) = delete; - - ScLogger& operator=(ScLogger&&) = delete; - /** - * @fn ~ScLogger() - * @brief the destructor generating the SystemC report - * - */ - virtual ~ScLogger() { - ::sc_core::sc_report_handler::report( - SEVERITY, t ? t : "SystemC", os.str().c_str(), level, file, line); - } - /** - * @fn ScLogger& type() - * @brief reset the category of the log entry - * - * @return reference to self for chaining - */ - inline ScLogger& type() { - this->t = nullptr; - return *this; - } - /** - * @fn ScLogger& type(const char*) - * @brief set the category of the log entry - * - * @param t type of th elog entry - * @return reference to self for chaining - */ - inline ScLogger& type(char const* t) { - this->t = const_cast(t); - return *this; - } - /** - * @fn ScLogger& type(std::string const&) - * @brief set the category of the log entry - * - * @param t type of th elog entry - * @return reference to self for chaining - */ - inline ScLogger& type(std::string const& t) { - this->t = const_cast(t.c_str()); - return *this; - } - /** - * @fn std::ostream& get() - * @brief get the underlying ostringstream - * - * @return the output stream collecting the log message - */ - inline std::ostream& get() { return os; }; - -protected: - std::ostringstream os{}; - char* t{ nullptr }; - const char* file; - const int line; - const int level; -}; - -/** - * logging macros - */ - -/** - * Boilerplate convenience macros - */ -#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__) -#define PRIMITIVE_CAT(a, ...) a##__VA_ARGS__ - -#define IIF(c) PRIMITIVE_CAT(IIF_, c) -#define IIF_0(t, ...) __VA_ARGS__ -#define IIF_1(t, ...) t - -#define CHECK_N(x, n, ...) n -#define CHECK(...) CHECK_N(__VA_ARGS__, 0, ) -#define PROBE(x) x, 1, - -#define EXPAND(...) __VA_ARGS__ - -#define FIRST_ARG(f, ...) f -#define POP_ARG(f, ...) __VA_ARGS__ - -#define STR_HELPER(x) #x -#define STR(x) STR_HELPER(x) - -#define IS_PAREN(x) CHECK(IS_PAREN_PROBE x) -#define IS_PAREN_PROBE(...) PROBE(~) -/********/ - -/* default logger cache name */ -#define SCP_LOGGER_NAME(x) CAT(_m_scp_log_level_cache_, x) - -/* User interface macros */ -#define SCMOD this->sc_core::sc_module::name() -#define SCP_LOGGER(...) \ - scp::scp_logger_cache IIF(IS_PAREN(FIRST_ARG(__VA_ARGS__)))( \ - SCP_LOGGER_NAME(EXPAND(FIRST_ARG FIRST_ARG(__VA_ARGS__))), \ - SCP_LOGGER_NAME()) = { sc_core::SC_UNSET, \ - "", \ - { IIF(IS_PAREN(FIRST_ARG(__VA_ARGS__)))( \ - POP_ARG(__VA_ARGS__), ##__VA_ARGS__) } } - -#define SCP_LOGGER_VECTOR(NAME) \ - std::vector SCP_LOGGER_NAME(NAME) -#define SCP_LOGGER_VECTOR_PUSH_BACK(NAME, ...) \ - SCP_LOGGER_NAME(NAME).push_back( \ - { sc_core::SC_UNSET, "", { __VA_ARGS__ } }); - -class call_sc_name_fn -{ - template - static auto test(T* p) - -> decltype(p->sc_core::sc_module::name(), std::true_type()); - template - static auto test(...) -> decltype(std::false_type()); - - template - static constexpr bool has_method = decltype(test(nullptr))::value; - -public: - // define a function IF the method exists - template - auto operator()(TYPE* p) const - -> std::enable_if_t, const char*> { - return p->sc_core::sc_module::name(); - } - - // define a function IF NOT the method exists - template - auto operator()(TYPE* p) const - -> std::enable_if_t, const char*> { - return nullptr; - } -}; - -// critical thing is that the initial if 'fails' as soon as possible - if it is -// going to pass, we have all the time we want, as we will be logging anyway -// This HAS to be done as a macro, because the first argument may be a string -// or a cache'd level - -/*** Helper macros for SCP_ report macros ****/ -#define SCP_VBSTY_CHECK_CACHED(lvl, features, cached, ...) \ - (cached.level >= lvl) && \ - (cached.get_log_verbosity_cached(scp::call_sc_name_fn()(this), \ - typeid(*this).name()) >= lvl) - -#define SCP_VBSTY_CHECK_UNCACHED(lvl, ...) \ - (::scp::get_log_verbosity(__VA_ARGS__) >= lvl) - -#define SCP_VBSTY_CHECK(lvl, ...) \ - IIF(IS_PAREN(FIRST_ARG(__VA_ARGS__))) \ - (SCP_VBSTY_CHECK_CACHED( \ - lvl, FIRST_ARG(__VA_ARGS__), \ - SCP_LOGGER_NAME(EXPAND(FIRST_ARG FIRST_ARG(__VA_ARGS__)))), \ - SCP_VBSTY_CHECK_UNCACHED(lvl, ##__VA_ARGS__)) - -#define SCP_GET_FEATURES(...) \ - IIF(IS_PAREN(FIRST_ARG(__VA_ARGS__))) \ - (FIRST_ARG EXPAND((POP_ARG( \ - __VA_ARGS__, \ - SCP_LOGGER_NAME(EXPAND(FIRST_ARG FIRST_ARG(__VA_ARGS__))).type))), \ - __VA_ARGS__) - -#ifdef FMT_SHARED -#define _SCP_FMT_EMPTY_STR(...) fmt::format(__VA_ARGS__) -#else -#define _SCP_FMT_EMPTY_STR(...) "Please add FMT library for FMT support." -#endif - -#define SCP_LOG(lvl, ...) \ - ::scp::ScLogger<::sc_core::SC_INFO>(__FILE__, __LINE__, lvl / 10) \ - .type(SCP_GET_FEATURES(__VA_ARGS__)) \ - .get() \ - << _SCP_FMT_EMPTY_STR -/*** End HELPER Macros *******/ - -//! macro for debug trace level output -#define SCP_TRACEALL(...) \ - if (SCP_VBSTY_CHECK(sc_core::SC_DEBUG, ##__VA_ARGS__)) \ - SCP_LOG(sc_core::SC_DEBUG, __VA_ARGS__) -//! macro for trace level output -#define SCP_TRACE(...) \ - if (SCP_VBSTY_CHECK(sc_core::SC_FULL, ##__VA_ARGS__)) \ - SCP_LOG(sc_core::SC_FULL, __VA_ARGS__) -//! macro for debug level output -#define SCP_DEBUG(...) \ - if (SCP_VBSTY_CHECK(sc_core::SC_HIGH, ##__VA_ARGS__)) \ - SCP_LOG(sc_core::SC_HIGH, __VA_ARGS__) -//! macro for info level output -#define SCP_INFO(...) \ - if (SCP_VBSTY_CHECK(sc_core::SC_MEDIUM, ##__VA_ARGS__)) \ - SCP_LOG(sc_core::SC_MEDIUM, __VA_ARGS__) -//! macro for warning level output -#define SCP_WARN(...) \ - if (SCP_VBSTY_CHECK(sc_core::SC_LOW, ##__VA_ARGS__)) \ - ::scp::ScLogger<::sc_core::SC_WARNING>(__FILE__, __LINE__, \ - sc_core::SC_MEDIUM) \ - .type(SCP_GET_FEATURES(__VA_ARGS__)) \ - .get() \ - << _SCP_FMT_EMPTY_STR -//! macro for error level output -#define SCP_ERR(...) \ - ::scp::ScLogger<::sc_core::SC_ERROR>(__FILE__, __LINE__, \ - sc_core::SC_MEDIUM) \ - .type(SCP_GET_FEATURES(__VA_ARGS__)) \ - .get() \ - << _SCP_FMT_EMPTY_STR -//! macro for fatal message output -#define SCP_FATAL(...) \ - ::scp::ScLogger<::sc_core::SC_FATAL>(__FILE__, __LINE__, \ - sc_core::SC_MEDIUM) \ - .type(SCP_GET_FEATURES(__VA_ARGS__)) \ - .get() \ - << _SCP_FMT_EMPTY_STR - -#ifdef NDEBUG -#define SCP_ASSERT(expr) ((void)0) -#else -#define SCP_ASSERT(expr) \ - ((void)((expr) ? 0 \ - : (SC_REPORT_FATAL(::sc_core::SC_ID_ASSERTION_FAILED_, \ - #expr), \ - 0))) -#endif - -} // namespace scp -/** @} */ // end of scp-report -#endif /* _SCP_REPORT_H_ */ diff --git a/report/include/scp/report_cci_setter.h b/report/include/scp/report_cci_setter.h new file mode 100644 index 0000000..7b99d58 --- /dev/null +++ b/report/include/scp/report_cci_setter.h @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +#ifndef _SCP_REPORT_CCI_SETTER_H_ +#define _SCP_REPORT_CCI_SETTER_H_ + +#include +#include +#include +#include +#include + +namespace scp { +static std::set logging_parameters; + +class scp_logger_from_cci : public scp_global_logger_handler +{ + std::vector split(const std::string& s) const { + std::vector result; + std::istringstream iss(s); + std::string item; + while (std::getline(iss, item, '.')) { + result.push_back(item); + } + return result; + } + + std::string join(std::vector vec) const { + if (vec.empty()) + return ""; + return std::accumulate( + vec.begin(), vec.end(), std::string(), + [](const std::string& a, const std::string& b) -> std::string { + return a + (a.length() > 0 ? "." : "") + b; + }); + } + + void insert(std::multimap>& map, + std::string s, bool interesting) const { + int n = std::count(s.begin(), s.end(), '.'); + map.insert(make_pair(n, s)); + + if (interesting) { + logging_parameters.insert(s + "." SCP_LOG_LEVEL_PARAM_NAME); + } + } + sc_core::sc_verbosity cci_lookup(cci::cci_broker_handle broker, + std::string name) const { + auto param_name = (name.empty()) ? SCP_LOG_LEVEL_PARAM_NAME + : name + "." SCP_LOG_LEVEL_PARAM_NAME; + auto h = broker.get_param_handle(param_name); + if (h.is_valid()) { + return verbosity.at(std::min(h.get_cci_value().get_int(), + verbosity.size() - 1)); + } else { + auto val = broker.get_preset_cci_value(param_name); + + if (val.is_int()) { + broker.lock_preset_value(param_name); + return verbosity.at( + std::min(val.get_int(), verbosity.size() - 1)); + } + } + return sc_core::SC_UNSET; + } +#ifdef __GNUG__ + std::string demangle(const char* name) const { + int status = -4; // some arbitrary value to eliminate the compiler + // warning + + // enable c++11 by passing the flag -std=c++11 to g++ + std::unique_ptr res{ + abi::__cxa_demangle(name, NULL, NULL, &status), std::free + }; + + return (status == 0) ? res.get() : name; + } +#else + // does nothing if not GNUG + std::string demangle(const char* name) { return name; } +#endif +public: + sc_core::sc_verbosity operator()(struct scp_logger_cache& logger, + const char* scname, + const char* tname) const { + try { + // we rely on there being a broker, allow this to throw if not + auto broker = sc_core::sc_get_current_object() + ? cci::cci_get_broker() + : cci::cci_get_global_broker(cci::cci_originator( + "scp_reporting_global")); + + std::multimap> allfeatures; + + /* initialize */ + for (auto scn = split(scname); scn.size(); scn.pop_back()) { + for (int first = 0; first < scn.size(); first++) { + auto f = scn.begin() + first; + std::vector p(f, scn.end()); + auto scn_str = ((first > 0) ? "*." : "") + join(p); + + for (auto ft : logger.features) { + for (auto ftn = split(ft); ftn.size(); + ftn.pop_back()) { + insert(allfeatures, scn_str + "." + join(ftn), + first == 0); + } + } + insert(allfeatures, scn_str + "." + demangle(tname), + first == 0); + insert(allfeatures, scn_str, first == 0); + } + } + for (auto ft : logger.features) { + for (auto ftn = split(ft); ftn.size(); ftn.pop_back()) { + insert(allfeatures, join(ftn), true); + insert(allfeatures, "*." + join(ftn), false); + } + } + insert(allfeatures, demangle(tname), true); + insert(allfeatures, "*", false); + insert(allfeatures, "", false); + + for (std::pair f : allfeatures) { + sc_core::sc_verbosity v = cci_lookup(broker, f.second); + if (v != sc_core::SC_UNSET) { + logger.level = v; + return v; + } + } + } catch (const std::exception&) { + // If there is no global broker, revert to initialized verbosity + // level + } + return logger.level = static_cast( + ::sc_core::sc_report_handler::get_verbosity_level()); + } + static std::vector get_logging_parameters() { + return std::vector(logging_parameters.begin(), + logging_parameters.end()); + } +}; + +} // namespace scp + +#endif \ No newline at end of file diff --git a/report/include/scp/sc_report.h b/report/include/scp/sc_report.h new file mode 100644 index 0000000..b83a425 --- /dev/null +++ b/report/include/scp/sc_report.h @@ -0,0 +1,463 @@ +/******************************************************************************* + * Copyright 2016-2022 MINRES Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +/* + * + * THIS FILE IS INTENDED TO BE UP-STREAMED + */ +#ifndef _SCP_REPORT_H_ +#define _SCP_REPORT_H_ + +#include +#include +#include +#include +#include + +#ifdef __GNUG__ +#include +#include +#endif + +#include +#include +#include + +#ifdef FMT_SHARED +#include +#endif + +#if defined(_MSC_VER) && defined(ERROR) +#undef ERROR +#endif +static const std::array severity = { + sc_core::SC_FATAL, // scp::log::NONE + sc_core::SC_FATAL, // scp::log::FATAL + sc_core::SC_ERROR, // scp::log::ERROR + sc_core::SC_WARNING, // scp::log::WARNING + sc_core::SC_INFO, // scp::log::INFO + sc_core::SC_INFO, // scp::log::DEBUG + sc_core::SC_INFO, // scp::log::TRACE + sc_core::SC_INFO // scp::log::TRACEALL +}; +static const std::array verbosity = { + sc_core::SC_NONE, // scp::log::NONE + sc_core::SC_LOW, // scp::log::FATAL + sc_core::SC_LOW, // scp::log::ERROR + sc_core::SC_LOW, // scp::log::WARNING + sc_core::SC_MEDIUM, // scp::log::INFO + sc_core::SC_HIGH, // scp::log::DEBUG + sc_core::SC_FULL, // scp::log::TRACE + sc_core::SC_DEBUG // scp::log::TRACEALL +}; +namespace sc_core { +const sc_core::sc_verbosity SC_UNSET = (sc_core::sc_verbosity)INT_MAX; +} + +//! the name of the CCI property to attach to modules to control logging of +//! this module +#define SCP_LOG_LEVEL_PARAM_NAME "log_level" + +// must be global for macro to work. +static const char* _SCP_FMT_EMPTY_STR = ""; + +/** \ingroup scp-report + * @{ + */ +/**@{*/ +//! @brief reporting utilities +namespace scp { +//! \brief array holding string representations of log levels +static std::array buffer = { + { "NONE", "FATAL", "ERROR", "WARNING", "INFO", "DEBUG", "TRACE", + "TRACEALL" } +}; +//! \brief enum defining the log levels +enum class log { + NONE, + FATAL, + ERROR, + WARNING, + INFO, + DEBUG, + TRACE, + TRACEALL, + DBGTRACE = TRACEALL +}; + +/** + * @fn log as_log(int) + * @brief safely convert an integer into a log level + * + * @param logLevel the logging level + * @return the log level + */ +inline log as_log(int logLevel) { + assert(logLevel >= static_cast(log::NONE) && + logLevel <= static_cast(log::TRACEALL)); + std::array m = { { log::NONE, log::FATAL, log::ERROR, + log::WARNING, log::INFO, log::DEBUG, + log::TRACE, log::TRACEALL } }; + return m[logLevel]; +} +/** + * @fn std::istream& operator >>(std::istream&, log&) + * @brief read a log level from input stream e.g. used by boost::lexical_cast + * + * @param is input stream holding the string representation + * @param val the value holding the resulting value + * @return the input stream + */ +inline std::istream& operator>>(std::istream& is, log& val) { + std::string buf; + is >> buf; + for (auto i = 0U; i <= static_cast(log::TRACEALL); ++i) { + if (std::strcmp(buf.c_str(), buffer[i]) == 0) { + val = as_log(i); + return is; + } + } + return is; +} +/** + * @fn std::ostream& operator <<(std::ostream&, const log&) + * @brief output the textual representation of the log level + * + * @param os output stream + * @param val logging level + * @return reference to the stream for chaining + */ +inline std::ostream& operator<<(std::ostream& os, log const& val) { + os << buffer[static_cast(val)]; + return os; +} + +/** + * @brief cached logging information used in the (logger) form. + * + */ +struct scp_logger_cache { + sc_core::sc_verbosity level = sc_core::SC_UNSET; + std::string type; + std::vector features; + + /** + * @brief Initialize the verbosity cache and/or return the cached value. + * + * @return sc_core::sc_verbosity + */ + sc_core::sc_verbosity get_log_verbosity_cached(const char*, const char*); +}; + +struct scp_global_logger_handler : sc_core::sc_object { + virtual sc_core::sc_verbosity operator()(struct scp_logger_cache& logger, + const char* scname, + const char* tname) const = 0; +}; + +inline sc_core::sc_verbosity get_log_verbosity() { + return static_cast( + ::sc_core::sc_report_handler::get_verbosity_level()); +} +/** + * @fn sc_core::sc_verbosity get_log_verbosity(const char*) + * @brief get the scope-based verbosity level + * + * The function returns a scope specific verbosity level if defined (e.g. by + * using a CCI param named "log_level"). Otherwise the global verbosity level + * is being returned + * + * @param t the SystemC hierarchy scope name + * @return the verbosity level + */ +sc_core::sc_verbosity get_log_verbosity(char const* t); +/** + * @fn sc_core::sc_verbosity get_log_verbosity(const char*) + * @brief get the scope-based verbosity level + * + * The function returns a scope specific verbosity level if defined (e.g. by + * using a CCI param named "log_level"). Otherwise the global verbosity level + * is being returned + * + * @param t the SystemC hierarchy scope name + * @return the verbosity level + */ +inline sc_core::sc_verbosity get_log_verbosity(std::string const& t) { + return get_log_verbosity(t.c_str()); +} + +/** + * @brief Return list of logging parameters that have been used + * + */ +std::vector get_logging_parameters(); + +/** + * @struct ScLogger + * @brief the logger class + * + * The ScLogger creates a RTTI based output stream to be used similar to + * std::cout + * + * @tparam SEVERITY + */ +template +struct ScLogger { + /** + * @fn ScLogger(const char*, int, int=sc_core::SC_MEDIUM) + * @brief + * + * @param file where the log entry originates + * @param line number where the log entry originates + * @param verbosity the log level + */ + ScLogger(const char* file, int line, int verbosity = sc_core::SC_MEDIUM): + t(nullptr), file(file), line(line), level(verbosity) {}; + + ScLogger() = delete; + + ScLogger(const ScLogger&) = delete; + + ScLogger(ScLogger&&) = delete; + + ScLogger& operator=(const ScLogger&) = delete; + + ScLogger& operator=(ScLogger&&) = delete; + /** + * @fn ~ScLogger() + * @brief the destructor generating the SystemC report + * + */ + virtual ~ScLogger() { + ::sc_core::sc_report_handler::report( + SEVERITY, t ? t : "SystemC", os.str().c_str(), level, file, line); + } + /** + * @fn ScLogger& type() + * @brief reset the category of the log entry + * + * @return reference to self for chaining + */ + inline ScLogger& type() { + this->t = nullptr; + return *this; + } + /** + * @fn ScLogger& type(const char*) + * @brief set the category of the log entry + * + * @param t type of th elog entry + * @return reference to self for chaining + */ + inline ScLogger& type(char const* t) { + this->t = const_cast(t); + return *this; + } + /** + * @fn ScLogger& type(std::string const&) + * @brief set the category of the log entry + * + * @param t type of th elog entry + * @return reference to self for chaining + */ + inline ScLogger& type(std::string const& t) { + this->t = const_cast(t.c_str()); + return *this; + } + /** + * @fn std::ostream& get() + * @brief get the underlying ostringstream + * + * @return the output stream collecting the log message + */ + inline std::ostream& get() { return os; }; + +protected: + std::ostringstream os{}; + char* t{ nullptr }; + const char* file; + const int line; + const int level; +}; + +/** + * logging macros + */ + +/** + * Boilerplate convenience macros + */ +#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__) +#define PRIMITIVE_CAT(a, ...) a##__VA_ARGS__ + +#define IIF(c) PRIMITIVE_CAT(IIF_, c) +#define IIF_0(t, ...) __VA_ARGS__ +#define IIF_1(t, ...) t + +#define CHECK_N(x, n, ...) n +#define CHECK(...) CHECK_N(__VA_ARGS__, 0, ) +#define PROBE(x) x, 1, + +#define EXPAND(...) __VA_ARGS__ + +#define FIRST_ARG(f, ...) f +#define POP_ARG(f, ...) __VA_ARGS__ + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +#define IS_PAREN(x) CHECK(IS_PAREN_PROBE x) +#define IS_PAREN_PROBE(...) PROBE(~) +/********/ + +/* default logger cache name */ +#define SCP_LOGGER_NAME(x) CAT(_m_scp_log_level_cache_, x) + +/* User interface macros */ +#define SCMOD this->sc_core::sc_module::name() +#define SCP_LOGGER(...) \ + scp::scp_logger_cache IIF(IS_PAREN(FIRST_ARG(__VA_ARGS__)))( \ + SCP_LOGGER_NAME(EXPAND(FIRST_ARG FIRST_ARG(__VA_ARGS__))), \ + SCP_LOGGER_NAME()) = { sc_core::SC_UNSET, \ + "", \ + { IIF(IS_PAREN(FIRST_ARG(__VA_ARGS__)))( \ + POP_ARG(__VA_ARGS__), ##__VA_ARGS__) } } + +#define SCP_LOGGER_VECTOR(NAME) \ + std::vector SCP_LOGGER_NAME(NAME) +#define SCP_LOGGER_VECTOR_PUSH_BACK(NAME, ...) \ + SCP_LOGGER_NAME(NAME).push_back( \ + { sc_core::SC_UNSET, "", { __VA_ARGS__ } }); + +class call_sc_name_fn +{ + template + static auto test(T* p) + -> decltype(p->sc_core::sc_module::name(), std::true_type()); + template + static auto test(...) -> decltype(std::false_type()); + + template + static constexpr bool has_method = decltype(test(nullptr))::value; + +public: + // define a function IF the method exists + template + auto operator()(TYPE* p) const + -> std::enable_if_t, const char*> { + return p->sc_core::sc_module::name(); + } + + // define a function IF NOT the method exists + template + auto operator()(TYPE* p) const + -> std::enable_if_t, const char*> { + return nullptr; + } +}; + +// critical thing is that the initial if 'fails' as soon as possible - if it is +// going to pass, we have all the time we want, as we will be logging anyway +// This HAS to be done as a macro, because the first argument may be a string +// or a cache'd level + +/*** Helper macros for SCP_ report macros ****/ +#define SCP_VBSTY_CHECK_CACHED(lvl, features, cached, ...) \ + (cached.level >= lvl) && \ + (cached.get_log_verbosity_cached(scp::call_sc_name_fn()(this), \ + typeid(*this).name()) >= lvl) + +#define SCP_VBSTY_CHECK_UNCACHED(lvl, ...) \ + (::scp::get_log_verbosity(__VA_ARGS__) >= lvl) + +#define SCP_VBSTY_CHECK(lvl, ...) \ + IIF(IS_PAREN(FIRST_ARG(__VA_ARGS__))) \ + (SCP_VBSTY_CHECK_CACHED( \ + lvl, FIRST_ARG(__VA_ARGS__), \ + SCP_LOGGER_NAME(EXPAND(FIRST_ARG FIRST_ARG(__VA_ARGS__)))), \ + SCP_VBSTY_CHECK_UNCACHED(lvl, ##__VA_ARGS__)) + +#define SCP_GET_FEATURES(...) \ + IIF(IS_PAREN(FIRST_ARG(__VA_ARGS__))) \ + (FIRST_ARG EXPAND((POP_ARG( \ + __VA_ARGS__, \ + SCP_LOGGER_NAME(EXPAND(FIRST_ARG FIRST_ARG(__VA_ARGS__))).type))), \ + __VA_ARGS__) + +#ifdef FMT_SHARED +#define _SCP_FMT_EMPTY_STR(...) fmt::format(__VA_ARGS__) +#else +#define _SCP_FMT_EMPTY_STR(...) "Please add FMT library for FMT support." +#endif + +#define SCP_LOG(lvl, ...) \ + ::scp::ScLogger<::sc_core::SC_INFO>(__FILE__, __LINE__, lvl / 10) \ + .type(SCP_GET_FEATURES(__VA_ARGS__)) \ + .get() \ + << _SCP_FMT_EMPTY_STR +/*** End HELPER Macros *******/ + +//! macro for debug trace level output +#define SCP_TRACEALL(...) \ + if (SCP_VBSTY_CHECK(sc_core::SC_DEBUG, ##__VA_ARGS__)) \ + SCP_LOG(sc_core::SC_DEBUG, __VA_ARGS__) +//! macro for trace level output +#define SCP_TRACE(...) \ + if (SCP_VBSTY_CHECK(sc_core::SC_FULL, ##__VA_ARGS__)) \ + SCP_LOG(sc_core::SC_FULL, __VA_ARGS__) +//! macro for debug level output +#define SCP_DEBUG(...) \ + if (SCP_VBSTY_CHECK(sc_core::SC_HIGH, ##__VA_ARGS__)) \ + SCP_LOG(sc_core::SC_HIGH, __VA_ARGS__) +//! macro for info level output +#define SCP_INFO(...) \ + if (SCP_VBSTY_CHECK(sc_core::SC_MEDIUM, ##__VA_ARGS__)) \ + SCP_LOG(sc_core::SC_MEDIUM, __VA_ARGS__) +//! macro for warning level output +#define SCP_WARN(...) \ + if (SCP_VBSTY_CHECK(sc_core::SC_LOW, ##__VA_ARGS__)) \ + ::scp::ScLogger<::sc_core::SC_WARNING>(__FILE__, __LINE__, \ + sc_core::SC_MEDIUM) \ + .type(SCP_GET_FEATURES(__VA_ARGS__)) \ + .get() \ + << _SCP_FMT_EMPTY_STR +//! macro for error level output +#define SCP_ERR(...) \ + ::scp::ScLogger<::sc_core::SC_ERROR>(__FILE__, __LINE__, \ + sc_core::SC_MEDIUM) \ + .type(SCP_GET_FEATURES(__VA_ARGS__)) \ + .get() \ + << _SCP_FMT_EMPTY_STR +//! macro for fatal message output +#define SCP_FATAL(...) \ + ::scp::ScLogger<::sc_core::SC_FATAL>(__FILE__, __LINE__, \ + sc_core::SC_MEDIUM) \ + .type(SCP_GET_FEATURES(__VA_ARGS__)) \ + .get() \ + << _SCP_FMT_EMPTY_STR + +#ifdef NDEBUG +#define SCP_ASSERT(expr) ((void)0) +#else +#define SCP_ASSERT(expr) \ + ((void)((expr) ? 0 \ + : (SC_REPORT_FATAL(::sc_core::SC_ID_ASSERTION_FAILED_, \ + #expr), \ + 0))) +#endif + +} // namespace scp +/** @} */ // end of scp-report +#endif /* _SCP_REPORT_H_ */ diff --git a/report/src/logger.cpp b/report/src/logger.cpp index e2cd965..8a1f793 100644 --- a/report/src/logger.cpp +++ b/report/src/logger.cpp @@ -26,9 +26,6 @@ #include #include #include -#ifdef HAS_CCI -#include -#endif #include #include #include @@ -336,26 +333,6 @@ void report_handler(const sc_core::sc_report& rep, // } } // namespace -static const std::array severity = { - sc_core::SC_FATAL, // scp::log::NONE - sc_core::SC_FATAL, // scp::log::FATAL - sc_core::SC_ERROR, // scp::log::ERROR - sc_core::SC_WARNING, // scp::log::WARNING - sc_core::SC_INFO, // scp::log::INFO - sc_core::SC_INFO, // scp::log::DEBUG - sc_core::SC_INFO, // scp::log::TRACE - sc_core::SC_INFO // scp::log::TRACEALL -}; -static const std::array verbosity = { - sc_core::SC_NONE, // scp::log::NONE - sc_core::SC_LOW, // scp::log::FATAL - sc_core::SC_LOW, // scp::log::ERROR - sc_core::SC_LOW, // scp::log::WARNING - sc_core::SC_MEDIUM, // scp::log::INFO - sc_core::SC_HIGH, // scp::log::DEBUG - sc_core::SC_FULL, // scp::log::TRACE - sc_core::SC_DEBUG // scp::log::TRACEALL -}; static std::mutex cfg_guard; static void configure_logging() { std::lock_guard lock(cfg_guard); @@ -528,4 +505,4 @@ auto scp::LogConfig::reportOnlyFirstError(bool v) -> scp::LogConfig& { auto scp::LogConfig::fileInfoFrom(int v) -> scp::LogConfig& { this->file_info_from = v; return *this; -} +} \ No newline at end of file diff --git a/report/src/report.cpp b/report/src/report.cpp deleted file mode 100644 index 081f0fc..0000000 --- a/report/src/report.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/******************************************************************************* - * Copyright 2017-2022 MINRES Technologies GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *******************************************************************************/ -/* - * report.cpp - * - * Created on: 19.09.2017 - * Author: eyck@minres.com - */ - -#include -#include -#include -#include -#ifdef HAS_CCI -#include -#endif -#include -#include -#include -#include -#include -#if defined(__GNUC__) || defined(__clang__) -#define likely(x) __builtin_expect(x, 1) -#define unlikely(x) __builtin_expect(x, 0) -#else -#define likely(x) x -#define unlikely(x) x -#endif - -#ifdef ERROR -#undef ERROR -#endif - -namespace { -// Making this thread_local could cause thread copies of the same cache -// entries, but more likely naming will be thread local too, and this avoids -// races in the unordered_map - -#ifdef DISABLE_REPORT_THREAD_LOCAL -std::unordered_map lut; -#else -thread_local std::unordered_map lut; -#endif - -#ifdef HAS_CCI -cci::cci_originator scp_global_originator("scp_reporting_global"); -#endif - -std::set logging_parameters; - -// BKDR hash algorithm -auto char_hash(char const* str) -> uint64_t { - constexpr unsigned int seed = 131; // 31 131 1313 13131131313 etc// - uint64_t hash = 0; - while (*str) { - hash = (hash * seed) + (*str); - str++; - } - return hash; -} -} // namespace - -static const std::array severity = { - sc_core::SC_FATAL, // scp::log::NONE - sc_core::SC_FATAL, // scp::log::FATAL - sc_core::SC_ERROR, // scp::log::ERROR - sc_core::SC_WARNING, // scp::log::WARNING - sc_core::SC_INFO, // scp::log::INFO - sc_core::SC_INFO, // scp::log::DEBUG - sc_core::SC_INFO, // scp::log::TRACE - sc_core::SC_INFO // scp::log::TRACEALL -}; -static const std::array verbosity = { - sc_core::SC_NONE, // scp::log::NONE - sc_core::SC_LOW, // scp::log::FATAL - sc_core::SC_LOW, // scp::log::ERROR - sc_core::SC_LOW, // scp::log::WARNING - sc_core::SC_MEDIUM, // scp::log::INFO - sc_core::SC_HIGH, // scp::log::DEBUG - sc_core::SC_FULL, // scp::log::TRACE - sc_core::SC_DEBUG // scp::log::TRACEALL -}; - -std::vector split(const std::string& s) { - std::vector result; - std::istringstream iss(s); - std::string item; - while (std::getline(iss, item, '.')) { - result.push_back(item); - } - return result; -} - -std::string join(std::vector vec) { - if (vec.empty()) - return ""; - return std::accumulate( - vec.begin(), vec.end(), std::string(), - [](const std::string& a, const std::string& b) -> std::string { - return a + (a.length() > 0 ? "." : "") + b; - }); -} - -std::vector scp::get_logging_parameters() { - return std::vector(logging_parameters.begin(), - logging_parameters.end()); -} - -sc_core::sc_verbosity cci_lookup(cci::cci_broker_handle broker, - std::string name) { - auto param_name = (name.empty()) ? SCP_LOG_LEVEL_PARAM_NAME - : name + "." SCP_LOG_LEVEL_PARAM_NAME; - auto h = broker.get_param_handle(param_name); - if (h.is_valid()) { - return verbosity.at(std::min(h.get_cci_value().get_int(), - verbosity.size() - 1)); - } else { - auto val = broker.get_preset_cci_value(param_name); - - if (val.is_int()) { - broker.lock_preset_value(param_name); - return verbosity.at( - std::min(val.get_int(), verbosity.size() - 1)); - } - } - return sc_core::SC_UNSET; -} - -#ifdef __GNUG__ -std::string demangle(const char* name) { - int status = -4; // some arbitrary value to eliminate the compiler - // warning - - // enable c++11 by passing the flag -std=c++11 to g++ - std::unique_ptr res{ - abi::__cxa_demangle(name, NULL, NULL, &status), std::free - }; - - return (status == 0) ? res.get() : name; -} -#else -// does nothing if not GNUG -std::string demangle(const char* name) { - return name; -} -#endif - -void insert(std::multimap>& map, - std::string s, bool interesting) { - int n = std::count(s.begin(), s.end(), '.'); - map.insert(make_pair(n, s)); - - if (interesting) { - logging_parameters.insert(s + "." SCP_LOG_LEVEL_PARAM_NAME); - } -} - -sc_core::sc_verbosity scp::scp_logger_cache::get_log_verbosity_cached( - const char* scname, const char* tname = "") { - if (level != sc_core::SC_UNSET) { - return level; - } - - if (!scname && features.size()) - scname = features[0].c_str(); - if (!scname) - scname = ""; - - type = std::string(scname); - -#ifdef HAS_CCI - try { - // we rely on there being a broker, allow this to throw if not - auto broker = sc_core::sc_get_current_object() - ? cci::cci_get_broker() - : cci::cci_get_global_broker(scp_global_originator); - - std::multimap> allfeatures; - - /* initialize */ - for (auto scn = split(scname); scn.size(); scn.pop_back()) { - for (int first = 0; first < scn.size(); first++) { - auto f = scn.begin() + first; - std::vector p(f, scn.end()); - auto scn_str = ((first > 0) ? "*." : "") + join(p); - - for (auto ft : features) { - for (auto ftn = split(ft); ftn.size(); ftn.pop_back()) { - insert(allfeatures, scn_str + "." + join(ftn), - first == 0); - } - } - insert(allfeatures, scn_str + "." + demangle(tname), - first == 0); - insert(allfeatures, scn_str, first == 0); - } - } - for (auto ft : features) { - for (auto ftn = split(ft); ftn.size(); ftn.pop_back()) { - insert(allfeatures, join(ftn), true); - insert(allfeatures, "*." + join(ftn), false); - } - } - insert(allfeatures, demangle(tname), true); - insert(allfeatures, "*", false); - insert(allfeatures, "", false); - - for (std::pair f : allfeatures) { - sc_core::sc_verbosity v = cci_lookup(broker, f.second); - if (v != sc_core::SC_UNSET) { - level = v; - return v; - } - } - } catch (const std::exception&) { - // If there is no global broker, revert to initialized verbosity level - } - -#endif - - return level = static_cast( - ::sc_core::sc_report_handler::get_verbosity_level()); -} - -auto scp::get_log_verbosity(char const* str) -> sc_core::sc_verbosity { - auto k = char_hash(str); - auto it = lut.find(k); - if (it != lut.end()) - return it->second; - - scp::scp_logger_cache tmp; - lut[k] = tmp.get_log_verbosity_cached(str); - return lut[k]; -} diff --git a/report/src/sc_report.cpp b/report/src/sc_report.cpp new file mode 100644 index 0000000..f1fcc51 --- /dev/null +++ b/report/src/sc_report.cpp @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright 2017-2022 MINRES Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +/* + * sc_report.cpp + * + * Created on: 19.09.2017 + * Author: eyck@minres.com + * + * THIS FILE IS INTENDED TO BE UP-STREAMED + */ + +#include +#include +#include + +#if defined(__GNUC__) || defined(__clang__) +#define likely(x) __builtin_expect(x, 1) +#define unlikely(x) __builtin_expect(x, 0) +#else +#define likely(x) x +#define unlikely(x) x +#endif + +#ifdef ERROR +#undef ERROR +#endif + +namespace { +// Making this thread_local could cause thread copies of the same cache +// entries, but more likely naming will be thread local too, and this avoids +// races in the unordered_map + +#ifdef DISABLE_REPORT_THREAD_LOCAL +std::unordered_map lut; +#else +thread_local std::unordered_map lut; +#endif + +// BKDR hash algorithm +auto char_hash(char const* str) -> uint64_t { + constexpr unsigned int seed = 131; // 31 131 1313 13131131313 etc// + uint64_t hash = 0; + while (*str) { + hash = (hash * seed) + (*str); + str++; + } + return hash; +} +} // namespace + +sc_core::sc_verbosity scp::scp_logger_cache::get_log_verbosity_cached( + const char* scname, const char* tname = "") { + if (level != sc_core::SC_UNSET) { + return level; + } + + if (!scname && features.size()) + scname = features[0].c_str(); + if (!scname) + scname = ""; + + type = std::string(scname); + + // find the first suitable registered sc_object that can handle the lookup + for (auto o : sc_core::sc_get_top_level_objects()) { + auto* h = dynamic_cast(o); + if (h) { + auto& h_ref = *h; + return h_ref(*this, scname, tname); + } + } + + return level = static_cast( + ::sc_core::sc_report_handler::get_verbosity_level()); +} + +auto scp::get_log_verbosity(char const* str) -> sc_core::sc_verbosity { + auto k = char_hash(str); + auto it = lut.find(k); + if (it != lut.end()) + return it->second; + + scp::scp_logger_cache tmp; + lut[k] = tmp.get_log_verbosity_cached(str); + return lut[k]; +}