1 | /* Handling for the various __analyzer_* known functions. |
2 | Copyright (C) 2020-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 "analyzer/analyzer.h" |
31 | #include "analyzer/analyzer-logging.h" |
32 | #include "diagnostic.h" |
33 | #include "tree-diagnostic.h" /* for default_tree_printer. */ |
34 | #include "analyzer/region-model.h" |
35 | #include "analyzer/pending-diagnostic.h" |
36 | #include "analyzer/call-details.h" |
37 | #include "make-unique.h" |
38 | |
39 | #if ENABLE_ANALYZER |
40 | |
41 | namespace ana { |
42 | |
43 | /* Handle calls to "__analyzer_break" by triggering a breakpoint within |
44 | the analyzer. */ |
45 | |
46 | class kf_analyzer_break : public known_function |
47 | { |
48 | public: |
49 | bool matches_call_types_p (const call_details &cd) const final override |
50 | { |
51 | return cd.num_args () == 0; |
52 | } |
53 | void impl_call_pre (const call_details &) const final override |
54 | { |
55 | /* TODO: is there a good cross-platform way to do this? */ |
56 | raise (SIGINT); |
57 | } |
58 | }; |
59 | |
60 | /* Handler for calls to "__analyzer_describe". |
61 | |
62 | Emit a warning describing the 2nd argument (which can be of any |
63 | type), at the given verbosity level. This is for use when |
64 | debugging, and may be of use in DejaGnu tests. */ |
65 | |
66 | class kf_analyzer_describe : public known_function |
67 | { |
68 | public: |
69 | bool matches_call_types_p (const call_details &cd) const final override |
70 | { |
71 | return cd.num_args () == 2; |
72 | } |
73 | void impl_call_pre (const call_details &cd) const final override |
74 | { |
75 | if (!cd.get_ctxt ()) |
76 | return; |
77 | tree t_verbosity = cd.get_arg_tree (idx: 0); |
78 | const svalue *sval = cd.get_arg_svalue (idx: 1); |
79 | bool simple = zerop (t_verbosity); |
80 | label_text desc = sval->get_desc (simple); |
81 | warning_at (cd.get_location (), 0, "svalue: %qs" , desc.get ()); |
82 | } |
83 | }; |
84 | |
85 | /* Handler for calls to "__analyzer_dump_capacity". |
86 | |
87 | Emit a warning describing the capacity of the base region of |
88 | the region pointed to by the 1st argument. |
89 | This is for use when debugging, and may be of use in DejaGnu tests. */ |
90 | |
91 | class kf_analyzer_dump_capacity : public known_function |
92 | { |
93 | public: |
94 | bool matches_call_types_p (const call_details &cd) const final override |
95 | { |
96 | return (cd.num_args () == 1 |
97 | && cd.arg_is_pointer_p (idx: 0)); |
98 | } |
99 | |
100 | void impl_call_pre (const call_details &cd) const final override |
101 | { |
102 | region_model_context *ctxt = cd.get_ctxt (); |
103 | if (!ctxt) |
104 | return; |
105 | region_model *model = cd.get_model (); |
106 | tree t_ptr = cd.get_arg_tree (idx: 0); |
107 | const svalue *sval_ptr = model->get_rvalue (expr: t_ptr, ctxt); |
108 | const region *reg = model->deref_rvalue (ptr_sval: sval_ptr, ptr_tree: t_ptr, ctxt); |
109 | const region *base_reg = reg->get_base_region (); |
110 | const svalue *capacity = model->get_capacity (reg: base_reg); |
111 | label_text desc = capacity->get_desc (simple: true); |
112 | warning_at (cd.get_call_stmt ()->location, 0, |
113 | "capacity: %qs" , desc.get ()); |
114 | } |
115 | }; |
116 | |
117 | /* Compare D1 and D2 using their names, and then IDs to order them. */ |
118 | |
119 | static int |
120 | cmp_decls (tree d1, tree d2) |
121 | { |
122 | gcc_assert (DECL_P (d1)); |
123 | gcc_assert (DECL_P (d2)); |
124 | if (DECL_NAME (d1) && DECL_NAME (d2)) |
125 | if (int cmp = strcmp (IDENTIFIER_POINTER (DECL_NAME (d1)), |
126 | IDENTIFIER_POINTER (DECL_NAME (d2)))) |
127 | return cmp; |
128 | return (int)DECL_UID (d1) - (int)DECL_UID (d2); |
129 | } |
130 | |
131 | /* Comparator for use by vec<tree>::qsort, |
132 | using their names, and then IDs to order them. */ |
133 | |
134 | static int |
135 | cmp_decls_ptr_ptr (const void *p1, const void *p2) |
136 | { |
137 | tree const *d1 = (tree const *)p1; |
138 | tree const *d2 = (tree const *)p2; |
139 | |
140 | return cmp_decls (d1: *d1, d2: *d2); |
141 | } |
142 | |
143 | /* Handler for calls to "__analyzer_dump_escaped". |
144 | |
145 | Emit a warning giving the number of decls that have escaped, followed |
146 | by a comma-separated list of their names, in alphabetical order. |
147 | |
148 | This is for use when debugging, and may be of use in DejaGnu tests. */ |
149 | |
150 | class kf_analyzer_dump_escaped : public known_function |
151 | { |
152 | public: |
153 | bool matches_call_types_p (const call_details &cd) const final override |
154 | { |
155 | return cd.num_args () == 0; |
156 | } |
157 | void impl_call_pre (const call_details &cd) const final override |
158 | { |
159 | region_model_context *ctxt = cd.get_ctxt (); |
160 | if (!ctxt) |
161 | return; |
162 | region_model *model = cd.get_model (); |
163 | |
164 | auto_vec<tree> escaped_decls; |
165 | for (auto iter : *model->get_store ()) |
166 | { |
167 | const binding_cluster *c = iter.second; |
168 | if (!c->escaped_p ()) |
169 | continue; |
170 | if (tree decl = c->get_base_region ()->maybe_get_decl ()) |
171 | escaped_decls.safe_push (obj: decl); |
172 | } |
173 | |
174 | /* Sort them into deterministic order; alphabetical is |
175 | probably most user-friendly. */ |
176 | escaped_decls.qsort (cmp_decls_ptr_ptr); |
177 | |
178 | pretty_printer pp; |
179 | pp_format_decoder (&pp) = default_tree_printer; |
180 | pp_show_color (&pp) = pp_show_color (global_dc->printer); |
181 | bool first = true; |
182 | for (auto iter : escaped_decls) |
183 | { |
184 | if (first) |
185 | first = false; |
186 | else |
187 | pp_string (&pp, ", " ); |
188 | pp_printf (&pp, "%qD" , iter); |
189 | } |
190 | /* Print the number to make it easier to write DejaGnu tests for |
191 | the "nothing has escaped" case. */ |
192 | warning_at (cd.get_location (), 0, "escaped: %i: %s" , |
193 | escaped_decls.length (), |
194 | pp_formatted_text (&pp)); |
195 | } |
196 | }; |
197 | |
198 | /* Placeholder handler for calls to "__analyzer_dump_exploded_nodes". |
199 | This is a no-op; the real implementation happens when the |
200 | exploded_graph is postprocessed. */ |
201 | |
202 | class kf_analyzer_dump_exploded_nodes : public known_function |
203 | { |
204 | public: |
205 | bool matches_call_types_p (const call_details &cd) const final override |
206 | { |
207 | return cd.num_args () == 1; |
208 | } |
209 | }; |
210 | |
211 | /* Handler for calls to "__analyzer_dump_named_constant". |
212 | |
213 | Look up the given name, and emit a warning describing the |
214 | state of the corresponding stashed value. |
215 | |
216 | This is for use when debugging, and for DejaGnu tests. */ |
217 | |
218 | class kf_analyzer_dump_named_constant : public known_function |
219 | { |
220 | public: |
221 | bool matches_call_types_p (const call_details &cd) const final override |
222 | { |
223 | return cd.num_args () == 1; |
224 | } |
225 | void impl_call_pre (const call_details &cd) const final override |
226 | { |
227 | region_model_context *ctxt = cd.get_ctxt (); |
228 | if (!ctxt) |
229 | return; |
230 | |
231 | const char *name = cd.get_arg_string_literal (idx: 0); |
232 | if (!name) |
233 | { |
234 | error_at (cd.get_location (), "cannot determine name" ); |
235 | return; |
236 | } |
237 | tree value = get_stashed_constant_by_name (name); |
238 | if (value) |
239 | warning_at (cd.get_location (), 0, "named constant %qs has value %qE" , |
240 | name, value); |
241 | else |
242 | warning_at (cd.get_location (), 0, "named constant %qs has unknown value" , |
243 | name); |
244 | } |
245 | }; |
246 | |
247 | /* A pending_diagnostic subclass for implementing "__analyzer_dump_path". */ |
248 | |
249 | class dump_path_diagnostic |
250 | : public pending_diagnostic_subclass<dump_path_diagnostic> |
251 | { |
252 | public: |
253 | int get_controlling_option () const final override |
254 | { |
255 | return 0; |
256 | } |
257 | |
258 | bool emit (diagnostic_emission_context &ctxt) final override |
259 | { |
260 | ctxt.inform ("path" ); |
261 | return true; |
262 | } |
263 | |
264 | const char *get_kind () const final override |
265 | { |
266 | return "dump_path_diagnostic" ; |
267 | } |
268 | |
269 | bool operator== (const dump_path_diagnostic &) const |
270 | { |
271 | return true; |
272 | } |
273 | }; |
274 | |
275 | /* Handle calls to "__analyzer_dump_path" by queuing a diagnostic at this |
276 | exploded_node. */ |
277 | |
278 | class kf_analyzer_dump_path : public known_function |
279 | { |
280 | public: |
281 | bool matches_call_types_p (const call_details &cd) const final override |
282 | { |
283 | return cd.num_args () == 0; |
284 | } |
285 | void impl_call_pre (const call_details &cd) const final override |
286 | { |
287 | region_model_context *ctxt = cd.get_ctxt (); |
288 | if (!ctxt) |
289 | return; |
290 | ctxt->warn (d: make_unique<dump_path_diagnostic> ()); |
291 | } |
292 | }; |
293 | |
294 | /* Handle calls to "__analyzer_dump_region_model" by dumping |
295 | the region model's state to stderr. */ |
296 | |
297 | class kf_analyzer_dump_region_model : public known_function |
298 | { |
299 | public: |
300 | bool matches_call_types_p (const call_details &cd) const final override |
301 | { |
302 | return cd.num_args () == 0; |
303 | } |
304 | void impl_call_pre (const call_details &cd) const final override |
305 | { |
306 | region_model_context *ctxt = cd.get_ctxt (); |
307 | if (!ctxt) |
308 | return; |
309 | region_model *model = cd.get_model (); |
310 | model->dump (simple: false); |
311 | } |
312 | }; |
313 | |
314 | /* Handle a call to "__analyzer_eval" by evaluating the input |
315 | and dumping as a dummy warning, so that test cases can use |
316 | dg-warning to validate the result (and so unexpected warnings will |
317 | lead to DejaGnu failures). |
318 | Broken out as a subroutine to make it easier to put a breakpoint on it |
319 | - though typically this doesn't help, as we have an SSA name as the arg, |
320 | and what's more interesting is usually the def stmt for that name. */ |
321 | |
322 | class kf_analyzer_eval : public known_function |
323 | { |
324 | public: |
325 | bool matches_call_types_p (const call_details &cd) const final override |
326 | { |
327 | return cd.num_args () == 1; |
328 | } |
329 | void impl_call_pre (const call_details &cd) const final override |
330 | { |
331 | region_model_context *ctxt = cd.get_ctxt (); |
332 | if (!ctxt) |
333 | return; |
334 | region_model *model = cd.get_model (); |
335 | |
336 | tree t_arg = cd.get_arg_tree (idx: 0); |
337 | tristate t = model->eval_condition (lhs: t_arg, op: NE_EXPR, integer_zero_node, |
338 | ctxt); |
339 | warning_at (cd.get_location (), 0, "%s" , t.as_string ()); |
340 | } |
341 | }; |
342 | |
343 | /* Handler for "__analyzer_get_unknown_ptr". */ |
344 | |
345 | class kf_analyzer_get_unknown_ptr : public known_function |
346 | { |
347 | public: |
348 | bool matches_call_types_p (const call_details &cd) const final override |
349 | { |
350 | return cd.num_args () == 0; |
351 | } |
352 | void impl_call_pre (const call_details &cd) const final override |
353 | { |
354 | region_model_manager *mgr = cd.get_manager (); |
355 | const svalue *ptr_sval |
356 | = mgr->get_or_create_unknown_svalue (type: cd.get_lhs_type ()); |
357 | cd.maybe_set_lhs (result: ptr_sval); |
358 | } |
359 | }; |
360 | |
361 | /* Populate KFM with instances of known functions used for debugging the |
362 | analyzer and for writing DejaGnu tests, all with a "__analyzer_" prefix. */ |
363 | |
364 | void |
365 | register_known_analyzer_functions (known_function_manager &kfm) |
366 | { |
367 | kfm.add (name: "__analyzer_break" , kf: make_unique<kf_analyzer_break> ()); |
368 | kfm.add (name: "__analyzer_describe" , kf: make_unique<kf_analyzer_describe> ()); |
369 | kfm.add (name: "__analyzer_dump_capacity" , |
370 | kf: make_unique<kf_analyzer_dump_capacity> ()); |
371 | kfm.add (name: "__analyzer_dump_escaped" , kf: make_unique<kf_analyzer_dump_escaped> ()); |
372 | kfm.add (name: "__analyzer_dump_exploded_nodes" , |
373 | kf: make_unique<kf_analyzer_dump_exploded_nodes> ()); |
374 | kfm.add (name: "__analyzer_dump_named_constant" , |
375 | kf: make_unique<kf_analyzer_dump_named_constant> ()); |
376 | kfm.add (name: "__analyzer_dump_path" , kf: make_unique<kf_analyzer_dump_path> ()); |
377 | kfm.add (name: "__analyzer_dump_region_model" , |
378 | kf: make_unique<kf_analyzer_dump_region_model> ()); |
379 | kfm.add (name: "__analyzer_eval" , kf: make_unique<kf_analyzer_eval> ()); |
380 | kfm.add (name: "__analyzer_get_unknown_ptr" , |
381 | kf: make_unique<kf_analyzer_get_unknown_ptr> ()); |
382 | kfm.add (name: "__analyzer_get_strlen" , kf: make_kf_strlen ()); |
383 | } |
384 | |
385 | } // namespace ana |
386 | |
387 | #endif /* #if ENABLE_ANALYZER */ |
388 | |