d: Add test for PR d/108167 to the testsuite [PR108167]
[official-gcc.git] / gcc / analyzer / checker-path.cc
blob4f201714f118088e768ec736c8ef0347c87b400a
1 /* Subclass of diagnostic_path for analyzer diagnostics.
2 Copyright (C) 2019-2023 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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.
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.
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/>. */
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"
61 #if ENABLE_ANALYZER
63 namespace ana {
65 /* Print a single-line representation of this path to PP. */
67 void
68 checker_path::dump (pretty_printer *pp) const
70 pp_character (pp, '[');
72 checker_event *e;
73 int i;
74 FOR_EACH_VEC_ELT (m_events, i, e)
76 if (i > 0)
77 pp_string (pp, ", ");
78 label_text event_desc (e->get_desc (false));
79 pp_printf (pp, "\"%s\"", event_desc.get ());
81 pp_character (pp, ']');
84 /* Print a multiline form of this path to LOGGER, prefixing it with DESC. */
86 void
87 checker_path::maybe_log (logger *logger, const char *desc) const
89 if (!logger)
90 return;
91 logger->start_log_line ();
92 logger->log_partial ("%s: ", desc);
93 dump (logger->get_printer ());
94 logger->end_log_line ();
95 for (unsigned i = 0; i < m_events.length (); i++)
97 logger->start_log_line ();
98 logger->log_partial ("%s[%i]: %s ", desc, i,
99 event_kind_to_string (m_events[i]->m_kind));
100 m_events[i]->dump (logger->get_printer ());
101 logger->end_log_line ();
105 void
106 checker_path::add_event (std::unique_ptr<checker_event> event)
108 if (m_logger)
110 m_logger->start_log_line ();
111 m_logger->log_partial ("added event[%i]: %s ",
112 m_events.length (),
113 event_kind_to_string (event.get ()->m_kind));
114 event.get ()->dump (m_logger->get_printer ());
115 m_logger->end_log_line ();
117 m_events.safe_push (event.release ());
120 /* Print a multiline form of this path to STDERR. */
122 DEBUG_FUNCTION void
123 checker_path::debug () const
125 checker_event *e;
126 int i;
127 FOR_EACH_VEC_ELT (m_events, i, e)
129 label_text event_desc (e->get_desc (false));
130 fprintf (stderr,
131 "[%i]: %s \"%s\"\n",
133 event_kind_to_string (m_events[i]->m_kind),
134 event_desc.get ());
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. */
143 void
144 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)
150 tree capacity = NULL_TREE;
151 if (model)
152 if (const svalue *capacity_sval = model->get_capacity (reg))
153 capacity = model->get_representative_tree (capacity_sval);
155 pd->add_region_creation_events (reg, capacity, loc_info, *this);
157 if (debug)
158 add_event (make_unique<region_creation_event_debug> (reg, capacity,
159 loc_info));
162 void
163 checker_path::fixup_locations (pending_diagnostic *pd)
165 for (checker_event *e : m_events)
166 e->set_location (pd->fixup_location (e->get_location (), false));
169 /* Return true if there is a (start_cfg_edge_event, end_cfg_edge_event) pair
170 at (IDX, IDX + 1). */
172 bool
173 checker_path::cfg_edge_pair_at_p (unsigned idx) const
175 if (m_events.length () < idx + 1)
176 return false;
177 return (m_events[idx]->m_kind == EK_START_CFG_EDGE
178 && m_events[idx + 1]->m_kind == EK_END_CFG_EDGE);
181 /* Consider a call from "outer" to "middle" which calls "inner",
182 where "inner" and "middle" have been inlined into "outer".
184 We expect the stmt locations for the inlined stmts to have a
185 chain like:
187 [{fndecl: inner},
188 {fndecl: middle, callsite: within middle to inner},
189 {fndecl: outer, callsite: without outer to middle}]
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):
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)
209 We want to add inlined_call_events showing the calls, so that
210 the above becomes:
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)
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.
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. */
239 void
240 checker_path::inject_any_inlined_call_events (logger *logger)
242 LOG_SCOPE (logger);
244 if (!flag_analyzer_undo_inlining)
245 return;
247 /* Build a copy of m_events with the new events inserted. */
248 auto_vec<checker_event *> updated_events;
250 maybe_log (logger, "before");
252 hash_set<tree> blocks_in_prev_event;
254 for (unsigned ev_idx = 0; ev_idx < m_events.length (); ev_idx++)
256 checker_event *curr_event = m_events[ev_idx];
257 location_t curr_loc = curr_event->get_location ();
258 hash_set<tree> blocks_in_curr_event;
260 if (logger)
262 logger->start_log_line ();
263 logger->log_partial ("event[%i]: %s ", ev_idx,
264 event_kind_to_string (curr_event->m_kind));
265 curr_event->dump (logger->get_printer ());
266 logger->end_log_line ();
267 for (inlining_iterator iter (curr_event->get_location ());
268 !iter.done_p (); iter.next ())
270 logger->start_log_line ();
271 logger->log_partial (" %qE", iter.get_block ());
272 if (!flag_dump_noaddr)
273 logger->log_partial (" (%p)", iter.get_block ());
274 logger->log_partial (", fndecl: %qE, callsite: 0x%x",
275 iter.get_fndecl (), iter.get_callsite ());
276 if (iter.get_callsite ())
277 dump_location (logger->get_printer (), iter.get_callsite ());
278 logger->end_log_line ();
282 /* We want to add events to show inlined calls.
284 We want to show changes relative to the previous event, omitting
285 the commonality between the inlining chain.
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 struct chain_element { tree m_block; tree m_fndecl; };
291 auto_vec<chain_element> elements;
292 for (inlining_iterator iter (curr_loc); !iter.done_p (); iter.next ())
294 chain_element ce;
295 ce.m_block = iter.get_block ();
296 ce.m_fndecl = iter.get_fndecl ();
298 if (!blocks_in_prev_event.contains (ce.m_block))
299 elements.safe_push (ce);
300 blocks_in_curr_event.add (ce.m_block);
303 /* Walk from outermost to innermost. */
304 if (elements.length () > 0)
306 int orig_stack_depth = curr_event->get_original_stack_depth ();
307 for (unsigned element_idx = elements.length () - 1; element_idx > 0;
308 element_idx--)
310 const chain_element &ce = elements[element_idx];
311 int stack_depth_adjustment
312 = (blocks_in_curr_event.elements () - element_idx) - 1;
313 if (location_t callsite = BLOCK_SOURCE_LOCATION (ce.m_block))
314 updated_events.safe_push
315 (new inlined_call_event (callsite,
316 elements[element_idx - 1].m_fndecl,
317 ce.m_fndecl,
318 orig_stack_depth,
319 stack_depth_adjustment));
323 /* Ideally we'd use assignment here:
324 blocks_in_prev_event = blocks_in_curr_event; */
325 blocks_in_prev_event.empty ();
326 for (auto iter : blocks_in_curr_event)
327 blocks_in_prev_event.add (iter);
329 /* Add the existing event. */
330 updated_events.safe_push (curr_event);
333 /* Replace m_events with updated_events. */
334 m_events.truncate (0);
335 m_events.safe_splice (updated_events);
337 maybe_log (logger, " after");
340 } // namespace ana
342 #endif /* #if ENABLE_ANALYZER */