0cc0b2bf81f4804499e365919cb2468d523243b8
[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                                           location_t loc,
148                                           tree fndecl, int depth,
149                                           bool debug)
150 {
151   tree capacity = NULL_TREE;
152   if (model)
153     if (const svalue *capacity_sval = model->get_capacity (reg))
154       capacity = model->get_representative_tree (capacity_sval);
155
156   pd->add_region_creation_events (reg, capacity, loc, fndecl, depth, *this);
157
158   if (debug)
159     add_event (make_unique<region_creation_event_debug> (reg, capacity,
160                                                          loc, fndecl, depth));
161 }
162
163 void
164 checker_path::fixup_locations (pending_diagnostic *pd)
165 {
166   for (checker_event *e : m_events)
167     e->set_location (pd->fixup_location (e->get_location (), false));
168 }
169
170 /* Return true if there is a (start_cfg_edge_event, end_cfg_edge_event) pair
171    at (IDX, IDX + 1).  */
172
173 bool
174 checker_path::cfg_edge_pair_at_p (unsigned idx) const
175 {
176   if (m_events.length () < idx + 1)
177     return false;
178   return (m_events[idx]->m_kind == EK_START_CFG_EDGE
179           && m_events[idx + 1]->m_kind == EK_END_CFG_EDGE);
180 }
181
182 /* Consider a call from "outer" to "middle" which calls "inner",
183    where "inner" and "middle" have been inlined into "outer".
184
185    We expect the stmt locations for the inlined stmts to have a
186    chain like:
187
188      [{fndecl: inner},
189       {fndecl: middle, callsite: within middle to inner},
190       {fndecl: outer, callsite: without outer to middle}]
191
192    The location for the stmt will already be fixed up to reflect
193    the two extra frames, so that we have e.g. this as input
194    (for gcc.dg/analyzer/inlining-4.c):
195
196     before[0]:
197       EK_FUNCTION_ENTRY "entry to ‘outer’"
198       (depth 1, fndecl ‘outer’, m_loc=511c4)
199     before[1]:
200       EK_START_CFG_EDGE "following ‘true’ branch (when ‘flag != 0’)..."
201       (depth 3 corrected from 1,
202        fndecl ‘inner’ corrected from ‘outer’, m_loc=8000000f)
203     before[2]:
204       EK_END_CFG_EDGE "...to here"
205       (depth 1, fndecl ‘outer’, m_loc=0)
206     before[3]:
207       EK_WARNING "here (‘<unknown>’ is in state ‘null’)"
208       (depth 1, fndecl ‘outer’, m_loc=80000004)
209
210    We want to add inlined_call_events showing the calls, so that
211    the above becomes:
212
213     after[0]:
214       EK_FUNCTION_ENTRY "entry to ‘outer’"
215       (depth 1, fndecl ‘outer’, m_loc=511c4)
216     after[1]:
217       EK_INLINED_CALL "inlined call to ‘middle’ from ‘outer’"
218       (depth 1, fndecl ‘outer’, m_loc=53300)
219     after[2]:
220       EK_INLINED_CALL "inlined call to ‘inner’ from ‘middle’"
221       (depth 2, fndecl ‘middle’, m_loc=4d2e0)
222     after[3]:
223       EK_START_CFG_EDGE "following ‘true’ branch (when ‘flag != 0’)..."
224       (depth 3 corrected from 1,
225        fndecl ‘inner’ corrected from ‘outer’, m_loc=8000000f)
226     after[4]: EK_END_CFG_EDGE "...to here"
227       (depth 1, fndecl ‘outer’, m_loc=0)
228     after[5]: EK_WARNING "here (‘<unknown>’ is in state ‘null’)"
229       (depth 1, fndecl ‘outer’, m_loc=80000004)
230
231     where we've added events between before[0] and before[1] to show
232     the inlined calls leading to the effective stack depths, making
233     the generated path much easier for a user to read.
234
235     Note how in the above we have a branch (before[1] to before[2])
236     where the locations were originally in different functions.
237     Hence we have to add these events quite late when generating
238     checker_path.  */
239
240 void
241 checker_path::inject_any_inlined_call_events (logger *logger)
242 {
243   LOG_SCOPE (logger);
244
245   if (!flag_analyzer_undo_inlining)
246     return;
247
248   /* Build a copy of m_events with the new events inserted.  */
249   auto_vec<checker_event *> updated_events;
250
251   maybe_log (logger, "before");
252
253   hash_set<tree> blocks_in_prev_event;
254
255   for (unsigned ev_idx = 0; ev_idx < m_events.length (); ev_idx++)
256     {
257       checker_event *curr_event = m_events[ev_idx];
258       location_t curr_loc = curr_event->get_location ();
259       hash_set<tree> blocks_in_curr_event;
260
261       if (logger)
262         {
263           logger->start_log_line ();
264           logger->log_partial ("event[%i]: %s ", ev_idx,
265                                event_kind_to_string (curr_event->m_kind));
266           curr_event->dump (logger->get_printer ());
267           logger->end_log_line ();
268           for (inlining_iterator iter (curr_event->get_location ());
269                !iter.done_p (); iter.next ())
270             {
271               logger->start_log_line ();
272               logger->log_partial ("  %qE", iter.get_block ());
273               if (!flag_dump_noaddr)
274                 logger->log_partial (" (%p)", iter.get_block ());
275               logger->log_partial (", fndecl: %qE, callsite: 0x%x",
276                                    iter.get_fndecl (), iter.get_callsite ());
277               if (iter.get_callsite ())
278                 dump_location (logger->get_printer (), iter.get_callsite ());
279               logger->end_log_line ();
280             }
281         }
282
283       /* We want to add events to show inlined calls.
284
285          We want to show changes relative to the previous event, omitting
286          the commonality between the inlining chain.
287
288          The chain is ordered from innermost frame to outermost frame;
289          we want to walk it backwards to show the calls, so capture it
290          in a vec.  */
291       struct chain_element { tree m_block; tree m_fndecl; };
292       auto_vec<chain_element> elements;
293       for (inlining_iterator iter (curr_loc); !iter.done_p (); iter.next ())
294         {
295           chain_element ce;
296           ce.m_block = iter.get_block ();
297           ce.m_fndecl = iter.get_fndecl ();
298
299           if (!blocks_in_prev_event.contains (ce.m_block))
300             elements.safe_push (ce);
301           blocks_in_curr_event.add (ce.m_block);
302         }
303
304       /* Walk from outermost to innermost.  */
305       if (elements.length () > 0)
306         {
307           int orig_stack_depth = curr_event->get_original_stack_depth ();
308           for (unsigned element_idx = elements.length () - 1; element_idx > 0;
309                element_idx--)
310             {
311               const chain_element &ce = elements[element_idx];
312               int stack_depth_adjustment
313                 = (blocks_in_curr_event.elements () - element_idx) - 1;
314               if (location_t callsite = BLOCK_SOURCE_LOCATION (ce.m_block))
315                 updated_events.safe_push
316                   (new inlined_call_event (callsite,
317                                            elements[element_idx - 1].m_fndecl,
318                                            ce.m_fndecl,
319                                            orig_stack_depth,
320                                            stack_depth_adjustment));
321             }
322         }
323
324       /* Ideally we'd use assignment here:
325            blocks_in_prev_event = blocks_in_curr_event; */
326       blocks_in_prev_event.empty ();
327       for (auto iter : blocks_in_curr_event)
328         blocks_in_prev_event.add (iter);
329
330       /* Add the existing event.  */
331       updated_events.safe_push (curr_event);
332     }
333
334   /* Replace m_events with updated_events.  */
335   m_events.truncate (0);
336   m_events.safe_splice (updated_events);
337
338   maybe_log (logger, " after");
339 }
340
341 } // namespace ana
342
343 #endif /* #if ENABLE_ANALYZER */