xaiger: update help text
[yosys.git] / kernel / log.cc
index cd16bb3449a73a7755f9338a3a7a62ea008c28c8..a21ba480a6d6313fe6c0d1c743aba664d9d09770 100644 (file)
@@ -25,7 +25,7 @@
 #  include <sys/time.h>
 #endif
 
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
 #  include <dlfcn.h>
 #endif
 
@@ -41,7 +41,12 @@ YOSYS_NAMESPACE_BEGIN
 std::vector<FILE*> log_files;
 std::vector<std::ostream*> log_streams;
 std::map<std::string, std::set<std::string>> log_hdump;
-std::vector<std::regex> log_warn_regexes;
+std::vector<YS_REGEX_TYPE> log_warn_regexes, log_nowarn_regexes, log_werror_regexes;
+std::vector<std::pair<YS_REGEX_TYPE,LogExpectedItem>> log_expect_log, log_expect_warning, log_expect_error;
+std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored;
+int log_warnings_count = 0;
+int log_warnings_count_noexpect = 0;
+bool log_expect_no_warnings = false;
 bool log_hdump_all = false;
 FILE *log_errfile = NULL;
 SHA1 *log_hasher = NULL;
@@ -52,15 +57,29 @@ bool log_cmd_error_throw = false;
 bool log_quiet_warnings = false;
 int log_verbose_level;
 string log_last_error;
+void (*log_error_atexit)() = NULL;
+
+int log_make_debug = 0;
+int log_force_debug = 0;
+int log_debug_suppressed = 0;
 
 vector<int> header_count;
-pool<RTLIL::IdString> log_id_cache;
+vector<char*> log_id_cache;
 vector<shared_str> string_buf;
 int string_buf_index = -1;
 
 static struct timeval initial_tv = { 0, 0 };
 static bool next_print_log = false;
 static int log_newline_count = 0;
+static bool check_expected_logs = true;
+static bool display_error_log_msg = true;
+
+static void log_id_cache_clear()
+{
+       for (auto p : log_id_cache)
+               free(p);
+       log_id_cache.clear();
+}
 
 #if defined(_WIN32) && !defined(__MINGW32__)
 // this will get time information and return it in timeval, simulating gettimeofday()
@@ -89,6 +108,9 @@ void logv(const char *format, va_list ap)
                format++;
        }
 
+       if (log_make_debug && !ys_debug(1))
+               return;
+
        std::string str = vstringf(format, ap);
 
        if (str.empty())
@@ -145,7 +167,7 @@ void logv(const char *format, va_list ap)
        {
                log_warn_regex_recusion_guard = true;
 
-               if (log_warn_regexes.empty())
+               if (log_warn_regexes.empty() && log_expect_log.empty())
                {
                        linebuffer.clear();
                }
@@ -155,8 +177,13 @@ void logv(const char *format, va_list ap)
 
                        if (!linebuffer.empty() && linebuffer.back() == '\n') {
                                for (auto &re : log_warn_regexes)
-                                       if (std::regex_search(linebuffer, re))
+                                       if (YS_REGEX_NS::regex_search(linebuffer, re))
                                                log_warning("Found log message matching -W regex:\n%s", str.c_str());
+
+                               for (auto &item : log_expect_log)
+                                       if (YS_REGEX_NS::regex_search(linebuffer, item.first))
+                                               item.second.current_count++;
+
                                linebuffer.clear();
                        }
                }
@@ -193,31 +220,115 @@ void logv_header(RTLIL::Design *design, const char *format, va_list ap)
        if (log_hdump.count(header_id) && design != nullptr)
                for (auto &filename : log_hdump.at(header_id)) {
                        log("Dumping current design to '%s'.\n", filename.c_str());
+                       if (yosys_xtrace)
+                               IdString::xtrace_db_dump();
                        Pass::call(design, {"dump", "-o", filename});
+                       if (yosys_xtrace)
+                               log("#X# -- end of dump --\n");
                }
 
        if (pop_errfile)
                log_files.pop_back();
 }
 
