]>
Commit | Line | Data |
---|---|---|
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 (0); | |
78 | const svalue *sval = cd.get_arg_svalue (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 (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 (0); | |
107 | const svalue *sval_ptr = model->get_rvalue (t_ptr, ctxt); | |
108 | const region *reg = model->deref_rvalue (sval_ptr, t_ptr, ctxt); | |
109 | const region *base_reg = reg->get_base_region (); | |
110 | const svalue *capacity = model->get_capacity (base_reg); | |
111 | label_text desc = capacity->get_desc (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, *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 (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 (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 (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 (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 (0); | |
337 | tristate t = model->eval_condition (t_arg, 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 (cd.get_lhs_type ()); | |
357 | cd.maybe_set_lhs (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 ("__analyzer_break", make_unique<kf_analyzer_break> ()); | |
368 | kfm.add ("__analyzer_describe", make_unique<kf_analyzer_describe> ()); | |
369 | kfm.add ("__analyzer_dump_capacity", | |
370 | make_unique<kf_analyzer_dump_capacity> ()); | |
371 | kfm.add ("__analyzer_dump_escaped", make_unique<kf_analyzer_dump_escaped> ()); | |
372 | kfm.add ("__analyzer_dump_exploded_nodes", | |
373 | make_unique<kf_analyzer_dump_exploded_nodes> ()); | |
374 | kfm.add ("__analyzer_dump_named_constant", | |
375 | make_unique<kf_analyzer_dump_named_constant> ()); | |
376 | kfm.add ("__analyzer_dump_path", make_unique<kf_analyzer_dump_path> ()); | |
377 | kfm.add ("__analyzer_dump_region_model", | |
378 | make_unique<kf_analyzer_dump_region_model> ()); | |
379 | kfm.add ("__analyzer_eval", make_unique<kf_analyzer_eval> ()); | |
380 | kfm.add ("__analyzer_get_unknown_ptr", | |
381 | make_unique<kf_analyzer_get_unknown_ptr> ()); | |
382 | kfm.add ("__analyzer_get_strlen", make_kf_strlen ()); | |
383 | } | |
384 | ||
385 | } // namespace ana | |
386 | ||
387 | #endif /* #if ENABLE_ANALYZER */ |