From patchwork Sat Jan 8 00:08:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 49738 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 100603857806 for ; Sat, 8 Jan 2022 00:09:20 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 100603857806 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1641600560; bh=u/htwHwGb1y+eapZNnuvOEOy5zjytanPYplLGF5sADg=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=MndgUsHu51M9paAqN7yFl378rQMnQAgHpGk9B1RJWxs7Sw6Ir4uB3G/8wR2qqNt+a aCoaj9ehgADYKWqhMU9Lu0Ui3CDYeD6r6dQ8Qn5JbbL648dr5GU8TdK43GvZiX09dd K5vwnFd5B9TNYjUG9CmTE5BVLJh463ZAnWuZMluE= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id DA6F63858D28 for ; Sat, 8 Jan 2022 00:08:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org DA6F63858D28 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-341-OHd-35KDOiSSMguuPMwuzg-1; Fri, 07 Jan 2022 19:08:45 -0500 X-MC-Unique: OHd-35KDOiSSMguuPMwuzg-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7BC842F25 for ; Sat, 8 Jan 2022 00:08:44 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.2.16.212]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1BD5779595; Sat, 8 Jan 2022 00:08:44 +0000 (UTC) To: gcc-patches@gcc.gnu.org Subject: [committed] analyzer: implement __analyzer_dump_escaped Date: Fri, 7 Jan 2022 19:08:42 -0500 Message-Id: <20220108000842.1076223-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: David Malcolm via Gcc-patches From: David Malcolm Reply-To: David Malcolm Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" 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. Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. Pushed to trunk as r12-6377-g4409152a4acaec5b58a93996088d0df9aaa779b8. 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.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 9063acd6be5..c20058ec778 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 b7371948873..cb86d79c99d 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 8e35be1f180..669f1c748ce 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 4672886e281..f30b6bc8b2e 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 62faac44d7f..06eb98fe4d3 100644 --- a/gcc/doc/analyzer.texi +++ b/gcc/doc/analyzer.texi @@ -486,6 +486,14 @@ extern void __analyzer_dump_capacity (const void *ptr); 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 e8745c0933f..d05257949ff 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 00000000000..2dfd02b9ede --- /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'" } */ +}