/* Hierarchical log messages for the analyzer. Copyright (C) 2014-2024 Free Software Foundation, Inc. Contributed by David Malcolm . This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include "config.h" #include "system.h" #include "coretypes.h" #include "toplev.h" /* for print_version */ #include "pretty-print.h" /* for print_version */ #include "diagnostic.h" #include "tree-diagnostic.h" #include "analyzer/analyzer-logging.h" #if ENABLE_ANALYZER #if __GNUC__ >= 10 #pragma GCC diagnostic ignored "-Wformat-diag" #endif namespace ana { /* Implementation of class logger. */ /* ctor for logger. */ logger::logger (FILE *f_out, int, /* flags */ int /* verbosity */, const pretty_printer &reference_pp) : m_refcount (0), m_f_out (f_out), m_indent_level (0), m_log_refcount_changes (false), m_pp (reference_pp.clone ()) { pp_show_color (m_pp) = 0; pp_buffer (m_pp)->stream = f_out; /* %qE in logs for SSA_NAMEs should show the ssa names, rather than trying to prettify things by showing the underlying var. */ pp_format_decoder (m_pp) = default_tree_printer; /* Begin the log by writing the GCC version. */ print_version (f_out, "", false); } /* The destructor for logger, invoked via the decref method when the refcount hits zero. Note that we do not close the underlying FILE * (m_f_out). */ logger::~logger () { /* This should be the last message emitted. */ log ("%s", __PRETTY_FUNCTION__); gcc_assert (m_refcount == 0); delete m_pp; } /* Increment the reference count of the logger. */ void logger::incref (const char *reason) { m_refcount++; if (m_log_refcount_changes) log ("%s: reason: %s refcount now %i ", __PRETTY_FUNCTION__, reason, m_refcount); } /* Decrement the reference count of the logger, deleting it if nothing is referring to it. */ void logger::decref (const char *reason) { gcc_assert (m_refcount > 0); --m_refcount; if (m_log_refcount_changes) log ("%s: reason: %s refcount now %i", __PRETTY_FUNCTION__, reason, m_refcount); if (m_refcount == 0) delete this; } /* Write a formatted message to the log, by calling the log_va method. */ void logger::log (const char *fmt, ...) { va_list ap; va_start (ap, fmt); log_va (fmt, &ap); va_end (ap); } /* Write an indented line to the log file. We explicitly flush after each line: if something crashes the process, we want the logfile/stream to contain the most up-to-date hint about the last thing that was happening, without it being hidden in an in-process buffer. */ void logger::log_va (const char *fmt, va_list *ap) { start_log_line (); log_va_partial (fmt, ap); end_log_line (); } void logger::start_log_line () { for (int i = 0; i < m_indent_level; i++) fputc (' ', m_f_out); } void logger::log_partial (const char *fmt, ...) { va_list ap; va_start (ap, fmt); log_va_partial (fmt, &ap); va_end (ap); } void logger::log_va_partial (const char *fmt, va_list *ap) { text_info text (fmt, ap, 0); pp_format (m_pp, &text); pp_output_formatted_text (m_pp); } void logger::end_log_line () { pp_flush (m_pp); pp_clear_output_area (m_pp); fprintf (m_f_out, "\n"); fflush (m_f_out); } /* Record the entry within a particular scope, indenting subsequent log lines accordingly. */ void logger::enter_scope (const char *scope_name) { log ("entering: %s", scope_name); inc_indent (); } void logger::enter_scope (const char *scope_name, const char *fmt, va_list *ap) { start_log_line (); log_partial ("entering: %s: ", scope_name); log_va_partial (fmt, ap); end_log_line (); inc_indent (); } /* Record the exit from a particular scope, restoring the indent level to before the scope was entered. */ void logger::exit_scope (const char *scope_name) { if (m_indent_level) dec_indent (); else log ("(mismatching indentation)"); log ("exiting: %s", scope_name); } /* Implementation of class log_user. */ /* The constructor for log_user. */ log_user::log_user (logger *logger) : m_logger (logger) { if (m_logger) m_logger->incref("log_user ctor"); } /* The destructor for log_user. */ log_user::~log_user () { if (m_logger) m_logger->decref("log_user dtor"); } /* Set the logger for a log_user, managing the reference counts of the old and new logger (either of which might be NULL). */ void log_user::set_logger (logger *logger) { if (logger) logger->incref ("log_user::set_logger"); if (m_logger) m_logger->decref ("log_user::set_logger"); m_logger = logger; } } // namespace ana #endif /* #if ENABLE_ANALYZER */