1 | /* Hierarchical log messages for the analyzer. |
2 | Copyright (C) 2014-2024 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 | #if __GNUC__ >= 10 |
34 | #pragma GCC diagnostic ignored "-Wformat-diag" |
35 | #endif |
36 | |
37 | namespace ana { |
38 | |
39 | /* Implementation of class logger. */ |
40 | |
41 | /* ctor for logger. */ |
42 | |
43 | logger::logger (FILE *f_out, |
44 | int, /* flags */ |
45 | int /* verbosity */, |
46 | const pretty_printer &reference_pp) : |
47 | m_refcount (0), |
48 | m_f_out (f_out), |
49 | m_indent_level (0), |
50 | m_log_refcount_changes (false), |
51 | m_pp (reference_pp.clone ()) |
52 | { |
53 | pp_show_color (m_pp) = 0; |
54 | pp_buffer (m_pp)->stream = f_out; |
55 | |
56 | /* %qE in logs for SSA_NAMEs should show the ssa names, rather than |
57 | trying to prettify things by showing the underlying var. */ |
58 | pp_format_decoder (m_pp) = default_tree_printer; |
59 | |
60 | /* Begin the log by writing the GCC version. */ |
61 | print_version (f_out, "" , false); |
62 | } |
63 | |
64 | /* The destructor for logger, invoked via |
65 | the decref method when the refcount hits zero. |
66 | Note that we do not close the underlying FILE * (m_f_out). */ |
67 | |
68 | logger::~logger () |
69 | { |
70 | /* This should be the last message emitted. */ |
71 | log (fmt: "%s" , __PRETTY_FUNCTION__); |
72 | gcc_assert (m_refcount == 0); |
73 | delete m_pp; |
74 | } |
75 | |
76 | /* Increment the reference count of the logger. */ |
77 | |
78 | void |
79 | logger::incref (const char *reason) |
80 | { |
81 | m_refcount++; |
82 | if (m_log_refcount_changes) |
83 | log (fmt: "%s: reason: %s refcount now %i " , |
84 | __PRETTY_FUNCTION__, reason, m_refcount); |
85 | } |
86 | |
87 | /* Decrement the reference count of the logger, |
88 | deleting it if nothing is referring to it. */ |
89 | |
90 | void |
91 | logger::decref (const char *reason) |
92 | { |
93 | gcc_assert (m_refcount > 0); |
94 | --m_refcount; |
95 | if (m_log_refcount_changes) |
96 | log (fmt: "%s: reason: %s refcount now %i" , |
97 | __PRETTY_FUNCTION__, reason, m_refcount); |
98 | if (m_refcount == 0) |
99 | delete this; |
100 | } |
101 | |
102 | /* Write a formatted message to the log, by calling the log_va method. */ |
103 | |
104 | void |
105 | logger::log (const char *fmt, ...) |
106 | { |
107 | va_list ap; |
108 | va_start (ap, fmt); |
109 | log_va (fmt, ap: &ap); |
110 | va_end (ap); |
111 | } |
112 | |
113 | /* Write an indented line to the log file. |
114 | |
115 | We explicitly flush after each line: if something crashes the process, |
116 | we want the logfile/stream to contain the most up-to-date hint about the |
117 | last thing that was happening, without it being hidden in an in-process |
118 | buffer. */ |
119 | |
120 | void |
121 | logger::log_va (const char *fmt, va_list *ap) |
122 | { |
123 | start_log_line (); |
124 | log_va_partial (fmt, ap); |
125 | end_log_line (); |
126 | } |
127 | |
128 | void |
129 | logger::start_log_line () |
130 | { |
131 | for (int i = 0; i < m_indent_level; i++) |
132 | fputc (c: ' ', stream: m_f_out); |
133 | } |
134 | |
135 | void |
136 | logger::log_partial (const char *fmt, ...) |
137 | { |
138 | va_list ap; |
139 | va_start (ap, fmt); |
140 | log_va_partial (fmt, ap: &ap); |
141 | va_end (ap); |
142 | } |
143 | |
144 | void |
145 | logger::log_va_partial (const char *fmt, va_list *ap) |
146 | { |
147 | text_info text (fmt, ap, 0); |
148 | pp_format (m_pp, &text); |
149 | pp_output_formatted_text (m_pp); |
150 | } |
151 | |
152 | void |
153 | logger::end_log_line () |
154 | { |
155 | pp_flush (m_pp); |
156 | pp_clear_output_area (m_pp); |
157 | fprintf (stream: m_f_out, format: "\n" ); |
158 | fflush (stream: m_f_out); |
159 | } |
160 | |
161 | /* Record the entry within a particular scope, indenting subsequent |
162 | log lines accordingly. */ |
163 | |
164 | void |
165 | logger::enter_scope (const char *scope_name) |
166 | { |
167 | log (fmt: "entering: %s" , scope_name); |
168 | inc_indent (); |
169 | } |
170 | |
171 | void |
172 | logger::enter_scope (const char *scope_name, const char *fmt, va_list *ap) |
173 | { |
174 | start_log_line (); |
175 | log_partial (fmt: "entering: %s: " , scope_name); |
176 | log_va_partial (fmt, ap); |
177 | end_log_line (); |
178 | |
179 | inc_indent (); |
180 | } |
181 | |
182 | |
183 | /* Record the exit from a particular scope, restoring the indent level to |
184 | before the scope was entered. */ |
185 | |
186 | void |
187 | logger::exit_scope (const char *scope_name) |
188 | { |
189 | if (m_indent_level) |
190 | dec_indent (); |
191 | else |
192 | log (fmt: "(mismatching indentation)" ); |
193 | log (fmt: "exiting: %s" , scope_name); |
194 | } |
195 | |
196 | /* Implementation of class log_user. */ |
197 | |
198 | /* The constructor for log_user. */ |
199 | |
200 | log_user::log_user (logger *logger) : m_logger (logger) |
201 | { |
202 | if (m_logger) |
203 | m_logger->incref(reason: "log_user ctor" ); |
204 | } |
205 | |
206 | /* The destructor for log_user. */ |
207 | |
208 | log_user::~log_user () |
209 | { |
210 | if (m_logger) |
211 | m_logger->decref(reason: "log_user dtor" ); |
212 | } |
213 | |
214 | /* Set the logger for a log_user, managing the reference counts |
215 | of the old and new logger (either of which might be NULL). */ |
216 | |
217 | void |
218 | log_user::set_logger (logger *logger) |
219 | { |
220 | if (logger) |
221 | logger->incref (reason: "log_user::set_logger" ); |
222 | if (m_logger) |
223 | m_logger->decref (reason: "log_user::set_logger" ); |
224 | m_logger = logger; |
225 | } |
226 | |
227 | } // namespace ana |
228 | |
229 | #endif /* #if ENABLE_ANALYZER */ |
230 | |