LCOV - code coverage report
Current view: top level - gcc/analyzer - kf-analyzer.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 90.3 % 165 149
Test Date: 2026-02-28 14:20:25 Functions: 85.2 % 27 23
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.4-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.