1 /* Hierarchical log messages for the analyzer. 2 Copyright (C) 2014-2020 Free Software Foundation, Inc. 3 Contributed by David Malcolm <dmalcolm@redhat.com>. 4 5 This file is part of GCC. 6 7 GCC is free software; you can redistribute it and/or modify it 8 under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3, or (at your option) 10 any later version. 11 12 GCC is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GCC; see the file COPYING3. If not see 19 <http://www.gnu.org/licenses/>. */ 20 21 #include "config.h" 22 #include "system.h" 23 #include "coretypes.h" 24 #include "toplev.h" /* for print_version */ 25 #include "pretty-print.h" /* for print_version */ 26 #include "diagnostic.h" 27 #include "tree-diagnostic.h" 28 29 #include "analyzer/analyzer-logging.h" 30 31 #if ENABLE_ANALYZER 32 33 namespace ana { 34 35 /* Implementation of class logger. */ 36 37 /* ctor for logger. */ 38 39 logger::logger (FILE *f_out, 40 int, /* flags */ 41 int /* verbosity */, 42 const pretty_printer &reference_pp) : 43 m_refcount (0), 44 m_f_out (f_out), 45 m_indent_level (0), 46 m_log_refcount_changes (false), 47 m_pp (reference_pp.clone ()) 48 { 49 pp_show_color (m_pp) = 0; 50 pp_buffer (m_pp)->stream = f_out; 51 52 /* %qE in logs for SSA_NAMEs should show the ssa names, rather than 53 trying to prettify things by showing the underlying var. */ 54 pp_format_decoder (m_pp) = default_tree_printer; 55 56 /* Begin the log by writing the GCC version. */ 57 print_version (f_out, "", false); 58 } 59 60 /* The destructor for logger, invoked via 61 the decref method when the refcount hits zero. 62 Note that we do not close the underlying FILE * (m_f_out). */ 63 64 logger::~logger () 65 { 66 /* This should be the last message emitted. */ 67 log ("%s", __PRETTY_FUNCTION__); 68 gcc_assert (m_refcount == 0); 69 delete m_pp; 70 } 71 72 /* Increment the reference count of the logger. */ 73 74 void 75 logger::incref (const char *reason) 76 { 77 m_refcount++; 78 if (m_log_refcount_changes) 79 log ("%s: reason: %s refcount now %i ", 80 __PRETTY_FUNCTION__, reason, m_refcount); 81 } 82 83 /* Decrement the reference count of the logger, 84 deleting it if nothing is referring to it. */ 85 86 void 87 logger::decref (const char *reason) 88 { 89 gcc_assert (m_refcount > 0); 90 --m_refcount; 91 if (m_log_refcount_changes) 92 log ("%s: reason: %s refcount now %i", 93 __PRETTY_FUNCTION__, reason, m_refcount); 94 if (m_refcount == 0) 95 delete this; 96 } 97 98 /* Write a formatted message to the log, by calling the log_va method. */ 99 100 void 101 logger::log (const char *fmt, ...) 102 { 103 va_list ap; 104 va_start (ap, fmt); 105 log_va (fmt, &ap); 106 va_end (ap); 107 } 108 109 /* Write an indented line to the log file. 110 111 We explicitly flush after each line: if something crashes the process, 112 we want the logfile/stream to contain the most up-to-date hint about the 113 last thing that was happening, without it being hidden in an in-process 114 buffer. */ 115 116 void 117 logger::log_va (const char *fmt, va_list *ap) 118 { 119 start_log_line (); 120 log_va_partial (fmt, ap); 121 end_log_line (); 122 } 123 124 void 125 logger::start_log_line () 126 { 127 for (int i = 0; i < m_indent_level; i++) 128 fputc (' ', m_f_out); 129 } 130 131 void 132 logger::log_partial (const char *fmt, ...) 133 { 134 va_list ap; 135 va_start (ap, fmt); 136 log_va_partial (fmt, &ap); 137 va_end (ap); 138 } 139 140 void 141 logger::log_va_partial (const char *fmt, va_list *ap) 142 { 143 text_info text; 144 text.format_spec = fmt; 145 text.args_ptr = ap; 146 text.err_no = 0; 147 pp_format (m_pp, &text); 148 pp_output_formatted_text (m_pp); 149 } 150 151 void 152 logger::end_log_line () 153 { 154 pp_flush (m_pp); 155 pp_clear_output_area (m_pp); 156 fprintf (m_f_out, "\n"); 157 fflush (m_f_out); 158 } 159 160 /* Record the entry within a particular scope, indenting subsequent 161 log lines accordingly. */ 162 163 void 164 logger::enter_scope (const char *scope_name) 165 { 166 log ("entering: %s", scope_name); 167 m_indent_level += 1; 168 } 169 170 void 171 logger::enter_scope (const char *scope_name, const char *fmt, va_list *ap) 172 { 173 start_log_line (); 174 log_partial ("entering: %s: ", scope_name); 175 log_va_partial (fmt, ap); 176 end_log_line (); 177 178 m_indent_level += 1; 179 } 180 181 182 /* Record the exit from a particular scope, restoring the indent level to 183 before the scope was entered. */ 184 185 void 186 logger::exit_scope (const char *scope_name) 187 { 188 if (m_indent_level) 189 m_indent_level -= 1; 190 else 191 log ("(mismatching indentation)"); 192 log ("exiting: %s", scope_name); 193 } 194 195 /* Implementation of class log_user. */ 196 197 /* The constructor for log_user. */ 198 199 log_user::log_user (logger *logger) : m_logger (logger) 200 { 201 if (m_logger) 202 m_logger->incref("log_user ctor"); 203 } 204 205 /* The destructor for log_user. */ 206 207 log_user::~log_user () 208 { 209 if (m_logger) 210 m_logger->decref("log_user dtor"); 211 } 212 213 /* Set the logger for a log_user, managing the reference counts 214 of the old and new logger (either of which might be NULL). */ 215 216 void 217 log_user::set_logger (logger *logger) 218 { 219 if (logger) 220 logger->incref ("log_user::set_logger"); 221 if (m_logger) 222 m_logger->decref ("log_user::set_logger"); 223 m_logger = logger; 224 } 225 226 } // namespace ana 227 228 #endif /* #if ENABLE_ANALYZER */ 229