+static void logv_warning_with_prefix(const char *prefix,
+                                     const char *format, va_list ap)
+{
+       std::string message = vstringf(format, ap);
+       bool suppressed = false;
+
+       for (auto &re : log_nowarn_regexes)
+               if (YS_REGEX_NS::regex_search(message, re))
+                       suppressed = true;
+
+       if (suppressed)
+       {
+               log("Suppressed %s%s", prefix, message.c_str());
+       }
+       else
+       {
+               int bak_log_make_debug = log_make_debug;
+               log_make_debug = 0;
+
+               for (auto &re : log_werror_regexes)
+                       if (YS_REGEX_NS::regex_search(message, re))
+                               log_error("%s",  message.c_str());
+
+               bool warning_match = false;
+               for (auto &item : log_expect_warning)
+                       if (YS_REGEX_NS::regex_search(message, item.first)) {
+                               item.second.current_count++;
+                               warning_match = true;
+                       }
+
+               if (log_warnings.count(message))
+               {
+                       log("%s%s", prefix, message.c_str());
+                       log_flush();
+               }
+               else
+               {
+                       if (log_errfile != NULL && !log_quiet_warnings)
+                               log_files.push_back(log_errfile);
+
+                       log("%s%s", prefix, message.c_str());
+                       log_flush();
+
+                       if (log_errfile != NULL && !log_quiet_warnings)
+                               log_files.pop_back();
+
+                       log_warnings.insert(message);
+               }
+
+               if (!warning_match)
+                       log_warnings_count_noexpect++;
+               log_warnings_count++;
+               log_make_debug = bak_log_make_debug;
+       }
+}
+
 void logv_warning(const char *format, va_list ap)
 {
-       if (log_errfile != NULL && !log_quiet_warnings)
-               log_files.push_back(log_errfile);
+       logv_warning_with_prefix("Warning: ", format, ap);
+}
 
-       log("Warning: ");
-       logv(format, ap);
-       log_flush();
+void logv_warning_noprefix(const char *format, va_list ap)
+{
+       logv_warning_with_prefix("", format, ap);
+}
 
-       if (log_errfile != NULL && !log_quiet_warnings)
-               log_files.pop_back();
+void log_file_warning(const std::string &filename, int lineno,
+                      const char *format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       std::string prefix = stringf("%s:%d: Warning: ",
+                       filename.c_str(), lineno);
+       logv_warning_with_prefix(prefix.c_str(), format, ap);
+       va_end(ap);
 }
 
-void logv_error(const char *format, va_list ap)
+void log_file_info(const std::string &filename, int lineno,
+                      const char *format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       std::string fmt = stringf("%s:%d: Info: %s",
+                       filename.c_str(), lineno, format);
+       logv(fmt.c_str(), ap);
+       va_end(ap);
+}
+
+YS_ATTRIBUTE(noreturn)
+static void logv_error_with_prefix(const char *prefix,
+                                   const char *format, va_list ap)
 {
 #ifdef EMSCRIPTEN
        auto backup_log_files = log_files;
 #endif
+       int bak_log_make_debug = log_make_debug;
+       log_make_debug = 0;
+       log_suppressed();
 
        if (log_errfile != NULL)
                log_files.push_back(log_errfile);
@@ -228,9 +339,24 @@ void logv_error(const char *format, va_list ap)
                                f = stderr;
 
        log_last_error = vstringf(format, ap);
-       log("ERROR: %s", log_last_error.c_str());
+       if (display_error_log_msg)
+               log("%s%s", prefix, log_last_error.c_str());
        log_flush();
 
+       log_make_debug = bak_log_make_debug;
+
+       if (log_error_atexit)
+               log_error_atexit();
+
+       for (auto &item : log_expect_error)
+               if (YS_REGEX_NS::regex_search(log_last_error, item.first))
+                       item.second.current_count++;
+
+       if (check_expected_logs)
+               log_check_expected();
+
+       YS_DEBUGTRAP_IF_DEBUGGING;
+
 #ifdef EMSCRIPTEN
        log_files = backup_log_files;
        throw 0;
@@ -241,6 +367,21 @@ void logv_error(const char *format, va_list ap)
 #endif
 }
 
+void logv_error(const char *format, va_list ap)
+{
+       logv_error_with_prefix("ERROR: ", format, ap);
+}
+
+void log_file_error(const string &filename, int lineno,
+                    const char *format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       std::string prefix = stringf("%s:%d: ERROR: ",
+                                    filename.c_str(), lineno);
+       logv_error_with_prefix(prefix.c_str(), format, ap);
+}
+
 void log(const char *format, ...)
 {
        va_list ap;
@@ -265,6 +406,27 @@ void log_warning(const char *format, ...)
        va_end(ap);
 }
 
