Branch data Line data Source code
1 : : /* Subclass of diagnostic_path for analyzer diagnostics.
2 : : Copyright (C) 2019-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 : : #define INCLUDE_MEMORY
23 : : #include "system.h"
24 : : #include "coretypes.h"
25 : : #include "tree.h"
26 : : #include "function.h"
27 : : #include "basic-block.h"
28 : : #include "gimple.h"
29 : : #include "diagnostic-core.h"
30 : : #include "gimple-pretty-print.h"
31 : : #include "fold-const.h"
32 : : #include "diagnostic-path.h"
33 : : #include "options.h"
34 : : #include "cgraph.h"
35 : : #include "cfg.h"
36 : : #include "digraph.h"
37 : : #include "diagnostic-event-id.h"
38 : : #include "analyzer/analyzer.h"
39 : : #include "analyzer/analyzer-logging.h"
40 : : #include "analyzer/sm.h"
41 : : #include "sbitmap.h"
42 : : #include "bitmap.h"
43 : : #include "ordered-hash-map.h"
44 : : #include "analyzer/call-string.h"
45 : : #include "analyzer/program-point.h"
46 : : #include "analyzer/store.h"
47 : : #include "analyzer/region-model.h"
48 : : #include "analyzer/program-state.h"
49 : : #include "analyzer/checker-path.h"
50 : : #include "gimple-iterator.h"
51 : : #include "inlining-iterator.h"
52 : : #include "analyzer/supergraph.h"
53 : : #include "analyzer/pending-diagnostic.h"
54 : : #include "analyzer/diagnostic-manager.h"
55 : : #include "analyzer/constraint-manager.h"
56 : : #include "analyzer/diagnostic-manager.h"
57 : : #include "analyzer/checker-path.h"
58 : : #include "analyzer/exploded-graph.h"
59 : : #include "make-unique.h"
60 : :
61 : : #if ENABLE_ANALYZER
62 : :
63 : : namespace ana {
64 : :
65 : : /* Print a single-line representation of this path to PP. */
66 : :
67 : : void
68 : 0 : checker_path::dump (pretty_printer *pp) const
69 : : {
70 : 0 : pp_character (pp, '[');
71 : :
72 : 0 : checker_event *e;
73 : 0 : int i;
74 : 0 : FOR_EACH_VEC_ELT (m_events, i, e)
75 : : {
76 : 0 : if (i > 0)
77 : 0 : pp_string (pp, ", ");
78 : 0 : label_text event_desc (e->get_desc (false));
79 : 0 : pp_printf (pp, "\"%s\"", event_desc.get ());
80 : 0 : }
81 : 0 : pp_character (pp, ']');
82 : 0 : }
83 : :
84 : : /* Print a multiline form of this path to LOGGER, prefixing it with DESC. */
85 : :
86 : : void
87 : 18026 : checker_path::maybe_log (logger *logger, const char *desc) const
88 : : {
89 : 18026 : if (!logger)
90 : : return;
91 : 0 : logger->start_log_line ();
92 : 0 : logger->log_partial ("%s: ", desc);
93 : 0 : dump (logger->get_printer ());
94 : 0 : logger->end_log_line ();
95 : 0 : for (unsigned i = 0; i < m_events.length (); i++)
96 : : {
97 : 0 : logger->start_log_line ();
98 : 0 : logger->log_partial ("%s[%i]: %s ", desc, i,
99 : 0 : event_kind_to_string (m_events[i]->m_kind));
100 : 0 : m_events[i]->dump (logger->get_printer ());
101 : 0 : logger->end_log_line ();
102 : : }
103 : : }
104 : :
105 : : void
106 : 65569 : checker_path::add_event (std::unique_ptr<checker_event> event)
107 : : {
108 : 65569 : if (m_logger)
109 : : {
110 : 0 : m_logger->start_log_line ();
111 : 0 : m_logger->log_partial ("added event[%i]: %s ",
112 : : m_events.length (),
113 : 0 : event_kind_to_string (event.get ()->m_kind));
114 : 0 : event.get ()->dump (m_logger->get_printer ());
115 : 0 : m_logger->end_log_line ();
116 : : }
117 : 65569 : m_events.safe_push (event.release ());
118 : 65569 : }
119 : :
120 : : /* Print a multiline form of this path to STDERR. */
121 : :
122 : : DEBUG_FUNCTION void
123 : 0 : checker_path::debug () const
124 : : {
125 : 0 : checker_event *e;
126 : 0 : int i;
127 : 0 : FOR_EACH_VEC_ELT (m_events, i, e)
128 : : {
129 : 0 : label_text event_desc (e->get_desc (false));
130 : 0 : fprintf (stderr,
131 : : "[%i]: %s \"%s\"\n",
132 : : i,
133 : 0 : event_kind_to_string (m_events[i]->m_kind),
134 : : event_desc.get ());
135 : 0 : }
136 : 0 : }
137 : :
138 : : /* Add region_creation_event instances to this path for REG,
139 : : describing whether REG is on the stack or heap and what
140 : : its capacity is (if known).
141 : : If DEBUG is true, also create an RCE_DEBUG event. */
142 : :
143 : : void
144 : 1404 : checker_path::add_region_creation_events (pending_diagnostic *pd,
145 : : const region *reg,
146 : : const region_model *model,
147 : : const event_loc_info &loc_info,
148 : : bool debug)
149 : : {
150 : 1404 : tree capacity = NULL_TREE;
151 : 1404 : if (model)
152 : 1175 : if (const svalue *capacity_sval = model->get_capacity (reg))
153 : 1175 : capacity = model->get_representative_tree (capacity_sval);
154 : :
155 : 1404 : pd->add_region_creation_events (reg, capacity, loc_info, *this);
156 : :
157 : 1404 : if (debug)
158 : 0 : add_event (make_unique<region_creation_event_debug> (reg, capacity,
159 : : loc_info));
160 : 1404 : }
161 : :
162 : : void
163 : 4509 : checker_path::fixup_locations (pending_diagnostic *pd)
164 : : {
165 : 32707 : for (checker_event *e : m_events)
166 : 19180 : e->set_location (pd->fixup_location (e->get_location (), false));
167 : 4509 : }
168 : :
169 : : /* Return true if there is a (start_cfg_edge_event, end_cfg_edge_event) pair
170 : : at (IDX, IDX + 1). */
171 : :
172 : : bool
173 : 13511 : checker_path::cfg_edge_pair_at_p (unsigned idx) const
174 : : {
175 : 27022 : if (m_events.length () < idx + 1)
176 : : return false;
177 : 13363 : return (m_events[idx]->m_kind == EK_START_CFG_EDGE
178 : 13363 : && m_events[idx + 1]->m_kind == EK_END_CFG_EDGE);
179 : : }
180 : :
181 : : /* Consider a call from "outer" to "middle" which calls "inner",
182 : : where "inner" and "middle" have been inlined into "outer".
183 : :
184 : : We expect the stmt locations for the inlined stmts to have a
185 : : chain like:
186 : :
187 : : [{fndecl: inner},
188 : : {fndecl: middle, callsite: within middle to inner},
189 : : {fndecl: outer, callsite: without outer to middle}]
190 : :
191 : : The location for the stmt will already be fixed up to reflect
192 : : the two extra frames, so that we have e.g. this as input
193 : : (for gcc.dg/analyzer/inlining-4.c):
194 : :
195 : : before[0]:
196 : : EK_FUNCTION_ENTRY "entry to ‘outer’"
197 : : (depth 1, fndecl ‘outer’, m_loc=511c4)
198 : : before[1]:
199 : : EK_START_CFG_EDGE "following ‘true’ branch (when ‘flag != 0’)..."
200 : : (depth 3 corrected from 1,
201 : : fndecl ‘inner’ corrected from ‘outer’, m_loc=8000000f)
202 : : before[2]:
203 : : EK_END_CFG_EDGE "...to here"
204 : : (depth 1, fndecl ‘outer’, m_loc=0)
205 : : before[3]:
206 : : EK_WARNING "here (‘<unknown>’ is in state ‘null’)"
207 : : (depth 1, fndecl ‘outer’, m_loc=80000004)
208 : :
209 : : We want to add inlined_call_events showing the calls, so that
210 : : the above becomes:
211 : :
212 : : after[0]:
213 : : EK_FUNCTION_ENTRY "entry to ‘outer’"
214 : : (depth 1, fndecl ‘outer’, m_loc=511c4)
215 : : after[1]:
216 : : EK_INLINED_CALL "inlined call to ‘middle’ from ‘outer’"
217 : : (depth 1, fndecl ‘outer’, m_loc=53300)
218 : : after[2]:
219 : : EK_INLINED_CALL "inlined call to ‘inner’ from ‘middle’"
220 : : (depth 2, fndecl ‘middle’, m_loc=4d2e0)
221 : : after[3]:
222 : : EK_START_CFG_EDGE "following ‘true’ branch (when ‘flag != 0’)..."
223 : : (depth 3 corrected from 1,
224 : : fndecl ‘inner’ corrected from ‘outer’, m_loc=8000000f)
225 : : after[4]: EK_END_CFG_EDGE "...to here"
226 : : (depth 1, fndecl ‘outer’, m_loc=0)
227 : : after[5]: EK_WARNING "here (‘<unknown>’ is in state ‘null’)"
228 : : (depth 1, fndecl ‘outer’, m_loc=80000004)
229 : :
230 : : where we've added events between before[0] and before[1] to show
231 : : the inlined calls leading to the effective stack depths, making
232 : : the generated path much easier for a user to read.
233 : :
234 : : Note how in the above we have a branch (before[1] to before[2])
235 : : where the locations were originally in different functions.
236 : : Hence we have to add these events quite late when generating
237 : : checker_path. */
238 : :
239 : : void
240 : 4509 : checker_path::inject_any_inlined_call_events (logger *logger)
241 : : {
242 : 4509 : LOG_SCOPE (logger);
243 : :
244 : 4509 : if (!flag_analyzer_undo_inlining)
245 : 5 : return;
246 : :
247 : : /* Build a copy of m_events with the new events inserted. */
248 : 4504 : auto_vec<checker_event *> updated_events;
249 : :
250 : 4504 : maybe_log (logger, "before");
251 : :
252 : 4504 : hash_set<tree> blocks_in_prev_event;
253 : :
254 : 46932 : for (unsigned ev_idx = 0; ev_idx < m_events.length (); ev_idx++)
255 : : {
256 : 18962 : checker_event *curr_event = m_events[ev_idx];
257 : 18962 : location_t curr_loc = curr_event->get_location ();
258 : 18962 : hash_set<tree> blocks_in_curr_event;
259 : :
260 : 18962 : if (logger)
261 : : {
262 : 0 : logger->start_log_line ();
263 : 0 : logger->log_partial ("event[%i]: %s ", ev_idx,
264 : 0 : event_kind_to_string (curr_event->m_kind));
265 : 0 : curr_event->dump (logger->get_printer ());
266 : 0 : logger->end_log_line ();
267 : 0 : for (inlining_iterator iter (curr_event->get_location ());
268 : 0 : !iter.done_p (); iter.next ())
269 : : {
270 : 0 : logger->start_log_line ();
271 : 0 : logger->log_partial (" %qE", iter.get_block ());
272 : 0 : if (!flag_dump_noaddr)
273 : 0 : logger->log_partial (" (%p)", iter.get_block ());
274 : 0 : logger->log_partial (", fndecl: %qE, callsite: 0x%x",
275 : : iter.get_fndecl (), iter.get_callsite ());
276 : 0 : if (iter.get_callsite ())
277 : 0 : dump_location (logger->get_printer (), iter.get_callsite ());
278 : 0 : logger->end_log_line ();
279 : : }
280 : : }
281 : :
282 : : /* We want to add events to show inlined calls.
283 : :
284 : : We want to show changes relative to the previous event, omitting
285 : : the commonality between the inlining chain.
286 : :
287 : : The chain is ordered from innermost frame to outermost frame;
288 : : we want to walk it backwards to show the calls, so capture it
289 : : in a vec. */
290 : 18962 : struct chain_element { tree m_block; tree m_fndecl; };
291 : 18962 : auto_vec<chain_element> elements;
292 : 50148 : for (inlining_iterator iter (curr_loc); !iter.done_p (); iter.next ())
293 : : {
294 : 15593 : chain_element ce;
295 : 15593 : ce.m_block = iter.get_block ();
296 : 15593 : ce.m_fndecl = iter.get_fndecl ();
297 : :
298 : 15593 : if (!blocks_in_prev_event.contains (ce.m_block))
299 : 6458 : elements.safe_push (ce);
300 : 15593 : blocks_in_curr_event.add (ce.m_block);
301 : : }
302 : :
303 : : /* Walk from outermost to innermost. */
304 : 18962 : if (elements.length () > 0)
305 : : {
306 : 6250 : int orig_stack_depth = curr_event->get_original_stack_depth ();
307 : 6458 : for (unsigned element_idx = elements.length () - 1; element_idx > 0;
308 : : element_idx--)
309 : : {
310 : 208 : const chain_element &ce = elements[element_idx];
311 : 208 : int stack_depth_adjustment
312 : 208 : = (blocks_in_curr_event.elements () - element_idx) - 1;
313 : 208 : if (location_t callsite = BLOCK_SOURCE_LOCATION (ce.m_block))
314 : 208 : updated_events.safe_push
315 : 208 : (new inlined_call_event (callsite,
316 : 416 : elements[element_idx - 1].m_fndecl,
317 : 208 : ce.m_fndecl,
318 : : orig_stack_depth,
319 : 416 : stack_depth_adjustment));
320 : : }
321 : : }
322 : :
323 : : /* Ideally we'd use assignment here:
324 : : blocks_in_prev_event = blocks_in_curr_event; */
325 : 18962 : blocks_in_prev_event.empty ();
326 : 34555 : for (auto iter : blocks_in_curr_event)
327 : 15593 : blocks_in_prev_event.add (iter);
328 : :
329 : : /* Add the existing event. */
330 : 18962 : updated_events.safe_push (curr_event);
331 : 18962 : }
332 : :
333 : : /* Replace m_events with updated_events. */
334 : 4504 : m_events.truncate (0);
335 : 4504 : m_events.safe_splice (updated_events);
336 : :
337 : 4504 : maybe_log (logger, " after");
338 : 4509 : }
339 : :
340 : : } // namespace ana
341 : :
342 : : #endif /* #if ENABLE_ANALYZER */
|