LCOV - code coverage report
Current view: top level - gcc/analyzer - checker-path.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 56.8 % 118 67
Test Date: 2025-06-21 16:26:05 Functions: 77.8 % 9 7
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* Subclass of diagnostic_path for analyzer diagnostics.
       2                 :             :    Copyright (C) 2019-2025 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 "tree-pretty-print.h"
      24                 :             : #include "sbitmap.h"
      25                 :             : #include "ordered-hash-map.h"
      26                 :             : #include "gimple-iterator.h"
      27                 :             : #include "inlining-iterator.h"
      28                 :             : 
      29                 :             : #include "analyzer/analyzer-logging.h"
      30                 :             : #include "analyzer/sm.h"
      31                 :             : #include "analyzer/call-string.h"
      32                 :             : #include "analyzer/program-point.h"
      33                 :             : #include "analyzer/store.h"
      34                 :             : #include "analyzer/region-model.h"
      35                 :             : #include "analyzer/program-state.h"
      36                 :             : #include "analyzer/checker-path.h"
      37                 :             : #include "analyzer/supergraph.h"
      38                 :             : #include "analyzer/pending-diagnostic.h"
      39                 :             : #include "analyzer/diagnostic-manager.h"
      40                 :             : #include "analyzer/constraint-manager.h"
      41                 :             : #include "analyzer/diagnostic-manager.h"
      42                 :             : #include "analyzer/checker-path.h"
      43                 :             : #include "analyzer/exploded-graph.h"
      44                 :             : 
      45                 :             : #if ENABLE_ANALYZER
      46                 :             : 
      47                 :             : namespace ana {
      48                 :             : 
      49                 :             : bool
      50                 :       11148 : checker_path::same_function_p (int event_idx_a,
      51                 :             :                                int event_idx_b) const
      52                 :             : {
      53                 :       11148 :   return (m_events[event_idx_a]->get_fndecl ()
      54                 :       11148 :           == m_events[event_idx_b]->get_fndecl ());
      55                 :             : }
      56                 :             : 
      57                 :             : /* Print a single-line representation of this path to PP.  */
      58                 :             : 
      59                 :             : void
      60                 :           0 : checker_path::dump (pretty_printer *pp) const
      61                 :             : {
      62                 :           0 :   pp_character (pp, '[');
      63                 :             : 
      64                 :           0 :   checker_event *e;
      65                 :           0 :   int i;
      66                 :           0 :   FOR_EACH_VEC_ELT (m_events, i, e)
      67                 :             :     {
      68                 :           0 :       if (i > 0)
      69                 :           0 :         pp_string (pp, ", ");
      70                 :           0 :       pp_character (pp, '"');
      71                 :           0 :       e->print_desc (*pp);
      72                 :           0 :       pp_character (pp, '"');
      73                 :             :     }
      74                 :           0 :   pp_character (pp, ']');
      75                 :           0 : }
      76                 :             : 
      77                 :             : /* Print a multiline form of this path to LOGGER, prefixing it with DESC.  */
      78                 :             : 
      79                 :             : void
      80                 :       15772 : checker_path::maybe_log (logger *logger, const char *desc) const
      81                 :             : {
      82                 :       15772 :   if (!logger)
      83                 :             :     return;
      84                 :           0 :   logger->start_log_line ();
      85                 :           0 :   logger->log_partial ("%s: ", desc);
      86                 :           0 :   dump (logger->get_printer ());
      87                 :           0 :   logger->end_log_line ();
      88                 :           0 :   for (unsigned i = 0; i < m_events.length (); i++)
      89                 :             :     {
      90                 :           0 :       logger->start_log_line ();
      91                 :           0 :       logger->log_partial ("%s[%i]: %s ", desc, i,
      92                 :           0 :                            event_kind_to_string (m_events[i]->get_kind ()));
      93                 :           0 :       m_events[i]->dump (logger->get_printer ());
      94                 :           0 :       logger->end_log_line ();
      95                 :             :     }
      96                 :             : }
      97                 :             : 
      98                 :             : void
      99                 :       58197 : checker_path::add_event (std::unique_ptr<checker_event> event)
     100                 :             : {
     101                 :       58197 :   if (m_logger)
     102                 :             :     {
     103                 :           0 :       m_logger->start_log_line ();
     104                 :           0 :       m_logger->log_partial ("added event[%i]: %s ",
     105                 :             :                              m_events.length (),
     106                 :             :                              event_kind_to_string (event.get ()->get_kind ()));
     107                 :           0 :       event.get ()->dump (m_logger->get_printer ());
     108                 :           0 :       m_logger->end_log_line ();
     109                 :             :     }
     110                 :       58197 :   m_events.safe_push (event.release ());
     111                 :       58197 : }
     112                 :             : 
     113                 :             : /* Print a multiline form of this path to STDERR.  */
     114                 :             : 
     115                 :             : DEBUG_FUNCTION void
     116                 :           0 : checker_path::debug () const
     117                 :             : {
     118                 :           0 :   checker_event *e;
     119                 :           0 :   int i;
     120                 :           0 :   FOR_EACH_VEC_ELT (m_events, i, e)
     121                 :             :     {
     122                 :           0 :       label_text event_desc (e->get_desc (*global_dc->get_reference_printer ()));
     123                 :           0 :       fprintf (stderr,
     124                 :             :                "[%i]: %s \"%s\"\n",
     125                 :             :                i,
     126                 :           0 :                event_kind_to_string (m_events[i]->get_kind ()),
     127                 :             :                event_desc.get ());
     128                 :           0 :     }
     129                 :           0 : }
     130                 :             : 
     131                 :             : /* Add region_creation_event instances to this path for REG,
     132                 :             :    describing whether REG is on the stack or heap and what
     133                 :             :    its capacity is (if known).
     134                 :             :    If DEBUG is true, also create an RCE_DEBUG event.  */
     135                 :             : 
     136                 :             : void
     137                 :        1259 : checker_path::add_region_creation_events (pending_diagnostic *pd,
     138                 :             :                                           const region *reg,
     139                 :             :                                           const region_model *model,
     140                 :             :                                           const event_loc_info &loc_info,
     141                 :             :                                           bool debug)
     142                 :             : {
     143                 :        1259 :   tree capacity = NULL_TREE;
     144                 :        1259 :   if (model)
     145                 :        1048 :     if (const svalue *capacity_sval = model->get_capacity (reg))
     146                 :        1048 :       capacity = model->get_representative_tree (capacity_sval);
     147                 :             : 
     148                 :        1259 :   pd->add_region_creation_events (reg, capacity, loc_info, *this);
     149                 :             : 
     150                 :        1259 :   if (debug)
     151                 :           0 :     add_event (std::make_unique<region_creation_event_debug> (reg, capacity,
     152                 :             :                                                               loc_info));
     153                 :        1259 : }
     154                 :             : 
     155                 :             : void
     156                 :        3945 : checker_path::fixup_locations (pending_diagnostic *pd)
     157                 :             : {
     158                 :       28791 :   for (checker_event *e : m_events)
     159                 :       16956 :     e->set_location (pd->fixup_location (e->get_location (), false));
     160                 :        3945 : }
     161                 :             : 
     162                 :             : /* Return true if there is a (start_cfg_edge_event, end_cfg_edge_event) pair
     163                 :             :    at (IDX, IDX + 1).  */
     164                 :             : 
     165                 :             : bool
     166                 :       12025 : checker_path::cfg_edge_pair_at_p (unsigned idx) const
     167                 :             : {
     168                 :       24050 :   if (m_events.length () < idx + 1)
     169                 :             :     return false;
     170                 :       11909 :   return (m_events[idx]->get_kind () == event_kind::start_cfg_edge
     171                 :       11909 :           && m_events[idx + 1]->get_kind () == event_kind::end_cfg_edge);
     172                 :             : }
     173                 :             : 
     174                 :             : /* Consider a call from "outer" to "middle" which calls "inner",
     175                 :             :    where "inner" and "middle" have been inlined into "outer".
     176                 :             : 
     177                 :             :    We expect the stmt locations for the inlined stmts to have a
     178                 :             :    chain like:
     179                 :             : 
     180                 :             :      [{fndecl: inner},
     181                 :             :       {fndecl: middle, callsite: within middle to inner},
     182                 :             :       {fndecl: outer, callsite: without outer to middle}]
     183                 :             : 
     184                 :             :    The location for the stmt will already be fixed up to reflect
     185                 :             :    the two extra frames, so that we have e.g. this as input
     186                 :             :    (for gcc.dg/analyzer/inlining-4.c):
     187                 :             : 
     188                 :             :     before[0]:
     189                 :             :       event_kind::function_entry "entry to ‘outer’"
     190                 :             :       (depth 1, fndecl ‘outer’, m_loc=511c4)
     191                 :             :     before[1]:
     192                 :             :       event_kind::start_cfg_edge "following ‘true’ branch (when ‘flag != 0’)..."
     193                 :             :       (depth 3 corrected from 1,
     194                 :             :        fndecl ‘inner’ corrected from ‘outer’, m_loc=8000000f)
     195                 :             :     before[2]:
     196                 :             :       event_kind::end_cfg_edge "...to here"
     197                 :             :       (depth 1, fndecl ‘outer’, m_loc=0)
     198                 :             :     before[3]:
     199                 :             :       event_kind::warning "here (‘<unknown>’ is in state ‘null’)"
     200                 :             :       (depth 1, fndecl ‘outer’, m_loc=80000004)
     201                 :             : 
     202                 :             :    We want to add inlined_call_events showing the calls, so that
     203                 :             :    the above becomes:
     204                 :             : 
     205                 :             :     after[0]:
     206                 :             :       event_kind::function_entry "entry to ‘outer’"
     207                 :             :       (depth 1, fndecl ‘outer’, m_loc=511c4)
     208                 :             :     after[1]:
     209                 :             :       event_kind::inlined_call "inlined call to ‘middle’ from ‘outer’"
     210                 :             :       (depth 1, fndecl ‘outer’, m_loc=53300)
     211                 :             :     after[2]:
     212                 :             :       event_kind::inlined_call "inlined call to ‘inner’ from ‘middle’"
     213                 :             :       (depth 2, fndecl ‘middle’, m_loc=4d2e0)
     214                 :             :     after[3]:
     215                 :             :       event_kind::start_cfg_edge "following ‘true’ branch (when ‘flag != 0’)..."
     216                 :             :       (depth 3 corrected from 1,
     217                 :             :        fndecl ‘inner’ corrected from ‘outer’, m_loc=8000000f)
     218                 :             :     after[4]: event_kind::end_cfg_edge "...to here"
     219                 :             :       (depth 1, fndecl ‘outer’, m_loc=0)
     220                 :             :     after[5]: event_kind::warning "here (‘<unknown>’ is in state ‘null’)"
     221                 :             :       (depth 1, fndecl ‘outer’, m_loc=80000004)
     222                 :             : 
     223                 :             :     where we've added events between before[0] and before[1] to show
     224                 :             :     the inlined calls leading to the effective stack depths, making
     225                 :             :     the generated path much easier for a user to read.
     226                 :             : 
     227                 :             :     Note how in the above we have a branch (before[1] to before[2])
     228                 :             :     where the locations were originally in different functions.
     229                 :             :     Hence we have to add these events quite late when generating
     230                 :             :     checker_path.  */
     231                 :             : 
     232                 :             : void
     233                 :        3945 : checker_path::inject_any_inlined_call_events (logger *logger)
     234                 :             : {
     235                 :        3945 :   LOG_SCOPE (logger);
     236                 :             : 
     237                 :        3945 :   if (!flag_analyzer_undo_inlining)
     238                 :           4 :     return;
     239                 :             : 
     240                 :             :   /* Build a copy of m_events with the new events inserted.  */
     241                 :        3941 :   auto_vec<checker_event *> updated_events;
     242                 :             : 
     243                 :        3941 :   maybe_log (logger, "before");
     244                 :             : 
     245                 :        3941 :   hash_set<tree> blocks_in_prev_event;
     246                 :             : 
     247                 :       20719 :   for (unsigned ev_idx = 0; ev_idx < m_events.length (); ev_idx++)
     248                 :             :     {
     249                 :       16778 :       checker_event *curr_event = m_events[ev_idx];
     250                 :       16778 :       location_t curr_loc = curr_event->get_location ();
     251                 :       16778 :       hash_set<tree> blocks_in_curr_event;
     252                 :             : 
     253                 :       16778 :       if (logger)
     254                 :             :         {
     255                 :           0 :           logger->start_log_line ();
     256                 :           0 :           logger->log_partial ("event[%i]: %s ", ev_idx,
     257                 :             :                                event_kind_to_string (curr_event->get_kind ()));
     258                 :           0 :           curr_event->dump (logger->get_printer ());
     259                 :           0 :           logger->end_log_line ();
     260                 :           0 :           for (inlining_iterator iter (curr_event->get_location ());
     261                 :           0 :                !iter.done_p (); iter.next ())
     262                 :             :             {
     263                 :           0 :               logger->start_log_line ();
     264                 :           0 :               logger->log_partial ("  %qE", iter.get_block ());
     265                 :           0 :               if (!flag_dump_noaddr)
     266                 :           0 :                 logger->log_partial (" (%p)", iter.get_block ());
     267                 :           0 :               logger->log_partial (", fndecl: %qE, callsite: 0x%llx",
     268                 :             :                                    iter.get_fndecl (),
     269                 :           0 :                                    (unsigned long long) iter.get_callsite ());
     270                 :           0 :               if (iter.get_callsite ())
     271                 :           0 :                 dump_location (logger->get_printer (), iter.get_callsite ());
     272                 :           0 :               logger->end_log_line ();
     273                 :             :             }
     274                 :             :         }
     275                 :             : 
     276                 :             :       /* We want to add events to show inlined calls.
     277                 :             : 
     278                 :             :          We want to show changes relative to the previous event, omitting
     279                 :             :          the commonality between the inlining chain.
     280                 :             : 
     281                 :             :          The chain is ordered from innermost frame to outermost frame;
     282                 :             :          we want to walk it backwards to show the calls, so capture it
     283                 :             :          in a vec.  */
     284                 :       16778 :       struct chain_element { tree m_block; tree m_fndecl; };
     285                 :       16778 :       auto_vec<chain_element> elements;
     286                 :       44320 :       for (inlining_iterator iter (curr_loc); !iter.done_p (); iter.next ())
     287                 :             :         {
     288                 :       13771 :           chain_element ce;
     289                 :       13771 :           ce.m_block = iter.get_block ();
     290                 :       13771 :           ce.m_fndecl = iter.get_fndecl ();
     291                 :             : 
     292                 :       13771 :           if (!blocks_in_prev_event.contains (ce.m_block))
     293                 :        5709 :             elements.safe_push (ce);
     294                 :       13771 :           blocks_in_curr_event.add (ce.m_block);
     295                 :             :         }
     296                 :             : 
     297                 :             :       /* Walk from outermost to innermost.  */
     298                 :       16778 :       if (elements.length () > 0)
     299                 :             :         {
     300                 :        5539 :           int orig_stack_depth = curr_event->get_original_stack_depth ();
     301                 :        5709 :           for (unsigned element_idx = elements.length () - 1; element_idx > 0;
     302                 :             :                element_idx--)
     303                 :             :             {
     304                 :         170 :               const chain_element &ce = elements[element_idx];
     305                 :         170 :               int stack_depth_adjustment
     306                 :         170 :                 = (blocks_in_curr_event.elements () - element_idx) - 1;
     307                 :         170 :               if (location_t callsite = BLOCK_SOURCE_LOCATION (ce.m_block))
     308                 :         170 :                 updated_events.safe_push
     309                 :         170 :                   (new inlined_call_event (callsite,
     310                 :         340 :                                            elements[element_idx - 1].m_fndecl,
     311                 :         170 :                                            ce.m_fndecl,
     312                 :             :                                            orig_stack_depth,
     313                 :         340 :                                            stack_depth_adjustment));
     314                 :             :             }
     315                 :             :         }
     316                 :             : 
     317                 :             :       /* Ideally we'd use assignment here:
     318                 :             :            blocks_in_prev_event = blocks_in_curr_event; */
     319                 :       16778 :       blocks_in_prev_event.empty ();
     320                 :       30549 :       for (auto iter : blocks_in_curr_event)
     321                 :       13771 :         blocks_in_prev_event.add (iter);
     322                 :             : 
     323                 :             :       /* Add the existing event.  */
     324                 :       16778 :       updated_events.safe_push (curr_event);
     325                 :       16778 :     }
     326                 :             : 
     327                 :             :   /* Replace m_events with updated_events.  */
     328                 :        3941 :   m_events.truncate (0);
     329                 :        3941 :   m_events.safe_splice (updated_events);
     330                 :             : 
     331                 :        3941 :   maybe_log (logger, " after");
     332                 :        3945 : }
     333                 :             : 
     334                 :             : } // namespace ana
     335                 :             : 
     336                 :             : #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.