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 | |
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 (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 | |
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 (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 | |
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 (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 | |
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 (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 | |
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 (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 | |
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 (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 | |
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, 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 | |