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
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along 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
63namespace ana {
64
65/* Print a single-line representation of this path to PP. */
66
67void
68checker_path::dump (pretty_printer *pp) const
69{
70 pp_character (pp, '[');
71
72 checker_event *e;
73 int i;
74 FOR_EACH_VEC_ELT (m_events, i, e)
75 {
76 if (i > 0)
77 pp_string (pp, ", ");
78 label_text event_desc (e->get_desc (can_colorize: false));
79 pp_printf (pp, "\"%s\"", event_desc.get ());
80 }
81 pp_character (pp, ']');
82}
83
84/* Print a multiline form of this path to LOGGER, prefixing it with DESC. */
85
86void
87checker_path::maybe_log (logger *logger, const char *desc) const
88{
89 if (!logger)
90 return;
91 logger->start_log_line ();
92 logger->log_partial (fmt: "%s: ", desc);
93 dump (pp: logger->get_printer ());
94 logger->end_log_line ();
95 for (unsigned i = 0; i < m_events.length (); i++)
96 {
97 logger->start_log_line ();
98 logger->log_partial (fmt: "%s[%i]: %s ", desc, i,
99 event_kind_to_string (ek: m_events[i]->m_kind));
100 m_events[i]->dump (pp: logger->get_printer ());
101 logger->end_log_line ();
102 }
103}
104
105void
106checker_path::add_event (std::unique_ptr<checker_event> event)
107{
108 if (m_logger)
109 {
110 m_logger->start_log_line ();
111 m_logger->log_partial (fmt: "added event[%i]: %s ",
112 m_events.length (),
113 event_kind_to_string (ek: event.get ()->m_kind));
114 event.get ()->dump (pp: m_logger->get_printer ());
115 m_logger->end_log_line ();
116 }
117 m_events.safe_push (obj: event.release ());
118}
119
120/* Print a multiline form of this path to STDERR. */
121
122DEBUG_FUNCTION void
123checker_path::debug () const
124{
125 checker_event *e;
126 int i;
127 FOR_EACH_VEC_ELT (m_events, i, e)
128 {
129 label_text event_desc (e->get_desc (can_colorize: false));
130 fprintf (stderr,
131 format: "[%i]: %s \"%s\"\n",
132 i,
133 event_kind_to_string (ek: m_events[i]->m_kind),
134 event_desc.get ());
135 }
136}
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
143void
144checker_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 tree capacity = NULL_TREE;
151 if (model)
152 if (const svalue *capacity_sval = model->get_capacity (reg))
153 capacity = model->get_representative_tree (sval: capacity_sval);
154
155 pd->add_region_creation_events (reg, capacity, loc_info, emission_path&: *this);
156
157 if (debug)
158 add_event (event: make_unique<region_creation_event_debug> (args&: reg, args&: capacity,
159 args: loc_info));
160}
161
162void
163checker_path::fixup_locations (pending_diagnostic *pd)
164{
165 for (checker_event *e : m_events)
166 e->set_location (pd->fixup_location (loc: e->get_location (), primary: false));
167}
168
169/* Return true if there is a (start_cfg_edge_event, end_cfg_edge_event) pair
170 at (IDX, IDX + 1). */
171
172bool
173checker_path::cfg_edge_pair_at_p (unsigned idx) const
174{
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);
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
239void
240checker_path::inject_any_inlined_call_events (logger *logger)
241{
242 LOG_SCOPE (logger);
243
244 if (!flag_analyzer_undo_inlining)
245 return;
246
247 /* Build a copy of m_events with the new events inserted. */
248 auto_vec<checker_event *> updated_events;
249
250 maybe_log (logger, desc: "before");
251
252 hash_set<tree> blocks_in_prev_event;
253
254 for (unsigned ev_idx = 0; ev_idx < m_events.length (); ev_idx++)
255 {
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;
259
260 if (logger)
261 {
262 logger->start_log_line ();
263 logger->log_partial (fmt: "event[%i]: %s ", ev_idx,
264 event_kind_to_string (ek: curr_event->m_kind));
265 curr_event->dump (pp: logger->get_printer ());
266 logger->end_log_line ();
267 for (inlining_iterator iter (curr_event->get_location ());
268 !iter.done_p (); iter.next ())
269 {
270 logger->start_log_line ();
271 logger->log_partial (fmt: " %qE", iter.get_block ());
272 if (!flag_dump_noaddr)
273 logger->log_partial (fmt: " (%p)", iter.get_block ());
274 logger->log_partial (fmt: ", fndecl: %qE, callsite: 0x%x",
275 iter.get_fndecl (), iter.get_callsite ());
276 if (iter.get_callsite ())
277 dump_location (buffer: logger->get_printer (), loc: iter.get_callsite ());
278 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 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 ())
293 {
294 chain_element ce;
295 ce.m_block = iter.get_block ();
296 ce.m_fndecl = iter.get_fndecl ();
297
298 if (!blocks_in_prev_event.contains (k: ce.m_block))
299 elements.safe_push (obj: ce);
300 blocks_in_curr_event.add (k: ce.m_block);
301 }
302
303 /* Walk from outermost to innermost. */
304 if (elements.length () > 0)
305 {
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--)
309 {
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 (obj: new inlined_call_event (callsite,
316 elements[element_idx - 1].m_fndecl,
317 ce.m_fndecl,
318 orig_stack_depth,
319 stack_depth_adjustment));
320 }
321 }
322
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 (k: iter);
328
329 /* Add the existing event. */
330 updated_events.safe_push (obj: curr_event);
331 }
332
333 /* Replace m_events with updated_events. */
334 m_events.truncate (size: 0);
335 m_events.safe_splice (src: updated_events);
336
337 maybe_log (logger, desc: " after");
338}
339
340} // namespace ana
341
342#endif /* #if ENABLE_ANALYZER */
343

source code of gcc/analyzer/checker-path.cc