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_VECTOR
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 : : bool
66 : 10903 : checker_path::same_function_p (int event_idx_a,
67 : : int event_idx_b) const
68 : : {
69 : 10903 : return (m_events[event_idx_a]->get_fndecl ()
70 : 10903 : == m_events[event_idx_b]->get_fndecl ());
71 : : }
72 : :
73 : : /* Print a single-line representation of this path to PP. */
74 : :
75 : : void
76 : 0 : checker_path::dump (pretty_printer *pp) const
77 : : {
78 : 0 : pp_character (pp, '[');
79 : :
80 : 0 : checker_event *e;
81 : 0 : int i;
82 : 0 : FOR_EACH_VEC_ELT (m_events, i, e)
83 : : {
84 : 0 : if (i > 0)
85 : 0 : pp_string (pp, ", ");
86 : 0 : pp_character (pp, '"');
87 : 0 : e->print_desc (*pp);
88 : 0 : pp_character (pp, '"');
89 : : }
90 : 0 : pp_character (pp, ']');
91 : 0 : }
92 : :
93 : : /* Print a multiline form of this path to LOGGER, prefixing it with DESC. */
94 : :
95 : : void
96 : 15388 : checker_path::maybe_log (logger *logger, const char *desc) const
97 : : {
98 : 15388 : if (!logger)
99 : : return;
100 : 0 : logger->start_log_line ();
101 : 0 : logger->log_partial ("%s: ", desc);
102 : 0 : dump (logger->get_printer ());
103 : 0 : logger->end_log_line ();
104 : 0 : for (unsigned i = 0; i < m_events.length (); i++)
105 : : {
106 : 0 : logger->start_log_line ();
107 : 0 : logger->log_partial ("%s[%i]: %s ", desc, i,
108 : 0 : event_kind_to_string (m_events[i]->m_kind));
109 : 0 : m_events[i]->dump (logger->get_printer ());
110 : 0 : logger->end_log_line ();
111 : : }
112 : : }
113 : :
114 : : void
115 : 56444 : checker_path::add_event (std::unique_ptr<checker_event> event)
116 : : {
117 : 56444 : if (m_logger)
118 : : {
119 : 0 : m_logger->start_log_line ();
120 : 0 : m_logger->log_partial ("added event[%i]: %s ",
121 : : m_events.length (),
122 : 0 : event_kind_to_string (event.get ()->m_kind));
123 : 0 : event.get ()->dump (m_logger->get_printer ());
124 : 0 : m_logger->end_log_line ();
125 : : }
126 : 56444 : m_events.safe_push (event.release ());
127 : 56444 : }
128 : :
129 : : /* Print a multiline form of this path to STDERR. */
130 : :
131 : : DEBUG_FUNCTION void
132 : 0 : checker_path::debug () const
133 : : {
134 : 0 : checker_event *e;
135 : 0 : int i;
136 : 0 : FOR_EACH_VEC_ELT (m_events, i, e)
137 : : {
138 : 0 : label_text event_desc (e->get_desc (*global_dc->get_reference_printer ()));
139 : 0 : fprintf (stderr,
140 : : "[%i]: %s \"%s\"\n",
141 : : i,
142 : 0 : event_kind_to_string (m_events[i]->m_kind),
143 : : event_desc.get ());
144 : 0 : }
145 : 0 : }
146 : :
147 : : /* Add region_creation_event instances to this path for REG,
148 : : describing whether REG is on the stack or heap and what
149 : : its capacity is (if known).
150 : : If DEBUG is true, also create an RCE_DEBUG event. */
151 : :
152 : : void
153 : 1251 : checker_path::add_region_creation_events (pending_diagnostic *pd,
154 : : const region *reg,
155 : : const region_model *model,
156 : : const event_loc_info &loc_info,
157 : : bool debug)
158 : : {
159 : 1251 : tree capacity = NULL_TREE;
160 : 1251 : if (model)
161 : 1040 : if (const svalue *capacity_sval = model->get_capacity (reg))
162 : 1040 : capacity = model->get_representative_tree (capacity_sval);
163 : :
164 : 1251 : pd->add_region_creation_events (reg, capacity, loc_info, *this);
165 : :
166 : 1251 : if (debug)
167 : 0 : add_event (make_unique<region_creation_event_debug> (reg, capacity,
168 : : loc_info));
169 : 1251 : }
170 : :
171 : : void
172 : 3849 : checker_path::fixup_locations (pending_diagnostic *pd)
173 : : {
174 : 28032 : for (checker_event *e : m_events)
175 : 16485 : e->set_location (pd->fixup_location (e->get_location (), false));
176 : 3849 : }
177 : :
178 : : /* Return true if there is a (start_cfg_edge_event, end_cfg_edge_event) pair
179 : : at (IDX, IDX + 1). */
180 : :
181 : : bool
182 : 11639 : checker_path::cfg_edge_pair_at_p (unsigned idx) const
183 : : {
184 : 23278 : if (m_events.length () < idx + 1)
185 : : return false;
186 : 11520 : return (m_events[idx]->m_kind == EK_START_CFG_EDGE
187 : 11520 : && m_events[idx + 1]->m_kind == EK_END_CFG_EDGE);
188 : : }
189 : :
190 : : /* Consider a call from "outer" to "middle" which calls "inner",
191 : : where "inner" and "middle" have been inlined into "outer".
192 : :
193 : : We expect the stmt locations for the inlined stmts to have a
194 : : chain like:
195 : :
196 : : [{fndecl: inner},
197 : : {fndecl: middle, callsite: within middle to inner},
198 : : {fndecl: outer, callsite: without outer to middle}]
199 : :
200 : : The location for the stmt will already be fixed up to reflect
201 : : the two extra frames, so that we have e.g. this as input
202 : : (for gcc.dg/analyzer/inlining-4.c):
203 : :
204 : : before[0]:
205 : : EK_FUNCTION_ENTRY "entry to ‘outer’"
206 : : (depth 1, fndecl ‘outer’, m_loc=511c4)
207 : : before[1]:
208 : : EK_START_CFG_EDGE "following ‘true’ branch (when ‘flag != 0’)..."
209 : : (depth 3 corrected from 1,
210 : : fndecl ‘inner’ corrected from ‘outer’, m_loc=8000000f)
211 : : before[2]:
212 : : EK_END_CFG_EDGE "...to here"
213 : : (depth 1, fndecl ‘outer’, m_loc=0)
214 : : before[3]:
215 : : EK_WARNING "here (‘<unknown>’ is in state ‘null’)"
216 : : (depth 1, fndecl ‘outer’, m_loc=80000004)
217 : :
218 : : We want to add inlined_call_events showing the calls, so that
219 : : the above becomes:
220 : :
221 : : after[0]:
222 : : EK_FUNCTION_ENTRY "entry to ‘outer’"
223 : : (depth 1, fndecl ‘outer’, m_loc=511c4)
224 : : after[1]:
225 : : EK_INLINED_CALL "inlined call to ‘middle’ from ‘outer’"
226 : : (depth 1, fndecl ‘outer’, m_loc=53300)
227 : : after[2]:
228 : : EK_INLINED_CALL "inlined call to ‘inner’ from ‘middle’"
229 : : (depth 2, fndecl ‘middle’, m_loc=4d2e0)
230 : : after[3]:
231 : : EK_START_CFG_EDGE "following ‘true’ branch (when ‘flag != 0’)..."
232 : : (depth 3 corrected from 1,
233 : : fndecl ‘inner’ corrected from ‘outer’, m_loc=8000000f)
234 : : after[4]: EK_END_CFG_EDGE "...to here"
235 : : (depth 1, fndecl ‘outer’, m_loc=0)
236 : : after[5]: EK_WARNING "here (‘<unknown>’ is in state ‘null’)"
237 : : (depth 1, fndecl ‘outer’, m_loc=80000004)
238 : :
239 : : where we've added events between before[0] and before[1] to show
240 : : the inlined calls leading to the effective stack depths, making
241 : : the generated path much easier for a user to read.
242 : :
243 : : Note how in the above we have a branch (before[1] to before[2])
244 : : where the locations were originally in different functions.
245 : : Hence we have to add these events quite late when generating
246 : : checker_path. */
247 : :
248 : : void
249 : 3849 : checker_path::inject_any_inlined_call_events (logger *logger)
250 : : {
251 : 3849 : LOG_SCOPE (logger);
252 : :
253 : 3849 : if (!flag_analyzer_undo_inlining)
254 : 4 : return;
255 : :
256 : : /* Build a copy of m_events with the new events inserted. */
257 : 3845 : auto_vec<checker_event *> updated_events;
258 : :
259 : 3845 : maybe_log (logger, "before");
260 : :
261 : 3845 : hash_set<tree> blocks_in_prev_event;
262 : :
263 : 20152 : for (unsigned ev_idx = 0; ev_idx < m_events.length (); ev_idx++)
264 : : {
265 : 16307 : checker_event *curr_event = m_events[ev_idx];
266 : 16307 : location_t curr_loc = curr_event->get_location ();
267 : 16307 : hash_set<tree> blocks_in_curr_event;
268 : :
269 : 16307 : if (logger)
270 : : {
271 : 0 : logger->start_log_line ();
272 : 0 : logger->log_partial ("event[%i]: %s ", ev_idx,
273 : 0 : event_kind_to_string (curr_event->m_kind));
274 : 0 : curr_event->dump (logger->get_printer ());
275 : 0 : logger->end_log_line ();
276 : 0 : for (inlining_iterator iter (curr_event->get_location ());
277 : 0 : !iter.done_p (); iter.next ())
278 : : {
279 : 0 : logger->start_log_line ();
280 : 0 : logger->log_partial (" %qE", iter.get_block ());
281 : 0 : if (!flag_dump_noaddr)
282 : 0 : logger->log_partial (" (%p)", iter.get_block ());
283 : 0 : logger->log_partial (", fndecl: %qE, callsite: 0x%llx",
284 : : iter.get_fndecl (),
285 : 0 : (unsigned long long) iter.get_callsite ());
286 : 0 : if (iter.get_callsite ())
287 : 0 : dump_location (logger->get_printer (), iter.get_callsite ());
288 : 0 : logger->end_log_line ();
289 : : }
290 : : }
291 : :
292 : : /* We want to add events to show inlined calls.
293 : :
294 : : We want to show changes relative to the previous event, omitting
295 : : the commonality between the inlining chain.
296 : :
297 : : The chain is ordered from innermost frame to outermost frame;
298 : : we want to walk it backwards to show the calls, so capture it
299 : : in a vec. */
300 : 16307 : struct chain_element { tree m_block; tree m_fndecl; };
301 : 16307 : auto_vec<chain_element> elements;
302 : 43039 : for (inlining_iterator iter (curr_loc); !iter.done_p (); iter.next ())
303 : : {
304 : 13366 : chain_element ce;
305 : 13366 : ce.m_block = iter.get_block ();
306 : 13366 : ce.m_fndecl = iter.get_fndecl ();
307 : :
308 : 13366 : if (!blocks_in_prev_event.contains (ce.m_block))
309 : 5498 : elements.safe_push (ce);
310 : 13366 : blocks_in_curr_event.add (ce.m_block);
311 : : }
312 : :
313 : : /* Walk from outermost to innermost. */
314 : 16307 : if (elements.length () > 0)
315 : : {
316 : 5328 : int orig_stack_depth = curr_event->get_original_stack_depth ();
317 : 5498 : for (unsigned element_idx = elements.length () - 1; element_idx > 0;
318 : : element_idx--)
319 : : {
320 : 170 : const chain_element &ce = elements[element_idx];
321 : 170 : int stack_depth_adjustment
322 : 170 : = (blocks_in_curr_event.elements () - element_idx) - 1;
323 : 170 : if (location_t callsite = BLOCK_SOURCE_LOCATION (ce.m_block))
324 : 170 : updated_events.safe_push
325 : 170 : (new inlined_call_event (callsite,
326 : 340 : elements[element_idx - 1].m_fndecl,
327 : 170 : ce.m_fndecl,
328 : : orig_stack_depth,
329 : 340 : stack_depth_adjustment));
330 : : }
331 : : }
332 : :
333 : : /* Ideally we'd use assignment here:
334 : : blocks_in_prev_event = blocks_in_curr_event; */
335 : 16307 : blocks_in_prev_event.empty ();
336 : 29673 : for (auto iter : blocks_in_curr_event)
337 : 13366 : blocks_in_prev_event.add (iter);
338 : :
339 : : /* Add the existing event. */
340 : 16307 : updated_events.safe_push (curr_event);
341 : 16307 : }
342 : :
343 : : /* Replace m_events with updated_events. */
344 : 3845 : m_events.truncate (0);
345 : 3845 : m_events.safe_splice (updated_events);
346 : :
347 : 3845 : maybe_log (logger, " after");
348 : 3849 : }
349 : :
350 : : } // namespace ana
351 : :
352 : : #endif /* #if ENABLE_ANALYZER */
|