analyzer: introduce struct event_loc_info
[platform/upstream/gcc.git] / gcc / analyzer / checker-path.cc
1 /* Subclass of diagnostic_path for analyzer diagnostics.
2    Copyright (C) 2019-2022 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_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
63 namespace ana {
64
65 /* Print a single-line representation of this path to PP.  */
66
67 void
68 checker_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 (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
86 void
87 checker_path::maybe_log (logger *logger, const char *desc) const
88 {
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++)
96     {
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 ();
102     }
103 }
104
105 void
106 checker_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 ("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 ();
116     }
117   m_events.safe_push (event.release ());
118 }
119
120 /* Print a multiline form of this path to STDERR.  */
121
122 DEBUG_FUNCTION void
123 checker_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 (false));
130       fprintf (stderr,
131                "[%i]: %s \"%s\"\n",
132                i,
133                event_kind_to_string (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
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)
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 (capacity_sval);
154
155   pd->add_region_creation_events (reg, capacity, loc_info, *this);
156
157   if (debug)
158     add_event (make_unique<region_creation_event_debug> (reg, capacity,
159                                                          loc_info));
160 }
161
162 void
163 checker_path::fixup_locations (pending_diagnostic *pd)
164 {
165   for (checker_event *e : m_events)
166     e->set_location (pd->fixup_location (e->get_location (), 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
172 bool
173 checker_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
239 void
240 checker_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, "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 ("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 ())
269             {
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 ();
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 (ce.m_block))
299             elements.safe_push (ce);
300           blocks_in_curr_event.add (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                   (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 (iter);
328
329       /* Add the existing event.  */
330       updated_events.safe_push (curr_event);
331     }
332
333   /* Replace m_events with updated_events.  */
334   m_events.truncate (0);
335   m_events.safe_splice (updated_events);
336
337   maybe_log (logger, " after");
338 }
339
340 } // namespace ana
341
342 #endif /* #if ENABLE_ANALYZER */