| /* Hierarchical log messages for the analyzer. |
| Copyright (C) 2014-2022 Free Software Foundation, Inc. |
| Contributed by David Malcolm <dmalcolm@redhat.com>. |
| |
| 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 |
| <http://www.gnu.org/licenses/>. */ |
| |
| #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; |
| text.format_spec = fmt; |
| text.args_ptr = ap; |
| text.err_no = 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 */ |