+void log_experimental(const char *format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       string s = vstringf(format, ap);
+       va_end(ap);
+
+       if (log_experimentals_ignored.count(s) == 0 && log_experimentals.count(s) == 0) {
+               log_warning("Feature '%s' is experimental.\n", s.c_str());
+               log_experimentals.insert(s);
+       }
+}
+
+void log_warning_noprefix(const char *format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       logv_warning_noprefix(format, ap);
+       va_end(ap);
+}
+
 void log_error(const char *format, ...)
 {
        va_list ap;
@@ -301,13 +463,13 @@ void log_push()
 void log_pop()
 {
        header_count.pop_back();
-       log_id_cache.clear();
+       log_id_cache_clear();
        string_buf.clear();
        string_buf_index = -1;
        log_flush();
 }
 
-#if defined(__linux__) && defined(YOSYS_ENABLE_PLUGINS)
+#if (defined(__linux__) || defined(__FreeBSD__)) && defined(YOSYS_ENABLE_PLUGINS)
 void log_backtrace(const char *prefix, int levels)
 {
        if (levels <= 0) return;
@@ -324,6 +486,9 @@ void log_backtrace(const char *prefix, int levels)
 
        if (levels <= 1) return;
 
+#ifndef DEBUG
+       log("%sframe #2: [build Yosys with ENABLE_DEBUG for deeper backtraces]\n", prefix);
+#else
        if ((p = __builtin_extract_return_addr(__builtin_return_address(1))) && dladdr(p, &dli)) {
                log("%sframe #2: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
        } else {
@@ -395,6 +560,7 @@ void log_backtrace(const char *prefix, int levels)
        }
 
        if (levels <= 9) return;
+#endif
 }
 #else
 void log_backtrace(const char*, int) { }
@@ -404,7 +570,7 @@ void log_reset_stack()
 {
        while (header_count.size() > 1)
                header_count.pop_back();
-       log_id_cache.clear();
+       log_id_cache_clear();
        string_buf.clear();
        string_buf_index = -1;
        log_flush();
@@ -427,6 +593,10 @@ void log_dump_val_worker(RTLIL::SigSpec v) {
        log("%s", log_signal(v));
 }
 
+void log_dump_val_worker(RTLIL::State v) {
+       log("%s", log_signal(v));
+}
+
 const char *log_signal(const RTLIL::SigSpec &sig, bool autoint)
 {
        std::stringstream buf;
@@ -463,8 +633,8 @@ const char *log_const(const RTLIL::Const &value, bool autoint)
 
 const char *log_id(RTLIL::IdString str)
 {
-       log_id_cache.insert(str);
-       const char *p = str.c_str();
+       log_id_cache.push_back(strdup(str.c_str()));
+       const char *p = log_id_cache.back();
        if (p[0] != '\\')
                return p;
        if (p[1] == '$' || p[1] == '\\' || p[1] == 0)
@@ -488,10 +658,63 @@ void log_cell(RTLIL::Cell *cell, std::string indent)
        log("%s", buf.str().c_str());
 }
 
+void log_wire(RTLIL::Wire *wire, std::string indent)
+{
+       std::stringstream buf;
+       ILANG_BACKEND::dump_wire(buf, indent, wire);
+       log("%s", buf.str().c_str());
+}
+
+void log_check_expected()
+{
+       check_expected_logs = false;
+
+       for (auto &item : log_expect_warning) {
+               if (item.second.current_count == 0) {
+                       log_warn_regexes.clear();
+                       log_error("Expected warning pattern '%s' not found !\n", item.second.pattern.c_str());
+               }
+               if (item.second.current_count != item.second.expected_count) {
+                       log_warn_regexes.clear();
+                       log_error("Expected warning pattern '%s' found %d time(s), instead of %d time(s) !\n",
+                               item.second.pattern.c_str(), item.second.current_count, item.second.expected_count);
+               }
+       }
+
+       for (auto &item : log_expect_log) {
+               if (item.second.current_count == 0) {
+                       log_warn_regexes.clear();
+                       log_error("Expected log pattern '%s' not found !\n", item.second.pattern.c_str());
+               }
+               if (item.second.current_count != item.second.expected_count) {
+                       log_warn_regexes.clear();
+                       log_error("Expected log pattern '%s' found %d time(s), instead of %d time(s) !\n",
+                               item.second.pattern.c_str(), item.second.current_count, item.second.expected_count);
+               }
+       }
+
+       for (auto &item : log_expect_error)
+               if (item.second.current_count == item.second.expected_count) {
+                       log_warn_regexes.clear();
+                       log("Expected error pattern '%s' found !!!\n", item.second.pattern.c_str());
+                       #ifdef EMSCRIPTEN
+                               throw 0;
+                       #elif defined(_MSC_VER)
+                               _exit(0);
+                       #else
+                               _Exit(0);
+                       #endif
+               } else {
+                       display_error_log_msg = false;
+                       log_warn_regexes.clear();
+                       log_error("Expected error pattern '%s' not found !\n", item.second.pattern.c_str());
+               }
+}
+
 // ---------------------------------------------------
 // This is the magic behind the code coverage counters
 // ---------------------------------------------------
-#if defined(YOSYS_ENABLE_COVER) && defined(__linux__)
+#if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__))
 
 dict<std::string, std::pair<std::string, int>> extra_coverage_data;
 
@@ -540,4 +763,3 @@ dict<std::string, std::pair<std::string, int>> get_coverage_data()
 #endif
 
 YOSYS_NAMESPACE_END
-