1 /* The analysis "engine".
2 Copyright (C) 2019-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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)
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.
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/>. */
22 #define INCLUDE_MEMORY
23 #define INCLUDE_VECTOR
25 #include "coretypes.h"
26 #include "make-unique.h"
28 #include "fold-const.h"
29 #include "gcc-rich-location.h"
30 #include "diagnostic-core.h"
31 #include "diagnostic-event-id.h"
32 #include "diagnostic-path.h"
34 #include "pretty-print.h"
37 #include "ordered-hash-map.h"
38 #include "analyzer/analyzer.h"
39 #include "analyzer/analyzer-logging.h"
40 #include "analyzer/call-string.h"
41 #include "analyzer/program-point.h"
42 #include "analyzer/store.h"
43 #include "analyzer/region-model.h"
44 #include "analyzer/constraint-manager.h"
45 #include "analyzer/sm.h"
46 #include "analyzer/pending-diagnostic.h"
47 #include "analyzer/diagnostic-manager.h"
49 #include "basic-block.h"
51 #include "gimple-iterator.h"
52 #include "gimple-pretty-print.h"
55 #include "analyzer/supergraph.h"
56 #include "analyzer/program-state.h"
57 #include "analyzer/exploded-graph.h"
58 #include "analyzer/analysis-plan.h"
59 #include "analyzer/checker-path.h"
60 #include "analyzer/state-purge.h"
61 #include "analyzer/bar-chart.h"
62 #include "analyzer/call-info.h"
67 #include "stringpool.h"
70 #include "analyzer/known-function-manager.h"
71 #include "analyzer/call-summary.h"
72 #include "text-art/dump.h"
74 /* For an overview, see gcc/doc/analyzer.texi. */
80 /* class impl_region_model_context : public region_model_context. */
82 impl_region_model_context::
83 impl_region_model_context (exploded_graph
&eg
,
84 exploded_node
*enode_for_diag
,
85 const program_state
*old_state
,
86 program_state
*new_state
,
87 uncertainty_t
*uncertainty
,
88 path_context
*path_ctxt
,
90 stmt_finder
*stmt_finder
,
91 bool *out_could_have_done_work
)
92 : m_eg (&eg
), m_logger (eg
.get_logger ()),
93 m_enode_for_diag (enode_for_diag
),
94 m_old_state (old_state
),
95 m_new_state (new_state
),
97 m_stmt_finder (stmt_finder
),
98 m_ext_state (eg
.get_ext_state ()),
99 m_uncertainty (uncertainty
),
100 m_path_ctxt (path_ctxt
),
101 m_out_could_have_done_work (out_could_have_done_work
)
105 impl_region_model_context::
106 impl_region_model_context (program_state
*state
,
107 const extrinsic_state
&ext_state
,
108 uncertainty_t
*uncertainty
,
110 : m_eg (NULL
), m_logger (logger
), m_enode_for_diag (NULL
),
114 m_stmt_finder (NULL
),
115 m_ext_state (ext_state
),
116 m_uncertainty (uncertainty
),
118 m_out_could_have_done_work (nullptr)
123 impl_region_model_context::warn (std::unique_ptr
<pending_diagnostic
> d
,
124 const stmt_finder
*custom_finder
)
126 LOG_FUNC (get_logger ());
127 auto curr_stmt_finder
= custom_finder
? custom_finder
: m_stmt_finder
;
128 if (m_stmt
== NULL
&& curr_stmt_finder
== NULL
)
131 get_logger ()->log ("rejecting diagnostic: no stmt");
136 bool terminate_path
= d
->terminate_path_p ();
137 pending_location
ploc (m_enode_for_diag
,
138 m_enode_for_diag
->get_supernode (),
141 if (m_eg
->get_diagnostic_manager ().add_diagnostic (ploc
,
146 && flag_analyzer_suppress_followups
)
147 m_path_ctxt
->terminate_path ();
155 impl_region_model_context::add_note (std::unique_ptr
<pending_note
> pn
)
157 LOG_FUNC (get_logger ());
159 m_eg
->get_diagnostic_manager ().add_note (std::move (pn
));
163 impl_region_model_context::add_event (std::unique_ptr
<checker_event
> event
)
165 LOG_FUNC (get_logger ());
167 m_eg
->get_diagnostic_manager ().add_event (std::move (event
));
171 impl_region_model_context::on_svalue_leak (const svalue
*sval
)
174 for (sm_state_map
*smap
: m_new_state
->m_checker_states
)
175 smap
->on_svalue_leak (sval
, this);
179 impl_region_model_context::
180 on_liveness_change (const svalue_set
&live_svalues
,
181 const region_model
*model
)
183 for (sm_state_map
*smap
: m_new_state
->m_checker_states
)
184 smap
->on_liveness_change (live_svalues
, model
, m_ext_state
, this);
188 impl_region_model_context::on_unknown_change (const svalue
*sval
,
191 if (!sval
->can_have_associated_state_p ())
193 for (sm_state_map
*smap
: m_new_state
->m_checker_states
)
194 smap
->on_unknown_change (sval
, is_mutable
, m_ext_state
);
198 impl_region_model_context::on_escaped_function (tree fndecl
)
200 m_eg
->on_escaped_function (fndecl
);
204 impl_region_model_context::get_uncertainty ()
206 return m_uncertainty
;
209 /* Purge state involving SVAL. The region_model has already been purged,
210 so we only need to purge other state in the program_state:
214 impl_region_model_context::purge_state_involving (const svalue
*sval
)
218 FOR_EACH_VEC_ELT (m_new_state
->m_checker_states
, i
, smap
)
219 smap
->purge_state_involving (sval
, m_ext_state
);
223 impl_region_model_context::bifurcate (std::unique_ptr
<custom_edge_info
> info
)
226 m_path_ctxt
->bifurcate (std::move (info
));
230 impl_region_model_context::terminate_path ()
233 return m_path_ctxt
->terminate_path ();
236 /* struct setjmp_record. */
239 setjmp_record::cmp (const setjmp_record
&rec1
, const setjmp_record
&rec2
)
241 if (int cmp_enode
= rec1
.m_enode
->m_index
- rec2
.m_enode
->m_index
)
243 gcc_assert (&rec1
== &rec2
);
247 /* class setjmp_svalue : public svalue. */
249 /* Implementation of svalue::accept vfunc for setjmp_svalue. */
252 setjmp_svalue::accept (visitor
*v
) const
254 v
->visit_setjmp_svalue (this);
257 /* Implementation of svalue::dump_to_pp vfunc for setjmp_svalue. */
260 setjmp_svalue::dump_to_pp (pretty_printer
*pp
, bool simple
) const
263 pp_printf (pp
, "SETJMP(EN: %i)", get_enode_index ());
265 pp_printf (pp
, "setjmp_svalue(EN%i)", get_enode_index ());
268 /* Implementation of svalue::print_dump_widget_label vfunc for
272 setjmp_svalue::print_dump_widget_label (pretty_printer
*pp
) const
274 pp_printf (pp
, "setjmp_svalue(EN: %i)", get_enode_index ());
277 /* Implementation of svalue::add_dump_widget_children vfunc for
282 add_dump_widget_children (text_art::tree_widget
&,
283 const text_art::dump_widget_info
&) const
288 /* Get the index of the stored exploded_node. */
291 setjmp_svalue::get_enode_index () const
293 return m_setjmp_record
.m_enode
->m_index
;
296 /* Concrete implementation of sm_context, wiring it up to the rest of this
299 class impl_sm_context
: public sm_context
302 impl_sm_context (exploded_graph
&eg
,
304 const state_machine
&sm
,
305 exploded_node
*enode_for_diag
,
306 const program_state
*old_state
,
307 program_state
*new_state
,
308 const sm_state_map
*old_smap
,
309 sm_state_map
*new_smap
,
310 path_context
*path_ctxt
,
311 const stmt_finder
*stmt_finder
= NULL
,
312 bool unknown_side_effects
= false)
313 : sm_context (sm_idx
, sm
),
314 m_logger (eg
.get_logger ()),
315 m_eg (eg
), m_enode_for_diag (enode_for_diag
),
316 m_old_state (old_state
), m_new_state (new_state
),
317 m_old_smap (old_smap
), m_new_smap (new_smap
),
318 m_path_ctxt (path_ctxt
),
319 m_stmt_finder (stmt_finder
),
320 m_unknown_side_effects (unknown_side_effects
)
324 logger
*get_logger () const { return m_logger
.get_logger (); }
326 tree
get_fndecl_for_call (const gcall
*call
) final override
328 impl_region_model_context old_ctxt
329 (m_eg
, m_enode_for_diag
, NULL
, NULL
, NULL
/*m_enode->get_state ()*/,
331 region_model
*model
= m_new_state
->m_region_model
;
332 return model
->get_fndecl_for_call (call
, &old_ctxt
);
335 state_machine::state_t
get_state (const gimple
*stmt ATTRIBUTE_UNUSED
,
336 tree var
) final override
338 logger
* const logger
= get_logger ();
340 /* Use NULL ctxt on this get_rvalue call to avoid triggering
341 uninitialized value warnings. */
342 const svalue
*var_old_sval
343 = m_old_state
->m_region_model
->get_rvalue (var
, NULL
);
345 state_machine::state_t current
346 = m_old_smap
->get_state (var_old_sval
, m_eg
.get_ext_state ());
349 state_machine::state_t
get_state (const gimple
*stmt ATTRIBUTE_UNUSED
,
350 const svalue
*sval
) final override
352 logger
* const logger
= get_logger ();
354 state_machine::state_t current
355 = m_old_smap
->get_state (sval
, m_eg
.get_ext_state ());
360 void set_next_state (const gimple
*,
362 state_machine::state_t to
,
363 tree origin
) final override
365 logger
* const logger
= get_logger ();
367 const svalue
*var_new_sval
368 = m_new_state
->m_region_model
->get_rvalue (var
, NULL
);
369 const svalue
*origin_new_sval
370 = m_new_state
->m_region_model
->get_rvalue (origin
, NULL
);
372 /* We use the new sval here to avoid issues with uninitialized values. */
373 state_machine::state_t current
374 = m_old_smap
->get_state (var_new_sval
, m_eg
.get_ext_state ());
376 logger
->log ("%s: state transition of %qE: %s -> %s",
379 current
->get_name (),
381 m_new_smap
->set_state (m_new_state
->m_region_model
, var_new_sval
,
382 to
, origin_new_sval
, m_eg
.get_ext_state ());
385 void set_next_state (const gimple
*stmt
,
387 state_machine::state_t to
,
388 tree origin
) final override
390 logger
* const logger
= get_logger ();
392 impl_region_model_context old_ctxt
393 (m_eg
, m_enode_for_diag
, NULL
, NULL
, NULL
/*m_enode->get_state ()*/,
396 const svalue
*origin_new_sval
397 = m_new_state
->m_region_model
->get_rvalue (origin
, NULL
);
399 state_machine::state_t current
400 = m_old_smap
->get_state (sval
, m_eg
.get_ext_state ());
403 logger
->start_log_line ();
404 logger
->log_partial ("%s: state transition of ",
406 sval
->dump_to_pp (logger
->get_printer (), true);
407 logger
->log_partial (": %s -> %s",
408 current
->get_name (),
410 logger
->end_log_line ();
412 m_new_smap
->set_state (m_new_state
->m_region_model
, sval
,
413 to
, origin_new_sval
, m_eg
.get_ext_state ());
416 void warn (const supernode
*snode
, const gimple
*stmt
,
418 std::unique_ptr
<pending_diagnostic
> d
) final override
420 LOG_FUNC (get_logger ());
422 const svalue
*var_old_sval
423 = m_old_state
->m_region_model
->get_rvalue (var
, NULL
);
424 state_machine::state_t current
426 ? m_old_smap
->get_state (var_old_sval
, m_eg
.get_ext_state ())
427 : m_old_smap
->get_global_state ());
428 bool terminate_path
= d
->terminate_path_p ();
429 pending_location
ploc (m_enode_for_diag
, snode
, stmt
, m_stmt_finder
);
430 m_eg
.get_diagnostic_manager ().add_diagnostic
432 var
, var_old_sval
, current
, std::move (d
));
435 && flag_analyzer_suppress_followups
)
436 m_path_ctxt
->terminate_path ();
439 void warn (const supernode
*snode
, const gimple
*stmt
,
441 std::unique_ptr
<pending_diagnostic
> d
) final override
443 LOG_FUNC (get_logger ());
445 state_machine::state_t current
447 ? m_old_smap
->get_state (sval
, m_eg
.get_ext_state ())
448 : m_old_smap
->get_global_state ());
449 bool terminate_path
= d
->terminate_path_p ();
450 pending_location
ploc (m_enode_for_diag
, snode
, stmt
, m_stmt_finder
);
451 m_eg
.get_diagnostic_manager ().add_diagnostic
453 NULL_TREE
, sval
, current
, std::move (d
));
456 && flag_analyzer_suppress_followups
)
457 m_path_ctxt
->terminate_path ();
460 /* Hook for picking more readable trees for SSA names of temporaries,
461 so that rather than e.g.
462 "double-free of '<unknown>'"
464 "double-free of 'inbuf.data'". */
466 tree
get_diagnostic_tree (tree expr
) final override
468 /* Only for SSA_NAMEs of temporaries; otherwise, return EXPR, as it's
469 likely to be the least surprising tree to report. */
470 if (TREE_CODE (expr
) != SSA_NAME
)
472 if (SSA_NAME_VAR (expr
) != NULL
)
475 gcc_assert (m_new_state
);
476 const svalue
*sval
= m_new_state
->m_region_model
->get_rvalue (expr
, NULL
);
477 /* Find trees for all regions storing the value. */
478 if (tree t
= m_new_state
->m_region_model
->get_representative_tree (sval
))
484 tree
get_diagnostic_tree (const svalue
*sval
) final override
486 return m_new_state
->m_region_model
->get_representative_tree (sval
);
489 state_machine::state_t
get_global_state () const final override
491 return m_old_state
->m_checker_states
[m_sm_idx
]->get_global_state ();
494 void set_global_state (state_machine::state_t state
) final override
496 m_new_state
->m_checker_states
[m_sm_idx
]->set_global_state (state
);
499 void clear_all_per_svalue_state () final override
501 m_new_state
->m_checker_states
[m_sm_idx
]->clear_all_per_svalue_state ();
504 void on_custom_transition (custom_transition
*transition
) final override
506 transition
->impl_transition (&m_eg
,
507 const_cast<exploded_node
*> (m_enode_for_diag
),
511 tree
is_zero_assignment (const gimple
*stmt
) final override
513 const gassign
*assign_stmt
= dyn_cast
<const gassign
*> (stmt
);
516 impl_region_model_context old_ctxt
517 (m_eg
, m_enode_for_diag
, m_old_state
, m_new_state
, NULL
, NULL
, stmt
);
518 if (const svalue
*sval
519 = m_new_state
->m_region_model
->get_gassign_result (assign_stmt
,
521 if (tree cst
= sval
->maybe_get_constant ())
523 return gimple_assign_lhs (assign_stmt
);
527 path_context
*get_path_context () const final override
532 bool unknown_side_effects_p () const final override
534 return m_unknown_side_effects
;
537 const program_state
*get_old_program_state () const final override
542 const program_state
*get_new_program_state () const final override
548 exploded_graph
&m_eg
;
549 exploded_node
*m_enode_for_diag
;
550 const program_state
*m_old_state
;
551 program_state
*m_new_state
;
552 const sm_state_map
*m_old_smap
;
553 sm_state_map
*m_new_smap
;
554 path_context
*m_path_ctxt
;
555 const stmt_finder
*m_stmt_finder
;
557 /* Are we handling an external function with unknown side effects? */
558 bool m_unknown_side_effects
;
562 impl_region_model_context::
563 get_state_map_by_name (const char *name
,
564 sm_state_map
**out_smap
,
565 const state_machine
**out_sm
,
566 unsigned *out_sm_idx
,
567 std::unique_ptr
<sm_context
> *out_sm_context
)
573 if (!m_ext_state
.get_sm_idx_by_name (name
, &sm_idx
))
576 const state_machine
*sm
= &m_ext_state
.get_sm (sm_idx
);
577 sm_state_map
*new_smap
= m_new_state
->m_checker_states
[sm_idx
];
579 *out_smap
= new_smap
;
582 *out_sm_idx
= sm_idx
;
585 const sm_state_map
*old_smap
= m_old_state
->m_checker_states
[sm_idx
];
587 = make_unique
<impl_sm_context
> (*m_eg
,
602 /* Subclass of stmt_finder for finding the best stmt to report the leak at,
603 given the emission path. */
605 class leak_stmt_finder
: public stmt_finder
608 leak_stmt_finder (const exploded_graph
&eg
, tree var
)
609 : m_eg (eg
), m_var (var
) {}
611 std::unique_ptr
<stmt_finder
> clone () const final override
613 return make_unique
<leak_stmt_finder
> (m_eg
, m_var
);
616 const gimple
*find_stmt (const exploded_path
&epath
)
619 logger
* const logger
= m_eg
.get_logger ();
622 if (m_var
&& TREE_CODE (m_var
) == SSA_NAME
)
624 /* Locate the final write to this SSA name in the path. */
625 const gimple
*def_stmt
= SSA_NAME_DEF_STMT (m_var
);
628 bool found
= epath
.find_stmt_backwards (def_stmt
, &idx_of_def_stmt
);
632 /* What was the next write to the underlying var
633 after the SSA name was set? (if any). */
635 for (unsigned idx
= idx_of_def_stmt
+ 1;
636 idx
< epath
.m_edges
.length ();
639 const exploded_edge
*eedge
= epath
.m_edges
[idx
];
641 logger
->log ("eedge[%i]: EN %i -> EN %i",
643 eedge
->m_src
->m_index
,
644 eedge
->m_dest
->m_index
);
645 const exploded_node
*dst_node
= eedge
->m_dest
;
646 const program_point
&dst_point
= dst_node
->get_point ();
647 const gimple
*stmt
= dst_point
.get_stmt ();
650 if (const gassign
*assign
= dyn_cast
<const gassign
*> (stmt
))
652 tree lhs
= gimple_assign_lhs (assign
);
653 if (TREE_CODE (lhs
) == SSA_NAME
654 && SSA_NAME_VAR (lhs
) == SSA_NAME_VAR (m_var
))
662 /* Look backwards for the first statement with a location. */
664 const exploded_edge
*eedge
;
665 FOR_EACH_VEC_ELT_REVERSE (epath
.m_edges
, i
, eedge
)
668 logger
->log ("eedge[%i]: EN %i -> EN %i",
670 eedge
->m_src
->m_index
,
671 eedge
->m_dest
->m_index
);
672 const exploded_node
*dst_node
= eedge
->m_dest
;
673 const program_point
&dst_point
= dst_node
->get_point ();
674 const gimple
*stmt
= dst_point
.get_stmt ();
676 if (get_pure_location (stmt
->location
) != UNKNOWN_LOCATION
)
684 void update_event_loc_info (event_loc_info
&) final override
690 const exploded_graph
&m_eg
;
694 /* A measurement of how good EXPR is for presenting to the user, so
695 that e.g. we can say prefer printing
696 "leak of 'tmp.m_ptr'"
698 "leak of '<unknown>'". */
701 readability (const_tree expr
)
703 /* Arbitrarily-chosen "high readability" value. */
704 const int HIGH_READABILITY
= 65536;
707 switch (TREE_CODE (expr
))
711 /* Impose a slight readability penalty relative to that of
713 return readability (TREE_OPERAND (expr
, 0)) - 16;
717 if (tree var
= SSA_NAME_VAR (expr
))
719 if (DECL_ARTIFICIAL (var
))
721 /* If we have an SSA name for an artificial var,
722 only use it if it has a debug expr associated with
723 it that fixup_tree_for_diagnostic can use. */
724 if (VAR_P (var
) && DECL_HAS_DEBUG_EXPR_P (var
))
725 return readability (DECL_DEBUG_EXPR (var
)) - 1;
729 /* Slightly favor the underlying var over the SSA name to
730 avoid having them compare equal. */
731 return readability (var
) - 1;
734 /* Avoid printing '<unknown>' for SSA names for temporaries. */
741 if (DECL_NAME (expr
))
742 return HIGH_READABILITY
;
744 /* We don't want to print temporaries. For example, the C FE
745 prints them as e.g. "<Uxxxx>" where "xxxx" is the low 16 bits
746 of the tree pointer (see pp_c_tree_decl_identifier). */
750 /* Printing "<return-value>" isn't ideal, but is less awful than
751 trying to print a temporary. */
752 return HIGH_READABILITY
/ 2;
756 /* Impose a moderate readability penalty for casts. */
757 const int CAST_PENALTY
= 32;
758 return readability (TREE_OPERAND (expr
, 0)) - CAST_PENALTY
;
762 return HIGH_READABILITY
;
771 /* A qsort comparator for trees to sort them into most user-readable to
772 least user-readable. */
775 readability_comparator (const void *p1
, const void *p2
)
777 path_var pv1
= *(path_var
const *)p1
;
778 path_var pv2
= *(path_var
const *)p2
;
780 const int tree_r1
= readability (pv1
.m_tree
);
781 const int tree_r2
= readability (pv2
.m_tree
);
783 /* Favor items that are deeper on the stack and hence more recent;
784 this also favors locals over globals. */
785 const int COST_PER_FRAME
= 64;
786 const int depth_r1
= pv1
.m_stack_depth
* COST_PER_FRAME
;
787 const int depth_r2
= pv2
.m_stack_depth
* COST_PER_FRAME
;
789 /* Combine the scores from the tree and from the stack depth.
790 This e.g. lets us have a slightly penalized cast in the most
791 recent stack frame "beat" an uncast value in an older stack frame. */
792 const int sum_r1
= tree_r1
+ depth_r1
;
793 const int sum_r2
= tree_r2
+ depth_r2
;
794 if (int cmp
= sum_r2
- sum_r1
)
797 /* Otherwise, more readable trees win. */
798 if (int cmp
= tree_r2
- tree_r1
)
801 /* Otherwise, if they have the same readability, then impose an
802 arbitrary deterministic ordering on them. */
804 if (int cmp
= TREE_CODE (pv1
.m_tree
) - TREE_CODE (pv2
.m_tree
))
807 switch (TREE_CODE (pv1
.m_tree
))
812 if (int cmp
= (SSA_NAME_VERSION (pv1
.m_tree
)
813 - SSA_NAME_VERSION (pv2
.m_tree
)))
819 if (int cmp
= DECL_UID (pv1
.m_tree
) - DECL_UID (pv2
.m_tree
))
824 /* TODO: We ought to find ways of sorting such cases. */
828 /* Return true is SNODE is the EXIT node of a function, or is one
829 of the final snodes within its function.
831 Specifically, handle the final supernodes before the EXIT node,
832 for the case of clobbers that happen immediately before exiting.
833 We need a run of snodes leading to the return_p snode, where all edges are
834 intraprocedural, and every snode has just one successor.
836 We use this when suppressing leak reports at the end of "main". */
839 returning_from_function_p (const supernode
*snode
)
845 const supernode
*iter
= snode
;
848 if (iter
->return_p ())
850 if (iter
->m_succs
.length () != 1)
852 const superedge
*sedge
= iter
->m_succs
[0];
853 if (sedge
->get_kind () != SUPEREDGE_CFG_EDGE
)
855 iter
= sedge
->m_dest
;
857 /* Impose a limit to ensure we terminate for pathological cases.
859 We only care about the final 3 nodes, due to cases like:
861 (clobber causing leak)
873 /* Find the best tree for SVAL and call SM's on_leak vfunc with it.
874 If on_leak returns a pending_diagnostic, queue it up to be reported,
875 so that we potentially complain about a leak of SVAL in the given STATE. */
878 impl_region_model_context::on_state_leak (const state_machine
&sm
,
880 state_machine::state_t state
)
882 logger
* const logger
= get_logger ();
886 logger
->start_log_line ();
887 logger
->log_partial ("considering leak of ");
888 sval
->dump_to_pp (logger
->get_printer (), true);
889 logger
->end_log_line ();
895 /* m_old_state also needs to be non-NULL so that the sm_ctxt can look
896 up the old state of SVAL. */
897 gcc_assert (m_old_state
);
899 /* SVAL has leaked within the new state: it is not used by any reachable
901 We need to convert it back to a tree, but since it's likely no regions
902 use it, we have to find the "best" tree for it in the old_state. */
905 = m_old_state
->m_region_model
->get_representative_path_var (sval
,
909 /* Strip off top-level casts */
910 if (leaked_pv
.m_tree
&& TREE_CODE (leaked_pv
.m_tree
) == NOP_EXPR
)
911 leaked_pv
.m_tree
= TREE_OPERAND (leaked_pv
.m_tree
, 0);
913 /* This might be NULL; the pending_diagnostic subclasses need to cope
915 tree leaked_tree
= leaked_pv
.m_tree
;
919 logger
->log ("best leaked_tree: %qE", leaked_tree
);
921 logger
->log ("best leaked_tree: NULL");
924 leak_stmt_finder
stmt_finder (*m_eg
, leaked_tree
);
925 gcc_assert (m_enode_for_diag
);
927 /* Don't complain about leaks when returning from "main". */
928 if (returning_from_function_p (m_enode_for_diag
->get_supernode ()))
930 tree fndecl
= m_enode_for_diag
->get_function ()->decl
;
931 if (id_equal (DECL_NAME (fndecl
), "main"))
934 logger
->log ("not reporting leak from main");
939 tree leaked_tree_for_diag
= fixup_tree_for_diagnostic (leaked_tree
);
940 std::unique_ptr
<pending_diagnostic
> pd
= sm
.on_leak (leaked_tree_for_diag
);
943 pending_location
ploc (m_enode_for_diag
,
944 m_enode_for_diag
->get_supernode (),
947 m_eg
->get_diagnostic_manager ().add_diagnostic
949 leaked_tree_for_diag
, sval
, state
, std::move (pd
));
953 /* Implementation of region_model_context::on_condition vfunc.
954 Notify all state machines about the condition, which could lead to
955 state transitions. */
958 impl_region_model_context::on_condition (const svalue
*lhs
,
964 FOR_EACH_VEC_ELT (m_new_state
->m_checker_states
, sm_idx
, smap
)
966 const state_machine
&sm
= m_ext_state
.get_sm (sm_idx
);
967 impl_sm_context
sm_ctxt (*m_eg
, sm_idx
, sm
, m_enode_for_diag
,
968 m_old_state
, m_new_state
,
969 m_old_state
->m_checker_states
[sm_idx
],
970 m_new_state
->m_checker_states
[sm_idx
],
972 sm
.on_condition (sm_ctxt
,
974 ? m_enode_for_diag
->get_supernode ()
981 /* Implementation of region_model_context::on_bounded_ranges vfunc.
982 Notify all state machines about the ranges, which could lead to
983 state transitions. */
986 impl_region_model_context::on_bounded_ranges (const svalue
&sval
,
987 const bounded_ranges
&ranges
)
991 FOR_EACH_VEC_ELT (m_new_state
->m_checker_states
, sm_idx
, smap
)
993 const state_machine
&sm
= m_ext_state
.get_sm (sm_idx
);
994 impl_sm_context
sm_ctxt (*m_eg
, sm_idx
, sm
, m_enode_for_diag
,
995 m_old_state
, m_new_state
,
996 m_old_state
->m_checker_states
[sm_idx
],
997 m_new_state
->m_checker_states
[sm_idx
],
999 sm
.on_bounded_ranges (sm_ctxt
,
1001 ? m_enode_for_diag
->get_supernode ()
1003 m_stmt
, sval
, ranges
);
1007 /* Implementation of region_model_context::on_pop_frame vfunc.
1008 Notify all state machines about the frame being popped, which
1009 could lead to states being discarded. */
1012 impl_region_model_context::on_pop_frame (const frame_region
*frame_reg
)
1016 FOR_EACH_VEC_ELT (m_new_state
->m_checker_states
, sm_idx
, smap
)
1018 const state_machine
&sm
= m_ext_state
.get_sm (sm_idx
);
1019 sm
.on_pop_frame (smap
, frame_reg
);
1023 /* Implementation of region_model_context::on_phi vfunc.
1024 Notify all state machines about the phi, which could lead to
1025 state transitions. */
1028 impl_region_model_context::on_phi (const gphi
*phi
, tree rhs
)
1032 FOR_EACH_VEC_ELT (m_new_state
->m_checker_states
, sm_idx
, smap
)
1034 const state_machine
&sm
= m_ext_state
.get_sm (sm_idx
);
1035 impl_sm_context
sm_ctxt (*m_eg
, sm_idx
, sm
, m_enode_for_diag
,
1036 m_old_state
, m_new_state
,
1037 m_old_state
->m_checker_states
[sm_idx
],
1038 m_new_state
->m_checker_states
[sm_idx
],
1040 sm
.on_phi (sm_ctxt
, m_enode_for_diag
->get_supernode (), phi
, rhs
);
1044 /* Implementation of region_model_context::on_unexpected_tree_code vfunc.
1045 Mark the new state as being invalid for further exploration.
1046 TODO(stage1): introduce a warning for when this occurs. */
1049 impl_region_model_context::on_unexpected_tree_code (tree t
,
1050 const dump_location_t
&loc
)
1052 logger
* const logger
= get_logger ();
1054 logger
->log ("unhandled tree code: %qs in %qs at %s:%i",
1055 get_tree_code_name (TREE_CODE (t
)),
1056 loc
.get_impl_location ().m_function
,
1057 loc
.get_impl_location ().m_file
,
1058 loc
.get_impl_location ().m_line
);
1060 m_new_state
->m_valid
= false;
1063 /* Implementation of region_model_context::maybe_did_work vfunc.
1064 Mark that "externally visible work" has occurred, and thus we
1065 shouldn't report an infinite loop here. */
1068 impl_region_model_context::maybe_did_work ()
1070 if (m_out_could_have_done_work
)
1071 *m_out_could_have_done_work
= true;
1074 /* struct point_and_state. */
1076 /* Assert that this object is sane. */
1079 point_and_state::validate (const extrinsic_state
&ext_state
) const
1081 /* Skip this in a release build. */
1086 m_point
.validate ();
1088 m_state
.validate (ext_state
);
1090 /* Verify that the callstring's model of the stack corresponds to that
1091 of the region_model. */
1092 /* They should have the same depth. */
1093 gcc_assert (m_point
.get_stack_depth ()
1094 == m_state
.m_region_model
->get_stack_depth ());
1095 /* Check the functions in the callstring vs those in the frames
1097 for (const frame_region
*iter_frame
1098 = m_state
.m_region_model
->get_current_frame ();
1099 iter_frame
; iter_frame
= iter_frame
->get_calling_frame ())
1101 int index
= iter_frame
->get_index ();
1102 gcc_assert (m_point
.get_function_at_depth (index
)
1103 == &iter_frame
->get_function ());
1107 /* Subroutine of print_enode_indices: print a run of indices from START_IDX
1108 to END_IDX to PP, using and updating *FIRST_RUN. */
1111 print_run (pretty_printer
*pp
, int start_idx
, int end_idx
,
1115 pp_string (pp
, ", ");
1117 if (start_idx
== end_idx
)
1118 pp_printf (pp
, "EN: %i", start_idx
);
1120 pp_printf (pp
, "EN: %i-%i", start_idx
, end_idx
);
1123 /* Print the indices within ENODES to PP, collecting them as
1124 runs/singletons e.g. "EN: 4-7, EN: 20-23, EN: 42". */
1127 print_enode_indices (pretty_printer
*pp
,
1128 const auto_vec
<exploded_node
*> &enodes
)
1130 int cur_start_idx
= -1;
1131 int cur_finish_idx
= -1;
1132 bool first_run
= true;
1134 exploded_node
*enode
;
1135 FOR_EACH_VEC_ELT (enodes
, i
, enode
)
1137 if (cur_start_idx
== -1)
1139 gcc_assert (cur_finish_idx
== -1);
1140 cur_start_idx
= cur_finish_idx
= enode
->m_index
;
1144 if (enode
->m_index
== cur_finish_idx
+ 1)
1145 /* Continuation of a run. */
1146 cur_finish_idx
= enode
->m_index
;
1149 /* Finish existing run, start a new one. */
1150 gcc_assert (cur_start_idx
>= 0);
1151 gcc_assert (cur_finish_idx
>= 0);
1152 print_run (pp
, cur_start_idx
, cur_finish_idx
,
1154 cur_start_idx
= cur_finish_idx
= enode
->m_index
;
1158 /* Finish any existing run. */
1159 if (cur_start_idx
>= 0)
1161 gcc_assert (cur_finish_idx
>= 0);
1162 print_run (pp
, cur_start_idx
, cur_finish_idx
,
1167 /* struct eg_traits::dump_args_t. */
1169 /* The <FILENAME>.eg.dot output can quickly become unwieldy if we show
1170 full details for all enodes (both in terms of CPU time to render it,
1171 and in terms of being meaningful to a human viewing it).
1173 If we show just the IDs then the resulting graph is usually viewable,
1174 but then we have to keep switching back and forth between the .dot
1175 view and other dumps.
1177 This function implements a heuristic for showing detail at the enodes
1178 that (we hope) matter, and just the ID at other enodes, fixing the CPU
1179 usage of the .dot viewer, and drawing the attention of the viewer
1182 Return true if ENODE should be shown in detail in .dot output.
1183 Return false if no detail should be shown for ENODE. */
1186 eg_traits::dump_args_t::show_enode_details_p (const exploded_node
&enode
) const
1188 /* If the number of exploded nodes isn't too large, we may as well show
1189 all enodes in full detail in the .dot output. */
1190 if (m_eg
.m_nodes
.length ()
1191 <= (unsigned) param_analyzer_max_enodes_for_full_dump
)
1194 /* Otherwise, assume that what's most interesting are state explosions,
1195 and thus the places where this happened.
1196 Expand enodes at program points where we hit the per-enode limit, so we
1197 can investigate what exploded. */
1198 const per_program_point_data
*per_point_data
1199 = m_eg
.get_per_program_point_data (enode
.get_point ());
1200 return per_point_data
->m_excess_enodes
> 0;
1203 /* class exploded_node : public dnode<eg_traits>. */
1206 exploded_node::status_to_str (enum status s
)
1210 default: gcc_unreachable ();
1211 case STATUS_WORKLIST
: return "WORKLIST";
1212 case STATUS_PROCESSED
: return "PROCESSED";
1213 case STATUS_MERGER
: return "MERGER";
1214 case STATUS_BULK_MERGED
: return "BULK_MERGED";
1218 /* exploded_node's ctor. */
1220 exploded_node::exploded_node (const point_and_state
&ps
,
1222 : m_ps (ps
), m_status (STATUS_WORKLIST
), m_index (index
),
1223 m_num_processed_stmts (0)
1225 gcc_checking_assert (ps
.get_state ().m_region_model
->canonicalized_p ());
1228 /* Get the stmt that was processed in this enode at index IDX.
1229 IDX is an index within the stmts processed at this enode, rather
1230 than within those of the supernode. */
1233 exploded_node::get_processed_stmt (unsigned idx
) const
1235 gcc_assert (idx
< m_num_processed_stmts
);
1236 const program_point
&point
= get_point ();
1237 gcc_assert (point
.get_kind () == PK_BEFORE_STMT
);
1238 const supernode
*snode
= get_supernode ();
1239 const unsigned int point_stmt_idx
= point
.get_stmt_idx ();
1240 const unsigned int idx_within_snode
= point_stmt_idx
+ idx
;
1241 const gimple
*stmt
= snode
->m_stmts
[idx_within_snode
];
1245 /* For use by dump_dot, get a value for the .dot "fillcolor" attribute.
1246 Colorize by sm-state, to make it easier to see how sm-state propagates
1247 through the exploded_graph. */
1250 exploded_node::get_dot_fillcolor () const
1252 const program_state
&state
= get_state ();
1254 /* We want to be able to easily distinguish the no-sm-state case,
1255 and to be able to distinguish cases where there's a single state
1258 Sum the sm_states, and use the result to choose from a table,
1259 modulo table-size, special-casing the "no sm-state" case. */
1260 int total_sm_state
= 0;
1263 FOR_EACH_VEC_ELT (state
.m_checker_states
, i
, smap
)
1265 for (sm_state_map::iterator_t iter
= smap
->begin ();
1266 iter
!= smap
->end ();
1268 total_sm_state
+= (*iter
).second
.m_state
->get_id ();
1269 total_sm_state
+= smap
->get_global_state ()->get_id ();
1272 if (total_sm_state
> 0)
1274 /* An arbitrarily-picked collection of light colors. */
1275 const char * const colors
[]
1276 = {"azure", "coral", "cornsilk", "lightblue", "yellow",
1277 "honeydew", "lightpink", "lightsalmon", "palegreen1",
1278 "wheat", "seashell"};
1279 const int num_colors
= ARRAY_SIZE (colors
);
1280 return colors
[total_sm_state
% num_colors
];
1287 /* Implementation of dnode::dump_dot vfunc for exploded_node. */
1290 exploded_node::dump_dot (graphviz_out
*gv
, const dump_args_t
&args
) const
1292 pretty_printer
*pp
= gv
->get_pp ();
1295 pp_printf (pp
, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
1296 get_dot_fillcolor ());
1297 pp_write_text_to_stream (pp
);
1299 pp_printf (pp
, "EN: %i", m_index
);
1300 if (m_status
== STATUS_MERGER
)
1301 pp_string (pp
, " (merger)");
1302 else if (m_status
== STATUS_BULK_MERGED
)
1303 pp_string (pp
, " (bulk merged)");
1306 if (args
.show_enode_details_p (*this))
1309 m_ps
.get_point ().print (pp
, f
);
1312 const extrinsic_state
&ext_state
= args
.m_eg
.get_ext_state ();
1313 const program_state
&state
= m_ps
.get_state ();
1314 state
.dump_to_pp (ext_state
, false, true, pp
);
1317 dump_processed_stmts (pp
);
1320 dump_saved_diagnostics (pp
);
1322 args
.dump_extra_info (this, pp
);
1324 pp_write_text_as_dot_label_to_stream (pp
, /*for_record=*/true);
1326 pp_string (pp
, "\"];\n\n");
1328 /* It can be hard to locate the saved diagnostics as text within the
1329 enode nodes, so add extra nodes to the graph for each saved_diagnostic,
1331 Compare with dump_saved_diagnostics. */
1334 const saved_diagnostic
*sd
;
1335 FOR_EACH_VEC_ELT (m_saved_diagnostics
, i
, sd
)
1337 sd
->dump_as_dot_node (pp
);
1339 /* Add edge connecting this enode to the saved_diagnostic. */
1341 pp_string (pp
, " -> ");
1342 sd
->dump_dot_id (pp
);
1343 pp_string (pp
, " [style=\"dotted\" arrowhead=\"none\"];");
1351 /* Show any stmts that were processed within this enode,
1352 and their index within the supernode. */
1354 exploded_node::dump_processed_stmts (pretty_printer
*pp
) const
1356 if (m_num_processed_stmts
> 0)
1358 const program_point
&point
= get_point ();
1359 gcc_assert (point
.get_kind () == PK_BEFORE_STMT
);
1360 const supernode
*snode
= get_supernode ();
1361 const unsigned int point_stmt_idx
= point
.get_stmt_idx ();
1363 pp_printf (pp
, "stmts: %i", m_num_processed_stmts
);
1365 for (unsigned i
= 0; i
< m_num_processed_stmts
; i
++)
1367 const unsigned int idx_within_snode
= point_stmt_idx
+ i
;
1368 const gimple
*stmt
= snode
->m_stmts
[idx_within_snode
];
1369 pp_printf (pp
, " %i: ", idx_within_snode
);
1370 pp_gimple_stmt_1 (pp
, stmt
, 0, (dump_flags_t
)0);
1376 /* Dump any saved_diagnostics at this enode to PP. */
1379 exploded_node::dump_saved_diagnostics (pretty_printer
*pp
) const
1382 const saved_diagnostic
*sd
;
1383 FOR_EACH_VEC_ELT (m_saved_diagnostics
, i
, sd
)
1385 pp_printf (pp
, "DIAGNOSTIC: %s (sd: %i)",
1386 sd
->m_d
->get_kind (), sd
->get_index ());
1391 /* Dump this to PP in a form suitable for use as an id in .dot output. */
1394 exploded_node::dump_dot_id (pretty_printer
*pp
) const
1396 pp_printf (pp
, "exploded_node_%i", m_index
);
1399 /* Dump a multiline representation of this node to PP. */
1402 exploded_node::dump_to_pp (pretty_printer
*pp
,
1403 const extrinsic_state
&ext_state
) const
1405 pp_printf (pp
, "EN: %i", m_index
);
1409 m_ps
.get_point ().print (pp
, f
);
1412 m_ps
.get_state ().dump_to_pp (ext_state
, false, true, pp
);
1416 /* Dump a multiline representation of this node to FILE. */
1419 exploded_node::dump (FILE *fp
,
1420 const extrinsic_state
&ext_state
) const
1422 tree_dump_pretty_printer
pp (fp
);
1423 dump_to_pp (&pp
, ext_state
);
1426 /* Dump a multiline representation of this node to stderr. */
1429 exploded_node::dump (const extrinsic_state
&ext_state
) const
1431 dump (stderr
, ext_state
);
1434 /* Return a new json::object of the form
1435 {"point" : object for program_point,
1436 "state" : object for program_state,
1439 "processed_stmts" : int}. */
1442 exploded_node::to_json (const extrinsic_state
&ext_state
) const
1444 json::object
*enode_obj
= new json::object ();
1446 enode_obj
->set ("point", get_point ().to_json ());
1447 enode_obj
->set ("state", get_state ().to_json (ext_state
));
1448 enode_obj
->set_string ("status", status_to_str (m_status
));
1449 enode_obj
->set_integer ("idx", m_index
);
1450 enode_obj
->set_integer ("processed_stmts", m_num_processed_stmts
);
1457 /* Return true if FNDECL has a gimple body. */
1458 // TODO: is there a pre-canned way to do this?
1461 fndecl_has_gimple_body_p (tree fndecl
)
1463 if (fndecl
== NULL_TREE
)
1466 cgraph_node
*n
= cgraph_node::get (fndecl
);
1470 return n
->has_gimple_body_p ();
1475 /* Modify STATE in place, applying the effects of the stmt at this node's
1478 exploded_node::on_stmt_flags
1479 exploded_node::on_stmt (exploded_graph
&eg
,
1480 const supernode
*snode
,
1482 program_state
*state
,
1483 uncertainty_t
*uncertainty
,
1484 bool *out_could_have_done_work
,
1485 path_context
*path_ctxt
)
1487 logger
*logger
= eg
.get_logger ();
1491 logger
->start_log_line ();
1492 pp_gimple_stmt_1 (logger
->get_printer (), stmt
, 0, (dump_flags_t
)0);
1493 logger
->end_log_line ();
1496 /* Update input_location in case of ICE: make it easier to track down which
1497 source construct we're failing to handle. */
1498 input_location
= stmt
->location
;
1500 gcc_assert (state
->m_region_model
);
1502 /* Preserve the old state. It is used here for looking
1503 up old checker states, for determining state transitions, and
1504 also within impl_region_model_context and impl_sm_context for
1505 going from tree to svalue_id. */
1506 const program_state
old_state (*state
);
1508 impl_region_model_context
ctxt (eg
, this,
1509 &old_state
, state
, uncertainty
,
1510 path_ctxt
, stmt
, nullptr,
1511 out_could_have_done_work
);
1513 /* Handle call summaries here. */
1514 if (cgraph_edge
*cgedge
1515 = supergraph_call_edge (snode
->get_function (), stmt
))
1516 if (eg
.get_analysis_plan ().use_summary_p (cgedge
))
1518 function
*called_fn
= get_ultimate_function_for_cgraph_edge (cgedge
);
1519 per_function_data
*called_fn_data
1520 = eg
.get_per_function_data (called_fn
);
1523 gcc_assert (called_fn
);
1524 return replay_call_summaries (eg
,
1526 as_a
<const gcall
*> (stmt
),
1535 bool unknown_side_effects
= false;
1536 bool terminate_path
= false;
1538 on_stmt_pre (eg
, stmt
, state
, &terminate_path
,
1539 &unknown_side_effects
, &ctxt
);
1542 return on_stmt_flags::terminate_path ();
1546 FOR_EACH_VEC_ELT (old_state
.m_checker_states
, sm_idx
, smap
)
1548 const state_machine
&sm
= eg
.get_ext_state ().get_sm (sm_idx
);
1549 const sm_state_map
*old_smap
1550 = old_state
.m_checker_states
[sm_idx
];
1551 sm_state_map
*new_smap
= state
->m_checker_states
[sm_idx
];
1552 impl_sm_context
sm_ctxt (eg
, sm_idx
, sm
, this, &old_state
, state
,
1553 old_smap
, new_smap
, path_ctxt
, NULL
,
1554 unknown_side_effects
);
1556 /* Allow the state_machine to handle the stmt. */
1557 if (sm
.on_stmt (sm_ctxt
, snode
, stmt
))
1558 unknown_side_effects
= false;
1561 if (path_ctxt
->terminate_path_p ())
1562 return on_stmt_flags::terminate_path ();
1564 on_stmt_post (stmt
, state
, unknown_side_effects
, &ctxt
);
1566 return on_stmt_flags ();
1569 /* Handle the pre-sm-state part of STMT, modifying STATE in-place.
1570 Write true to *OUT_TERMINATE_PATH if the path should be terminated.
1571 Write true to *OUT_UNKNOWN_SIDE_EFFECTS if the stmt has unknown
1575 exploded_node::on_stmt_pre (exploded_graph
&eg
,
1577 program_state
*state
,
1578 bool *out_terminate_path
,
1579 bool *out_unknown_side_effects
,
1580 region_model_context
*ctxt
)
1582 /* Handle special-case calls that require the full program_state. */
1583 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
1585 if (is_special_named_call_p (call
, "__analyzer_dump", 0))
1587 /* Handle the builtin "__analyzer_dump" by dumping state
1589 state
->dump (eg
.get_ext_state (), true);
1592 else if (is_special_named_call_p (call
, "__analyzer_dump_state", 2))
1594 state
->impl_call_analyzer_dump_state (call
, eg
.get_ext_state (),
1598 else if (is_setjmp_call_p (call
))
1600 state
->m_region_model
->on_setjmp (call
, this, ctxt
);
1602 ctxt
->maybe_did_work ();
1605 else if (is_longjmp_call_p (call
))
1607 on_longjmp (eg
, call
, state
, ctxt
);
1608 *out_terminate_path
= true;
1610 ctxt
->maybe_did_work ();
1615 /* Otherwise, defer to m_region_model. */
1616 state
->m_region_model
->on_stmt_pre (stmt
,
1617 out_unknown_side_effects
,
1621 /* Handle the post-sm-state part of STMT, modifying STATE in-place. */
1624 exploded_node::on_stmt_post (const gimple
*stmt
,
1625 program_state
*state
,
1626 bool unknown_side_effects
,
1627 region_model_context
*ctxt
)
1629 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
1630 state
->m_region_model
->on_call_post (call
, unknown_side_effects
, ctxt
);
1633 /* A concrete call_info subclass representing a replay of a call summary. */
1635 class call_summary_edge_info
: public call_info
1638 call_summary_edge_info (const call_details
&cd
,
1639 const function
&called_fn
,
1640 call_summary
*summary
,
1641 const extrinsic_state
&ext_state
)
1642 : call_info (cd
, called_fn
),
1643 m_called_fn (called_fn
),
1644 m_summary (summary
),
1645 m_ext_state (ext_state
)
1648 bool update_state (program_state
*state
,
1649 const exploded_edge
*,
1650 region_model_context
*ctxt
) const final override
1652 /* Update STATE based on summary_end_state. */
1653 call_details
cd (get_call_details (state
->m_region_model
, ctxt
));
1654 call_summary_replay
r (cd
, m_called_fn
, m_summary
, m_ext_state
);
1655 const program_state
&summary_end_state
= m_summary
->get_state ();
1656 return state
->replay_call_summary (r
, summary_end_state
);
1659 bool update_model (region_model
*model
,
1660 const exploded_edge
*,
1661 region_model_context
*ctxt
) const final override
1663 /* Update STATE based on summary_end_state. */
1664 call_details
cd (get_call_details (model
, ctxt
));
1665 call_summary_replay
r (cd
, m_called_fn
, m_summary
, m_ext_state
);
1666 const program_state
&summary_end_state
= m_summary
->get_state ();
1667 model
->replay_call_summary (r
, *summary_end_state
.m_region_model
);
1671 label_text
get_desc (bool /*can_colorize*/) const final override
1673 return m_summary
->get_desc ();
1677 const function
&m_called_fn
;
1678 call_summary
*m_summary
;
1679 const extrinsic_state
&m_ext_state
;
1682 /* Use PATH_CTXT to bifurcate, which when handled will add custom edges
1683 for a replay of the various feasible summaries in CALLED_FN_DATA. */
1685 exploded_node::on_stmt_flags
1686 exploded_node::replay_call_summaries (exploded_graph
&eg
,
1687 const supernode
*snode
,
1688 const gcall
*call_stmt
,
1689 program_state
*state
,
1690 path_context
*path_ctxt
,
1691 const function
&called_fn
,
1692 per_function_data
&called_fn_data
,
1693 region_model_context
*ctxt
)
1695 logger
*logger
= eg
.get_logger ();
1698 /* Each summary will call bifurcate on the PATH_CTXT. */
1699 for (auto summary
: called_fn_data
.m_summaries
)
1700 replay_call_summary (eg
, snode
, call_stmt
, state
,
1701 path_ctxt
, called_fn
, summary
, ctxt
);
1702 path_ctxt
->terminate_path ();
1704 return on_stmt_flags ();
1707 /* Use PATH_CTXT to bifurcate, which when handled will add a
1708 custom edge for a replay of SUMMARY, if the summary's
1709 conditions are feasible based on the current state. */
1712 exploded_node::replay_call_summary (exploded_graph
&eg
,
1713 const supernode
*snode
,
1714 const gcall
*call_stmt
,
1715 program_state
*old_state
,
1716 path_context
*path_ctxt
,
1717 const function
&called_fn
,
1718 call_summary
*summary
,
1719 region_model_context
*ctxt
)
1721 logger
*logger
= eg
.get_logger ();
1724 gcc_assert (call_stmt
);
1725 gcc_assert (old_state
);
1726 gcc_assert (summary
);
1729 logger
->log ("using %s as summary for call to %qE from %qE",
1730 summary
->get_desc ().get (),
1732 snode
->get_function ()->decl
);
1733 const extrinsic_state
&ext_state
= eg
.get_ext_state ();
1734 const program_state
&summary_end_state
= summary
->get_state ();
1737 pretty_printer
*pp
= logger
->get_printer ();
1739 logger
->start_log_line ();
1740 pp_string (pp
, "callsite state: ");
1741 old_state
->dump_to_pp (ext_state
, true, false, pp
);
1742 logger
->end_log_line ();
1744 logger
->start_log_line ();
1745 pp_string (pp
, "summary end state: ");
1746 summary_end_state
.dump_to_pp (ext_state
, true, false, pp
);
1747 logger
->end_log_line ();
1750 program_state
new_state (*old_state
);
1752 call_details
cd (call_stmt
, new_state
.m_region_model
, ctxt
);
1753 call_summary_replay
r (cd
, called_fn
, summary
, ext_state
);
1756 path_ctxt
->bifurcate (make_unique
<call_summary_edge_info
> (cd
,
1763 /* Consider the effect of following superedge SUCC from this node.
1765 Return true if it's feasible to follow the edge, or false
1768 Examples: if it's the "true" branch within
1769 a CFG and we know the conditional is false, we know it's infeasible.
1770 If it's one of multiple interprocedual "return" edges, then only
1771 the edge back to the most recent callsite is feasible.
1773 Update NEXT_STATE accordingly (e.g. to record that a condition was
1774 true or false, or that the NULL-ness of a pointer has been checked,
1775 pushing/popping stack frames, etc).
1777 Update NEXT_POINT accordingly (updating the call string). */
1780 exploded_node::on_edge (exploded_graph
&eg
,
1781 const superedge
*succ
,
1782 program_point
*next_point
,
1783 program_state
*next_state
,
1784 uncertainty_t
*uncertainty
)
1786 LOG_FUNC (eg
.get_logger ());
1788 if (!next_point
->on_edge (eg
, succ
))
1791 if (!next_state
->on_edge (eg
, this, succ
, uncertainty
))
1797 /* Verify that the stack at LONGJMP_POINT is still valid, given a call
1798 to "setjmp" at SETJMP_POINT - the stack frame that "setjmp" was
1799 called in must still be valid.
1801 Caveat: this merely checks the call_strings in the points; it doesn't
1802 detect the case where a frame returns and is then called again. */
1805 valid_longjmp_stack_p (const program_point
&longjmp_point
,
1806 const program_point
&setjmp_point
)
1808 const call_string
&cs_at_longjmp
= longjmp_point
.get_call_string ();
1809 const call_string
&cs_at_setjmp
= setjmp_point
.get_call_string ();
1811 if (cs_at_longjmp
.length () < cs_at_setjmp
.length ())
1814 /* Check that the call strings match, up to the depth of the
1816 for (unsigned depth
= 0; depth
< cs_at_setjmp
.length (); depth
++)
1817 if (cs_at_longjmp
[depth
] != cs_at_setjmp
[depth
])
1823 /* A pending_diagnostic subclass for complaining about bad longjmps,
1824 where the enclosing function of the "setjmp" has returned (and thus
1825 the stack frame no longer exists). */
1827 class stale_jmp_buf
: public pending_diagnostic_subclass
<stale_jmp_buf
>
1830 stale_jmp_buf (const gcall
*setjmp_call
, const gcall
*longjmp_call
,
1831 const program_point
&setjmp_point
)
1832 : m_setjmp_call (setjmp_call
), m_longjmp_call (longjmp_call
),
1833 m_setjmp_point (setjmp_point
), m_stack_pop_event (NULL
)
1836 int get_controlling_option () const final override
1838 return OPT_Wanalyzer_stale_setjmp_buffer
;
1841 bool emit (diagnostic_emission_context
&ctxt
) final override
1843 return ctxt
.warn ("%qs called after enclosing function of %qs has returned",
1844 get_user_facing_name (m_longjmp_call
),
1845 get_user_facing_name (m_setjmp_call
));
1848 const char *get_kind () const final override
1849 { return "stale_jmp_buf"; }
1851 bool operator== (const stale_jmp_buf
&other
) const
1853 return (m_setjmp_call
== other
.m_setjmp_call
1854 && m_longjmp_call
== other
.m_longjmp_call
);
1858 maybe_add_custom_events_for_superedge (const exploded_edge
&eedge
,
1859 checker_path
*emission_path
)
1862 /* Detect exactly when the stack first becomes invalid,
1863 and issue an event then. */
1864 if (m_stack_pop_event
)
1866 const exploded_node
*src_node
= eedge
.m_src
;
1867 const program_point
&src_point
= src_node
->get_point ();
1868 const exploded_node
*dst_node
= eedge
.m_dest
;
1869 const program_point
&dst_point
= dst_node
->get_point ();
1870 if (valid_longjmp_stack_p (src_point
, m_setjmp_point
)
1871 && !valid_longjmp_stack_p (dst_point
, m_setjmp_point
))
1873 /* Compare with diagnostic_manager::add_events_for_superedge. */
1874 const int src_stack_depth
= src_point
.get_stack_depth ();
1875 m_stack_pop_event
= new precanned_custom_event
1876 (event_loc_info (src_point
.get_location (),
1877 src_point
.get_fndecl (),
1879 "stack frame is popped here, invalidating saved environment");
1880 emission_path
->add_event
1881 (std::unique_ptr
<custom_event
> (m_stack_pop_event
));
1887 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
1889 if (m_stack_pop_event
)
1890 return ev
.formatted_print
1891 ("%qs called after enclosing function of %qs returned at %@",
1892 get_user_facing_name (m_longjmp_call
),
1893 get_user_facing_name (m_setjmp_call
),
1894 m_stack_pop_event
->get_id_ptr ());
1896 return ev
.formatted_print
1897 ("%qs called after enclosing function of %qs has returned",
1898 get_user_facing_name (m_longjmp_call
),
1899 get_user_facing_name (m_setjmp_call
));;
1904 const gcall
*m_setjmp_call
;
1905 const gcall
*m_longjmp_call
;
1906 program_point m_setjmp_point
;
1907 custom_event
*m_stack_pop_event
;
1910 /* Handle LONGJMP_CALL, a call to longjmp or siglongjmp.
1912 Attempt to locate where setjmp/sigsetjmp was called on the jmp_buf and build
1913 an exploded_node and exploded_edge to it representing a rewind to that frame,
1914 handling the various kinds of failure that can occur. */
1917 exploded_node::on_longjmp (exploded_graph
&eg
,
1918 const gcall
*longjmp_call
,
1919 program_state
*new_state
,
1920 region_model_context
*ctxt
)
1922 tree buf_ptr
= gimple_call_arg (longjmp_call
, 0);
1923 gcc_assert (POINTER_TYPE_P (TREE_TYPE (buf_ptr
)));
1925 region_model
*new_region_model
= new_state
->m_region_model
;
1926 const svalue
*buf_ptr_sval
= new_region_model
->get_rvalue (buf_ptr
, ctxt
);
1927 const region
*buf
= new_region_model
->deref_rvalue (buf_ptr_sval
, buf_ptr
,
1930 const svalue
*buf_content_sval
1931 = new_region_model
->get_store_value (buf
, ctxt
);
1932 const setjmp_svalue
*setjmp_sval
1933 = buf_content_sval
->dyn_cast_setjmp_svalue ();
1937 const setjmp_record tmp_setjmp_record
= setjmp_sval
->get_setjmp_record ();
1939 /* Build a custom enode and eedge for rewinding from the longjmp/siglongjmp
1940 call back to the setjmp/sigsetjmp. */
1941 rewind_info_t
rewind_info (tmp_setjmp_record
, longjmp_call
);
1943 const gcall
*setjmp_call
= rewind_info
.get_setjmp_call ();
1944 const program_point
&setjmp_point
= rewind_info
.get_setjmp_point ();
1946 const program_point
&longjmp_point
= get_point ();
1948 /* Verify that the setjmp's call_stack hasn't been popped. */
1949 if (!valid_longjmp_stack_p (longjmp_point
, setjmp_point
))
1951 ctxt
->warn (make_unique
<stale_jmp_buf
> (setjmp_call
,
1957 gcc_assert (longjmp_point
.get_stack_depth ()
1958 >= setjmp_point
.get_stack_depth ());
1960 /* Update the state for use by the destination node. */
1962 /* Stash the current number of diagnostics so that we can update
1963 any that this adds to show where the longjmp is rewinding to. */
1965 diagnostic_manager
*dm
= &eg
.get_diagnostic_manager ();
1966 unsigned prev_num_diagnostics
= dm
->get_num_diagnostics ();
1968 new_region_model
->on_longjmp (longjmp_call
, setjmp_call
,
1969 setjmp_point
.get_stack_depth (), ctxt
);
1971 /* Detect leaks in the new state relative to the old state. */
1972 program_state::detect_leaks (get_state (), *new_state
, NULL
,
1973 eg
.get_ext_state (), ctxt
);
1975 program_point next_point
1976 = program_point::after_supernode (setjmp_point
.get_supernode (),
1977 setjmp_point
.get_call_string ());
1980 = eg
.get_or_create_node (next_point
, *new_state
, this);
1982 /* Create custom exploded_edge for a longjmp. */
1985 exploded_edge
*eedge
1986 = eg
.add_edge (const_cast<exploded_node
*> (this), next
, NULL
, true,
1987 make_unique
<rewind_info_t
> (tmp_setjmp_record
,
1990 /* For any diagnostics that were queued here (such as leaks) we want
1991 the checker_path to show the rewinding events after the "final event"
1992 so that the user sees where the longjmp is rewinding to (otherwise the
1993 path is meaningless).
1995 For example, we want to emit something like:
1997 | NN | longjmp (env, 1);
1998 | | ~~~~~~~~~~~~~~~~
2000 | | (10) 'ptr' leaks here; was allocated at (7)
2001 | | (11) rewinding from 'longjmp' in 'inner'...
2007 | NN | i = setjmp(env);
2010 | | (12) ...to 'setjmp' in 'outer' (saved at (2))
2012 where the "final" event above is event (10), but we want to append
2013 events (11) and (12) afterwards.
2015 Do this by setting m_trailing_eedge on any diagnostics that were
2017 unsigned num_diagnostics
= dm
->get_num_diagnostics ();
2018 for (unsigned i
= prev_num_diagnostics
; i
< num_diagnostics
; i
++)
2020 saved_diagnostic
*sd
= dm
->get_saved_diagnostic (i
);
2021 sd
->m_trailing_eedge
= eedge
;
2026 /* Subroutine of exploded_graph::process_node for finding the successors
2027 of the supernode for a function exit basic block.
2029 Ensure that pop_frame is called, potentially queuing diagnostics about
2033 exploded_node::detect_leaks (exploded_graph
&eg
)
2035 LOG_FUNC_1 (eg
.get_logger (), "EN: %i", m_index
);
2037 gcc_assert (get_point ().get_supernode ()->return_p ());
2039 /* If we're not a "top-level" function, do nothing; pop_frame
2040 will be called when handling the return superedge. */
2041 if (get_point ().get_stack_depth () > 1)
2044 /* We have a "top-level" function. */
2045 gcc_assert (get_point ().get_stack_depth () == 1);
2047 const program_state
&old_state
= get_state ();
2049 /* Work with a temporary copy of the state: pop the frame, and see
2050 what leaks (via purge_unused_svalues). */
2051 program_state
new_state (old_state
);
2053 gcc_assert (new_state
.m_region_model
);
2055 uncertainty_t uncertainty
;
2056 impl_region_model_context
ctxt (eg
, this,
2057 &old_state
, &new_state
, &uncertainty
, NULL
,
2059 const svalue
*result
= NULL
;
2060 new_state
.m_region_model
->pop_frame (NULL
, &result
, &ctxt
, nullptr);
2061 program_state::detect_leaks (old_state
, new_state
, result
,
2062 eg
.get_ext_state (), &ctxt
);
2065 /* Dump the successors and predecessors of this enode to OUTF. */
2068 exploded_node::dump_succs_and_preds (FILE *outf
) const
2073 auto_vec
<exploded_node
*> preds (m_preds
.length ());
2074 FOR_EACH_VEC_ELT (m_preds
, i
, e
)
2075 preds
.quick_push (e
->m_src
);
2077 print_enode_indices (&pp
, preds
);
2078 fprintf (outf
, "preds: %s\n",
2079 pp_formatted_text (&pp
));
2082 auto_vec
<exploded_node
*> succs (m_succs
.length ());
2083 FOR_EACH_VEC_ELT (m_succs
, i
, e
)
2084 succs
.quick_push (e
->m_dest
);
2086 print_enode_indices (&pp
, succs
);
2087 fprintf (outf
, "succs: %s\n",
2088 pp_formatted_text (&pp
));
2092 /* class dynamic_call_info_t : public custom_edge_info. */
2094 /* Implementation of custom_edge_info::update_model vfunc
2095 for dynamic_call_info_t.
2097 Update state for a dynamically discovered call (or return), by pushing
2098 or popping the a frame for the appropriate function. */
2101 dynamic_call_info_t::update_model (region_model
*model
,
2102 const exploded_edge
*eedge
,
2103 region_model_context
*ctxt
) const
2106 if (m_is_returning_call
)
2107 model
->update_for_return_gcall (m_dynamic_call
, ctxt
);
2110 function
*callee
= eedge
->m_dest
->get_function ();
2111 model
->update_for_gcall (m_dynamic_call
, ctxt
, callee
);
2116 /* Implementation of custom_edge_info::add_events_to_path vfunc
2117 for dynamic_call_info_t. */
2120 dynamic_call_info_t::add_events_to_path (checker_path
*emission_path
,
2121 const exploded_edge
&eedge
) const
2123 const exploded_node
*src_node
= eedge
.m_src
;
2124 const program_point
&src_point
= src_node
->get_point ();
2125 const int src_stack_depth
= src_point
.get_stack_depth ();
2126 const exploded_node
*dest_node
= eedge
.m_dest
;
2127 const program_point
&dest_point
= dest_node
->get_point ();
2128 const int dest_stack_depth
= dest_point
.get_stack_depth ();
2130 if (m_is_returning_call
)
2131 emission_path
->add_event
2132 (make_unique
<return_event
> (eedge
,
2133 event_loc_info (m_dynamic_call
2134 ? m_dynamic_call
->location
2136 dest_point
.get_fndecl (),
2137 dest_stack_depth
)));
2139 emission_path
->add_event
2140 (make_unique
<call_event
> (eedge
,
2141 event_loc_info (m_dynamic_call
2142 ? m_dynamic_call
->location
2144 src_point
.get_fndecl (),
2148 /* class rewind_info_t : public custom_edge_info. */
2150 /* Implementation of custom_edge_info::update_model vfunc
2153 Update state for the special-case of a rewind of a longjmp
2154 to a setjmp (which doesn't have a superedge, but does affect
2158 rewind_info_t::update_model (region_model
*model
,
2159 const exploded_edge
*eedge
,
2160 region_model_context
*) const
2163 const program_point
&longjmp_point
= eedge
->m_src
->get_point ();
2164 const program_point
&setjmp_point
= eedge
->m_dest
->get_point ();
2166 gcc_assert (longjmp_point
.get_stack_depth ()
2167 >= setjmp_point
.get_stack_depth ());
2169 model
->on_longjmp (get_longjmp_call (),
2171 setjmp_point
.get_stack_depth (), NULL
);
2175 /* Implementation of custom_edge_info::add_events_to_path vfunc
2176 for rewind_info_t. */
2179 rewind_info_t::add_events_to_path (checker_path
*emission_path
,
2180 const exploded_edge
&eedge
) const
2182 const exploded_node
*src_node
= eedge
.m_src
;
2183 const program_point
&src_point
= src_node
->get_point ();
2184 const int src_stack_depth
= src_point
.get_stack_depth ();
2185 const exploded_node
*dst_node
= eedge
.m_dest
;
2186 const program_point
&dst_point
= dst_node
->get_point ();
2187 const int dst_stack_depth
= dst_point
.get_stack_depth ();
2189 emission_path
->add_event
2190 (make_unique
<rewind_from_longjmp_event
>
2192 event_loc_info (get_longjmp_call ()->location
,
2193 src_point
.get_fndecl (),
2196 emission_path
->add_event
2197 (make_unique
<rewind_to_setjmp_event
>
2199 event_loc_info (get_setjmp_call ()->location
,
2200 dst_point
.get_fndecl (),
2205 /* class exploded_edge : public dedge<eg_traits>. */
2207 /* exploded_edge's ctor. */
2209 exploded_edge::exploded_edge (exploded_node
*src
, exploded_node
*dest
,
2210 const superedge
*sedge
, bool could_do_work
,
2211 std::unique_ptr
<custom_edge_info
> custom_info
)
2212 : dedge
<eg_traits
> (src
, dest
), m_sedge (sedge
),
2213 m_custom_info (std::move (custom_info
)),
2214 m_could_do_work_p (could_do_work
)
2218 /* Implementation of dedge::dump_dot vfunc for exploded_edge.
2219 Use the label of the underlying superedge, if any. */
2222 exploded_edge::dump_dot (graphviz_out
*gv
, const dump_args_t
&) const
2224 pretty_printer
*pp
= gv
->get_pp ();
2226 m_src
->dump_dot_id (pp
);
2227 pp_string (pp
, " -> ");
2228 m_dest
->dump_dot_id (pp
);
2229 dump_dot_label (pp
);
2232 /* Second half of exploded_edge::dump_dot. This is split out
2233 for use by trimmed_graph::dump_dot and base_feasible_edge::dump_dot. */
2236 exploded_edge::dump_dot_label (pretty_printer
*pp
) const
2238 const char *style
= "\"solid,bold\"";
2239 const char *color
= "black";
2241 const char *constraint
= "true";
2244 switch (m_sedge
->m_kind
)
2248 case SUPEREDGE_CFG_EDGE
:
2250 case SUPEREDGE_CALL
:
2252 //constraint = "false";
2254 case SUPEREDGE_RETURN
:
2256 //constraint = "false";
2258 case SUPEREDGE_INTRAPROCEDURAL_CALL
:
2259 style
= "\"dotted\"";
2265 style
= "\"dotted\"";
2269 (" [style=%s, color=%s, weight=%d, constraint=%s,"
2271 style
, color
, weight
, constraint
);
2274 m_sedge
->dump_label_to_pp (pp
, false);
2275 else if (m_custom_info
)
2276 m_custom_info
->print (pp
);
2278 pp_printf (pp
, "%s",
2279 could_do_work_p () ? "(could do work)" : "DOES NO WORK");
2281 //pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
2283 pp_printf (pp
, "\"];\n");
2286 /* Return a new json::object of the form
2287 {"src_idx": int, the index of the source exploded edge,
2288 "dst_idx": int, the index of the destination exploded edge,
2289 "sedge": (optional) object for the superedge, if any,
2290 "custom": (optional) str, a description, if this is a custom edge}. */
2293 exploded_edge::to_json () const
2295 json::object
*eedge_obj
= new json::object ();
2296 eedge_obj
->set_integer ("src_idx", m_src
->m_index
);
2297 eedge_obj
->set_integer ("dst_idx", m_dest
->m_index
);
2299 eedge_obj
->set ("sedge", m_sedge
->to_json ());
2303 pp_format_decoder (&pp
) = default_tree_printer
;
2304 m_custom_info
->print (&pp
);
2305 eedge_obj
->set_string ("custom", pp_formatted_text (&pp
));
2314 stats::stats (int num_supernodes
)
2315 : m_node_reuse_count (0),
2316 m_node_reuse_after_merge_count (0),
2317 m_num_supernodes (num_supernodes
)
2319 for (int i
= 0; i
< NUM_POINT_KINDS
; i
++)
2323 /* Log these stats in multiline form to LOGGER. */
2326 stats::log (logger
*logger
) const
2328 gcc_assert (logger
);
2329 for (int i
= 0; i
< NUM_POINT_KINDS
; i
++)
2330 if (m_num_nodes
[i
] > 0)
2331 logger
->log ("m_num_nodes[%s]: %i",
2332 point_kind_to_string (static_cast <enum point_kind
> (i
)),
2334 logger
->log ("m_node_reuse_count: %i", m_node_reuse_count
);
2335 logger
->log ("m_node_reuse_after_merge_count: %i",
2336 m_node_reuse_after_merge_count
);
2339 /* Dump these stats in multiline form to OUT. */
2342 stats::dump (FILE *out
) const
2344 for (int i
= 0; i
< NUM_POINT_KINDS
; i
++)
2345 if (m_num_nodes
[i
] > 0)
2346 fprintf (out
, "m_num_nodes[%s]: %i\n",
2347 point_kind_to_string (static_cast <enum point_kind
> (i
)),
2349 fprintf (out
, "m_node_reuse_count: %i\n", m_node_reuse_count
);
2350 fprintf (out
, "m_node_reuse_after_merge_count: %i\n",
2351 m_node_reuse_after_merge_count
);
2353 if (m_num_supernodes
> 0)
2354 fprintf (out
, "PK_AFTER_SUPERNODE nodes per supernode: %.2f\n",
2355 (float)m_num_nodes
[PK_AFTER_SUPERNODE
] / (float)m_num_supernodes
);
2358 /* Return the total number of enodes recorded within this object. */
2361 stats::get_total_enodes () const
2364 for (int i
= 0; i
< NUM_POINT_KINDS
; i
++)
2365 result
+= m_num_nodes
[i
];
2369 /* struct per_function_data. */
2371 per_function_data::~per_function_data ()
2373 for (auto iter
: m_summaries
)
2378 per_function_data::add_call_summary (exploded_node
*node
)
2380 m_summaries
.safe_push (new call_summary (this, node
));
2383 /* strongly_connected_components's ctor. Tarjan's SCC algorithm. */
2385 strongly_connected_components::
2386 strongly_connected_components (const supergraph
&sg
, logger
*logger
)
2387 : m_sg (sg
), m_per_node (m_sg
.num_nodes ())
2390 auto_timevar
tv (TV_ANALYZER_SCC
);
2392 for (int i
= 0; i
< m_sg
.num_nodes (); i
++)
2393 m_per_node
.quick_push (per_node_data ());
2395 for (int i
= 0; i
< m_sg
.num_nodes (); i
++)
2396 if (m_per_node
[i
].m_index
== -1)
2403 /* Dump this object to stderr. */
2406 strongly_connected_components::dump () const
2408 for (int i
= 0; i
< m_sg
.num_nodes (); i
++)
2410 const per_node_data
&v
= m_per_node
[i
];
2411 fprintf (stderr
, "SN %i: index: %i lowlink: %i on_stack: %i\n",
2412 i
, v
.m_index
, v
.m_lowlink
, v
.m_on_stack
);
2416 /* Return a new json::array of per-snode SCC ids. */
2419 strongly_connected_components::to_json () const
2421 json::array
*scc_arr
= new json::array ();
2422 for (int i
= 0; i
< m_sg
.num_nodes (); i
++)
2423 scc_arr
->append (new json::integer_number (get_scc_id (i
)));
2427 /* Subroutine of strongly_connected_components's ctor, part of Tarjan's
2431 strongly_connected_components::strong_connect (unsigned index
)
2433 supernode
*v_snode
= m_sg
.get_node_by_index (index
);
2435 /* Set the depth index for v to the smallest unused index. */
2436 per_node_data
*v
= &m_per_node
[index
];
2438 v
->m_lowlink
= index
;
2439 m_stack
.safe_push (index
);
2440 v
->m_on_stack
= true;
2443 /* Consider successors of v. */
2446 FOR_EACH_VEC_ELT (v_snode
->m_succs
, i
, sedge
)
2448 if (sedge
->get_kind () != SUPEREDGE_CFG_EDGE
2449 && sedge
->get_kind () != SUPEREDGE_INTRAPROCEDURAL_CALL
)
2451 supernode
*w_snode
= sedge
->m_dest
;
2452 per_node_data
*w
= &m_per_node
[w_snode
->m_index
];
2453 if (w
->m_index
== -1)
2455 /* Successor w has not yet been visited; recurse on it. */
2456 strong_connect (w_snode
->m_index
);
2457 v
->m_lowlink
= MIN (v
->m_lowlink
, w
->m_lowlink
);
2459 else if (w
->m_on_stack
)
2461 /* Successor w is in stack S and hence in the current SCC
2462 If w is not on stack, then (v, w) is a cross-edge in the DFS
2463 tree and must be ignored. */
2464 v
->m_lowlink
= MIN (v
->m_lowlink
, w
->m_index
);
2468 /* If v is a root node, pop the stack and generate an SCC. */
2470 if (v
->m_lowlink
== v
->m_index
)
2474 int idx
= m_stack
.pop ();
2475 w
= &m_per_node
[idx
];
2476 w
->m_on_stack
= false;
2481 /* worklist's ctor. */
2483 worklist::worklist (const exploded_graph
&eg
, const analysis_plan
&plan
)
2484 : m_scc (eg
.get_supergraph (), eg
.get_logger ()),
2486 m_queue (key_t (*this, NULL
))
2490 /* Return the number of nodes in the worklist. */
2493 worklist::length () const
2495 return m_queue
.nodes ();
2498 /* Return the next node in the worklist, removing it. */
2501 worklist::take_next ()
2503 return m_queue
.extract_min ();
2506 /* Return the next node in the worklist without removing it. */
2509 worklist::peek_next ()
2511 return m_queue
.min ();
2514 /* Add ENODE to the worklist. */
2517 worklist::add_node (exploded_node
*enode
)
2519 gcc_assert (enode
->get_status () == exploded_node::STATUS_WORKLIST
);
2520 m_queue
.insert (key_t (*this, enode
), enode
);
2523 /* Comparator for implementing worklist::key_t comparison operators.
2524 Return negative if KA is before KB
2525 Return positive if KA is after KB
2526 Return 0 if they are equal.
2528 The ordering of the worklist is critical for performance and for
2529 avoiding node explosions. Ideally we want all enodes at a CFG join-point
2530 with the same callstring to be sorted next to each other in the worklist
2531 so that a run of consecutive enodes can be merged and processed "in bulk"
2532 rather than individually or pairwise, minimizing the number of new enodes
2536 worklist::key_t::cmp (const worklist::key_t
&ka
, const worklist::key_t
&kb
)
2538 const program_point
&point_a
= ka
.m_enode
->get_point ();
2539 const program_point
&point_b
= kb
.m_enode
->get_point ();
2540 const call_string
&call_string_a
= point_a
.get_call_string ();
2541 const call_string
&call_string_b
= point_b
.get_call_string ();
2543 /* Order empty-callstring points with different functions based on the
2544 analysis_plan, so that we generate summaries before they are used. */
2545 if (flag_analyzer_call_summaries
2546 && call_string_a
.empty_p ()
2547 && call_string_b
.empty_p ()
2548 && point_a
.get_function () != NULL
2549 && point_b
.get_function () != NULL
2550 && point_a
.get_function () != point_b
.get_function ())
2552 if (int cmp
= ka
.m_worklist
.m_plan
.cmp_function (point_a
.get_function (),
2553 point_b
.get_function ()))
2557 /* Sort by callstring, so that nodes with deeper call strings are processed
2558 before those with shallower call strings.
2567 then we want the path inside the function call to be fully explored up
2568 to the return to the join BB before we explore on the "no fn call" path,
2569 so that both enodes at the join BB reach the front of the worklist at
2570 the same time and thus have a chance of being merged. */
2571 int cs_cmp
= call_string::cmp (call_string_a
, call_string_b
);
2576 int scc_id_a
= ka
.get_scc_id (ka
.m_enode
);
2577 int scc_id_b
= kb
.get_scc_id (kb
.m_enode
);
2578 if (scc_id_a
!= scc_id_b
)
2579 return scc_id_a
- scc_id_b
;
2581 /* If in same SCC, order by supernode index (an arbitrary but stable
2583 const supernode
*snode_a
= ka
.m_enode
->get_supernode ();
2584 const supernode
*snode_b
= kb
.m_enode
->get_supernode ();
2585 if (snode_a
== NULL
)
2587 if (snode_b
!= NULL
)
2591 /* Both are NULL. */
2594 if (snode_b
== NULL
)
2597 /* Neither are NULL. */
2598 gcc_assert (snode_a
&& snode_b
);
2599 if (snode_a
->m_index
!= snode_b
->m_index
)
2600 return snode_a
->m_index
- snode_b
->m_index
;
2602 gcc_assert (snode_a
== snode_b
);
2604 /* Order within supernode via program point. */
2605 int within_snode_cmp
2606 = function_point::cmp_within_supernode (point_a
.get_function_point (),
2607 point_b
.get_function_point ());
2608 if (within_snode_cmp
)
2609 return within_snode_cmp
;
2611 /* Otherwise, we ought to have the same program_point. */
2612 gcc_assert (point_a
== point_b
);
2614 const program_state
&state_a
= ka
.m_enode
->get_state ();
2615 const program_state
&state_b
= kb
.m_enode
->get_state ();
2617 /* Sort by sm-state, so that identical sm-states are grouped
2618 together in the worklist. */
2619 for (unsigned sm_idx
= 0; sm_idx
< state_a
.m_checker_states
.length ();
2622 sm_state_map
*smap_a
= state_a
.m_checker_states
[sm_idx
];
2623 sm_state_map
*smap_b
= state_b
.m_checker_states
[sm_idx
];
2625 if (int smap_cmp
= sm_state_map::cmp (*smap_a
, *smap_b
))
2629 /* Otherwise, we have two enodes at the same program point but with
2630 different states. We don't have a good total ordering on states,
2631 so order them by enode index, so that we have at least have a
2633 return ka
.m_enode
->m_index
- kb
.m_enode
->m_index
;
2636 /* Return a new json::object of the form
2637 {"scc" : [per-snode-IDs]}, */
2640 worklist::to_json () const
2642 json::object
*worklist_obj
= new json::object ();
2644 worklist_obj
->set ("scc", m_scc
.to_json ());
2646 /* The following field isn't yet being JSONified:
2649 return worklist_obj
;
2652 /* exploded_graph's ctor. */
2654 exploded_graph::exploded_graph (const supergraph
&sg
, logger
*logger
,
2655 const extrinsic_state
&ext_state
,
2656 const state_purge_map
*purge_map
,
2657 const analysis_plan
&plan
,
2659 : m_sg (sg
), m_logger (logger
),
2660 m_worklist (*this, plan
),
2661 m_ext_state (ext_state
),
2662 m_purge_map (purge_map
),
2664 m_diagnostic_manager (logger
, ext_state
.get_engine (), verbosity
),
2665 m_global_stats (m_sg
.num_nodes ()),
2666 m_functionless_stats (m_sg
.num_nodes ()),
2667 m_PK_AFTER_SUPERNODE_per_snode (m_sg
.num_nodes ())
2669 m_origin
= get_or_create_node
2670 (program_point::origin (*ext_state
.get_model_manager ()),
2671 program_state (ext_state
), NULL
);
2672 for (int i
= 0; i
< m_sg
.num_nodes (); i
++)
2673 m_PK_AFTER_SUPERNODE_per_snode
.quick_push (i
);
2676 /* exploded_graph's dtor. */
2678 exploded_graph::~exploded_graph ()
2680 for (auto iter
: m_per_point_data
)
2682 for (auto iter
: m_per_function_data
)
2684 for (auto iter
: m_per_function_stats
)
2686 for (auto iter
: m_per_call_string_data
)
2690 /* Subroutine for use when implementing __attribute__((tainted_args))
2691 on functions and on function pointer fields in structs.
2693 Called on STATE representing a call to FNDECL.
2694 Mark all params of FNDECL in STATE as "tainted". Mark the value of all
2695 regions pointed to by params of FNDECL as "tainted".
2697 Return true if successful; return false if the "taint" state machine
2701 mark_params_as_tainted (program_state
*state
, tree fndecl
,
2702 const extrinsic_state
&ext_state
)
2704 unsigned taint_sm_idx
;
2705 if (!ext_state
.get_sm_idx_by_name ("taint", &taint_sm_idx
))
2707 sm_state_map
*smap
= state
->m_checker_states
[taint_sm_idx
];
2709 const state_machine
&sm
= ext_state
.get_sm (taint_sm_idx
);
2710 state_machine::state_t tainted
= sm
.get_state_by_name ("tainted");
2712 region_model_manager
*mgr
= ext_state
.get_model_manager ();
2714 function
*fun
= DECL_STRUCT_FUNCTION (fndecl
);
2717 for (tree iter_parm
= DECL_ARGUMENTS (fndecl
); iter_parm
;
2718 iter_parm
= DECL_CHAIN (iter_parm
))
2720 tree param
= iter_parm
;
2721 if (tree parm_default_ssa
= ssa_default_def (fun
, iter_parm
))
2722 param
= parm_default_ssa
;
2723 const region
*param_reg
= state
->m_region_model
->get_lvalue (param
, NULL
);
2724 const svalue
*init_sval
= mgr
->get_or_create_initial_value (param_reg
);
2725 smap
->set_state (state
->m_region_model
, init_sval
,
2726 tainted
, NULL
/*origin_new_sval*/, ext_state
);
2727 if (POINTER_TYPE_P (TREE_TYPE (param
)))
2729 const region
*pointee_reg
= mgr
->get_symbolic_region (init_sval
);
2730 /* Mark "*param" as tainted. */
2731 const svalue
*init_pointee_sval
2732 = mgr
->get_or_create_initial_value (pointee_reg
);
2733 smap
->set_state (state
->m_region_model
, init_pointee_sval
,
2734 tainted
, NULL
/*origin_new_sval*/, ext_state
);
2741 /* Custom event for use by tainted_args_function_info when a function
2742 has been marked with __attribute__((tainted_args)). */
2744 class tainted_args_function_custom_event
: public custom_event
2747 tainted_args_function_custom_event (const event_loc_info
&loc_info
)
2748 : custom_event (loc_info
),
2749 m_fndecl (loc_info
.m_fndecl
)
2753 label_text
get_desc (bool can_colorize
) const final override
2755 return make_label_text
2757 "function %qE marked with %<__attribute__((tainted_args))%>",
2765 /* Custom exploded_edge info for top-level calls to a function
2766 marked with __attribute__((tainted_args)). */
2768 class tainted_args_function_info
: public custom_edge_info
2771 tainted_args_function_info (tree fndecl
)
2775 void print (pretty_printer
*pp
) const final override
2777 pp_string (pp
, "call to tainted_args function");
2780 bool update_model (region_model
*,
2781 const exploded_edge
*,
2782 region_model_context
*) const final override
2788 void add_events_to_path (checker_path
*emission_path
,
2789 const exploded_edge
&) const final override
2791 emission_path
->add_event
2792 (make_unique
<tainted_args_function_custom_event
>
2793 (event_loc_info (DECL_SOURCE_LOCATION (m_fndecl
), m_fndecl
, 0)));
2800 /* Ensure that there is an exploded_node representing an external call to
2801 FUN, adding it to the worklist if creating it.
2803 Add an edge from the origin exploded_node to the function entrypoint
2806 Return the exploded_node for the entrypoint to the function. */
2809 exploded_graph::add_function_entry (const function
&fun
)
2811 gcc_assert (gimple_has_body_p (fun
.decl
));
2813 /* Be idempotent. */
2814 function
*key
= const_cast<function
*> (&fun
);
2815 if (m_functions_with_enodes
.contains (key
))
2817 logger
* const logger
= get_logger ();
2819 logger
->log ("entrypoint for %qE already exists", fun
.decl
);
2824 = program_point::from_function_entry (*m_ext_state
.get_model_manager (),
2826 program_state
state (m_ext_state
);
2827 state
.push_frame (m_ext_state
, fun
);
2829 std::unique_ptr
<custom_edge_info
> edge_info
= NULL
;
2831 if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (fun
.decl
)))
2833 if (mark_params_as_tainted (&state
, fun
.decl
, m_ext_state
))
2834 edge_info
= make_unique
<tainted_args_function_info
> (fun
.decl
);
2840 exploded_node
*enode
= get_or_create_node (point
, state
, NULL
);
2844 add_edge (m_origin
, enode
, NULL
, false, std::move (edge_info
));
2846 m_functions_with_enodes
.add (key
);
2851 /* Get or create an exploded_node for (POINT, STATE).
2852 If a new node is created, it is added to the worklist.
2854 Use ENODE_FOR_DIAG, a pre-existing enode, for any diagnostics
2855 that need to be emitted (e.g. when purging state *before* we have
2859 exploded_graph::get_or_create_node (const program_point
&point
,
2860 const program_state
&state
,
2861 exploded_node
*enode_for_diag
)
2863 logger
* const logger
= get_logger ();
2868 pretty_printer
*pp
= logger
->get_printer ();
2869 logger
->start_log_line ();
2870 pp_string (pp
, "point: ");
2871 point
.print (pp
, f
);
2872 logger
->end_log_line ();
2873 logger
->start_log_line ();
2874 pp_string (pp
, "state: ");
2875 state
.dump_to_pp (m_ext_state
, true, false, pp
);
2876 logger
->end_log_line ();
2879 /* Stop exploring paths for which we don't know how to effectively
2884 logger
->log ("invalid state; not creating node");
2888 auto_cfun
sentinel (point
.get_function ());
2890 state
.validate (get_ext_state ());
2892 //state.dump (get_ext_state ());
2894 /* Prune state to try to improve the chances of a cache hit,
2895 avoiding generating redundant nodes. */
2896 uncertainty_t uncertainty
;
2897 program_state pruned_state
2898 = state
.prune_for_point (*this, point
, enode_for_diag
, &uncertainty
);
2900 pruned_state
.validate (get_ext_state ());
2902 //pruned_state.dump (get_ext_state ());
2906 pretty_printer
*pp
= logger
->get_printer ();
2907 logger
->start_log_line ();
2908 pp_string (pp
, "pruned_state: ");
2909 pruned_state
.dump_to_pp (m_ext_state
, true, false, pp
);
2910 logger
->end_log_line ();
2911 pruned_state
.m_region_model
->dump_to_pp (logger
->get_printer (), true,
2915 stats
*per_fn_stats
= get_or_create_function_stats (point
.get_function ());
2918 = &get_or_create_per_call_string_data (point
.get_call_string ())->m_stats
;
2920 point_and_state
ps (point
, pruned_state
);
2921 ps
.validate (m_ext_state
);
2922 if (exploded_node
**slot
= m_point_and_state_to_node
.get (&ps
))
2924 /* An exploded_node for PS already exists. */
2926 logger
->log ("reused EN: %i", (*slot
)->m_index
);
2927 m_global_stats
.m_node_reuse_count
++;
2928 per_fn_stats
->m_node_reuse_count
++;
2929 per_cs_stats
->m_node_reuse_count
++;
2933 per_program_point_data
*per_point_data
2934 = get_or_create_per_program_point_data (point
);
2936 /* Consider merging state with another enode at this program_point. */
2937 if (flag_analyzer_state_merge
)
2939 exploded_node
*existing_enode
;
2941 FOR_EACH_VEC_ELT (per_point_data
->m_enodes
, i
, existing_enode
)
2944 logger
->log ("considering merging with existing EN: %i for point",
2945 existing_enode
->m_index
);
2946 gcc_assert (existing_enode
->get_point () == point
);
2947 const program_state
&existing_state
= existing_enode
->get_state ();
2949 /* This merges successfully within the loop. */
2951 program_state
merged_state (m_ext_state
);
2952 if (pruned_state
.can_merge_with_p (existing_state
, m_ext_state
, point
,
2955 merged_state
.validate (m_ext_state
);
2957 logger
->log ("merging new state with that of EN: %i",
2958 existing_enode
->m_index
);
2960 /* Try again for a cache hit.
2961 Whether we get one or not, merged_state's value_ids have no
2962 relationship to those of the input state, and thus to those
2963 of CHANGE, so we must purge any svalue_ids from *CHANGE. */
2964 ps
.set_state (merged_state
);
2966 if (exploded_node
**slot
= m_point_and_state_to_node
.get (&ps
))
2968 /* An exploded_node for PS already exists. */
2970 logger
->log ("reused EN: %i", (*slot
)->m_index
);
2971 m_global_stats
.m_node_reuse_after_merge_count
++;
2972 per_fn_stats
->m_node_reuse_after_merge_count
++;
2973 per_cs_stats
->m_node_reuse_after_merge_count
++;
2979 logger
->log ("not merging new state with that of EN: %i",
2980 existing_enode
->m_index
);
2984 /* Impose a limit on the number of enodes per program point, and
2985 simply stop if we exceed it. */
2986 if ((int)per_point_data
->m_enodes
.length ()
2987 >= param_analyzer_max_enodes_per_program_point
)
2990 point
.print (&pp
, format (false));
2991 print_enode_indices (&pp
, per_point_data
->m_enodes
);
2993 logger
->log ("not creating enode; too many at program point: %s",
2994 pp_formatted_text (&pp
));
2995 warning_at (point
.get_location (), OPT_Wanalyzer_too_complex
,
2996 "terminating analysis for this program point: %s",
2997 pp_formatted_text (&pp
));
2998 per_point_data
->m_excess_enodes
++;
3002 ps
.validate (m_ext_state
);
3004 /* An exploded_node for "ps" doesn't already exist; create one. */
3005 exploded_node
*node
= new exploded_node (ps
, m_nodes
.length ());
3007 m_point_and_state_to_node
.put (node
->get_ps_key (), node
);
3009 /* Update per-program_point data. */
3010 per_point_data
->m_enodes
.safe_push (node
);
3012 const enum point_kind node_pk
= node
->get_point ().get_kind ();
3013 m_global_stats
.m_num_nodes
[node_pk
]++;
3014 per_fn_stats
->m_num_nodes
[node_pk
]++;
3015 per_cs_stats
->m_num_nodes
[node_pk
]++;
3017 if (node_pk
== PK_AFTER_SUPERNODE
)
3018 m_PK_AFTER_SUPERNODE_per_snode
[point
.get_supernode ()->m_index
]++;
3023 pretty_printer
*pp
= logger
->get_printer ();
3024 logger
->log ("created EN: %i", node
->m_index
);
3025 logger
->start_log_line ();
3026 pp_string (pp
, "point: ");
3027 point
.print (pp
, f
);
3028 logger
->end_log_line ();
3029 logger
->start_log_line ();
3030 pp_string (pp
, "pruned_state: ");
3031 pruned_state
.dump_to_pp (m_ext_state
, true, false, pp
);
3032 logger
->end_log_line ();
3035 /* Add the new node to the worlist. */
3036 m_worklist
.add_node (node
);
3040 /* Add an exploded_edge from SRC to DEST, recording its association
3041 with SEDGE (which may be NULL), and, if non-NULL, taking ownership
3042 of CUSTOM_INFO. COULD_DO_WORK is used for detecting infinite loops.
3043 Return the newly-created eedge. */
3046 exploded_graph::add_edge (exploded_node
*src
, exploded_node
*dest
,
3047 const superedge
*sedge
, bool could_do_work
,
3048 std::unique_ptr
<custom_edge_info
> custom_info
)
3051 get_logger ()->log ("creating edge EN: %i -> EN: %i",
3052 src
->m_index
, dest
->m_index
);
3054 = new exploded_edge (src
, dest
, sedge
, could_do_work
,
3055 std::move (custom_info
));
3056 digraph
<eg_traits
>::add_edge (e
);
3060 /* Ensure that this graph has per-program_point-data for POINT;
3061 borrow a pointer to it. */
3063 per_program_point_data
*
3065 get_or_create_per_program_point_data (const program_point
&point
)
3067 if (per_program_point_data
**slot
= m_per_point_data
.get (&point
))
3070 per_program_point_data
*per_point_data
= new per_program_point_data (point
);
3071 m_per_point_data
.put (&per_point_data
->m_key
, per_point_data
);
3072 return per_point_data
;
3075 /* Get this graph's per-program-point-data for POINT if there is any,
3078 per_program_point_data
*
3079 exploded_graph::get_per_program_point_data (const program_point
&point
) const
3081 if (per_program_point_data
**slot
3082 = const_cast <point_map_t
&> (m_per_point_data
).get (&point
))
3088 /* Ensure that this graph has per-call_string-data for CS;
3089 borrow a pointer to it. */
3091 per_call_string_data
*
3092 exploded_graph::get_or_create_per_call_string_data (const call_string
&cs
)
3094 if (per_call_string_data
**slot
= m_per_call_string_data
.get (&cs
))
3097 per_call_string_data
*data
= new per_call_string_data (cs
, m_sg
.num_nodes ());
3098 m_per_call_string_data
.put (&data
->m_key
,
3103 /* Ensure that this graph has per-function-data for FUN;
3104 borrow a pointer to it. */
3107 exploded_graph::get_or_create_per_function_data (function
*fun
)
3109 if (per_function_data
**slot
= m_per_function_data
.get (fun
))
3112 per_function_data
*data
= new per_function_data ();
3113 m_per_function_data
.put (fun
, data
);
3117 /* Get this graph's per-function-data for FUN if there is any,
3121 exploded_graph::get_per_function_data (function
*fun
) const
3123 if (per_function_data
**slot
3124 = const_cast <per_function_data_t
&> (m_per_function_data
).get (fun
))
3130 /* Return true if FUN should be traversed directly, rather than only as
3131 called via other functions. */
3134 toplevel_function_p (const function
&fun
, logger
*logger
)
3136 /* Don't directly traverse into functions that have an "__analyzer_"
3137 prefix. Doing so is useful for the analyzer test suite, allowing
3138 us to have functions that are called in traversals, but not directly
3139 explored, thus testing how the analyzer handles calls and returns.
3140 With this, we can have DejaGnu directives that cover just the case
3141 of where a function is called by another function, without generating
3142 excess messages from the case of the first function being traversed
3144 #define ANALYZER_PREFIX "__analyzer_"
3145 if (!strncmp (IDENTIFIER_POINTER (DECL_NAME (fun
.decl
)), ANALYZER_PREFIX
,
3146 strlen (ANALYZER_PREFIX
)))
3149 logger
->log ("not traversing %qE (starts with %qs)",
3150 fun
.decl
, ANALYZER_PREFIX
);
3155 logger
->log ("traversing %qE (all checks passed)", fun
.decl
);
3160 /* Custom event for use by tainted_call_info when a callback field has been
3161 marked with __attribute__((tainted_args)), for labelling the field. */
3163 class tainted_args_field_custom_event
: public custom_event
3166 tainted_args_field_custom_event (tree field
)
3167 : custom_event (event_loc_info (DECL_SOURCE_LOCATION (field
), NULL_TREE
, 0)),
3172 label_text
get_desc (bool can_colorize
) const final override
3174 return make_label_text (can_colorize
,
3176 " is marked with %<__attribute__((tainted_args))%>",
3177 m_field
, DECL_CONTEXT (m_field
));
3184 /* Custom event for use by tainted_call_info when a callback field has been
3185 marked with __attribute__((tainted_args)), for labelling the function used
3186 in that callback. */
3188 class tainted_args_callback_custom_event
: public custom_event
3191 tainted_args_callback_custom_event (const event_loc_info
&loc_info
,
3193 : custom_event (loc_info
),
3198 label_text
get_desc (bool can_colorize
) const final override
3200 return make_label_text (can_colorize
,
3201 "function %qE used as initializer for field %qE"
3202 " marked with %<__attribute__((tainted_args))%>",
3203 get_fndecl (), m_field
);
3210 /* Custom edge info for use when adding a function used by a callback field
3211 marked with '__attribute__((tainted_args))'. */
3213 class tainted_args_call_info
: public custom_edge_info
3216 tainted_args_call_info (tree field
, tree fndecl
, location_t loc
)
3217 : m_field (field
), m_fndecl (fndecl
), m_loc (loc
)
3220 void print (pretty_printer
*pp
) const final override
3222 pp_string (pp
, "call to tainted field");
3225 bool update_model (region_model
*,
3226 const exploded_edge
*,
3227 region_model_context
*) const final override
3233 void add_events_to_path (checker_path
*emission_path
,
3234 const exploded_edge
&) const final override
3236 /* Show the field in the struct declaration, e.g.
3237 "(1) field 'store' is marked with '__attribute__((tainted_args))'" */
3238 emission_path
->add_event
3239 (make_unique
<tainted_args_field_custom_event
> (m_field
));
3241 /* Show the callback in the initializer
3243 "(2) function 'gadget_dev_desc_UDC_store' used as initializer
3244 for field 'store' marked with '__attribute__((tainted_args))'". */
3245 emission_path
->add_event
3246 (make_unique
<tainted_args_callback_custom_event
>
3247 (event_loc_info (m_loc
, m_fndecl
, 0),
3257 /* Given an initializer at LOC for FIELD marked with
3258 '__attribute__((tainted_args))' initialized with FNDECL, add an
3259 entrypoint to FNDECL to EG (and to its worklist) where the params to
3260 FNDECL are marked as tainted. */
3263 add_tainted_args_callback (exploded_graph
*eg
, tree field
, tree fndecl
,
3266 logger
*logger
= eg
->get_logger ();
3270 if (!gimple_has_body_p (fndecl
))
3273 const extrinsic_state
&ext_state
= eg
->get_ext_state ();
3275 function
*fun
= DECL_STRUCT_FUNCTION (fndecl
);
3279 = program_point::from_function_entry (*ext_state
.get_model_manager (),
3280 eg
->get_supergraph (), *fun
);
3281 program_state
state (ext_state
);
3282 state
.push_frame (ext_state
, *fun
);
3284 if (!mark_params_as_tainted (&state
, fndecl
, ext_state
))
3290 exploded_node
*enode
= eg
->get_or_create_node (point
, state
, NULL
);
3294 logger
->log ("created EN %i for tainted_args %qE entrypoint",
3295 enode
->m_index
, fndecl
);
3298 logger
->log ("did not create enode for tainted_args %qE entrypoint",
3304 eg
->add_edge (eg
->get_origin (), enode
, NULL
, false,
3305 make_unique
<tainted_args_call_info
> (field
, fndecl
, loc
));
3308 /* Callback for walk_tree for finding callbacks within initializers;
3309 ensure that any callback initializer where the corresponding field is
3310 marked with '__attribute__((tainted_args))' is treated as an entrypoint
3311 to the analysis, special-casing that the inputs to the callback are
3315 add_any_callbacks (tree
*tp
, int *, void *data
)
3317 exploded_graph
*eg
= (exploded_graph
*)data
;
3318 if (TREE_CODE (*tp
) == CONSTRUCTOR
)
3320 /* Find fields with the "tainted_args" attribute.
3321 walk_tree only walks the values, not the index values;
3322 look at the index values. */
3323 unsigned HOST_WIDE_INT idx
;
3324 constructor_elt
*ce
;
3326 for (idx
= 0; vec_safe_iterate (CONSTRUCTOR_ELTS (*tp
), idx
, &ce
);
3328 if (ce
->index
&& TREE_CODE (ce
->index
) == FIELD_DECL
)
3329 if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (ce
->index
)))
3331 tree value
= ce
->value
;
3332 if (TREE_CODE (value
) == ADDR_EXPR
3333 && TREE_CODE (TREE_OPERAND (value
, 0)) == FUNCTION_DECL
)
3334 add_tainted_args_callback (eg
, ce
->index
,
3335 TREE_OPERAND (value
, 0),
3336 EXPR_LOCATION (value
));
3343 /* Add initial nodes to EG, with entrypoints for externally-callable
3347 exploded_graph::build_initial_worklist ()
3349 logger
* const logger
= get_logger ();
3353 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node
)
3355 function
*fun
= node
->get_fun ();
3357 if (!toplevel_function_p (*fun
, logger
))
3359 exploded_node
*enode
= add_function_entry (*fun
);
3363 logger
->log ("created EN %i for %qE entrypoint",
3364 enode
->m_index
, fun
->decl
);
3366 logger
->log ("did not create enode for %qE entrypoint", fun
->decl
);
3370 /* Find callbacks that are reachable from global initializers. */
3371 varpool_node
*vpnode
;
3372 FOR_EACH_VARIABLE (vpnode
)
3374 tree decl
= vpnode
->decl
;
3375 tree init
= DECL_INITIAL (decl
);
3378 walk_tree (&init
, add_any_callbacks
, this, NULL
);
3382 /* The main loop of the analysis.
3383 Take freshly-created exploded_nodes from the worklist, calling
3384 process_node on them to explore the <point, state> graph.
3385 Add edges to their successors, potentially creating new successors
3386 (which are also added to the worklist). */
3389 exploded_graph::process_worklist ()
3391 logger
* const logger
= get_logger ();
3393 auto_timevar
tv (TV_ANALYZER_WORKLIST
);
3395 while (m_worklist
.length () > 0)
3397 exploded_node
*node
= m_worklist
.take_next ();
3398 gcc_assert (node
->get_status () == exploded_node::STATUS_WORKLIST
);
3399 gcc_assert (node
->m_succs
.length () == 0
3400 || node
== m_origin
);
3403 logger
->log ("next to process: EN: %i", node
->m_index
);
3405 /* If we have a run of nodes that are before-supernode, try merging and
3406 processing them together, rather than pairwise or individually. */
3407 if (flag_analyzer_state_merge
&& node
!= m_origin
)
3408 if (maybe_process_run_of_before_supernode_enodes (node
))
3411 /* Avoid exponential explosions of nodes by attempting to merge
3412 nodes that are at the same program point and which have
3413 sufficiently similar state. */
3414 if (flag_analyzer_state_merge
&& node
!= m_origin
)
3415 if (exploded_node
*node_2
= m_worklist
.peek_next ())
3417 gcc_assert (node_2
->get_status ()
3418 == exploded_node::STATUS_WORKLIST
);
3419 gcc_assert (node
->m_succs
.length () == 0);
3420 gcc_assert (node_2
->m_succs
.length () == 0);
3422 gcc_assert (node
!= node_2
);
3425 logger
->log ("peek worklist: EN: %i", node_2
->m_index
);
3427 if (node
->get_point () == node_2
->get_point ())
3429 const program_point
&point
= node
->get_point ();
3433 pretty_printer
*pp
= logger
->get_printer ();
3434 logger
->start_log_line ();
3436 ("got potential merge EN: %i and EN: %i at ",
3437 node
->m_index
, node_2
->m_index
);
3438 point
.print (pp
, f
);
3439 logger
->end_log_line ();
3441 const program_state
&state
= node
->get_state ();
3442 const program_state
&state_2
= node_2
->get_state ();
3444 /* They shouldn't be equal, or we wouldn't have two
3446 gcc_assert (state
!= state_2
);
3448 program_state
merged_state (m_ext_state
);
3449 if (state
.can_merge_with_p (state_2
, m_ext_state
,
3450 point
, &merged_state
))
3453 logger
->log ("merging EN: %i and EN: %i",
3454 node
->m_index
, node_2
->m_index
);
3456 if (merged_state
== state
)
3458 /* Then merge node_2 into node by adding an edge. */
3459 add_edge (node_2
, node
, NULL
, false);
3461 /* Remove node_2 from the worklist. */
3462 m_worklist
.take_next ();
3463 node_2
->set_status (exploded_node::STATUS_MERGER
);
3465 /* Continue processing "node" below. */
3467 else if (merged_state
== state_2
)
3469 /* Then merge node into node_2, and leave node_2
3470 in the worklist, to be processed on the next
3472 add_edge (node
, node_2
, NULL
, false);
3473 node
->set_status (exploded_node::STATUS_MERGER
);
3478 /* We have a merged state that differs from
3479 both state and state_2. */
3481 /* Remove node_2 from the worklist. */
3482 m_worklist
.take_next ();
3484 /* Create (or get) an exploded node for the merged
3485 states, adding to the worklist. */
3486 exploded_node
*merged_enode
3487 = get_or_create_node (node
->get_point (),
3488 merged_state
, node
);
3489 if (merged_enode
== NULL
)
3493 logger
->log ("merged EN: %i and EN: %i into EN: %i",
3494 node
->m_index
, node_2
->m_index
,
3495 merged_enode
->m_index
);
3497 /* "node" and "node_2" have both now been removed
3498 from the worklist; we should not process them.
3500 "merged_enode" may be a new node; if so it will be
3501 processed in a subsequent iteration.
3502 Alternatively, "merged_enode" could be an existing
3503 node; one way the latter can
3504 happen is if we end up merging a succession of
3505 similar nodes into one. */
3507 /* If merged_node is one of the two we were merging,
3508 add it back to the worklist to ensure it gets
3511 Add edges from the merged nodes to it (but not a
3513 if (merged_enode
== node
)
3514 m_worklist
.add_node (merged_enode
);
3517 add_edge (node
, merged_enode
, NULL
, false);
3518 node
->set_status (exploded_node::STATUS_MERGER
);
3521 if (merged_enode
== node_2
)
3522 m_worklist
.add_node (merged_enode
);
3525 add_edge (node_2
, merged_enode
, NULL
, false);
3526 node_2
->set_status (exploded_node::STATUS_MERGER
);
3533 /* TODO: should we attempt more than two nodes,
3534 or just do pairs of nodes? (and hope that we get
3535 a cascade of mergers). */
3539 process_node (node
);
3542 /* Impose a hard limit on the number of exploded nodes, to ensure
3543 that the analysis terminates in the face of pathological state
3544 explosion (or bugs).
3546 Specifically, the limit is on the number of PK_AFTER_SUPERNODE
3547 exploded nodes, looking at supernode exit events.
3549 We use exit rather than entry since there can be multiple
3550 entry ENs, one per phi; the number of PK_AFTER_SUPERNODE ought
3551 to be equivalent to the number of supernodes multiplied by the
3552 number of states. */
3553 const int limit
= m_sg
.num_nodes () * param_analyzer_bb_explosion_factor
;
3554 if (m_global_stats
.m_num_nodes
[PK_AFTER_SUPERNODE
] > limit
)
3557 logger
->log ("bailing out; too many nodes");
3558 warning_at (node
->get_point ().get_location (),
3559 OPT_Wanalyzer_too_complex
,
3560 "analysis bailed out early"
3561 " (%i 'after-snode' enodes; %i enodes)",
3562 m_global_stats
.m_num_nodes
[PK_AFTER_SUPERNODE
],
3569 /* Attempt to process a consecutive run of sufficiently-similar nodes in
3570 the worklist at a CFG join-point (having already popped ENODE from the
3571 head of the worklist).
3573 If ENODE's point is of the form (before-supernode, SNODE) and the next
3574 nodes in the worklist are a consecutive run of enodes of the same form,
3575 for the same supernode as ENODE (but potentially from different in-edges),
3576 process them all together, setting their status to STATUS_BULK_MERGED,
3578 Otherwise, return false, in which case ENODE must be processed in the
3581 When processing them all together, generate successor states based
3582 on phi nodes for the appropriate CFG edges, and then attempt to merge
3583 these states into a minimal set of merged successor states, partitioning
3584 the inputs by merged successor state.
3586 Create new exploded nodes for all of the merged states, and add edges
3587 connecting the input enodes to the corresponding merger exploded nodes.
3589 We hope we have a much smaller number of merged successor states
3590 compared to the number of input enodes - ideally just one,
3591 if all successor states can be merged.
3593 Processing and merging many together as one operation rather than as
3594 pairs avoids scaling issues where per-pair mergers could bloat the
3595 graph with merger nodes (especially so after switch statements). */
3599 maybe_process_run_of_before_supernode_enodes (exploded_node
*enode
)
3601 /* A struct for tracking per-input state. */
3604 item (exploded_node
*input_enode
)
3605 : m_input_enode (input_enode
),
3606 m_processed_state (input_enode
->get_state ()),
3610 exploded_node
*m_input_enode
;
3611 program_state m_processed_state
;
3615 gcc_assert (enode
->get_status () == exploded_node::STATUS_WORKLIST
);
3616 gcc_assert (enode
->m_succs
.length () == 0);
3618 const program_point
&point
= enode
->get_point ();
3620 if (point
.get_kind () != PK_BEFORE_SUPERNODE
)
3623 const supernode
*snode
= point
.get_supernode ();
3625 logger
* const logger
= get_logger ();
3628 /* Find a run of enodes in the worklist that are before the same supernode,
3629 but potentially from different in-edges. */
3630 auto_vec
<exploded_node
*> enodes
;
3631 enodes
.safe_push (enode
);
3632 while (exploded_node
*enode_2
= m_worklist
.peek_next ())
3634 gcc_assert (enode_2
->get_status ()
3635 == exploded_node::STATUS_WORKLIST
);
3636 gcc_assert (enode_2
->m_succs
.length () == 0);
3638 const program_point
&point_2
= enode_2
->get_point ();
3640 if (point_2
.get_kind () == PK_BEFORE_SUPERNODE
3641 && point_2
.get_supernode () == snode
3642 && &point_2
.get_call_string () == &point
.get_call_string ())
3644 enodes
.safe_push (enode_2
);
3645 m_worklist
.take_next ();
3651 /* If the only node is ENODE, then give up. */
3652 if (enodes
.length () == 1)
3656 logger
->log ("got run of %i enodes for SN: %i",
3657 enodes
.length (), snode
->m_index
);
3659 /* All of these enodes have a shared successor point (even if they
3660 were for different in-edges). */
3661 program_point
next_point (point
.get_next ());
3663 /* Calculate the successor state for each enode in enodes. */
3664 auto_delete_vec
<item
> items (enodes
.length ());
3666 exploded_node
*iter_enode
;
3667 FOR_EACH_VEC_ELT (enodes
, i
, iter_enode
)
3669 item
*it
= new item (iter_enode
);
3670 items
.quick_push (it
);
3671 const program_state
&state
= iter_enode
->get_state ();
3672 program_state
*next_state
= &it
->m_processed_state
;
3673 next_state
->validate (m_ext_state
);
3674 const program_point
&iter_point
= iter_enode
->get_point ();
3675 if (const superedge
*iter_sedge
= iter_point
.get_from_edge ())
3677 uncertainty_t uncertainty
;
3678 impl_region_model_context
ctxt (*this, iter_enode
,
3680 &uncertainty
, NULL
, NULL
);
3681 const cfg_superedge
*last_cfg_superedge
3682 = iter_sedge
->dyn_cast_cfg_superedge ();
3683 if (last_cfg_superedge
)
3684 next_state
->m_region_model
->update_for_phis
3685 (snode
, last_cfg_superedge
, &ctxt
);
3687 next_state
->validate (m_ext_state
);
3690 /* Attempt to partition the items into a set of merged states.
3691 We hope we have a much smaller number of merged states
3692 compared to the number of input enodes - ideally just one,
3693 if all can be merged. */
3694 auto_delete_vec
<program_state
> merged_states
;
3695 auto_vec
<item
*> first_item_for_each_merged_state
;
3697 FOR_EACH_VEC_ELT (items
, i
, it
)
3699 const program_state
&it_state
= it
->m_processed_state
;
3700 program_state
*merged_state
;
3701 unsigned iter_merger_idx
;
3702 FOR_EACH_VEC_ELT (merged_states
, iter_merger_idx
, merged_state
)
3704 merged_state
->validate (m_ext_state
);
3705 program_state
merge (m_ext_state
);
3706 if (it_state
.can_merge_with_p (*merged_state
, m_ext_state
,
3707 next_point
, &merge
))
3709 *merged_state
= merge
;
3710 merged_state
->validate (m_ext_state
);
3711 it
->m_merger_idx
= iter_merger_idx
;
3713 logger
->log ("reusing merger state %i for item %i (EN: %i)",
3714 it
->m_merger_idx
, i
, it
->m_input_enode
->m_index
);
3718 /* If it couldn't be merged with any existing merged_states,
3719 create a new one. */
3720 if (it
->m_merger_idx
== -1)
3722 it
->m_merger_idx
= merged_states
.length ();
3723 merged_states
.safe_push (new program_state (it_state
));
3724 first_item_for_each_merged_state
.safe_push (it
);
3726 logger
->log ("using new merger state %i for item %i (EN: %i)",
3727 it
->m_merger_idx
, i
, it
->m_input_enode
->m_index
);
3730 gcc_assert (it
->m_merger_idx
>= 0);
3731 gcc_assert ((unsigned)it
->m_merger_idx
< merged_states
.length ());
3734 /* Create merger nodes. */
3735 auto_vec
<exploded_node
*> next_enodes (merged_states
.length ());
3736 program_state
*merged_state
;
3737 FOR_EACH_VEC_ELT (merged_states
, i
, merged_state
)
3739 exploded_node
*src_enode
3740 = first_item_for_each_merged_state
[i
]->m_input_enode
;
3742 = get_or_create_node (next_point
, *merged_state
, src_enode
);
3743 /* "next" could be NULL; we handle that when adding the edges below. */
3744 next_enodes
.quick_push (next
);
3748 logger
->log ("using EN: %i for merger state %i", next
->m_index
, i
);
3750 logger
->log ("using NULL enode for merger state %i", i
);
3754 /* Create edges from each input enode to the appropriate successor enode.
3755 Update the status of the now-processed input enodes. */
3756 FOR_EACH_VEC_ELT (items
, i
, it
)
3758 exploded_node
*next
= next_enodes
[it
->m_merger_idx
];
3760 add_edge (it
->m_input_enode
, next
, NULL
,
3761 false); /* no "work" is done during merger. */
3762 it
->m_input_enode
->set_status (exploded_node::STATUS_BULK_MERGED
);
3766 logger
->log ("merged %i in-enodes into %i out-enode(s) at SN: %i",
3767 items
.length (), merged_states
.length (), snode
->m_index
);
3772 /* Return true if STMT must appear at the start of its exploded node, and
3773 thus we can't consolidate its effects within a run of other statements,
3774 where PREV_STMT was the previous statement. */
3777 stmt_requires_new_enode_p (const gimple
*stmt
,
3778 const gimple
*prev_stmt
)
3780 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
3782 /* Stop consolidating at calls to
3783 "__analyzer_dump_exploded_nodes", so they always appear at the
3784 start of an exploded_node. */
3785 if (is_special_named_call_p (call
, "__analyzer_dump_exploded_nodes",
3789 /* sm-signal.cc injects an additional custom eedge at "signal" calls
3790 from the registration enode to the handler enode, separate from the
3791 regular next state, which defeats the "detect state change" logic
3792 in process_node. Work around this via special-casing, to ensure
3793 we split the enode immediately before any "signal" call. */
3794 if (is_special_named_call_p (call
, "signal", 2, true))
3798 /* If we had a PREV_STMT with an unknown location, and this stmt
3799 has a known location, then if a state change happens here, it
3800 could be consolidated into PREV_STMT, giving us an event with
3801 no location. Ensure that STMT gets its own exploded_node to
3803 if (get_pure_location (prev_stmt
->location
) == UNKNOWN_LOCATION
3804 && get_pure_location (stmt
->location
) != UNKNOWN_LOCATION
)
3810 /* Return true if OLD_STATE and NEW_STATE are sufficiently different that
3811 we should split enodes and create an exploded_edge separating them
3812 (which makes it easier to identify state changes of intereset when
3813 constructing checker_paths). */
3816 state_change_requires_new_enode_p (const program_state
&old_state
,
3817 const program_state
&new_state
)
3819 /* Changes in dynamic extents signify creations of heap/alloca regions
3820 and resizings of heap regions; likely to be of interest in
3821 diagnostic paths. */
3822 if (old_state
.m_region_model
->get_dynamic_extents ()
3823 != new_state
.m_region_model
->get_dynamic_extents ())
3826 /* Changes in sm-state are of interest. */
3829 FOR_EACH_VEC_ELT (old_state
.m_checker_states
, sm_idx
, smap
)
3831 const sm_state_map
*old_smap
= old_state
.m_checker_states
[sm_idx
];
3832 const sm_state_map
*new_smap
= new_state
.m_checker_states
[sm_idx
];
3833 if (*old_smap
!= *new_smap
)
3840 /* Create enodes and eedges for the function calls that doesn't have an
3841 underlying call superedge.
3843 Such case occurs when GCC's middle end didn't know which function to
3844 call but the analyzer does (with the help of current state).
3846 Some example such calls are dynamically dispatched calls to virtual
3847 functions or calls that happen via function pointer. */
3850 exploded_graph::maybe_create_dynamic_call (const gcall
*call
,
3852 exploded_node
*node
,
3853 program_state next_state
,
3854 program_point
&next_point
,
3855 uncertainty_t
*uncertainty
,
3860 const program_point
*this_point
= &node
->get_point ();
3861 function
*fun
= DECL_STRUCT_FUNCTION (fn_decl
);
3864 const supergraph
&sg
= this->get_supergraph ();
3865 supernode
*sn_entry
= sg
.get_node_for_function_entry (*fun
);
3866 supernode
*sn_exit
= sg
.get_node_for_function_exit (*fun
);
3868 program_point new_point
3869 = program_point::before_supernode (sn_entry
,
3871 this_point
->get_call_string ());
3873 new_point
.push_to_call_stack (sn_exit
,
3874 next_point
.get_supernode());
3876 /* Impose a maximum recursion depth and don't analyze paths
3877 that exceed it further.
3878 This is something of a blunt workaround, but it only
3879 applies to recursion (and mutual recursion), not to
3880 general call stacks. */
3881 if (new_point
.get_call_string ().calc_recursion_depth ()
3882 > param_analyzer_max_recursion_depth
)
3885 logger
->log ("rejecting call edge: recursion limit exceeded");
3889 next_state
.push_call (*this, node
, call
, uncertainty
);
3891 if (next_state
.m_valid
)
3894 logger
->log ("Discovered call to %s [SN: %i -> SN: %i]",
3896 this_point
->get_supernode ()->m_index
,
3899 exploded_node
*enode
= get_or_create_node (new_point
,
3903 add_edge (node
,enode
, NULL
,
3904 false, /* No work is done by the call itself. */
3905 make_unique
<dynamic_call_info_t
> (call
));
3912 /* Subclass of path_context for use within exploded_graph::process_node,
3913 so that we can split states e.g. at "realloc" calls. */
3915 class impl_path_context
: public path_context
3918 impl_path_context (const program_state
*cur_state
,
3920 : m_cur_state (cur_state
),
3922 m_terminate_path (false)
3926 bool bifurcation_p () const
3928 return m_custom_eedge_infos
.length () > 0;
3931 const program_state
&get_state_at_bifurcation () const
3933 gcc_assert (m_state_at_bifurcation
);
3934 return *m_state_at_bifurcation
;
3938 bifurcate (std::unique_ptr
<custom_edge_info
> info
) final override
3941 m_logger
->log ("bifurcating path");
3943 if (m_state_at_bifurcation
)
3944 /* Verify that the state at bifurcation is consistent when we
3945 split into multiple out-edges. */
3946 gcc_assert (*m_state_at_bifurcation
== *m_cur_state
);
3948 /* Take a copy of the cur_state at the moment when bifurcation
3950 m_state_at_bifurcation
3951 = std::unique_ptr
<program_state
> (new program_state (*m_cur_state
));
3953 /* Take ownership of INFO. */
3954 m_custom_eedge_infos
.safe_push (info
.release ());
3957 void terminate_path () final override
3960 m_logger
->log ("terminating path");
3961 m_terminate_path
= true;
3964 bool terminate_path_p () const final override
3966 return m_terminate_path
;
3969 const vec
<custom_edge_info
*> & get_custom_eedge_infos ()
3971 return m_custom_eedge_infos
;
3975 const program_state
*m_cur_state
;
3979 /* Lazily-created copy of the state before the split. */
3980 std::unique_ptr
<program_state
> m_state_at_bifurcation
;
3982 auto_vec
<custom_edge_info
*> m_custom_eedge_infos
;
3984 bool m_terminate_path
;
3987 /* A subclass of pending_diagnostic for complaining about jumps through NULL
3988 function pointers. */
3990 class jump_through_null
: public pending_diagnostic_subclass
<jump_through_null
>
3993 jump_through_null (const gcall
*call
)
3997 const char *get_kind () const final override
3999 return "jump_through_null";
4002 bool operator== (const jump_through_null
&other
) const
4004 return m_call
== other
.m_call
;
4007 int get_controlling_option () const final override
4009 return OPT_Wanalyzer_jump_through_null
;
4012 bool emit (diagnostic_emission_context
&ctxt
) final override
4014 return ctxt
.warn ("jump through null pointer");
4017 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
4019 return ev
.formatted_print ("jump through null pointer here");
4023 const gcall
*m_call
;
4026 /* The core of exploded_graph::process_worklist (the main analysis loop),
4027 handling one node in the worklist.
4029 Get successor <point, state> pairs for NODE, calling get_or_create on
4030 them, and adding an exploded_edge to each successors.
4032 Freshly-created nodes will be added to the worklist. */
4035 exploded_graph::process_node (exploded_node
*node
)
4037 logger
* const logger
= get_logger ();
4038 LOG_FUNC_1 (logger
, "EN: %i", node
->m_index
);
4040 node
->set_status (exploded_node::STATUS_PROCESSED
);
4042 const program_point
&point
= node
->get_point ();
4044 /* Update cfun and input_location in case of an ICE: make it easier to
4045 track down which source construct we're failing to handle. */
4046 auto_cfun
sentinel (node
->get_function ());
4047 const gimple
*stmt
= point
.get_stmt ();
4049 input_location
= stmt
->location
;
4051 const program_state
&state
= node
->get_state ();
4054 pretty_printer
*pp
= logger
->get_printer ();
4055 logger
->start_log_line ();
4056 pp_string (pp
, "point: ");
4057 point
.print (pp
, format (false));
4058 pp_string (pp
, ", state: ");
4059 state
.dump_to_pp (m_ext_state
, true, false, pp
);
4060 logger
->end_log_line ();
4063 switch (point
.get_kind ())
4068 /* This node exists to simplify finding the shortest path
4069 to an exploded_node. */
4072 case PK_BEFORE_SUPERNODE
:
4074 program_state
next_state (state
);
4075 uncertainty_t uncertainty
;
4077 if (point
.get_from_edge ())
4079 impl_region_model_context
ctxt (*this, node
,
4080 &state
, &next_state
,
4081 &uncertainty
, NULL
, NULL
);
4082 const cfg_superedge
*last_cfg_superedge
4083 = point
.get_from_edge ()->dyn_cast_cfg_superedge ();
4084 if (last_cfg_superedge
)
4085 next_state
.m_region_model
->update_for_phis
4086 (node
->get_supernode (),
4089 program_state::detect_leaks (state
, next_state
, NULL
,
4090 get_ext_state (), &ctxt
);
4093 program_point
next_point (point
.get_next ());
4094 exploded_node
*next
= get_or_create_node (next_point
, next_state
, node
);
4096 add_edge (node
, next
, NULL
,
4097 false); /* Assume no work is done at phi nodes. */
4100 case PK_BEFORE_STMT
:
4102 /* Determine the effect of a run of one or more statements
4103 within one supernode, generating an edge to the program_point
4104 after the last statement that's processed.
4106 Stop iterating statements and thus consolidating into one enode
4108 - reaching the end of the statements in the supernode
4109 - if an sm-state-change occurs (so that it gets its own
4111 - if "-fanalyzer-fine-grained" is active
4112 - encountering certain statements must appear at the start of
4113 their enode (for which stmt_requires_new_enode_p returns true)
4115 Update next_state in-place, to get the result of the one
4116 or more stmts that are processed.
4118 Split the node in-place if an sm-state-change occurs, so that
4119 the sm-state-change occurs on an edge where the src enode has
4120 exactly one stmt, the one that caused the change. */
4121 program_state
next_state (state
);
4123 impl_path_context
path_ctxt (&next_state
, logger
);
4125 bool could_have_done_work
= false;
4126 uncertainty_t uncertainty
;
4127 const supernode
*snode
= point
.get_supernode ();
4129 const gimple
*prev_stmt
= NULL
;
4130 for (stmt_idx
= point
.get_stmt_idx ();
4131 stmt_idx
< snode
->m_stmts
.length ();
4134 const gimple
*stmt
= snode
->m_stmts
[stmt_idx
];
4136 if (stmt_idx
> point
.get_stmt_idx ())
4137 if (stmt_requires_new_enode_p (stmt
, prev_stmt
))
4144 program_state
old_state (next_state
);
4146 /* Process the stmt. */
4147 exploded_node::on_stmt_flags flags
4148 = node
->on_stmt (*this, snode
, stmt
, &next_state
, &uncertainty
,
4149 &could_have_done_work
, &path_ctxt
);
4150 node
->m_num_processed_stmts
++;
4152 /* If flags.m_terminate_path, stop analyzing; any nodes/edges
4153 will have been added by on_stmt (e.g. for handling longjmp). */
4154 if (flags
.m_terminate_path
)
4157 if (next_state
.m_region_model
)
4159 impl_region_model_context
ctxt (*this, node
,
4160 &old_state
, &next_state
,
4161 &uncertainty
, NULL
, stmt
);
4162 program_state::detect_leaks (old_state
, next_state
, NULL
,
4163 get_ext_state (), &ctxt
);
4166 unsigned next_idx
= stmt_idx
+ 1;
4167 program_point next_point
4168 = (next_idx
< point
.get_supernode ()->m_stmts
.length ()
4169 ? program_point::before_stmt (point
.get_supernode (), next_idx
,
4170 point
.get_call_string ())
4171 : program_point::after_supernode (point
.get_supernode (),
4172 point
.get_call_string ()));
4173 next_state
= next_state
.prune_for_point (*this, next_point
, node
,
4176 if (flag_analyzer_fine_grained
4177 || state_change_requires_new_enode_p (old_state
, next_state
)
4178 || path_ctxt
.bifurcation_p ()
4179 || path_ctxt
.terminate_path_p ())
4181 program_point split_point
4182 = program_point::before_stmt (point
.get_supernode (),
4184 point
.get_call_string ());
4185 if (split_point
!= node
->get_point ())
4187 /* If we're not at the start of NODE, split the enode at
4188 this stmt, so we have:
4190 so that when split_enode is processed the next edge
4193 and any state change will effectively occur on that
4194 latter edge, and split_enode will contain just stmt. */
4196 logger
->log ("getting split_enode");
4197 exploded_node
*split_enode
4198 = get_or_create_node (split_point
, old_state
, node
);
4201 /* "stmt" will be reprocessed when split_enode is
4203 node
->m_num_processed_stmts
--;
4205 logger
->log ("creating edge to split_enode");
4206 add_edge (node
, split_enode
, NULL
, could_have_done_work
);
4210 /* If we're at the start of NODE, stop iterating,
4211 so that an edge will be created from NODE to
4212 (next_point, next_state) below. */
4216 unsigned next_idx
= stmt_idx
+ 1;
4217 program_point next_point
4218 = (next_idx
< point
.get_supernode ()->m_stmts
.length ()
4219 ? program_point::before_stmt (point
.get_supernode (), next_idx
,
4220 point
.get_call_string ())
4221 : program_point::after_supernode (point
.get_supernode (),
4222 point
.get_call_string ()));
4223 if (path_ctxt
.terminate_path_p ())
4226 logger
->log ("not adding node: terminating path");
4231 = get_or_create_node (next_point
, next_state
, node
);
4233 add_edge (node
, next
, NULL
, could_have_done_work
);
4236 /* If we have custom edge infos, "bifurcate" the state
4237 accordingly, potentially creating a new state/enode/eedge
4238 instances. For example, to handle a "realloc" call, we
4239 might split into 3 states, for the "failure",
4240 "resizing in place", and "moving to a new buffer" cases. */
4241 for (auto edge_info_iter
: path_ctxt
.get_custom_eedge_infos ())
4243 /* Take ownership of the edge infos from the path_ctxt. */
4244 std::unique_ptr
<custom_edge_info
> edge_info (edge_info_iter
);
4247 logger
->start_log_line ();
4248 logger
->log_partial ("bifurcating for edge: ");
4249 edge_info
->print (logger
->get_printer ());
4250 logger
->end_log_line ();
4252 program_state bifurcated_new_state
4253 (path_ctxt
.get_state_at_bifurcation ());
4255 /* Apply edge_info to state. */
4256 impl_region_model_context
4257 bifurcation_ctxt (*this,
4258 node
, // enode_for_diag
4259 &path_ctxt
.get_state_at_bifurcation (),
4260 &bifurcated_new_state
,
4261 NULL
, // uncertainty_t *uncertainty
4262 NULL
, // path_context *path_ctxt
4264 if (edge_info
->update_state (&bifurcated_new_state
,
4265 NULL
, /* no exploded_edge yet. */
4268 exploded_node
*next2
4269 = get_or_create_node (next_point
, bifurcated_new_state
, node
);
4271 add_edge (node
, next2
, NULL
,
4272 true /* assume that work could be done */,
4273 std::move (edge_info
));
4278 logger
->log ("infeasible state, not adding node");
4283 case PK_AFTER_SUPERNODE
:
4285 bool found_a_superedge
= false;
4286 bool is_an_exit_block
= false;
4287 /* If this is an EXIT BB, detect leaks, and potentially
4288 create a function summary. */
4289 if (point
.get_supernode ()->return_p ())
4291 is_an_exit_block
= true;
4292 node
->detect_leaks (*this);
4293 if (flag_analyzer_call_summaries
4294 && point
.get_call_string ().empty_p ())
4296 /* TODO: create function summary
4297 There can be more than one; each corresponds to a different
4298 final enode in the function. */
4301 pretty_printer
*pp
= logger
->get_printer ();
4302 logger
->start_log_line ();
4304 ("would create function summary for %qE; state: ",
4305 point
.get_fndecl ());
4306 state
.dump_to_pp (m_ext_state
, true, false, pp
);
4307 logger
->end_log_line ();
4309 per_function_data
*per_fn_data
4310 = get_or_create_per_function_data (point
.get_function ());
4311 per_fn_data
->add_call_summary (node
);
4314 /* Traverse into successors of the supernode. */
4317 FOR_EACH_VEC_ELT (point
.get_supernode ()->m_succs
, i
, succ
)
4319 found_a_superedge
= true;
4322 label_text
succ_desc (succ
->get_description (false));
4323 logger
->log ("considering SN: %i -> SN: %i (%s)",
4324 succ
->m_src
->m_index
, succ
->m_dest
->m_index
,
4328 program_point next_point
4329 = program_point::before_supernode (succ
->m_dest
, succ
,
4330 point
.get_call_string ());
4331 program_state
next_state (state
);
4332 uncertainty_t uncertainty
;
4334 /* Make use the current state and try to discover and analyse
4335 indirect function calls (a call that doesn't have an underlying
4336 cgraph edge representing call).
4338 Some examples of such calls are virtual function calls
4339 and calls that happen via a function pointer. */
4340 if (succ
->m_kind
== SUPEREDGE_INTRAPROCEDURAL_CALL
4341 && !(succ
->get_any_callgraph_edge ()))
4344 = point
.get_supernode ()->get_final_call ();
4346 impl_region_model_context
ctxt (*this,
4354 region_model
*model
= state
.m_region_model
;
4355 bool call_discovered
= false;
4357 if (tree fn_decl
= model
->get_fndecl_for_call (call
, &ctxt
))
4358 call_discovered
= maybe_create_dynamic_call (call
,
4365 if (!call_discovered
)
4367 /* Check for jump through NULL. */
4368 if (tree fn_ptr
= gimple_call_fn (call
))
4370 const svalue
*fn_ptr_sval
4371 = model
->get_rvalue (fn_ptr
, &ctxt
);
4372 if (fn_ptr_sval
->all_zeroes_p ())
4373 ctxt
.warn (make_unique
<jump_through_null
> (call
));
4376 /* An unknown function or a special function was called
4377 at this point, in such case, don't terminate the
4378 analysis of the current function.
4380 The analyzer handles calls to such functions while
4381 analysing the stmt itself, so the function call
4382 must have been handled by the anlyzer till now. */
4384 = get_or_create_node (next_point
,
4388 add_edge (node
, next
, succ
,
4389 true /* assume that work is done */);
4393 if (!node
->on_edge (*this, succ
, &next_point
, &next_state
,
4397 logger
->log ("skipping impossible edge to SN: %i",
4398 succ
->m_dest
->m_index
);
4401 exploded_node
*next
= get_or_create_node (next_point
, next_state
,
4405 add_edge (node
, next
, succ
, false);
4407 /* We might have a function entrypoint. */
4408 detect_infinite_recursion (next
);
4412 /* Return from the calls which doesn't have a return superedge.
4413 Such case occurs when GCC's middle end didn't knew which function to
4414 call but analyzer did. */
4415 if ((is_an_exit_block
&& !found_a_superedge
)
4416 && (!point
.get_call_string ().empty_p ()))
4418 const call_string
&cs
= point
.get_call_string ();
4419 program_point next_point
4420 = program_point::before_supernode (cs
.get_caller_node (),
4423 program_state
next_state (state
);
4424 uncertainty_t uncertainty
;
4427 = next_point
.get_supernode ()->get_returning_call ();
4430 next_state
.returning_call (*this, node
, call
, &uncertainty
);
4432 if (next_state
.m_valid
)
4434 next_point
.pop_from_call_stack ();
4435 exploded_node
*enode
= get_or_create_node (next_point
,
4439 add_edge (node
, enode
, NULL
, false,
4440 make_unique
<dynamic_call_info_t
> (call
, true));
4448 /* Ensure that this graph has a stats instance for FN, return it.
4449 FN can be NULL, in which case a stats instances is returned covering
4450 "functionless" parts of the graph (the origin node). */
4453 exploded_graph::get_or_create_function_stats (function
*fn
)
4456 return &m_functionless_stats
;
4458 if (stats
**slot
= m_per_function_stats
.get (fn
))
4462 int num_supernodes
= fn
? n_basic_blocks_for_fn (fn
) : 0;
4463 /* not quite the num supernodes, but nearly. */
4464 stats
*new_stats
= new stats (num_supernodes
);
4465 m_per_function_stats
.put (fn
, new_stats
);
4470 /* Print bar charts to PP showing:
4471 - the number of enodes per function, and
4472 - for each function:
4473 - the number of enodes per supernode/BB
4474 - the number of excess enodes per supernode/BB beyond the
4475 per-program-point limit, if there were any. */
4478 exploded_graph::print_bar_charts (pretty_printer
*pp
) const
4480 cgraph_node
*cgnode
;
4482 pp_string (pp
, "enodes per function:");
4484 bar_chart enodes_per_function
;
4485 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode
)
4487 function
*fn
= cgnode
->get_fun ();
4488 const stats
* const *s_ptr
4489 = const_cast <function_stat_map_t
&> (m_per_function_stats
).get (fn
);
4490 enodes_per_function
.add_item (function_name (fn
),
4491 s_ptr
? (*s_ptr
)->get_total_enodes () : 0);
4493 enodes_per_function
.print (pp
);
4495 /* Accumulate number of enodes per supernode. */
4496 auto_vec
<unsigned> enodes_per_supernode (m_sg
.num_nodes ());
4497 for (int i
= 0; i
< m_sg
.num_nodes (); i
++)
4498 enodes_per_supernode
.quick_push (0);
4500 exploded_node
*enode
;
4501 FOR_EACH_VEC_ELT (m_nodes
, i
, enode
)
4503 const supernode
*iter_snode
= enode
->get_supernode ();
4506 enodes_per_supernode
[iter_snode
->m_index
]++;
4509 /* Accumulate excess enodes per supernode. */
4510 auto_vec
<unsigned> excess_enodes_per_supernode (m_sg
.num_nodes ());
4511 for (int i
= 0; i
< m_sg
.num_nodes (); i
++)
4512 excess_enodes_per_supernode
.quick_push (0);
4513 for (point_map_t::iterator iter
= m_per_point_data
.begin ();
4514 iter
!= m_per_point_data
.end (); ++iter
)
4516 const program_point
*point
= (*iter
).first
;
4517 const supernode
*iter_snode
= point
->get_supernode ();
4520 const per_program_point_data
*point_data
= (*iter
).second
;
4521 excess_enodes_per_supernode
[iter_snode
->m_index
]
4522 += point_data
->m_excess_enodes
;
4525 /* Show per-function bar_charts of enodes per supernode/BB. */
4526 pp_string (pp
, "per-function enodes per supernode/BB:");
4528 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode
)
4530 function
*fn
= cgnode
->get_fun ();
4531 pp_printf (pp
, "function: %qs", function_name (fn
));
4534 bar_chart enodes_per_snode
;
4535 bar_chart excess_enodes_per_snode
;
4536 bool have_excess_enodes
= false;
4537 for (int i
= 0; i
< m_sg
.num_nodes (); i
++)
4539 const supernode
*iter_snode
= m_sg
.get_node_by_index (i
);
4540 if (iter_snode
->get_function () != fn
)
4542 pretty_printer tmp_pp
;
4543 pp_printf (&tmp_pp
, "sn %i (bb %i)",
4544 iter_snode
->m_index
, iter_snode
->m_bb
->index
);
4545 enodes_per_snode
.add_item (pp_formatted_text (&tmp_pp
),
4546 enodes_per_supernode
[iter_snode
->m_index
]);
4547 const int num_excess
4548 = excess_enodes_per_supernode
[iter_snode
->m_index
];
4549 excess_enodes_per_snode
.add_item (pp_formatted_text (&tmp_pp
),
4552 have_excess_enodes
= true;
4554 enodes_per_snode
.print (pp
);
4555 if (have_excess_enodes
)
4557 pp_printf (pp
, "EXCESS ENODES:");
4559 excess_enodes_per_snode
.print (pp
);
4564 /* Write all stats information to this graph's logger, if any. */
4567 exploded_graph::log_stats () const
4569 logger
* const logger
= get_logger ();
4575 m_ext_state
.get_engine ()->log_stats (logger
);
4577 logger
->log ("m_sg.num_nodes (): %i", m_sg
.num_nodes ());
4578 logger
->log ("m_nodes.length (): %i", m_nodes
.length ());
4579 logger
->log ("m_edges.length (): %i", m_edges
.length ());
4580 logger
->log ("remaining enodes in worklist: %i", m_worklist
.length ());
4582 logger
->log ("global stats:");
4583 m_global_stats
.log (logger
);
4585 for (function_stat_map_t::iterator iter
= m_per_function_stats
.begin ();
4586 iter
!= m_per_function_stats
.end ();
4589 function
*fn
= (*iter
).first
;
4590 log_scope
s (logger
, function_name (fn
));
4591 (*iter
).second
->log (logger
);
4594 print_bar_charts (logger
->get_printer ());
4597 /* Dump all stats information to OUT. */
4600 exploded_graph::dump_stats (FILE *out
) const
4602 fprintf (out
, "m_sg.num_nodes (): %i\n", m_sg
.num_nodes ());
4603 fprintf (out
, "m_nodes.length (): %i\n", m_nodes
.length ());
4604 fprintf (out
, "m_edges.length (): %i\n", m_edges
.length ());
4605 fprintf (out
, "remaining enodes in worklist: %i", m_worklist
.length ());
4607 fprintf (out
, "global stats:\n");
4608 m_global_stats
.dump (out
);
4610 for (function_stat_map_t::iterator iter
= m_per_function_stats
.begin ();
4611 iter
!= m_per_function_stats
.end ();
4614 function
*fn
= (*iter
).first
;
4615 fprintf (out
, "function: %s\n", function_name (fn
));
4616 (*iter
).second
->dump (out
);
4619 fprintf (out
, "PK_AFTER_SUPERNODE per supernode:\n");
4620 for (unsigned i
= 0; i
< m_PK_AFTER_SUPERNODE_per_snode
.length (); i
++)
4621 fprintf (out
, " SN %i: %3i\n", i
, m_PK_AFTER_SUPERNODE_per_snode
[i
]);
4625 exploded_graph::dump_states_for_supernode (FILE *out
,
4626 const supernode
*snode
) const
4628 fprintf (out
, "PK_AFTER_SUPERNODE nodes for SN: %i\n", snode
->m_index
);
4630 exploded_node
*enode
;
4632 FOR_EACH_VEC_ELT (m_nodes
, i
, enode
)
4634 const supernode
*iter_snode
= enode
->get_supernode ();
4635 if (enode
->get_point ().get_kind () == PK_AFTER_SUPERNODE
4636 && iter_snode
== snode
)
4639 pp_format_decoder (&pp
) = default_tree_printer
;
4640 enode
->get_state ().dump_to_pp (m_ext_state
, true, false, &pp
);
4641 fprintf (out
, "state %i: EN: %i\n %s\n",
4642 state_idx
++, enode
->m_index
,
4643 pp_formatted_text (&pp
));
4646 fprintf (out
, "#exploded_node for PK_AFTER_SUPERNODE for SN: %i = %i\n",
4647 snode
->m_index
, state_idx
);
4650 /* Return a new json::object of the form
4651 {"nodes" : [objs for enodes],
4652 "edges" : [objs for eedges],
4653 "ext_state": object for extrinsic_state,
4654 "diagnostic_manager": object for diagnostic_manager}. */
4657 exploded_graph::to_json () const
4659 json::object
*egraph_obj
= new json::object ();
4663 json::array
*nodes_arr
= new json::array ();
4666 FOR_EACH_VEC_ELT (m_nodes
, i
, n
)
4667 nodes_arr
->append (n
->to_json (m_ext_state
));
4668 egraph_obj
->set ("nodes", nodes_arr
);
4673 json::array
*edges_arr
= new json::array ();
4676 FOR_EACH_VEC_ELT (m_edges
, i
, n
)
4677 edges_arr
->append (n
->to_json ());
4678 egraph_obj
->set ("edges", edges_arr
);
4681 /* m_sg is JSONified at the top-level. */
4683 egraph_obj
->set ("ext_state", m_ext_state
.to_json ());
4684 egraph_obj
->set ("worklist", m_worklist
.to_json ());
4685 egraph_obj
->set ("diagnostic_manager", m_diagnostic_manager
.to_json ());
4687 /* The following fields aren't yet being JSONified:
4688 const state_purge_map *const m_purge_map;
4689 const analysis_plan &m_plan;
4690 stats m_global_stats;
4691 function_stat_map_t m_per_function_stats;
4692 stats m_functionless_stats;
4693 call_string_data_map_t m_per_call_string_data;
4694 auto_vec<int> m_PK_AFTER_SUPERNODE_per_snode; */
4699 /* class exploded_path. */
4703 exploded_path::exploded_path (const exploded_path
&other
)
4704 : m_edges (other
.m_edges
.length ())
4707 const exploded_edge
*eedge
;
4708 FOR_EACH_VEC_ELT (other
.m_edges
, i
, eedge
)
4709 m_edges
.quick_push (eedge
);
4712 /* Look for the last use of SEARCH_STMT within this path.
4713 If found write the edge's index to *OUT_IDX and return true, otherwise
4717 exploded_path::find_stmt_backwards (const gimple
*search_stmt
,
4721 const exploded_edge
*eedge
;
4722 FOR_EACH_VEC_ELT_REVERSE (m_edges
, i
, eedge
)
4724 const exploded_node
*dst_node
= eedge
->m_dest
;
4725 const program_point
&dst_point
= dst_node
->get_point ();
4726 const gimple
*stmt
= dst_point
.get_stmt ();
4727 if (stmt
== search_stmt
)
4736 /* Get the final exploded_node in this path, which must be non-empty. */
4739 exploded_path::get_final_enode () const
4741 gcc_assert (m_edges
.length () > 0);
4742 return m_edges
[m_edges
.length () - 1]->m_dest
;
4745 /* Check state along this path, returning true if it is feasible.
4746 If OUT is non-NULL, and the path is infeasible, write a new
4747 feasibility_problem to *OUT. */
4750 exploded_path::feasible_p (logger
*logger
,
4751 std::unique_ptr
<feasibility_problem
> *out
,
4752 engine
*eng
, const exploded_graph
*eg
) const
4756 feasibility_state
state (eng
->get_model_manager (),
4757 eg
->get_supergraph ());
4759 /* Traverse the path, updating this state. */
4760 for (unsigned edge_idx
= 0; edge_idx
< m_edges
.length (); edge_idx
++)
4762 const exploded_edge
*eedge
= m_edges
[edge_idx
];
4764 logger
->log ("considering edge %i: EN:%i -> EN:%i",
4766 eedge
->m_src
->m_index
,
4767 eedge
->m_dest
->m_index
);
4769 std::unique_ptr
<rejected_constraint
> rc
;
4770 if (!state
.maybe_update_for_edge (logger
, eedge
, nullptr, &rc
))
4775 const exploded_node
&src_enode
= *eedge
->m_src
;
4776 const program_point
&src_point
= src_enode
.get_point ();
4777 const gimple
*last_stmt
4778 = src_point
.get_supernode ()->get_last_stmt ();
4779 *out
= ::make_unique
<feasibility_problem
> (edge_idx
, *eedge
,
4788 logger
->log ("state after edge %i: EN:%i -> EN:%i",
4790 eedge
->m_src
->m_index
,
4791 eedge
->m_dest
->m_index
);
4792 logger
->start_log_line ();
4793 state
.get_model ().dump_to_pp (logger
->get_printer (), true, false);
4794 logger
->end_log_line ();
4801 /* Dump this path in multiline form to PP.
4802 If EXT_STATE is non-NULL, then show the nodes. */
4805 exploded_path::dump_to_pp (pretty_printer
*pp
,
4806 const extrinsic_state
*ext_state
) const
4808 for (unsigned i
= 0; i
< m_edges
.length (); i
++)
4810 const exploded_edge
*eedge
= m_edges
[i
];
4811 pp_printf (pp
, "m_edges[%i]: EN %i -> EN %i",
4813 eedge
->m_src
->m_index
,
4814 eedge
->m_dest
->m_index
);
4818 eedge
->m_dest
->dump_to_pp (pp
, *ext_state
);
4822 /* Dump this path in multiline form to FP. */
4825 exploded_path::dump (FILE *fp
, const extrinsic_state
*ext_state
) const
4827 tree_dump_pretty_printer
pp (fp
);
4828 dump_to_pp (&pp
, ext_state
);
4831 /* Dump this path in multiline form to stderr. */
4834 exploded_path::dump (const extrinsic_state
*ext_state
) const
4836 dump (stderr
, ext_state
);
4839 /* Dump this path verbosely to FILENAME. */
4842 exploded_path::dump_to_file (const char *filename
,
4843 const extrinsic_state
&ext_state
) const
4845 FILE *fp
= fopen (filename
, "w");
4849 pp_format_decoder (&pp
) = default_tree_printer
;
4850 pp
.set_output_stream (fp
);
4851 dump_to_pp (&pp
, &ext_state
);
4856 /* class feasibility_problem. */
4859 feasibility_problem::dump_to_pp (pretty_printer
*pp
) const
4861 pp_printf (pp
, "edge from EN: %i to EN: %i",
4862 m_eedge
.m_src
->m_index
, m_eedge
.m_dest
->m_index
);
4865 pp_string (pp
, "; rejected constraint: ");
4866 m_rc
->dump_to_pp (pp
);
4867 pp_string (pp
, "; rmodel: ");
4868 m_rc
->get_model ().dump_to_pp (pp
, true, false);
4872 /* class feasibility_state. */
4874 /* Ctor for feasibility_state, at the beginning of a path. */
4876 feasibility_state::feasibility_state (region_model_manager
*manager
,
4877 const supergraph
&sg
)
4878 : m_model (manager
),
4879 m_snodes_visited (sg
.m_nodes
.length ())
4881 bitmap_clear (m_snodes_visited
);
4884 /* Copy ctor for feasibility_state, for extending a path. */
4886 feasibility_state::feasibility_state (const feasibility_state
&other
)
4887 : m_model (other
.m_model
),
4888 m_snodes_visited (const_sbitmap (other
.m_snodes_visited
)->n_bits
)
4890 bitmap_copy (m_snodes_visited
, other
.m_snodes_visited
);
4893 feasibility_state::feasibility_state (const region_model
&model
,
4894 const supergraph
&sg
)
4896 m_snodes_visited (sg
.m_nodes
.length ())
4898 bitmap_clear (m_snodes_visited
);
4902 feasibility_state::operator= (const feasibility_state
&other
)
4904 m_model
= other
.m_model
;
4905 bitmap_copy (m_snodes_visited
, other
.m_snodes_visited
);
4909 /* The heart of feasibility-checking.
4911 Attempt to update this state in-place based on traversing EEDGE
4913 Update the model for the stmts in the src enode.
4914 Attempt to add constraints for EEDGE.
4916 If feasible, return true.
4917 Otherwise, return false and write to *OUT_RC. */
4921 maybe_update_for_edge (logger
*logger
,
4922 const exploded_edge
*eedge
,
4923 region_model_context
*ctxt
,
4924 std::unique_ptr
<rejected_constraint
> *out_rc
)
4926 const exploded_node
&src_enode
= *eedge
->m_src
;
4927 const program_point
&src_point
= src_enode
.get_point ();
4930 logger
->start_log_line ();
4931 src_point
.print (logger
->get_printer (), format (false));
4932 logger
->end_log_line ();
4935 /* Update state for the stmts that were processed in each enode. */
4936 for (unsigned stmt_idx
= 0; stmt_idx
< src_enode
.m_num_processed_stmts
;
4939 const gimple
*stmt
= src_enode
.get_processed_stmt (stmt_idx
);
4941 /* Update cfun and input_location in case of ICE: make it easier to
4942 track down which source construct we're failing to handle. */
4943 auto_cfun
sentinel (src_point
.get_function ());
4944 input_location
= stmt
->location
;
4946 update_for_stmt (stmt
);
4949 const superedge
*sedge
= eedge
->m_sedge
;
4954 label_text
desc (sedge
->get_description (false));
4955 logger
->log (" sedge: SN:%i -> SN:%i %s",
4956 sedge
->m_src
->m_index
,
4957 sedge
->m_dest
->m_index
,
4961 const gimple
*last_stmt
= src_point
.get_supernode ()->get_last_stmt ();
4962 if (!m_model
.maybe_update_for_edge (*sedge
, last_stmt
, ctxt
, out_rc
))
4966 logger
->start_log_line ();
4967 logger
->log_partial ("rejecting due to region model: ");
4968 m_model
.dump_to_pp (logger
->get_printer (), true, false);
4969 logger
->end_log_line ();
4976 /* Special-case the initial eedge from the origin node to the
4977 initial function by pushing a frame for it. */
4978 if (src_point
.get_kind () == PK_ORIGIN
)
4980 gcc_assert (eedge
->m_src
->m_index
== 0);
4981 gcc_assert (eedge
->m_dest
->get_point ().get_kind ()
4982 == PK_BEFORE_SUPERNODE
);
4983 function
*fun
= eedge
->m_dest
->get_function ();
4985 m_model
.push_frame (*fun
, NULL
, ctxt
);
4987 logger
->log (" pushing frame for %qD", fun
->decl
);
4989 else if (eedge
->m_custom_info
)
4991 eedge
->m_custom_info
->update_model (&m_model
, eedge
, ctxt
);
4995 /* Handle phi nodes on an edge leaving a PK_BEFORE_SUPERNODE (to
4996 a PK_BEFORE_STMT, or a PK_AFTER_SUPERNODE if no stmts).
4997 This will typically not be associated with a superedge. */
4998 if (src_point
.get_from_edge ())
5000 const cfg_superedge
*last_cfg_superedge
5001 = src_point
.get_from_edge ()->dyn_cast_cfg_superedge ();
5002 const exploded_node
&dst_enode
= *eedge
->m_dest
;
5003 const unsigned dst_snode_idx
= dst_enode
.get_supernode ()->m_index
;
5004 if (last_cfg_superedge
)
5007 logger
->log (" update for phis");
5008 m_model
.update_for_phis (src_enode
.get_supernode (),
5011 /* If we've entering an snode that we've already visited on this
5012 epath, then we need do fix things up for loops; see the
5013 comment for store::loop_replay_fixup.
5014 Perhaps we should probably also verify the callstring,
5015 and track program_points, but hopefully doing it by supernode
5017 if (bitmap_bit_p (m_snodes_visited
, dst_snode_idx
))
5018 m_model
.loop_replay_fixup (dst_enode
.get_state ().m_region_model
);
5020 bitmap_set_bit (m_snodes_visited
, dst_snode_idx
);
5025 /* Update this object for the effects of STMT. */
5028 feasibility_state::update_for_stmt (const gimple
*stmt
)
5030 if (const gassign
*assign
= dyn_cast
<const gassign
*> (stmt
))
5031 m_model
.on_assignment (assign
, NULL
);
5032 else if (const gasm
*asm_stmt
= dyn_cast
<const gasm
*> (stmt
))
5033 m_model
.on_asm_stmt (asm_stmt
, NULL
);
5034 else if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
5036 bool unknown_side_effects
= m_model
.on_call_pre (call
, NULL
);
5037 m_model
.on_call_post (call
, unknown_side_effects
, NULL
);
5039 else if (const greturn
*return_
= dyn_cast
<const greturn
*> (stmt
))
5040 m_model
.on_return (return_
, NULL
);
5043 /* Dump this object to PP. */
5046 feasibility_state::dump_to_pp (pretty_printer
*pp
,
5047 bool simple
, bool multiline
) const
5049 m_model
.dump_to_pp (pp
, simple
, multiline
);
5052 /* A family of cluster subclasses for use when generating .dot output for
5053 exploded graphs (-fdump-analyzer-exploded-graph), for grouping the
5054 enodes into hierarchical boxes.
5056 All functionless enodes appear in the top-level graph.
5057 Every (function, call_string) pair gets its own cluster. Within that
5058 cluster, each supernode gets its own cluster.
5060 Hence all enodes relating to a particular function with a particular
5061 callstring will be in a cluster together; all enodes for the same
5062 function but with a different callstring will be in a different
5065 /* Base class of cluster for clustering exploded_node instances in .dot
5066 output, based on various subclass-specific criteria. */
5068 class exploded_cluster
: public cluster
<eg_traits
>
5072 /* Cluster containing all exploded_node instances for one supernode. */
5074 class supernode_cluster
: public exploded_cluster
5077 supernode_cluster (const supernode
*supernode
) : m_supernode (supernode
) {}
5081 void dump_dot (graphviz_out
*gv
, const dump_args_t
&args
) const final override
5083 gv
->println ("subgraph \"cluster_supernode_%i\" {", m_supernode
->m_index
);
5085 gv
->println ("style=\"dashed\";");
5086 gv
->println ("label=\"SN: %i (bb: %i; scc: %i)\";",
5087 m_supernode
->m_index
, m_supernode
->m_bb
->index
,
5088 args
.m_eg
.get_scc_id (*m_supernode
));
5091 exploded_node
*enode
;
5092 FOR_EACH_VEC_ELT (m_enodes
, i
, enode
)
5093 enode
->dump_dot (gv
, args
);
5095 /* Terminate subgraph. */
5100 void add_node (exploded_node
*en
) final override
5102 m_enodes
.safe_push (en
);
5105 /* Comparator for use by auto_vec<supernode_cluster *>::qsort. */
5107 static int cmp_ptr_ptr (const void *p1
, const void *p2
)
5109 const supernode_cluster
*c1
5110 = *(const supernode_cluster
* const *)p1
;
5111 const supernode_cluster
*c2
5112 = *(const supernode_cluster
* const *)p2
;
5113 return c1
->m_supernode
->m_index
- c2
->m_supernode
->m_index
;
5117 const supernode
*m_supernode
;
5118 auto_vec
<exploded_node
*> m_enodes
;
5121 /* Cluster containing all supernode_cluster instances for one
5122 (function, call_string) pair. */
5124 class function_call_string_cluster
: public exploded_cluster
5127 function_call_string_cluster (function
*fun
, const call_string
&cs
)
5128 : m_fun (fun
), m_cs (cs
) {}
5130 ~function_call_string_cluster ()
5132 for (map_t::iterator iter
= m_map
.begin ();
5133 iter
!= m_map
.end ();
5135 delete (*iter
).second
;
5138 void dump_dot (graphviz_out
*gv
, const dump_args_t
&args
) const final override
5140 const char *funcname
= function_name (m_fun
);
5142 gv
->println ("subgraph \"cluster_function_%s\" {",
5143 IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (m_fun
->decl
)));
5145 gv
->write_indent ();
5146 gv
->print ("label=\"call string: ");
5147 m_cs
.print (gv
->get_pp ());
5148 gv
->print (" function: %s \";", funcname
);
5151 /* Dump m_map, sorting it to avoid churn when comparing dumps. */
5152 auto_vec
<supernode_cluster
*> child_clusters (m_map
.elements ());
5153 for (map_t::iterator iter
= m_map
.begin ();
5154 iter
!= m_map
.end ();
5156 child_clusters
.quick_push ((*iter
).second
);
5158 child_clusters
.qsort (supernode_cluster::cmp_ptr_ptr
);
5161 supernode_cluster
*child_cluster
;
5162 FOR_EACH_VEC_ELT (child_clusters
, i
, child_cluster
)
5163 child_cluster
->dump_dot (gv
, args
);
5165 /* Terminate subgraph. */
5170 void add_node (exploded_node
*en
) final override
5172 const supernode
*supernode
= en
->get_supernode ();
5173 gcc_assert (supernode
);
5174 supernode_cluster
**slot
= m_map
.get (supernode
);
5176 (*slot
)->add_node (en
);
5179 supernode_cluster
*child
= new supernode_cluster (supernode
);
5180 m_map
.put (supernode
, child
);
5181 child
->add_node (en
);
5185 /* Comparator for use by auto_vec<function_call_string_cluster *>. */
5187 static int cmp_ptr_ptr (const void *p1
, const void *p2
)
5189 const function_call_string_cluster
*c1
5190 = *(const function_call_string_cluster
* const *)p1
;
5191 const function_call_string_cluster
*c2
5192 = *(const function_call_string_cluster
* const *)p2
;
5194 = strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (c1
->m_fun
->decl
)),
5195 IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (c2
->m_fun
->decl
))))
5197 return call_string::cmp (c1
->m_cs
, c2
->m_cs
);
5202 const call_string
&m_cs
;
5203 typedef ordered_hash_map
<const supernode
*, supernode_cluster
*> map_t
;
5207 /* Keys for root_cluster. */
5209 struct function_call_string
5211 function_call_string (function
*fun
, const call_string
*cs
)
5212 : m_fun (fun
), m_cs (cs
)
5219 const call_string
*m_cs
;
5224 template <> struct default_hash_traits
<function_call_string
>
5225 : public pod_hash_traits
<function_call_string
>
5227 static const bool empty_zero_p
= false;
5232 pod_hash_traits
<function_call_string
>::hash (value_type v
)
5234 return (pointer_hash
<function
>::hash (v
.m_fun
)
5235 ^ pointer_hash
<const call_string
>::hash (v
.m_cs
));
5240 pod_hash_traits
<function_call_string
>::equal (const value_type
&existing
,
5241 const value_type
&candidate
)
5243 return existing
.m_fun
== candidate
.m_fun
&& &existing
.m_cs
== &candidate
.m_cs
;
5247 pod_hash_traits
<function_call_string
>::mark_deleted (value_type
&v
)
5249 v
.m_fun
= reinterpret_cast<function
*> (1);
5253 pod_hash_traits
<function_call_string
>::mark_empty (value_type
&v
)
5259 pod_hash_traits
<function_call_string
>::is_deleted (value_type v
)
5261 return v
.m_fun
== reinterpret_cast<function
*> (1);
5265 pod_hash_traits
<function_call_string
>::is_empty (value_type v
)
5267 return v
.m_fun
== NULL
;
5272 /* Top-level cluster for generating .dot output for exploded graphs,
5273 handling the functionless nodes, and grouping the remaining nodes by
5276 class root_cluster
: public exploded_cluster
5281 for (map_t::iterator iter
= m_map
.begin ();
5282 iter
!= m_map
.end ();
5284 delete (*iter
).second
;
5287 void dump_dot (graphviz_out
*gv
, const dump_args_t
&args
) const final override
5290 exploded_node
*enode
;
5291 FOR_EACH_VEC_ELT (m_functionless_enodes
, i
, enode
)
5292 enode
->dump_dot (gv
, args
);
5294 /* Dump m_map, sorting it to avoid churn when comparing dumps. */
5295 auto_vec
<function_call_string_cluster
*> child_clusters (m_map
.elements ());
5296 for (map_t::iterator iter
= m_map
.begin ();
5297 iter
!= m_map
.end ();
5299 child_clusters
.quick_push ((*iter
).second
);
5301 child_clusters
.qsort (function_call_string_cluster::cmp_ptr_ptr
);
5303 function_call_string_cluster
*child_cluster
;
5304 FOR_EACH_VEC_ELT (child_clusters
, i
, child_cluster
)
5305 child_cluster
->dump_dot (gv
, args
);
5308 void add_node (exploded_node
*en
) final override
5310 function
*fun
= en
->get_function ();
5313 m_functionless_enodes
.safe_push (en
);
5317 const call_string
&cs
= en
->get_point ().get_call_string ();
5318 function_call_string
key (fun
, &cs
);
5319 function_call_string_cluster
**slot
= m_map
.get (key
);
5321 (*slot
)->add_node (en
);
5324 function_call_string_cluster
*child
5325 = new function_call_string_cluster (fun
, cs
);
5326 m_map
.put (key
, child
);
5327 child
->add_node (en
);
5332 typedef hash_map
<function_call_string
, function_call_string_cluster
*> map_t
;
5335 /* This should just be the origin exploded_node. */
5336 auto_vec
<exploded_node
*> m_functionless_enodes
;
5339 /* Subclass of range_label for use within
5340 exploded_graph::dump_exploded_nodes for implementing
5341 -fdump-analyzer-exploded-nodes: a label for a specific
5344 class enode_label
: public range_label
5347 enode_label (const extrinsic_state
&ext_state
,
5348 exploded_node
*enode
)
5349 : m_ext_state (ext_state
), m_enode (enode
) {}
5351 label_text
get_text (unsigned) const final override
5354 pp_format_decoder (&pp
) = default_tree_printer
;
5355 m_enode
->get_state ().dump_to_pp (m_ext_state
, true, false, &pp
);
5356 return make_label_text (false, "EN: %i: %s",
5357 m_enode
->m_index
, pp_formatted_text (&pp
));
5361 const extrinsic_state
&m_ext_state
;
5362 exploded_node
*m_enode
;
5365 /* Postprocessing support for dumping the exploded nodes.
5366 Handle -fdump-analyzer-exploded-nodes,
5367 -fdump-analyzer-exploded-nodes-2, and the
5368 "__analyzer_dump_exploded_nodes" builtin. */
5371 exploded_graph::dump_exploded_nodes () const
5374 /* Locate calls to __analyzer_dump_exploded_nodes. */
5375 // Print how many egs there are for them?
5376 /* Better: log them as we go, and record the exploded nodes
5379 /* Show every enode. */
5381 /* Gather them by stmt, so that we can more clearly see the
5382 "hotspots" requiring numerous exploded nodes. */
5384 /* Alternatively, simply throw them all into one big rich_location
5385 and see if the label-printing will sort it out...
5386 This requires them all to be in the same source file. */
5388 if (flag_dump_analyzer_exploded_nodes
)
5390 auto_timevar
tv (TV_ANALYZER_DUMP
);
5391 gcc_rich_location
richloc (UNKNOWN_LOCATION
);
5393 exploded_node
*enode
;
5394 FOR_EACH_VEC_ELT (m_nodes
, i
, enode
)
5396 if (const gimple
*stmt
= enode
->get_stmt ())
5398 if (get_pure_location (richloc
.get_loc ()) == UNKNOWN_LOCATION
)
5399 richloc
.set_range (0, stmt
->location
, SHOW_RANGE_WITH_CARET
);
5401 richloc
.add_range (stmt
->location
,
5402 SHOW_RANGE_WITHOUT_CARET
,
5403 new enode_label (m_ext_state
, enode
));
5406 warning_at (&richloc
, 0, "%i exploded nodes", m_nodes
.length ());
5408 /* Repeat the warning without all the labels, so that message is visible
5409 (the other one may well have scrolled past the terminal limit). */
5410 warning_at (richloc
.get_loc (), 0,
5411 "%i exploded nodes", m_nodes
.length ());
5413 if (m_worklist
.length () > 0)
5414 warning_at (richloc
.get_loc (), 0,
5415 "worklist still contains %i nodes", m_worklist
.length ());
5418 /* Dump the egraph in textual form to a dump file. */
5419 if (flag_dump_analyzer_exploded_nodes_2
)
5421 auto_timevar
tv (TV_ANALYZER_DUMP
);
5423 = concat (dump_base_name
, ".eg.txt", NULL
);
5424 FILE *outf
= fopen (filename
, "w");
5426 error_at (UNKNOWN_LOCATION
, "unable to open %qs for writing", filename
);
5429 fprintf (outf
, "exploded graph for %s\n", dump_base_name
);
5430 fprintf (outf
, " nodes: %i\n", m_nodes
.length ());
5431 fprintf (outf
, " edges: %i\n", m_edges
.length ());
5434 exploded_node
*enode
;
5435 FOR_EACH_VEC_ELT (m_nodes
, i
, enode
)
5437 fprintf (outf
, "\nEN %i:\n", enode
->m_index
);
5438 enode
->dump_succs_and_preds (outf
);
5440 enode
->get_point ().print (&pp
, format (true));
5441 fprintf (outf
, "%s\n", pp_formatted_text (&pp
));
5442 text_art::dump_to_file (enode
->get_state (), outf
);
5448 /* Dump the egraph in textual form to multiple dump files, one per enode. */
5449 if (flag_dump_analyzer_exploded_nodes_3
)
5451 auto_timevar
tv (TV_ANALYZER_DUMP
);
5454 exploded_node
*enode
;
5455 FOR_EACH_VEC_ELT (m_nodes
, i
, enode
)
5458 = xasprintf ("%s.en-%i.txt", dump_base_name
, i
);
5459 FILE *outf
= fopen (filename
, "w");
5461 error_at (UNKNOWN_LOCATION
, "unable to open %qs for writing",
5465 fprintf (outf
, "EN %i:\n", enode
->m_index
);
5466 enode
->dump_succs_and_preds (outf
);
5468 enode
->get_point ().print (&pp
, format (true));
5469 fprintf (outf
, "%s\n", pp_formatted_text (&pp
));
5470 text_art::dump_to_file (enode
->get_state (), outf
);
5476 /* Emit a warning at any call to "__analyzer_dump_exploded_nodes",
5477 giving the number of processed exploded nodes for "before-stmt",
5478 and the IDs of processed, merger, and worklist enodes.
5480 We highlight the count of *processed* enodes since this is of most
5481 interest in DejaGnu tests for ensuring that state merger has
5484 We don't show the count of merger and worklist enodes, as this is
5485 more of an implementation detail of the merging/worklist that we
5486 don't want to bake into our expected DejaGnu messages. */
5489 exploded_node
*enode
;
5490 hash_set
<const gimple
*> seen
;
5491 FOR_EACH_VEC_ELT (m_nodes
, i
, enode
)
5493 if (enode
->get_point ().get_kind () != PK_BEFORE_STMT
)
5496 if (const gimple
*stmt
= enode
->get_stmt ())
5497 if (const gcall
*call
= dyn_cast
<const gcall
*> (stmt
))
5498 if (is_special_named_call_p (call
, "__analyzer_dump_exploded_nodes",
5501 if (seen
.contains (stmt
))
5504 auto_vec
<exploded_node
*> processed_enodes
;
5505 auto_vec
<exploded_node
*> merger_enodes
;
5506 auto_vec
<exploded_node
*> worklist_enodes
;
5507 /* This is O(N^2). */
5509 exploded_node
*other_enode
;
5510 FOR_EACH_VEC_ELT (m_nodes
, j
, other_enode
)
5512 if (other_enode
->get_point ().get_kind () != PK_BEFORE_STMT
)
5514 if (other_enode
->get_stmt () == stmt
)
5515 switch (other_enode
->get_status ())
5519 case exploded_node::STATUS_WORKLIST
:
5520 worklist_enodes
.safe_push (other_enode
);
5522 case exploded_node::STATUS_PROCESSED
:
5523 processed_enodes
.safe_push (other_enode
);
5525 case exploded_node::STATUS_MERGER
:
5526 merger_enodes
.safe_push (other_enode
);
5532 pp_character (&pp
, '[');
5533 print_enode_indices (&pp
, processed_enodes
);
5534 if (merger_enodes
.length () > 0)
5536 pp_string (&pp
, "] merger(s): [");
5537 print_enode_indices (&pp
, merger_enodes
);
5539 if (worklist_enodes
.length () > 0)
5541 pp_string (&pp
, "] worklist: [");
5542 print_enode_indices (&pp
, worklist_enodes
);
5544 pp_character (&pp
, ']');
5546 warning_n (stmt
->location
, 0, processed_enodes
.length (),
5547 "%i processed enode: %s",
5548 "%i processed enodes: %s",
5549 processed_enodes
.length (), pp_formatted_text (&pp
));
5552 /* If the argument is non-zero, then print all of the states
5553 of the various enodes. */
5554 tree t_arg
= fold (gimple_call_arg (call
, 0));
5555 if (TREE_CODE (t_arg
) != INTEGER_CST
)
5557 error_at (call
->location
,
5558 "integer constant required for arg 1");
5561 int i_arg
= TREE_INT_CST_LOW (t_arg
);
5564 exploded_node
*other_enode
;
5565 FOR_EACH_VEC_ELT (processed_enodes
, j
, other_enode
)
5567 fprintf (stderr
, "%i of %i: EN %i:\n",
5568 j
+ 1, processed_enodes
.length (),
5569 other_enode
->m_index
);
5570 other_enode
->dump_succs_and_preds (stderr
);
5572 other_enode
->get_state ().dump (m_ext_state
, false);
5579 DEBUG_FUNCTION exploded_node
*
5580 exploded_graph::get_node_by_index (int idx
) const
5582 exploded_node
*enode
= m_nodes
[idx
];
5583 gcc_assert (enode
->m_index
== idx
);
5587 /* Ensure that there is an exploded_node for a top-level call to FNDECL. */
5590 exploded_graph::on_escaped_function (tree fndecl
)
5592 logger
* const logger
= get_logger ();
5593 LOG_FUNC_1 (logger
, "%qE", fndecl
);
5595 cgraph_node
*cgnode
= cgraph_node::get (fndecl
);
5599 function
*fun
= cgnode
->get_fun ();
5603 if (!gimple_has_body_p (fndecl
))
5606 exploded_node
*enode
= add_function_entry (*fun
);
5610 logger
->log ("created EN %i for %qE entrypoint",
5611 enode
->m_index
, fun
->decl
);
5613 logger
->log ("did not create enode for %qE entrypoint", fun
->decl
);
5617 /* A collection of classes for visualizing the callgraph in .dot form
5618 (as represented in the supergraph). */
5620 /* Forward decls. */
5621 class viz_callgraph_node
;
5622 class viz_callgraph_edge
;
5623 class viz_callgraph
;
5624 class viz_callgraph_cluster
;
5626 /* Traits for using "digraph.h" to visualize the callgraph. */
5628 struct viz_callgraph_traits
5630 typedef viz_callgraph_node node_t
;
5631 typedef viz_callgraph_edge edge_t
;
5632 typedef viz_callgraph graph_t
;
5635 dump_args_t (const exploded_graph
*eg
) : m_eg (eg
) {}
5636 const exploded_graph
*m_eg
;
5638 typedef viz_callgraph_cluster cluster_t
;
5641 /* Subclass of dnode representing a function within the callgraph. */
5643 class viz_callgraph_node
: public dnode
<viz_callgraph_traits
>
5645 friend class viz_callgraph
;
5648 viz_callgraph_node (function
*fun
, int index
)
5649 : m_fun (fun
), m_index (index
), m_num_supernodes (0), m_num_superedges (0)
5654 void dump_dot (graphviz_out
*gv
, const dump_args_t
&args
) const final override
5656 pretty_printer
*pp
= gv
->get_pp ();
5659 pp_printf (pp
, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
5661 pp_write_text_to_stream (pp
);
5663 pp_printf (pp
, "VCG: %i: %s", m_index
, function_name (m_fun
));
5666 pp_printf (pp
, "supernodes: %i\n", m_num_supernodes
);
5669 pp_printf (pp
, "superedges: %i\n", m_num_superedges
);
5675 exploded_node
*enode
;
5676 unsigned num_enodes
= 0;
5677 FOR_EACH_VEC_ELT (args
.m_eg
->m_nodes
, i
, enode
)
5679 if (enode
->get_point ().get_function () == m_fun
)
5682 pp_printf (pp
, "enodes: %i\n", num_enodes
);
5685 // TODO: also show the per-callstring breakdown
5686 const exploded_graph::call_string_data_map_t
*per_cs_data
5687 = args
.m_eg
->get_per_call_string_data ();
5688 for (exploded_graph::call_string_data_map_t::iterator iter
5689 = per_cs_data
->begin ();
5690 iter
!= per_cs_data
->end ();
5693 const call_string
*cs
= (*iter
).first
;
5694 //per_call_string_data *data = (*iter).second;
5696 FOR_EACH_VEC_ELT (args
.m_eg
->m_nodes
, i
, enode
)
5698 if (enode
->get_point ().get_function () == m_fun
5699 && &enode
->get_point ().get_call_string () == cs
)
5705 pp_printf (pp
, ": %i\n", num_enodes
);
5709 /* Show any summaries. */
5710 per_function_data
*data
= args
.m_eg
->get_per_function_data (m_fun
);
5714 pp_printf (pp
, "summaries: %i\n", data
->m_summaries
.length ());
5715 for (auto summary
: data
->m_summaries
)
5717 pp_printf (pp
, "\nsummary: %s:\n", summary
->get_desc ().get ());
5718 const extrinsic_state
&ext_state
= args
.m_eg
->get_ext_state ();
5719 const program_state
&state
= summary
->get_state ();
5720 state
.dump_to_pp (ext_state
, false, true, pp
);
5726 pp_write_text_as_dot_label_to_stream (pp
, /*for_record=*/true);
5727 pp_string (pp
, "\"];\n\n");
5731 void dump_dot_id (pretty_printer
*pp
) const
5733 pp_printf (pp
, "vcg_%i", m_index
);
5739 int m_num_supernodes
;
5740 int m_num_superedges
;
5743 /* Subclass of dedge representing a callgraph edge. */
5745 class viz_callgraph_edge
: public dedge
<viz_callgraph_traits
>
5748 viz_callgraph_edge (viz_callgraph_node
*src
, viz_callgraph_node
*dest
)
5749 : dedge
<viz_callgraph_traits
> (src
, dest
)
5752 void dump_dot (graphviz_out
*gv
, const dump_args_t
&) const
5755 pretty_printer
*pp
= gv
->get_pp ();
5757 const char *style
= "\"solid,bold\"";
5758 const char *color
= "black";
5760 const char *constraint
= "true";
5762 m_src
->dump_dot_id (pp
);
5763 pp_string (pp
, " -> ");
5764 m_dest
->dump_dot_id (pp
);
5766 (" [style=%s, color=%s, weight=%d, constraint=%s,"
5768 style
, color
, weight
, constraint
);
5769 pp_printf (pp
, "\"];\n");
5773 /* Subclass of digraph representing the callgraph. */
5775 class viz_callgraph
: public digraph
<viz_callgraph_traits
>
5778 viz_callgraph (const supergraph
&sg
);
5780 viz_callgraph_node
*get_vcg_node_for_function (function
*fun
)
5782 return *m_map
.get (fun
);
5785 viz_callgraph_node
*get_vcg_node_for_snode (supernode
*snode
)
5787 return get_vcg_node_for_function (snode
->m_fun
);
5791 hash_map
<function
*, viz_callgraph_node
*> m_map
;
5794 /* Placeholder subclass of cluster. */
5796 class viz_callgraph_cluster
: public cluster
<viz_callgraph_traits
>
5800 /* viz_callgraph's ctor. */
5802 viz_callgraph::viz_callgraph (const supergraph
&sg
)
5805 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node
)
5807 function
*fun
= node
->get_fun ();
5808 viz_callgraph_node
*vcg_node
5809 = new viz_callgraph_node (fun
, m_nodes
.length ());
5810 m_map
.put (fun
, vcg_node
);
5811 add_node (vcg_node
);
5816 FOR_EACH_VEC_ELT (sg
.m_edges
, i
, sedge
)
5818 viz_callgraph_node
*vcg_src
= get_vcg_node_for_snode (sedge
->m_src
);
5820 get_vcg_node_for_function (vcg_src
->m_fun
)->m_num_superedges
++;
5821 if (sedge
->dyn_cast_call_superedge ())
5823 viz_callgraph_node
*vcg_dest
= get_vcg_node_for_snode (sedge
->m_dest
);
5824 viz_callgraph_edge
*vcg_edge
5825 = new viz_callgraph_edge (vcg_src
, vcg_dest
);
5826 add_edge (vcg_edge
);
5831 FOR_EACH_VEC_ELT (sg
.m_nodes
, i
, snode
)
5834 get_vcg_node_for_function (snode
->m_fun
)->m_num_supernodes
++;
5838 /* Dump the callgraph to FILENAME. */
5841 dump_callgraph (const supergraph
&sg
, const char *filename
,
5842 const exploded_graph
*eg
)
5844 FILE *outf
= fopen (filename
, "w");
5849 viz_callgraph
vcg (sg
);
5850 vcg
.dump_dot (filename
, NULL
, viz_callgraph_traits::dump_args_t (eg
));
5855 /* Dump the callgraph to "<srcfile>.callgraph.dot". */
5858 dump_callgraph (const supergraph
&sg
, const exploded_graph
*eg
)
5860 auto_timevar
tv (TV_ANALYZER_DUMP
);
5861 char *filename
= concat (dump_base_name
, ".callgraph.dot", NULL
);
5862 dump_callgraph (sg
, filename
, eg
);
5866 /* Subclass of dot_annotator for implementing
5867 DUMP_BASE_NAME.supergraph-eg.dot, a post-analysis dump of the supergraph.
5869 Annotate the supergraph nodes by printing the exploded nodes in concise
5870 form within them, next to their pertinent statements where appropriate,
5871 colorizing the exploded nodes based on sm-state.
5872 Also show saved diagnostics within the exploded nodes, giving information
5873 on whether they were feasible, and, if infeasible, where the problem
5876 class exploded_graph_annotator
: public dot_annotator
5879 exploded_graph_annotator (const exploded_graph
&eg
)
5882 /* Avoid O(N^2) by prepopulating m_enodes_per_snodes. */
5885 FOR_EACH_VEC_ELT (eg
.get_supergraph ().m_nodes
, i
, snode
)
5886 m_enodes_per_snodes
.safe_push (new auto_vec
<exploded_node
*> ());
5887 exploded_node
*enode
;
5888 FOR_EACH_VEC_ELT (m_eg
.m_nodes
, i
, enode
)
5889 if (enode
->get_supernode ())
5890 m_enodes_per_snodes
[enode
->get_supernode ()->m_index
]->safe_push (enode
);
5893 /* Show exploded nodes for BEFORE_SUPERNODE points before N. */
5894 bool add_node_annotations (graphviz_out
*gv
, const supernode
&n
,
5896 const final override
5901 pretty_printer
*pp
= gv
->get_pp ();
5904 pp_string (pp
, "BEFORE");
5905 pp_printf (pp
, " (scc: %i)", m_eg
.get_scc_id (n
));
5909 exploded_node
*enode
;
5910 bool had_enode
= false;
5911 FOR_EACH_VEC_ELT (*m_enodes_per_snodes
[n
.m_index
], i
, enode
)
5913 gcc_assert (enode
->get_supernode () == &n
);
5914 const program_point
&point
= enode
->get_point ();
5915 if (point
.get_kind () != PK_BEFORE_SUPERNODE
)
5917 print_enode (gv
, enode
);
5921 pp_string (pp
, "<TD BGCOLOR=\"red\">UNREACHED</TD>");
5927 /* Show exploded nodes for STMT. */
5928 void add_stmt_annotations (graphviz_out
*gv
, const gimple
*stmt
,
5930 const final override
5934 pretty_printer
*pp
= gv
->get_pp ();
5936 const supernode
*snode
5937 = m_eg
.get_supergraph ().get_supernode_for_stmt (stmt
);
5939 exploded_node
*enode
;
5940 bool had_td
= false;
5941 FOR_EACH_VEC_ELT (*m_enodes_per_snodes
[snode
->m_index
], i
, enode
)
5943 const program_point
&point
= enode
->get_point ();
5944 if (point
.get_kind () != PK_BEFORE_STMT
)
5946 if (point
.get_stmt () != stmt
)
5948 print_enode (gv
, enode
);
5959 /* Show exploded nodes for AFTER_SUPERNODE points after N. */
5960 bool add_after_node_annotations (graphviz_out
*gv
, const supernode
&n
)
5961 const final override
5964 pretty_printer
*pp
= gv
->get_pp ();
5967 pp_string (pp
, "AFTER");
5971 exploded_node
*enode
;
5972 FOR_EACH_VEC_ELT (*m_enodes_per_snodes
[n
.m_index
], i
, enode
)
5974 gcc_assert (enode
->get_supernode () == &n
);
5975 const program_point
&point
= enode
->get_point ();
5976 if (point
.get_kind () != PK_AFTER_SUPERNODE
)
5978 print_enode (gv
, enode
);
5986 /* Concisely print a TD element for ENODE, showing the index, status,
5987 and any saved_diagnostics at the enode. Colorize it to show sm-state.
5989 Ideally we'd dump ENODE's state here, hidden behind some kind of
5990 interactive disclosure method like a tooltip, so that the states
5991 can be explored without overwhelming the graph.
5992 However, I wasn't able to get graphviz/xdot to show tooltips on
5993 individual elements within a HTML-like label. */
5994 void print_enode (graphviz_out
*gv
, const exploded_node
*enode
) const
5996 pretty_printer
*pp
= gv
->get_pp ();
5997 pp_printf (pp
, "<TD BGCOLOR=\"%s\">",
5998 enode
->get_dot_fillcolor ());
5999 pp_printf (pp
, "<TABLE BORDER=\"0\">");
6001 pp_printf (pp
, "EN: %i", enode
->m_index
);
6002 switch (enode
->get_status ())
6006 case exploded_node::STATUS_WORKLIST
:
6007 pp_string (pp
, "(W)");
6009 case exploded_node::STATUS_PROCESSED
:
6011 case exploded_node::STATUS_MERGER
:
6012 pp_string (pp
, "(M)");
6014 case exploded_node::STATUS_BULK_MERGED
:
6015 pp_string (pp
, "(BM)");
6020 /* Dump any saved_diagnostics at this enode. */
6021 for (unsigned i
= 0; i
< enode
->get_num_diagnostics (); i
++)
6023 const saved_diagnostic
*sd
= enode
->get_saved_diagnostic (i
);
6024 print_saved_diagnostic (gv
, sd
);
6026 pp_printf (pp
, "</TABLE>");
6027 pp_printf (pp
, "</TD>");
6030 /* Print a TABLE element for SD, showing the kind, the length of the
6031 exploded_path, whether the path was feasible, and if infeasible,
6032 what the problem was. */
6033 void print_saved_diagnostic (graphviz_out
*gv
,
6034 const saved_diagnostic
*sd
) const
6036 pretty_printer
*pp
= gv
->get_pp ();
6038 pp_printf (pp
, "<TABLE BORDER=\"0\">");
6040 pp_string (pp
, "<TD BGCOLOR=\"green\">");
6041 pp_printf (pp
, "DIAGNOSTIC: %s", sd
->m_d
->get_kind ());
6044 if (sd
->get_best_epath ())
6045 pp_printf (pp
, "epath length: %i", sd
->get_epath_length ());
6047 pp_printf (pp
, "no best epath");
6049 if (const feasibility_problem
*p
= sd
->get_feasibility_problem ())
6052 pp_printf (pp
, "INFEASIBLE at eedge %i: EN:%i -> EN:%i",
6054 p
->m_eedge
.m_src
->m_index
,
6055 p
->m_eedge
.m_dest
->m_index
);
6056 pp_write_text_as_html_like_dot_to_stream (pp
);
6059 p
->m_eedge
.m_sedge
->dump (pp
);
6060 pp_write_text_as_html_like_dot_to_stream (pp
);
6063 pp_gimple_stmt_1 (pp
, p
->m_last_stmt
, 0, (dump_flags_t
)0);
6064 pp_write_text_as_html_like_dot_to_stream (pp
);
6066 /* Ideally we'd print p->m_model here; see the notes above about
6069 pp_printf (pp
, "</TABLE>");
6073 const exploded_graph
&m_eg
;
6074 auto_delete_vec
<auto_vec
<exploded_node
*> > m_enodes_per_snodes
;
6077 /* Implement -fdump-analyzer-json. */
6080 dump_analyzer_json (const supergraph
&sg
,
6081 const exploded_graph
&eg
)
6083 auto_timevar
tv (TV_ANALYZER_DUMP
);
6084 char *filename
= concat (dump_base_name
, ".analyzer.json.gz", NULL
);
6085 gzFile output
= gzopen (filename
, "w");
6088 error_at (UNKNOWN_LOCATION
, "unable to open %qs for writing", filename
);
6093 json::object
*toplev_obj
= new json::object ();
6094 toplev_obj
->set ("sgraph", sg
.to_json ());
6095 toplev_obj
->set ("egraph", eg
.to_json ());
6098 toplev_obj
->print (&pp
, flag_diagnostics_json_formatting
);
6099 pp_formatted_text (&pp
);
6103 if (gzputs (output
, pp_formatted_text (&pp
)) == EOF
6104 || gzclose (output
))
6105 error_at (UNKNOWN_LOCATION
, "error writing %qs", filename
);
6110 /* Concrete subclass of plugin_analyzer_init_iface, allowing plugins
6111 to register new state machines. */
6113 class plugin_analyzer_init_impl
: public plugin_analyzer_init_iface
6116 plugin_analyzer_init_impl (auto_delete_vec
<state_machine
> *checkers
,
6117 known_function_manager
*known_fn_mgr
,
6119 : m_checkers (checkers
),
6120 m_known_fn_mgr (known_fn_mgr
),
6124 void register_state_machine (std::unique_ptr
<state_machine
> sm
) final override
6126 LOG_SCOPE (m_logger
);
6127 m_checkers
->safe_push (sm
.release ());
6130 void register_known_function (const char *name
,
6131 std::unique_ptr
<known_function
> kf
) final override
6133 LOG_SCOPE (m_logger
);
6134 m_known_fn_mgr
->add (name
, std::move (kf
));
6137 logger
*get_logger () const final override
6143 auto_delete_vec
<state_machine
> *m_checkers
;
6144 known_function_manager
*m_known_fn_mgr
;
6148 /* Run the analysis "engine". */
6151 impl_run_checkers (logger
*logger
)
6157 logger
->log ("BITS_BIG_ENDIAN: %i", BITS_BIG_ENDIAN
? 1 : 0);
6158 logger
->log ("BYTES_BIG_ENDIAN: %i", BYTES_BIG_ENDIAN
? 1 : 0);
6159 logger
->log ("WORDS_BIG_ENDIAN: %i", WORDS_BIG_ENDIAN
? 1 : 0);
6160 log_stashed_constants (logger
);
6163 /* If using LTO, ensure that the cgraph nodes have function bodies. */
6165 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node
)
6166 node
->get_untransformed_body ();
6168 /* Create the supergraph. */
6169 supergraph
sg (logger
);
6171 engine
eng (&sg
, logger
);
6173 state_purge_map
*purge_map
= NULL
;
6175 if (flag_analyzer_state_purge
)
6176 purge_map
= new state_purge_map (sg
, eng
.get_model_manager (), logger
);
6178 if (flag_dump_analyzer_supergraph
)
6180 /* Dump supergraph pre-analysis. */
6181 auto_timevar
tv (TV_ANALYZER_DUMP
);
6182 char *filename
= concat (dump_base_name
, ".supergraph.dot", NULL
);
6183 supergraph::dump_args_t
args ((enum supergraph_dot_flags
)0, NULL
);
6184 sg
.dump_dot (filename
, args
);
6188 if (flag_dump_analyzer_state_purge
)
6190 auto_timevar
tv (TV_ANALYZER_DUMP
);
6191 state_purge_annotator
a (purge_map
);
6192 char *filename
= concat (dump_base_name
, ".state-purge.dot", NULL
);
6193 supergraph::dump_args_t
args ((enum supergraph_dot_flags
)0, &a
);
6194 sg
.dump_dot (filename
, args
);
6198 auto_delete_vec
<state_machine
> checkers
;
6199 make_checkers (checkers
, logger
);
6201 register_known_functions (*eng
.get_known_function_manager (),
6202 *eng
.get_model_manager ());
6204 plugin_analyzer_init_impl
data (&checkers
,
6205 eng
.get_known_function_manager (),
6207 invoke_plugin_callbacks (PLUGIN_ANALYZER_INIT
, &data
);
6213 FOR_EACH_VEC_ELT (checkers
, i
, sm
)
6214 logger
->log ("checkers[%i]: %s", i
, sm
->get_name ());
6217 /* Extrinsic state shared by nodes in the graph. */
6218 const extrinsic_state
ext_state (checkers
, &eng
, logger
);
6220 const analysis_plan
plan (sg
, logger
);
6222 /* The exploded graph. */
6223 exploded_graph
eg (sg
, logger
, ext_state
, purge_map
, plan
,
6224 analyzer_verbosity
);
6226 /* Add entrypoints to the graph for externally-callable functions. */
6227 eg
.build_initial_worklist ();
6229 /* Now process the worklist, exploring the <point, state> graph. */
6230 eg
.process_worklist ();
6232 if (warn_analyzer_infinite_loop
)
6233 eg
.detect_infinite_loops ();
6235 if (flag_dump_analyzer_exploded_graph
)
6237 auto_timevar
tv (TV_ANALYZER_DUMP
);
6239 = concat (dump_base_name
, ".eg.dot", NULL
);
6240 exploded_graph::dump_args_t
args (eg
);
6242 eg
.dump_dot (filename
, &c
, args
);
6246 /* Now emit any saved diagnostics. */
6247 eg
.get_diagnostic_manager ().emit_saved_diagnostics (eg
);
6249 eg
.dump_exploded_nodes ();
6253 if (flag_dump_analyzer_callgraph
)
6254 dump_callgraph (sg
, &eg
);
6256 if (flag_dump_analyzer_supergraph
)
6258 /* Dump post-analysis form of supergraph. */
6259 auto_timevar
tv (TV_ANALYZER_DUMP
);
6260 char *filename
= concat (dump_base_name
, ".supergraph-eg.dot", NULL
);
6261 exploded_graph_annotator
a (eg
);
6262 supergraph::dump_args_t
args ((enum supergraph_dot_flags
)0, &a
);
6263 sg
.dump_dot (filename
, args
);
6267 if (flag_dump_analyzer_json
)
6268 dump_analyzer_json (sg
, eg
);
6270 if (flag_dump_analyzer_untracked
)
6271 eng
.get_model_manager ()->dump_untracked_regions ();
6275 /* Free up any dominance info that we may have created. */
6276 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node
)
6278 function
*fun
= node
->get_fun ();
6279 free_dominance_info (fun
, CDI_DOMINATORS
);
6283 /* Handle -fdump-analyzer and -fdump-analyzer-stderr. */
6284 static FILE *dump_fout
= NULL
;
6286 /* Track if we're responsible for closing dump_fout. */
6287 static bool owns_dump_fout
= false;
6289 /* If dumping is enabled, attempt to create dump_fout if it hasn't already
6290 been opened. Return it. */
6293 get_or_create_any_logfile ()
6297 if (flag_dump_analyzer_stderr
)
6299 else if (flag_dump_analyzer
)
6301 char *dump_filename
= concat (dump_base_name
, ".analyzer.txt", NULL
);
6302 dump_fout
= fopen (dump_filename
, "w");
6303 free (dump_filename
);
6305 owns_dump_fout
= true;
6311 /* External entrypoint to the analysis "engine".
6312 Set up any dumps, then call impl_run_checkers. */
6317 /* Save input_location. */
6318 location_t saved_input_location
= input_location
;
6321 log_user
the_logger (NULL
);
6322 get_or_create_any_logfile ();
6324 the_logger
.set_logger (new logger (dump_fout
, 0, 0,
6325 *global_dc
->m_printer
));
6326 LOG_SCOPE (the_logger
.get_logger ());
6328 impl_run_checkers (the_logger
.get_logger ());
6330 /* end of lifetime of the_logger (so that dump file is closed after the
6331 various dtors run). */
6337 owns_dump_fout
= false;
6341 /* Restore input_location. Subsequent passes may assume that input_location
6342 is some arbitrary value *not* in the block tree, which might be violated
6343 if we didn't restore it. */
6344 input_location
= saved_input_location
;
6349 #endif /* #if ENABLE_ANALYZER */