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