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