From 4409152a4acaec5b58a93996088d0df9aaa779b8 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Fri, 7 Jan 2022 13:36:00 -0500 Subject: [PATCH] analyzer: implement __analyzer_dump_escaped PR analyzer/103546 seems to involve an issue in how the analyzer tracks which decls have escaped, so this patch adds a way to directly test this from DejaGnu. gcc/analyzer/ChangeLog: * region-model-impl-calls.cc (cmp_decls): New. (cmp_decls_ptr_ptr): New. (region_model::impl_call_analyzer_dump_escaped): New. * region-model.cc (region_model::on_stmt_pre): Handle __analyzer_dump_escaped. * region-model.h (region_model::impl_call_analyzer_dump_escaped): New decl. * store.h (binding_cluster::get_base_region): New accessor. gcc/ChangeLog: * doc/analyzer.texi (Special Functions for Debugging the Analyzer): Document __analyzer_dump_escaped. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/analyzer-decls.h (__analyzer_dump_escaped): New decl. * gcc.dg/analyzer/escaping-1.c: New test. --- gcc/analyzer/region-model-impl-calls.cc | 69 ++++++++++++++++++++++++++ gcc/analyzer/region-model.cc | 2 + gcc/analyzer/region-model.h | 1 + gcc/analyzer/store.h | 2 + gcc/doc/analyzer.texi | 8 +++ gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h | 3 ++ gcc/testsuite/gcc.dg/analyzer/escaping-1.c | 27 ++++++++++ 7 files changed, 112 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/analyzer/escaping-1.c diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc index 9063acd..c20058e 100644 --- a/gcc/analyzer/region-model-impl-calls.cc +++ b/gcc/analyzer/region-model-impl-calls.cc @@ -264,6 +264,75 @@ region_model::impl_call_analyzer_dump_capacity (const gcall *call, warning_at (call->location, 0, "capacity: %qs", desc.m_buffer); } +/* Compare D1 and D2 using their names, and then IDs to order them. */ + +static int +cmp_decls (tree d1, tree d2) +{ + gcc_assert (DECL_P (d1)); + gcc_assert (DECL_P (d2)); + if (DECL_NAME (d1) && DECL_NAME (d2)) + if (int cmp = strcmp (IDENTIFIER_POINTER (DECL_NAME (d1)), + IDENTIFIER_POINTER (DECL_NAME (d2)))) + return cmp; + return (int)DECL_UID (d1) - (int)DECL_UID (d2); +} + +/* Comparator for use by vec::qsort, + using their names, and then IDs to order them. */ + +static int +cmp_decls_ptr_ptr (const void *p1, const void *p2) +{ + tree const *d1 = (tree const *)p1; + tree const *d2 = (tree const *)p2; + + return cmp_decls (*d1, *d2); +} + +/* Handle a call to "__analyzer_dump_escaped". + + Emit a warning giving the number of decls that have escaped, followed + by a comma-separated list of their names, in alphabetical order. + + This is for use when debugging, and may be of use in DejaGnu tests. */ + +void +region_model::impl_call_analyzer_dump_escaped (const gcall *call) +{ + auto_vec escaped_decls; + for (auto iter : m_store) + { + const binding_cluster *c = iter.second; + if (!c->escaped_p ()) + continue; + if (tree decl = c->get_base_region ()->maybe_get_decl ()) + escaped_decls.safe_push (decl); + } + + /* Sort them into deterministic order; alphabetical is + probably most user-friendly. */ + escaped_decls.qsort (cmp_decls_ptr_ptr); + + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_show_color (&pp) = pp_show_color (global_dc->printer); + bool first = true; + for (auto iter : escaped_decls) + { + if (first) + first = false; + else + pp_string (&pp, ", "); + pp_printf (&pp, "%qD", iter); + } + /* Print the number to make it easier to write DejaGnu tests for + the "nothing has escaped" case. */ + warning_at (call->location, 0, "escaped: %i: %s", + escaped_decls.length (), + pp_formatted_text (&pp)); +} + /* Handle a call to "__analyzer_eval" by evaluating the input and dumping as a dummy warning, so that test cases can use dg-warning to validate the result (and so unexpected warnings will diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index b737194..cb86d79 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -999,6 +999,8 @@ region_model::on_stmt_pre (const gimple *stmt, impl_call_analyzer_describe (call, ctxt); else if (is_special_named_call_p (call, "__analyzer_dump_capacity", 1)) impl_call_analyzer_dump_capacity (call, ctxt); + else if (is_special_named_call_p (call, "__analyzer_dump_escaped", 0)) + impl_call_analyzer_dump_escaped (call); else if (is_special_named_call_p (call, "__analyzer_dump_path", 0)) { /* Handle the builtin "__analyzer_dump_path" by queuing a diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index 8e35be1..669f1c7 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -573,6 +573,7 @@ class region_model region_model_context *ctxt); void impl_call_analyzer_dump_capacity (const gcall *call, region_model_context *ctxt); + void impl_call_analyzer_dump_escaped (const gcall *call); void impl_call_analyzer_eval (const gcall *call, region_model_context *ctxt); void impl_call_builtin_expect (const call_details &cd); diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h index 4672886..f30b6bc 100644 --- a/gcc/analyzer/store.h +++ b/gcc/analyzer/store.h @@ -559,6 +559,8 @@ public: bool symbolic_p () const; + const region *get_base_region () const { return m_base_region; } + void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const; void dump (bool simple) const; diff --git a/gcc/doc/analyzer.texi b/gcc/doc/analyzer.texi index 62faac4..06eb98f 100644 --- a/gcc/doc/analyzer.texi +++ b/gcc/doc/analyzer.texi @@ -487,6 +487,14 @@ will emit a warning describing the capacity of the base region of the region pointed to by the 1st argument. @smallexample +extern void __analyzer_dump_escaped (void); +@end smallexample + +will emit a warning giving the number of decls that have escaped on this +analysis path, followed by a comma-separated list of their names, +in alphabetical order. + +@smallexample __analyzer_dump_path (); @end smallexample diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h b/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h index e8745c0..d052579 100644 --- a/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h +++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h @@ -18,6 +18,9 @@ extern void __analyzer_dump (void); /* Emit a warning describing the size of the base region of (*ptr). */ extern void __analyzer_dump_capacity (const void *ptr); +/* Dump information about what decls have escaped at this point on the path. */ +extern void __analyzer_dump_escaped (void); + /* Dump information after analysis on all of the exploded nodes at this program point. diff --git a/gcc/testsuite/gcc.dg/analyzer/escaping-1.c b/gcc/testsuite/gcc.dg/analyzer/escaping-1.c new file mode 100644 index 0000000..2dfd02b --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/escaping-1.c @@ -0,0 +1,27 @@ +#include "analyzer-decls.h" + +#define NULL ((void *)0) + +extern void unknown_fn (void *); + +static int only_used_by_test_1; + +static void test_1 (void) +{ + int local_1, local_2; + __analyzer_dump_escaped (); /* { dg-warning "escaped: 0: " } */ + + unknown_fn (NULL); + __analyzer_dump_escaped (); /* { dg-warning "escaped: 0: " } */ + + unknown_fn (&local_1); + __analyzer_dump_escaped (); /* { dg-warning "escaped: 1: 'local_1'" } */ + + /* Should be idempotent. */ + unknown_fn (&local_1); + __analyzer_dump_escaped (); /* { dg-warning "escaped: 1: 'local_1'" } */ + + /* Escape a static global. */ + unknown_fn (&only_used_by_test_1); + __analyzer_dump_escaped (); /* { dg-warning "escaped: 2: 'local_1', 'only_used_by_test_1'" } */ +} -- 2.7.4