LCOV - code coverage report
Current view: top level - gcc/analyzer - checker-path.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 91.5 % 118 108
Test Date: 2026-02-28 14:20:25 Functions: 88.9 % 9 8
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Subclass of diagnostics::paths::path for analyzer diagnostics.
       2              :    Copyright (C) 2019-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 "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        10748 : checker_path::same_function_p (int event_idx_a,
      51              :                                int event_idx_b) const
      52              : {
      53        10748 :   return (m_events[event_idx_a]->get_fndecl ()
      54        10748 :           == m_events[event_idx_b]->get_fndecl ());
      55              : }
      56              : 
      57              : /* Print a single-line representation of this path to PP.  */
      58              : 
      59              : void
      60            8 : checker_path::dump (pretty_printer *pp) const
      61              : {
      62            8 :   pp_character (pp, '[');
      63              : 
      64            8 :   checker_event *e;
      65            8 :   int i;
      66           36 :   FOR_EACH_VEC_ELT (m_events, i, e)
      67              :     {
      68           20 :       if (i > 0)
      69           14 :         pp_string (pp, ", ");
      70           20 :       pp_character (pp, '"');
      71           20 :       e->print_desc (*pp);
      72           20 :       pp_character (pp, '"');
      73              :     }
      74            8 :   pp_character (pp, ']');
      75            8 : }
      76              : 
      77              : /* Print a multiline form of this path to LOGGER, prefixing it with DESC.  */
      78              : 
      79              : void
      80        15956 : checker_path::maybe_log (logger *logger, const char *desc) const
      81              : {
      82        15956 :   if (!logger)
      83              :     return;
      84            8 :   logger->start_log_line ();
      85            8 :   logger->log_partial ("%s: ", desc);
      86            8 :   dump (logger->get_printer ());
      87            8 :   logger->end_log_line ();
      88           28 :   for (unsigned i = 0; i < m_events.length (); i++)
      89              :     {
      90           20 :       logger->start_log_line ();
      91           20 :       logger->log_partial ("%s[%i]: %s ", desc, i,
      92           20 :                            event_kind_to_string (m_events[i]->get_kind ()));
      93           20 :       m_events[i]->dump (logger->get_printer ());
      94           20 :       logger->end_log_line ();
      95              :     }
      96              : }
      97              : 
      98              : void
      99        36173 : checker_path::add_event (std::unique_ptr<checker_event> event)
     100              : {
     101        36173 :   if (m_logger)
     102              :     {
     103           11 :       m_logger->start_log_line ();
     104           20 :       m_logger->log_partial ("added event[%i]: %s ",
     105              :                              m_events.length (),
     106              :                              event_kind_to_string (event.get ()->get_kind ()));
     107           11 :       event.get ()->dump (m_logger->get_printer ());
     108           11 :       m_logger->end_log_line ();
     109              :     }
     110        36173 :   m_events.safe_push (event.release ());
     111        36173 : }
     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         1275 : 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         1275 :   tree capacity = NULL_TREE;
     144         1275 :   if (model)
     145         1068 :     if (const svalue *capacity_sval = model->get_capacity (reg))
     146         1068 :       capacity = model->get_representative_tree (capacity_sval);
     147              : 
     148         1275 :   pd->add_region_creation_events (reg, capacity, loc_info, *this);
     149              : 
     150         1275 :   if (debug)
     151            0 :     add_event (std::make_unique<region_creation_event_debug> (reg, capacity,
     152              :                                                               loc_info));
     153         1275 : }
     154              : 
     155              : void
     156         3991 : checker_path::fixup_locations (pending_diagnostic *pd)
     157              : {
     158        28680 :   for (checker_event *e : m_events)
     159        16707 :     e->set_location (pd->fixup_location (e->get_location (), false));
     160         3991 : }
     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        11758 : checker_path::cfg_edge_pair_at_p (unsigned idx) const
     167              : {
     168        23516 :   if (m_events.length () < idx + 1)
     169              :     return false;
     170        11617 :   return (m_events[idx]->get_kind () == event_kind::start_cfg_edge
     171        11617 :           && 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         3991 : checker_path::inject_any_inlined_call_events (logger *logger)
     234              : {
     235         3991 :   LOG_SCOPE (logger);
     236              : 
     237         3991 :   if (!flag_analyzer_undo_inlining)
     238            4 :     return;
     239              : 
     240              :   /* Build a copy of m_events with the new events inserted.  */
     241         3987 :   auto_vec<checker_event *> updated_events;
     242              : 
     243         3987 :   maybe_log (logger, "before");
     244              : 
     245         3987 :   hash_set<tree> blocks_in_prev_event;
     246              : 
     247        20521 :   for (unsigned ev_idx = 0; ev_idx < m_events.length (); ev_idx++)
     248              :     {
     249        16534 :       checker_event *curr_event = m_events[ev_idx];
     250        16534 :       location_t curr_loc = curr_event->get_location ();
     251        16534 :       hash_set<tree> blocks_in_curr_event;
     252              : 
     253        16534 :       if (logger)
     254              :         {
     255            6 :           logger->start_log_line ();
     256            6 :           logger->log_partial ("event[%i]: %s ", ev_idx,
     257              :                                event_kind_to_string (curr_event->get_kind ()));
     258            6 :           curr_event->dump (logger->get_printer ());
     259            6 :           logger->end_log_line ();
     260            6 :           for (inlining_iterator iter (curr_event->get_location ());
     261           18 :                !iter.done_p (); iter.next ())
     262              :             {
     263            6 :               logger->start_log_line ();
     264            6 :               logger->log_partial ("  %qE", iter.get_block ());
     265            6 :               if (!flag_dump_noaddr)
     266            6 :                 logger->log_partial (" (%p)", iter.get_block ());
     267            6 :               logger->log_partial (", fndecl: %qE, callsite: 0x%llx",
     268              :                                    iter.get_fndecl (),
     269            6 :                                    (unsigned long long) iter.get_callsite ());
     270            6 :               if (iter.get_callsite ())
     271            2 :                 dump_location (logger->get_printer (), iter.get_callsite ());
     272            6 :               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        16534 :       struct chain_element { tree m_block; tree m_fndecl; };
     285        16534 :       auto_vec<chain_element> elements;
     286        43426 :       for (inlining_iterator iter (curr_loc); !iter.done_p (); iter.next ())
     287              :         {
     288        13446 :           chain_element ce;
     289        13446 :           ce.m_block = iter.get_block ();
     290        13446 :           ce.m_fndecl = iter.get_fndecl ();
     291              : 
     292        13446 :           if (!blocks_in_prev_event.contains (ce.m_block))
     293         5624 :             elements.safe_push (ce);
     294        13446 :           blocks_in_curr_event.add (ce.m_block);
     295              :         }
     296              : 
     297              :       /* Walk from outermost to innermost.  */
     298        16534 :       if (elements.length () > 0)
     299              :         {
     300         5459 :           int orig_stack_depth = curr_event->get_original_stack_depth ();
     301         5624 :           for (unsigned element_idx = elements.length () - 1; element_idx > 0;
     302              :                element_idx--)
     303              :             {
     304          165 :               const chain_element &ce = elements[element_idx];
     305          165 :               int stack_depth_adjustment
     306          165 :                 = (blocks_in_curr_event.elements () - element_idx) - 1;
     307          165 :               if (location_t callsite = BLOCK_SOURCE_LOCATION (ce.m_block))
     308          165 :                 updated_events.safe_push
     309          165 :                   (new inlined_call_event (callsite,
     310          330 :                                            elements[element_idx - 1].m_fndecl,
     311          165 :                                            ce.m_fndecl,
     312              :                                            orig_stack_depth,
     313          330 :                                            stack_depth_adjustment));
     314              :             }
     315              :         }
     316              : 
     317              :       /* Ideally we'd use assignment here:
     318              :            blocks_in_prev_event = blocks_in_curr_event; */
     319        16534 :       blocks_in_prev_event.empty ();
     320        29980 :       for (auto iter : blocks_in_curr_event)
     321        13446 :         blocks_in_prev_event.add (iter);
     322              : 
     323              :       /* Add the existing event.  */
     324        16534 :       updated_events.safe_push (curr_event);
     325        16534 :     }
     326              : 
     327              :   /* Replace m_events with updated_events.  */
     328         3987 :   m_events.truncate (0);
     329         3987 :   m_events.safe_splice (updated_events);
     330              : 
     331         3987 :   maybe_log (logger, " after");
     332         3991 : }
     333              : 
     334              : } // namespace ana
     335              : 
     336              : #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.