Line data Source code
1 : /* Subclass of diagnostics::paths::path for analyzer diagnostics.
2 : Copyright (C) 2019-2026 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 10748 : checker_path::same_function_p (int event_idx_a,
51 : int event_idx_b) const
52 : {
53 10748 : return (m_events[event_idx_a]->get_fndecl ()
54 10748 : == m_events[event_idx_b]->get_fndecl ());
55 : }
56 :
57 : /* Print a single-line representation of this path to PP. */
58 :
59 : void
60 8 : checker_path::dump (pretty_printer *pp) const
61 : {
62 8 : pp_character (pp, '[');
63 :
64 8 : checker_event *e;
65 8 : int i;
66 36 : FOR_EACH_VEC_ELT (m_events, i, e)
67 : {
68 20 : if (i > 0)
69 14 : pp_string (pp, ", ");
70 20 : pp_character (pp, '"');
71 20 : e->print_desc (*pp);
72 20 : pp_character (pp, '"');
73 : }
74 8 : pp_character (pp, ']');
75 8 : }
76 :
77 : /* Print a multiline form of this path to LOGGER, prefixing it with DESC. */
78 :
79 : void
80 15956 : checker_path::maybe_log (logger *logger, const char *desc) const
81 : {
82 15956 : if (!logger)
83 : return;
84 8 : logger->start_log_line ();
85 8 : logger->log_partial ("%s: ", desc);
86 8 : dump (logger->get_printer ());
87 8 : logger->end_log_line ();
88 28 : for (unsigned i = 0; i < m_events.length (); i++)
89 : {
90 20 : logger->start_log_line ();
91 20 : logger->log_partial ("%s[%i]: %s ", desc, i,
92 20 : event_kind_to_string (m_events[i]->get_kind ()));
93 20 : m_events[i]->dump (logger->get_printer ());
94 20 : logger->end_log_line ();
95 : }
96 : }
97 :
98 : void
99 36173 : checker_path::add_event (std::unique_ptr<checker_event> event)
100 : {
101 36173 : if (m_logger)
102 : {
103 11 : m_logger->start_log_line ();
104 20 : m_logger->log_partial ("added event[%i]: %s ",
105 : m_events.length (),
106 : event_kind_to_string (event.get ()->get_kind ()));
107 11 : event.get ()->dump (m_logger->get_printer ());
108 11 : m_logger->end_log_line ();
109 : }
110 36173 : m_events.safe_push (event.release ());
111 36173 : }
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 1275 : 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 1275 : tree capacity = NULL_TREE;
144 1275 : if (model)
145 1068 : if (const svalue *capacity_sval = model->get_capacity (reg))
146 1068 : capacity = model->get_representative_tree (capacity_sval);
147 :
148 1275 : pd->add_region_creation_events (reg, capacity, loc_info, *this);
149 :
150 1275 : if (debug)
151 0 : add_event (std::make_unique<region_creation_event_debug> (reg, capacity,
152 : loc_info));
153 1275 : }
154 :
155 : void
156 3991 : checker_path::fixup_locations (pending_diagnostic *pd)
157 : {
158 28680 : for (checker_event *e : m_events)
159 16707 : e->set_location (pd->fixup_location (e->get_location (), false));
160 3991 : }
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 11758 : checker_path::cfg_edge_pair_at_p (unsigned idx) const
167 : {
168 23516 : if (m_events.length () < idx + 1)
169 : return false;
170 11617 : return (m_events[idx]->get_kind () == event_kind::start_cfg_edge
171 11617 : && 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 3991 : checker_path::inject_any_inlined_call_events (logger *logger)
234 : {
235 3991 : LOG_SCOPE (logger);
236 :
237 3991 : if (!flag_analyzer_undo_inlining)
238 4 : return;
239 :
240 : /* Build a copy of m_events with the new events inserted. */
241 3987 : auto_vec<checker_event *> updated_events;
242 :
243 3987 : maybe_log (logger, "before");
244 :
245 3987 : hash_set<tree> blocks_in_prev_event;
246 :
247 20521 : for (unsigned ev_idx = 0; ev_idx < m_events.length (); ev_idx++)
248 : {
249 16534 : checker_event *curr_event = m_events[ev_idx];
250 16534 : location_t curr_loc = curr_event->get_location ();
251 16534 : hash_set<tree> blocks_in_curr_event;
252 :
253 16534 : if (logger)
254 : {
255 6 : logger->start_log_line ();
256 6 : logger->log_partial ("event[%i]: %s ", ev_idx,
257 : event_kind_to_string (curr_event->get_kind ()));
258 6 : curr_event->dump (logger->get_printer ());
259 6 : logger->end_log_line ();
260 6 : for (inlining_iterator iter (curr_event->get_location ());
261 18 : !iter.done_p (); iter.next ())
262 : {
263 6 : logger->start_log_line ();
264 6 : logger->log_partial (" %qE", iter.get_block ());
265 6 : if (!flag_dump_noaddr)
266 6 : logger->log_partial (" (%p)", iter.get_block ());
267 6 : logger->log_partial (", fndecl: %qE, callsite: 0x%llx",
268 : iter.get_fndecl (),
269 6 : (unsigned long long) iter.get_callsite ());
270 6 : if (iter.get_callsite ())
271 2 : dump_location (logger->get_printer (), iter.get_callsite ());
272 6 : 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 16534 : struct chain_element { tree m_block; tree m_fndecl; };
285 16534 : auto_vec<chain_element> elements;
286 43426 : for (inlining_iterator iter (curr_loc); !iter.done_p (); iter.next ())
287 : {
288 13446 : chain_element ce;
289 13446 : ce.m_block = iter.get_block ();
290 13446 : ce.m_fndecl = iter.get_fndecl ();
291 :
292 13446 : if (!blocks_in_prev_event.contains (ce.m_block))
293 5624 : elements.safe_push (ce);
294 13446 : blocks_in_curr_event.add (ce.m_block);
295 : }
296 :
297 : /* Walk from outermost to innermost. */
298 16534 : if (elements.length () > 0)
299 : {
300 5459 : int orig_stack_depth = curr_event->get_original_stack_depth ();
301 5624 : for (unsigned element_idx = elements.length () - 1; element_idx > 0;
302 : element_idx--)
303 : {
304 165 : const chain_element &ce = elements[element_idx];
305 165 : int stack_depth_adjustment
306 165 : = (blocks_in_curr_event.elements () - element_idx) - 1;
307 165 : if (location_t callsite = BLOCK_SOURCE_LOCATION (ce.m_block))
308 165 : updated_events.safe_push
309 165 : (new inlined_call_event (callsite,
310 330 : elements[element_idx - 1].m_fndecl,
311 165 : ce.m_fndecl,
312 : orig_stack_depth,
313 330 : stack_depth_adjustment));
314 : }
315 : }
316 :
317 : /* Ideally we'd use assignment here:
318 : blocks_in_prev_event = blocks_in_curr_event; */
319 16534 : blocks_in_prev_event.empty ();
320 29980 : for (auto iter : blocks_in_curr_event)
321 13446 : blocks_in_prev_event.add (iter);
322 :
323 : /* Add the existing event. */
324 16534 : updated_events.safe_push (curr_event);
325 16534 : }
326 :
327 : /* Replace m_events with updated_events. */
328 3987 : m_events.truncate (0);
329 3987 : m_events.safe_splice (updated_events);
330 :
331 3987 : maybe_log (logger, " after");
332 3991 : }
333 :
334 : } // namespace ana
335 :
336 : #endif /* #if ENABLE_ANALYZER */
|