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