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