LCOV - code coverage report
Current view: top level - gcc/analyzer - kf-analyzer.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 89.0 % 155 138
Test Date: 2024-12-21 13:15:12 Functions: 84.6 % 26 22
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             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 */
        

Generated by: LCOV version 2.1-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.