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
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along 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
41namespace ana {
42
43/* Handle calls to "__analyzer_break" by triggering a breakpoint within
44 the analyzer. */
45
46class kf_analyzer_break : public known_function
47{
48public:
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
66class kf_analyzer_describe : public known_function
67{
68public:
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
91class kf_analyzer_dump_capacity : public known_function
92{
93public:
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
119static int
120cmp_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
134static int
135cmp_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
150class kf_analyzer_dump_escaped : public known_function
151{
152public:
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
202class kf_analyzer_dump_exploded_nodes : public known_function
203{
204public:
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
218class kf_analyzer_dump_named_constant : public known_function
219{
220public:
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
249class dump_path_diagnostic
250 : public pending_diagnostic_subclass<dump_path_diagnostic>
251{
252public:
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
278class kf_analyzer_dump_path : public known_function
279{
280public:
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
297class kf_analyzer_dump_region_model : public known_function
298{
299public:
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
322class kf_analyzer_eval : public known_function
323{
324public:
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
345class kf_analyzer_get_unknown_ptr : public known_function
346{
347public:
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
364void
365register_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

source code of gcc/analyzer/kf-analyzer.cc