aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer/program-state.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/analyzer/program-state.cc')
-rw-r--r--gcc/analyzer/program-state.cc1271
1 files changed, 490 insertions, 781 deletions
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index 1a5843b..ede20a7 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -33,13 +33,15 @@ along with GCC; see the file COPYING3. If not see
#include "tristate.h"
#include "ordered-hash-map.h"
#include "selftest.h"
+#include "analyzer/call-string.h"
+#include "analyzer/program-point.h"
+#include "analyzer/store.h"
#include "analyzer/region-model.h"
#include "analyzer/program-state.h"
#include "analyzer/constraint-manager.h"
#include "alloc-pool.h"
#include "fibonacci_heap.h"
#include "shortest-paths.h"
-#include "analyzer/constraint-manager.h"
#include "diagnostic-event-id.h"
#include "analyzer/pending-diagnostic.h"
#include "analyzer/diagnostic-manager.h"
@@ -50,8 +52,6 @@ along with GCC; see the file COPYING3. If not see
#include "cgraph.h"
#include "digraph.h"
#include "analyzer/supergraph.h"
-#include "analyzer/call-string.h"
-#include "analyzer/program-point.h"
#include "analyzer/program-state.h"
#include "analyzer/exploded-graph.h"
#include "analyzer/state-purge.h"
@@ -99,12 +99,23 @@ extrinsic_state::dump () const
dump_to_file (stderr);
}
+/* Get the region_model_manager for this extrinsic_state. */
+
+region_model_manager *
+extrinsic_state::get_model_manager () const
+{
+ if (m_engine)
+ return m_engine->get_model_manager ();
+ else
+ return NULL; /* for selftests. */
+}
+
/* class sm_state_map. */
/* sm_state_map's ctor. */
-sm_state_map::sm_state_map ()
-: m_map (), m_global_state (0)
+sm_state_map::sm_state_map (const state_machine &sm, int sm_idx)
+: m_sm (sm), m_sm_idx (sm_idx), m_map (), m_global_state (0)
{
}
@@ -116,77 +127,56 @@ sm_state_map::clone () const
return new sm_state_map (*this);
}
-/* Clone this sm_state_map, remapping all svalue_ids within it with ID_MAP.
-
- Return NULL if there are any svalue_ids that have sm-state for which
- ID_MAP maps them to svalue_id::null (and thus the clone would have lost
- the sm-state information). */
-
-sm_state_map *
-sm_state_map::clone_with_remapping (const one_way_svalue_id_map &id_map) const
-{
- sm_state_map *result = new sm_state_map ();
- result->m_global_state = m_global_state;
- for (map_t::iterator iter = m_map.begin ();
- iter != m_map.end ();
- ++iter)
- {
- svalue_id sid = (*iter).first;
- gcc_assert (!sid.null_p ());
- entry_t e = (*iter).second;
- /* TODO: what should we do if the origin maps from non-null to null?
- Is that loss of information acceptable? */
- id_map.update (&e.m_origin);
-
- svalue_id new_sid = id_map.get_dst_for_src (sid);
- if (new_sid.null_p ())
- {
- delete result;
- return NULL;
- }
- result->m_map.put (new_sid, e);
- }
- return result;
-}
-
-/* Print this sm_state_map (for SM) to PP.
+/* Print this sm_state_map to PP.
If MODEL is non-NULL, print representative tree values where
available. */
void
-sm_state_map::print (const state_machine &sm, const region_model *model,
- pretty_printer *pp) const
+sm_state_map::print (const region_model *model,
+ bool simple, bool multiline,
+ pretty_printer *pp) const
{
bool first = true;
- pp_string (pp, "{");
+ if (!multiline)
+ pp_string (pp, "{");
if (m_global_state != 0)
{
- pp_printf (pp, "global: %s", sm.get_state_name (m_global_state));
+ if (multiline)
+ pp_string (pp, " ");
+ pp_printf (pp, "global: %s", m_sm.get_state_name (m_global_state));
+ if (multiline)
+ pp_newline (pp);
first = false;
}
for (map_t::iterator iter = m_map.begin ();
iter != m_map.end ();
++iter)
{
- if (!first)
+ if (multiline)
+ pp_string (pp, " ");
+ else if (!first)
pp_string (pp, ", ");
first = false;
- svalue_id sid = (*iter).first;
- sid.print (pp);
+ const svalue *sval = (*iter).first;
+ pp_pointer (pp, sval);
+ pp_string (pp, ": ");
+ sval->dump_to_pp (pp, simple);
entry_t e = (*iter).second;
- pp_printf (pp, ": %s", sm.get_state_name (e.m_state));
+ pp_printf (pp, ": %s", m_sm.get_state_name (e.m_state));
if (model)
- if (tree rep = model->get_representative_tree (sid))
+ if (tree rep = model->get_representative_tree (sval))
{
pp_string (pp, " (");
dump_quoted_tree (pp, rep);
pp_character (pp, ')');
}
- if (!e.m_origin.null_p ())
+ if (e.m_origin)
{
pp_string (pp, " (origin: ");
- e.m_origin.print (pp);
+ pp_pointer (pp, e.m_origin);
+ pp_string (pp, ": ");
+ e.m_origin->dump_to_pp (pp, simple);
if (model)
if (tree rep = model->get_representative_tree (e.m_origin))
{
@@ -196,19 +186,22 @@ sm_state_map::print (const state_machine &sm, const region_model *model,
}
pp_string (pp, ")");
}
+ if (multiline)
+ pp_newline (pp);
}
- pp_string (pp, "}");
+ if (!multiline)
+ pp_string (pp, "}");
}
-/* Dump this object (for SM) to stderr. */
+/* Dump this object to stderr. */
DEBUG_FUNCTION void
-sm_state_map::dump (const state_machine &sm) const
+sm_state_map::dump (bool simple) const
{
pretty_printer pp;
pp_show_color (&pp) = pp_show_color (global_dc->printer);
pp.buffer->stream = stderr;
- print (sm, NULL, &pp);
+ print (NULL, simple, true, &pp);
pp_newline (&pp);
pp_flush (&pp);
}
@@ -237,10 +230,10 @@ sm_state_map::hash () const
++iter)
{
inchash::hash hstate;
- inchash::add ((*iter).first, hstate);
+ hstate.add_ptr ((*iter).first);
entry_t e = (*iter).second;
hstate.add_int (e.m_state);
- inchash::add (e.m_origin, hstate);
+ hstate.add_ptr (e.m_origin);
result ^= hstate.end ();
}
result ^= m_global_state;
@@ -263,9 +256,9 @@ sm_state_map::operator== (const sm_state_map &other) const
iter != m_map.end ();
++iter)
{
- svalue_id sid = (*iter).first;
+ const svalue *sval = (*iter).first;
entry_t e = (*iter).second;
- entry_t *other_slot = const_cast <map_t &> (other.m_map).get (sid);
+ entry_t *other_slot = const_cast <map_t &> (other.m_map).get (sval);
if (other_slot == NULL)
return false;
if (e != *other_slot)
@@ -277,34 +270,63 @@ sm_state_map::operator== (const sm_state_map &other) const
return true;
}
-/* Get the state of SID within this object.
+/* Get the state of SVAL within this object.
States default to the start state. */
state_machine::state_t
-sm_state_map::get_state (svalue_id sid) const
+sm_state_map::get_state (const svalue *sval,
+ const extrinsic_state &ext_state) const
{
- gcc_assert (!sid.null_p ());
+ gcc_assert (sval);
+
+ sval = canonicalize_svalue (sval, ext_state);
if (entry_t *slot
- = const_cast <map_t &> (m_map).get (sid))
+ = const_cast <map_t &> (m_map).get (sval))
return slot->m_state;
- else
- return 0;
+
+ /* SVAL has no explicit sm-state.
+ If this sm allows for state inheritance, then SVAL might have implicit
+ sm-state inherited via a parent.
+ For example INIT_VAL(foo.field) might inherit taintedness state from
+ INIT_VAL(foo). */
+ if (m_sm.inherited_state_p ())
+ if (region_model_manager *mgr = ext_state.get_model_manager ())
+ if (const initial_svalue *init_sval = sval->dyn_cast_initial_svalue ())
+ {
+ const region *reg = init_sval->get_region ();
+ /* Try recursing upwards (up to the base region for the cluster). */
+ if (!reg->base_region_p ())
+ if (const region *parent_reg = reg->get_parent_region ())
+ {
+ const svalue *parent_init_sval
+ = mgr->get_or_create_initial_value (parent_reg);
+ state_machine::state_t parent_state
+ = get_state (parent_init_sval, ext_state);
+ if (parent_state)
+ return parent_state;
+ }
+ }
+
+ return m_sm.get_default_state (sval);
}
-/* Get the "origin" svalue_id for any state of SID. */
+/* Get the "origin" svalue for any state of SVAL. */
-svalue_id
-sm_state_map::get_origin (svalue_id sid) const
+const svalue *
+sm_state_map::get_origin (const svalue *sval,
+ const extrinsic_state &ext_state) const
{
- gcc_assert (!sid.null_p ());
+ gcc_assert (sval);
+
+ sval = canonicalize_svalue (sval, ext_state);
entry_t *slot
- = const_cast <map_t &> (m_map).get (sid);
+ = const_cast <map_t &> (m_map).get (sval);
if (slot)
return slot->m_origin;
else
- return svalue_id::null ();
+ return NULL;
}
/* Set the state of SID within MODEL to STATE, recording that
@@ -312,28 +334,21 @@ sm_state_map::get_origin (svalue_id sid) const
void
sm_state_map::set_state (region_model *model,
- svalue_id sid,
+ const svalue *sval,
state_machine::state_t state,
- svalue_id origin)
+ const svalue *origin,
+ const extrinsic_state &ext_state)
{
if (model == NULL)
return;
- equiv_class &ec = model->get_constraints ()->get_equiv_class (sid);
- if (!set_state (ec, state, origin))
- return;
- /* Also do it for all svalues that are equal via non-cm, so that
- e.g. (void *)&r and (foo *)&r transition together. */
- for (unsigned i = 0; i < model->get_num_svalues (); i++)
- {
- svalue_id other_sid = svalue_id::from_int (i);
- if (other_sid == sid)
- continue;
+ /* Reject attempts to set state on UNKNOWN. */
+ if (sval->get_kind () == SK_UNKNOWN)
+ return;
- tristate eq = model->eval_condition_without_cm (sid, EQ_EXPR, other_sid);
- if (eq.is_true ())
- impl_set_state (other_sid, state, origin);
- }
+ equiv_class &ec = model->get_constraints ()->get_equiv_class (sval);
+ if (!set_state (ec, state, origin, ext_state))
+ return;
}
/* Set the state of EC to STATE, recording that the state came from
@@ -343,35 +358,40 @@ sm_state_map::set_state (region_model *model,
bool
sm_state_map::set_state (const equiv_class &ec,
state_machine::state_t state,
- svalue_id origin)
+ const svalue *origin,
+ const extrinsic_state &ext_state)
{
int i;
- svalue_id *sid;
+ const svalue *sval;
bool any_changed = false;
- FOR_EACH_VEC_ELT (ec.m_vars, i, sid)
- any_changed |= impl_set_state (*sid, state, origin);
+ FOR_EACH_VEC_ELT (ec.m_vars, i, sval)
+ any_changed |= impl_set_state (sval, state, origin, ext_state);
return any_changed;
}
-/* Set state of SID to STATE, bypassing equivalence classes.
+/* Set state of SVAL to STATE, bypassing equivalence classes.
Return true if the state changed. */
bool
-sm_state_map::impl_set_state (svalue_id sid, state_machine::state_t state,
- svalue_id origin)
+sm_state_map::impl_set_state (const svalue *sval,
+ state_machine::state_t state,
+ const svalue *origin,
+ const extrinsic_state &ext_state)
{
- if (get_state (sid) == state)
+ sval = canonicalize_svalue (sval, ext_state);
+
+ if (get_state (sval, ext_state) == state)
return false;
/* Special-case state 0 as the default value. */
if (state == 0)
{
- if (m_map.get (sid))
- m_map.remove (sid);
+ if (m_map.get (sval))
+ m_map.remove (sval);
return true;
}
- gcc_assert (!sid.null_p ());
- m_map.put (sid, entry_t (state, origin));
+ gcc_assert (sval);
+ m_map.put (sval, entry_t (state, origin));
return true;
}
@@ -391,203 +411,105 @@ sm_state_map::get_global_state () const
return m_global_state;
}
-/* Handle CALL to unknown FNDECL with an unknown function body, which
- could do anything to the states passed to it.
- Clear any state for SM for the params and any LHS.
- Note that the function might be known to other state machines, but
- not to this one. */
+/* Purge any state for SVAL.
+ If !SM::can_purge_p, then report the state as leaking,
+ using CTXT. */
void
-sm_state_map::purge_for_unknown_fncall (const exploded_graph &eg,
- const state_machine &sm,
- const gcall *call,
- tree fndecl,
- region_model *new_model,
- region_model_context *ctxt)
+sm_state_map::on_svalue_leak (const svalue *sval,
+ impl_region_model_context *ctxt)
{
- logger * const logger = eg.get_logger ();
- if (logger)
+ if (state_machine::state_t state = get_state (sval, ctxt->m_ext_state))
{
- if (fndecl)
- logger->log ("function %qE is unknown to checker %qs",
- fndecl, sm.get_name ());
- else
- logger->log ("unknown function pointer for checker %qs",
- sm.get_name ());
- }
-
- /* Purge any state for parms. */
- tree iter_param_types = NULL_TREE;
- if (fndecl)
- iter_param_types = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
- for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++)
- {
- /* Track expected param type, where available. */
- if (iter_param_types)
- {
- tree param_type = TREE_VALUE (iter_param_types);
- gcc_assert (param_type);
- iter_param_types = TREE_CHAIN (iter_param_types);
-
- /* Don't purge state if it was passed as a const pointer
- e.g. for things like strlen (PTR). */
- if (TREE_CODE (param_type) == POINTER_TYPE)
- if (TYPE_READONLY (TREE_TYPE (param_type)))
- continue;
- }
- tree parm = gimple_call_arg (call, arg_idx);
- svalue_id parm_sid = new_model->get_rvalue (parm, ctxt);
- set_state (new_model, parm_sid, 0, svalue_id::null ());
-
- /* Also clear sm-state from svalue_ids that are passed via a
- pointer. */
- if (TREE_CODE (parm) == ADDR_EXPR)
- {
- tree pointee = TREE_OPERAND (parm, 0);
- svalue_id parm_sid = new_model->get_rvalue (pointee, ctxt);
- set_state (new_model, parm_sid, 0, svalue_id::null ());
- }
- }
-
- /* Purge any state for any LHS. */
- if (tree lhs = gimple_call_lhs (call))
- {
- svalue_id lhs_sid = new_model->get_rvalue (lhs, ctxt);
- set_state (new_model, lhs_sid, 0, svalue_id::null ());
+ if (!m_sm.can_purge_p (state))
+ ctxt->on_state_leak (m_sm, sval, state);
+ m_map.remove (sval);
}
}
-/* Update this map based on MAP. */
+/* Purge any state for svalues that aren't live with respect to LIVE_SVALUES
+ and MODEL. */
void
-sm_state_map::remap_svalue_ids (const svalue_id_map &map)
+sm_state_map::on_liveness_change (const svalue_set &live_svalues,
+ const region_model *model,
+ impl_region_model_context *ctxt)
{
- map_t tmp_map;
+ svalue_set svals_to_unset;
- /* Build an intermediate map, using the new sids. */
for (map_t::iterator iter = m_map.begin ();
iter != m_map.end ();
++iter)
{
- svalue_id sid = (*iter).first;
- entry_t e = (*iter).second;
-
- map.update (&sid);
- map.update (&e.m_origin);
- tmp_map.put (sid, e);
- }
-
- /* Clear the existing values. */
- m_map.empty ();
-
- /* Copy over from intermediate map. */
- for (map_t::iterator iter = tmp_map.begin ();
- iter != tmp_map.end ();
- ++iter)
- {
- svalue_id sid = (*iter).first;
- entry_t e = (*iter).second;
-
- impl_set_state (sid, e.m_state, e.m_origin);
- }
-}
-
-/* Purge any state for svalue_ids >= FIRST_UNUSED_SID.
- If !SM::can_purge_p, then report the state as leaking,
- using SM_IDX, CTXT, and MAP.
- Return the number of states that were purged. */
-
-int
-sm_state_map::on_svalue_purge (const state_machine &sm,
- int sm_idx,
- svalue_id first_unused_sid,
- const svalue_id_map &map,
- impl_region_model_context *ctxt)
-{
- /* TODO: ideally remove the slot directly; for now
- do it in two stages. */
- auto_vec<svalue_id> to_remove;
- for (map_t::iterator iter = m_map.begin ();
- iter != m_map.end ();
- ++iter)
- {
- svalue_id dst_sid ((*iter).first);
- if (dst_sid.as_int () >= first_unused_sid.as_int ())
+ const svalue *iter_sval = (*iter).first;
+ if (!iter_sval->live_p (live_svalues, model))
{
- /* Complain about leaks here. */
+ svals_to_unset.add (iter_sval);
entry_t e = (*iter).second;
-
- if (!sm.can_purge_p (e.m_state))
- ctxt->on_state_leak (sm, sm_idx, dst_sid, first_unused_sid,
- map, e.m_state);
-
- to_remove.safe_push (dst_sid);
- }
- else if ((*iter).second.m_origin.as_int () >= first_unused_sid.as_int ())
- {
- /* If the origin svalue is being purged, then reset it to null. */
- (*iter).second.m_origin = svalue_id::null ();
+ if (!m_sm.can_purge_p (e.m_state))
+ ctxt->on_state_leak (m_sm, iter_sval, e.m_state);
}
}
- int i;
- svalue_id *dst_sid;
- FOR_EACH_VEC_ELT (to_remove, i, dst_sid)
- m_map.remove (*dst_sid);
-
- return to_remove.length ();
+ for (svalue_set::iterator iter = svals_to_unset.begin ();
+ iter != svals_to_unset.end (); ++iter)
+ m_map.remove (*iter);
}
-/* Set the state of CHILD_SID to that of PARENT_SID. */
+/* Purge state from SVAL (in response to a call to an unknown function). */
void
-sm_state_map::on_inherited_svalue (svalue_id parent_sid,
- svalue_id child_sid)
+sm_state_map::on_unknown_change (const svalue *sval,
+ bool is_mutable,
+ const extrinsic_state &ext_state)
{
- state_machine::state_t state = get_state (parent_sid);
- impl_set_state (child_sid, state, parent_sid);
-}
-
-/* Set the state of DST_SID to that of SRC_SID. */
-
-void
-sm_state_map::on_cast (svalue_id src_sid,
- svalue_id dst_sid)
-{
- state_machine::state_t state = get_state (src_sid);
- impl_set_state (dst_sid, state, get_origin (src_sid));
-}
-
-/* Purge state from SID (in response to a call to an unknown function). */
-
-void
-sm_state_map::on_unknown_change (svalue_id sid)
-{
- impl_set_state (sid, (state_machine::state_t)0, svalue_id::null ());
-}
-
-/* Assert that this object is sane. */
-
-void
-sm_state_map::validate (const state_machine &sm,
- int num_svalues) const
-{
- /* Skip this in a release build. */
-#if !CHECKING_P
- return;
-#endif
+ svalue_set svals_to_unset;
for (map_t::iterator iter = m_map.begin ();
iter != m_map.end ();
++iter)
{
- svalue_id sid = (*iter).first;
+ const svalue *key = (*iter).first;
entry_t e = (*iter).second;
-
- gcc_assert (sid.as_int () < num_svalues);
- sm.validate (e.m_state);
- gcc_assert (e.m_origin.as_int () < num_svalues);
+ /* We only want to purge state for some states when things
+ are mutable. For example, in sm-malloc.cc, an on-stack ptr
+ doesn't stop being stack-allocated when passed to an unknown fn. */
+ if (!m_sm.reset_when_passed_to_unknown_fn_p (e.m_state, is_mutable))
+ continue;
+ if (key == sval)
+ svals_to_unset.add (key);
+ /* If we have INIT_VAL(BASE_REG), then unset any INIT_VAL(REG)
+ for REG within BASE_REG. */
+ if (const initial_svalue *init_sval = sval->dyn_cast_initial_svalue ())
+ if (const initial_svalue *init_key = key->dyn_cast_initial_svalue ())
+ {
+ const region *changed_reg = init_sval->get_region ();
+ const region *changed_key = init_key->get_region ();
+ if (changed_key->get_base_region () == changed_reg)
+ svals_to_unset.add (key);
+ }
}
+
+ for (svalue_set::iterator iter = svals_to_unset.begin ();
+ iter != svals_to_unset.end (); ++iter)
+ impl_set_state (*iter, (state_machine::state_t)0, NULL, ext_state);
+}
+
+/* Canonicalize SVAL before getting/setting it within the map.
+ Convert all NULL pointers to (void *) to avoid state explosions
+ involving all of the various (foo *)NULL vs (bar *)NULL. */
+
+const svalue *
+sm_state_map::canonicalize_svalue (const svalue *sval,
+ const extrinsic_state &ext_state)
+{
+ region_model_manager *mgr = ext_state.get_model_manager ();
+ if (mgr && sval->get_type () && POINTER_TYPE_P (sval->get_type ()))
+ if (tree cst = sval->maybe_get_constant ())
+ if (zerop (cst))
+ return mgr->get_or_create_constant_svalue (null_pointer_node);
+
+ return sval;
}
/* class program_state. */
@@ -595,13 +517,19 @@ sm_state_map::validate (const state_machine &sm,
/* program_state's ctor. */
program_state::program_state (const extrinsic_state &ext_state)
-: m_region_model (new region_model ()),
+: m_region_model (NULL),
m_checker_states (ext_state.get_num_checkers ()),
m_valid (true)
{
- int num_states = ext_state.get_num_checkers ();
+ engine *eng = ext_state.get_engine ();
+ region_model_manager *mgr = eng->get_model_manager ();
+ m_region_model = new region_model (mgr);
+ const int num_states = ext_state.get_num_checkers ();
for (int i = 0; i < num_states; i++)
- m_checker_states.quick_push (new sm_state_map ());
+ {
+ sm_state_map *sm = new sm_state_map (ext_state.get_sm (i), i);
+ m_checker_states.quick_push (sm);
+ }
}
/* program_state's copy ctor. */
@@ -708,7 +636,7 @@ program_state::print (const extrinsic_state &ext_state,
pretty_printer *pp) const
{
pp_printf (pp, "rmodel: ");
- m_region_model->print (pp);
+ m_region_model->dump_to_pp (pp, true, false);
pp_newline (pp);
int i;
@@ -718,7 +646,7 @@ program_state::print (const extrinsic_state &ext_state,
if (!smap->is_empty_p ())
{
pp_printf (pp, "%s: ", ext_state.get_name (i));
- smap->print (ext_state.get_sm (i), m_region_model, pp);
+ smap->print (m_region_model, true, false, pp);
pp_newline (pp);
}
}
@@ -729,17 +657,25 @@ program_state::print (const extrinsic_state &ext_state,
}
}
-/* Dump a representation of this state to PP.
- If SUMMARIZE is true, print a one-line summary;
- if false, print a detailed multiline representation. */
+/* Dump a representation of this state to PP. */
void
program_state::dump_to_pp (const extrinsic_state &ext_state,
- bool summarize,
+ bool /*summarize*/, bool multiline,
pretty_printer *pp) const
{
- pp_printf (pp, "rmodel: ");
- m_region_model->dump_to_pp (pp, summarize);
+ if (!multiline)
+ pp_string (pp, "{");
+ {
+ pp_printf (pp, "rmodel:");
+ if (multiline)
+ pp_newline (pp);
+ else
+ pp_string (pp, " {");
+ m_region_model->dump_to_pp (pp, true, multiline);
+ if (!multiline)
+ pp_string (pp, "}");
+ }
int i;
sm_state_map *smap;
@@ -747,30 +683,34 @@ program_state::dump_to_pp (const extrinsic_state &ext_state,
{
if (!smap->is_empty_p ())
{
- if (summarize)
- pp_space (pp);
+ if (!multiline)
+ pp_string (pp, " {");
pp_printf (pp, "%s: ", ext_state.get_name (i));
- smap->print (ext_state.get_sm (i), m_region_model, pp);
- if (!summarize)
+ if (multiline)
pp_newline (pp);
+ smap->print (m_region_model, true, multiline, pp);
+ if (!multiline)
+ pp_string (pp, "}");
}
}
if (!m_valid)
{
- if (summarize)
+ if (!multiline)
pp_space (pp);
pp_printf (pp, "invalid state");
- if (!summarize)
+ if (multiline)
pp_newline (pp);
}
+ if (!multiline)
+ pp_string (pp, "}");
}
-/* Dump a multiline representation of this state to OUTF. */
+/* Dump a representation of this state to OUTF. */
void
program_state::dump_to_file (const extrinsic_state &ext_state,
- bool summarize,
+ bool summarize, bool multiline,
FILE *outf) const
{
pretty_printer pp;
@@ -778,7 +718,7 @@ program_state::dump_to_file (const extrinsic_state &ext_state,
if (outf == stderr)
pp_show_color (&pp) = pp_show_color (global_dc->printer);
pp.buffer->stream = outf;
- dump_to_pp (ext_state, summarize, &pp);
+ dump_to_pp (ext_state, summarize, multiline, &pp);
pp_flush (&pp);
}
@@ -788,7 +728,25 @@ DEBUG_FUNCTION void
program_state::dump (const extrinsic_state &ext_state,
bool summarize) const
{
- dump_to_file (ext_state, summarize, stderr);
+ dump_to_file (ext_state, summarize, true, stderr);
+}
+
+/* Update this program_state to reflect a top-level call to FUN.
+ The params will have initial_svalues. */
+
+void
+program_state::push_frame (const extrinsic_state &ext_state ATTRIBUTE_UNUSED,
+ function *fun)
+{
+ m_region_model->push_frame (fun, NULL, NULL);
+}
+
+/* Get the current function of this state. */
+
+function *
+program_state::get_current_function () const
+{
+ return m_region_model->get_current_function ();
}
/* Determine if following edge SUCC from ENODE is valid within the graph EG
@@ -806,8 +764,7 @@ program_state::dump (const extrinsic_state &ext_state,
bool
program_state::on_edge (exploded_graph &eg,
const exploded_node &enode,
- const superedge *succ,
- state_change *change)
+ const superedge *succ)
{
/* Update state. */
const program_point &point = enode.get_point ();
@@ -824,7 +781,7 @@ program_state::on_edge (exploded_graph &eg,
impl_region_model_context ctxt (eg, &enode,
&enode.get_state (),
- this, change,
+ this,
last_stmt);
if (!m_region_model->maybe_update_for_edge (*succ,
last_stmt,
@@ -838,6 +795,10 @@ program_state::on_edge (exploded_graph &eg,
return false;
}
+ program_state::detect_leaks (enode.get_state (), *this,
+ NULL, eg.get_ext_state (),
+ &ctxt);
+
return true;
}
@@ -845,15 +806,12 @@ program_state::on_edge (exploded_graph &eg,
relevant at POINT.
The idea is that we're more likely to be able to consolidate
multiple (point, state) into single exploded_nodes if we discard
- irrelevant state (e.g. at the end of functions).
-
- Retain state affected by CHANGE, to make it easier to generate
- state_change_events. */
+ irrelevant state (e.g. at the end of functions). */
program_state
program_state::prune_for_point (exploded_graph &eg,
const program_point &point,
- state_change *change) const
+ const exploded_node *enode_for_diag) const
{
logger * const logger = eg.get_logger ();
LOG_SCOPE (logger);
@@ -864,207 +822,110 @@ program_state::prune_for_point (exploded_graph &eg,
program_state new_state (*this);
- purge_stats stats;
-
const state_purge_map *pm = eg.get_purge_map ();
if (pm)
{
- region_id_set purgeable_ssa_regions (new_state.m_region_model);
- region_id frame_rid
- = new_state.m_region_model->get_current_frame_id ();
- frame_region *frame
- = new_state.m_region_model->get_region <frame_region>(frame_rid);
-
- /* TODO: maybe move to a member of region_model? */
-
- auto_vec<tree> ssa_names_to_purge;
- for (frame_region::map_t::iterator iter = frame->begin ();
- iter != frame->end ();
- ++iter)
+ unsigned num_ssas_purged = 0;
+ auto_vec<const decl_region *> ssa_name_regs;
+ new_state.m_region_model->get_ssa_name_regions_for_current_frame
+ (&ssa_name_regs);
+ unsigned i;
+ const decl_region *reg;
+ FOR_EACH_VEC_ELT (ssa_name_regs, i, reg)
{
- tree var = (*iter).first;
- region_id rid = (*iter).second;
- if (TREE_CODE (var) == SSA_NAME)
+ tree ssa_name = reg->get_decl ();
+ const state_purge_per_ssa_name &per_ssa
+ = pm->get_data_for_ssa_name (ssa_name);
+ if (!per_ssa.needed_at_point_p (point.get_function_point ()))
{
- const state_purge_per_ssa_name &per_ssa
- = pm->get_data_for_ssa_name (var);
- if (!per_ssa.needed_at_point_p (point.get_function_point ()))
+ /* Don't purge bindings of SSA names to svalues
+ that have unpurgable sm-state, so that leaks are
+ reported at the end of the function, rather than
+ at the last place that such an SSA name is referred to.
+
+ But do purge them for temporaries (when SSA_NAME_VAR is
+ NULL), so that we report for cases where a leak happens when
+ a variable is overwritten with another value, so that the leak
+ is reported at the point of overwrite, rather than having
+ temporaries keep the value reachable until the frame is
+ popped. */
+ const svalue *sval
+ = new_state.m_region_model->get_store_value (reg);
+ if (!new_state.can_purge_p (eg.get_ext_state (), sval)
+ && SSA_NAME_VAR (ssa_name))
{
- region *region
- = new_state.m_region_model->get_region (rid);
- svalue_id sid = region->get_value_direct ();
- if (!sid.null_p ())
- {
- if (!new_state.can_purge_p (eg.get_ext_state (), sid))
- {
- /* (currently only state maps can keep things
- alive). */
- if (logger)
- logger->log ("not purging RID: %i for %qE"
- " (used by state map)",
- rid.as_int (), var);
- continue;
- }
-
- /* Don't purge regions containing svalues that
- have a change of sm-state, to make it easier to
- generate state_change_event messages. */
- if (change)
- if (change->affects_p (sid))
- {
- if (logger)
- logger->log ("not purging RID: %i for %qE"
- " (affected by change)",
- rid.as_int (), var);
- continue;
- }
- }
- purgeable_ssa_regions.add_region (rid);
- ssa_names_to_purge.safe_push (var);
+ /* (currently only state maps can keep things
+ alive). */
if (logger)
- logger->log ("purging RID: %i for %qE", rid.as_int (), var);
- /* We also need to remove the region from the map.
- We're in mid-traversal, so the removal is done in
- unbind below. */
+ logger->log ("not purging binding for %qE"
+ " (used by state map)", ssa_name);
+ continue;
}
+
+ new_state.m_region_model->purge_region (reg);
+ num_ssas_purged++;
}
}
- /* Unbind the regions from the frame's map of vars-to-regions. */
- unsigned i;
- tree var;
- FOR_EACH_VEC_ELT (ssa_names_to_purge, i, var)
- frame->unbind (var);
-
- /* Purge the regions. Nothing should point to them, and they
- should have no children, as they are for SSA names. */
- new_state.m_region_model->purge_regions (purgeable_ssa_regions,
- &stats,
- eg.get_logger ());
- }
-
- /* Purge unused svalues. */
- // TODO: which enode to use, if any?
- impl_region_model_context ctxt (eg, NULL,
- this,
- &new_state,
- change,
- NULL);
- new_state.m_region_model->purge_unused_svalues (&stats, &ctxt);
- if (logger)
- {
- logger->log ("num svalues purged: %i", stats.m_num_svalues);
- logger->log ("num regions purged: %i", stats.m_num_regions);
- logger->log ("num equiv_classes purged: %i", stats.m_num_equiv_classes);
- logger->log ("num constraints purged: %i", stats.m_num_constraints);
- logger->log ("num sm map items purged: %i", stats.m_num_client_items);
+ if (num_ssas_purged > 0)
+ {
+ if (logger)
+ logger->log ("num_ssas_purged: %i", num_ssas_purged);
+ impl_region_model_context ctxt (eg, enode_for_diag,
+ this,
+ &new_state,
+ point.get_stmt ());
+ detect_leaks (*this, new_state, NULL, eg.get_ext_state (), &ctxt);
+ }
}
- new_state.m_region_model->canonicalize (&ctxt);
+ new_state.m_region_model->canonicalize ();
return new_state;
}
-/* Remap all svalue_ids in this state's m_checker_states according to MAP.
- The svalues_ids in the region_model are assumed to already have been
- remapped. */
-
-void
-program_state::remap_svalue_ids (const svalue_id_map &map)
-{
- int i;
- sm_state_map *smap;
- FOR_EACH_VEC_ELT (m_checker_states, i, smap)
- smap->remap_svalue_ids (map);
-}
-
-/* Attempt to return a tree that represents SID, or return NULL_TREE.
- Find the first region that stores the value (e.g. a local) and
- generate a representative tree for it. */
+/* Get a representative tree to use for describing SVAL. */
tree
-program_state::get_representative_tree (svalue_id sid) const
+program_state::get_representative_tree (const svalue *sval) const
{
- return m_region_model->get_representative_tree (sid);
+ gcc_assert (m_region_model);
+ return m_region_model->get_representative_tree (sval);
}
-/* Attempt to merge this state with OTHER, both using EXT_STATE.
+/* Attempt to merge this state with OTHER, both at POINT.
Write the result to *OUT.
If the states were merged successfully, return true. */
bool
program_state::can_merge_with_p (const program_state &other,
- const extrinsic_state &ext_state,
+ const program_point &point,
program_state *out) const
{
gcc_assert (out);
+ gcc_assert (m_region_model);
- /* TODO: initially I had an early reject here if there
- are sm-differences between the states. However, this was
- falsely rejecting merger opportunities for states where the
- only difference was in svalue_id ordering. */
+ /* Early reject if there are sm-differences between the states. */
+ int i;
+ sm_state_map *smap;
+ FOR_EACH_VEC_ELT (out->m_checker_states, i, smap)
+ if (*m_checker_states[i] != *other.m_checker_states[i])
+ return false;
/* Attempt to merge the region_models. */
-
- svalue_id_merger_mapping sid_mapping (*m_region_model,
- *other.m_region_model);
if (!m_region_model->can_merge_with_p (*other.m_region_model,
- out->m_region_model,
- &sid_mapping))
+ point,
+ out->m_region_model))
return false;
- /* Copy m_checker_states to result, remapping svalue_ids using
- sid_mapping. */
- int i;
- sm_state_map *smap;
+ /* Copy m_checker_states to OUT. */
FOR_EACH_VEC_ELT (out->m_checker_states, i, smap)
- delete smap;
- out->m_checker_states.truncate (0);
-
- /* Remap this and other's m_checker_states using sid_mapping.
- Only merge states that have equality between the two end-results:
- sm-state differences are likely to be interesting to end-users, and
- hence are worth exploring as separate paths in the exploded graph. */
- FOR_EACH_VEC_ELT (m_checker_states, i, smap)
{
- sm_state_map *other_smap = other.m_checker_states[i];
-
- /* If clone_with_remapping returns NULL for one of the input smaps,
- then it has sm-state for an svalue_id where the svalue_id is
- being mapped to svalue_id::null in its sid_mapping, meaning that
- the svalue is to be dropped during the merger. We don't want
- to lose sm-state during a state merger, so return false for these
- cases. */
- sm_state_map *remapped_a_smap
- = smap->clone_with_remapping (sid_mapping.m_map_from_a_to_m);
- if (!remapped_a_smap)
- return false;
- sm_state_map *remapped_b_smap
- = other_smap->clone_with_remapping (sid_mapping.m_map_from_b_to_m);
- if (!remapped_b_smap)
- {
- delete remapped_a_smap;
- return false;
- }
-
- /* Both states have sm-state for the same values; now ensure that the
- states are equal. */
- if (*remapped_a_smap == *remapped_b_smap)
- {
- out->m_checker_states.safe_push (remapped_a_smap);
- delete remapped_b_smap;
- }
- else
- {
- /* Don't merge if there are sm-state differences. */
- delete remapped_a_smap;
- delete remapped_b_smap;
- return false;
- }
+ delete smap;
+ out->m_checker_states[i] = m_checker_states[i]->clone ();
}
- impl_region_model_context ctxt (out, NULL, ext_state);
- out->m_region_model->canonicalize (&ctxt);
+ out->m_region_model->canonicalize ();
return true;
}
@@ -1079,212 +940,107 @@ program_state::validate (const extrinsic_state &ext_state) const
return;
#endif
- m_region_model->validate ();
gcc_assert (m_checker_states.length () == ext_state.get_num_checkers ());
- int sm_idx;
- sm_state_map *smap;
- FOR_EACH_VEC_ELT (m_checker_states, sm_idx, smap)
- {
- const state_machine &sm = ext_state.get_sm (sm_idx);
- smap->validate (sm, m_region_model->get_num_svalues ());
- }
}
-/* Dump this sm_change to PP. */
-
-void
-state_change::sm_change::dump (pretty_printer *pp,
- const extrinsic_state &ext_state) const
-{
- const state_machine &sm = get_sm (ext_state);
- pp_string (pp, "(");
- m_new_sid.print (pp);
- pp_printf (pp, ": %s: %qs -> %qs)",
- sm.get_name (),
- sm.get_state_name (m_old_state),
- sm.get_state_name (m_new_state));
-}
-
-/* Remap all svalue_ids in this change according to MAP. */
-
-void
-state_change::sm_change::remap_svalue_ids (const svalue_id_map &map)
-{
- map.update (&m_new_sid);
-}
-
-/* Purge any svalue_ids >= FIRST_UNUSED_SID.
- Return the number of states that were purged. */
-
-int
-state_change::sm_change::on_svalue_purge (svalue_id first_unused_sid)
+static void
+log_set_of_svalues (logger *logger, const char *name,
+ const svalue_set &set)
{
- if (m_new_sid.as_int () >= first_unused_sid.as_int ())
+ logger->log (name);
+ logger->inc_indent ();
+ for (svalue_set::iterator iter = set.begin ();
+ iter != set.end (); ++iter)
{
- m_new_sid = svalue_id::null ();
- return 1;
+ logger->start_log_line ();
+ pretty_printer *pp = logger->get_printer ();
+ const svalue *sval = (*iter);
+ pp_pointer (pp, sval);
+ pp_string (pp, ": ");
+ sval->dump_to_pp (pp, false);
+ logger->end_log_line ();
}
-
- return 0;
-}
-
-/* Assert that this object is sane. */
-
-void
-state_change::sm_change::validate (const program_state &new_state,
- const extrinsic_state &ext_state) const
-{
- gcc_assert ((unsigned)m_sm_idx < ext_state.get_num_checkers ());
- const state_machine &sm = ext_state.get_sm (m_sm_idx);
- sm.validate (m_old_state);
- sm.validate (m_new_state);
- m_new_sid.validate (*new_state.m_region_model);
-}
-
-/* state_change's ctor. */
-
-state_change::state_change ()
-{
+ logger->dec_indent ();
}
-/* state_change's copy ctor. */
+/* Compare the sets of svalues reachable from each of SRC_STATE and DEST_STATE.
+ For all svalues that are reachable in SRC_STATE and are not live in
+ DEST_STATE (whether explicitly reachable in DEST_STATE, or implicitly live
+ based on the former set), call CTXT->on_svalue_leak for them.
-state_change::state_change (const state_change &other)
-: m_sm_changes (other.m_sm_changes.length ())
-{
- unsigned i;
- sm_change *change;
- FOR_EACH_VEC_ELT (other.m_sm_changes, i, change)
- m_sm_changes.quick_push (*change);
-}
+ Call on_liveness_change on both the CTXT and on the DEST_STATE's
+ constraint_manager, purging dead svalues from sm-state and from
+ constraints, respectively.
-/* Record a state-machine state change. */
+ This function should be called at each fine-grained state change, not
+ just at exploded edges. */
void
-state_change::add_sm_change (int sm_idx,
- svalue_id new_sid,
- state_machine::state_t old_state,
- state_machine::state_t new_state)
-{
- m_sm_changes.safe_push (sm_change (sm_idx,
- new_sid,
- old_state, new_state));
-}
-
-/* Return true if SID (in the new state) was affected by any
- sm-state changes. */
-
-bool
-state_change::affects_p (svalue_id sid) const
+program_state::detect_leaks (const program_state &src_state,
+ const program_state &dest_state,
+ const svalue *extra_sval,
+ const extrinsic_state &ext_state,
+ region_model_context *ctxt)
{
- unsigned i;
- sm_change *change;
- FOR_EACH_VEC_ELT (m_sm_changes, i, change)
+ logger *logger = ext_state.get_logger ();
+ LOG_SCOPE (logger);
+ if (logger)
{
- if (sid == change->m_new_sid)
- return true;
+ pretty_printer *pp = logger->get_printer ();
+ logger->start_log_line ();
+ pp_string (pp, "src_state: ");
+ src_state.dump_to_pp (ext_state, true, false, pp);
+ logger->end_log_line ();
+ logger->start_log_line ();
+ pp_string (pp, "dest_state: ");
+ dest_state.dump_to_pp (ext_state, true, false, pp);
+ logger->end_log_line ();
+ if (extra_sval)
+ {
+ logger->start_log_line ();
+ pp_string (pp, "extra_sval: ");
+ extra_sval->dump_to_pp (pp, true);
+ logger->end_log_line ();
+ }
}
- return false;
-}
-/* Dump this state_change to PP. */
+ /* Get svalues reachable from each of src_state and dst_state. */
+ svalue_set src_svalues;
+ svalue_set dest_svalues;
+ src_state.m_region_model->get_reachable_svalues (&src_svalues, NULL);
+ dest_state.m_region_model->get_reachable_svalues (&dest_svalues, extra_sval);
-void
-state_change::dump (pretty_printer *pp,
- const extrinsic_state &ext_state) const
-{
- unsigned i;
- sm_change *change;
- FOR_EACH_VEC_ELT (m_sm_changes, i, change)
+ if (logger)
{
- if (i > 0)
- pp_string (pp, ", ");
- change->dump (pp, ext_state);
+ log_set_of_svalues (logger, "src_state reachable svalues:", src_svalues);
+ log_set_of_svalues (logger, "dest_state reachable svalues:",
+ dest_svalues);
}
-}
-
-/* Dump this state_change to stderr. */
-void
-state_change::dump (const extrinsic_state &ext_state) const
-{
- pretty_printer pp;
- pp_show_color (&pp) = pp_show_color (global_dc->printer);
- pp.buffer->stream = stderr;
- dump (&pp, ext_state);
- pp_newline (&pp);
- pp_flush (&pp);
-}
-
-/* Remap all svalue_ids in this state_change according to MAP. */
-
-void
-state_change::remap_svalue_ids (const svalue_id_map &map)
-{
- unsigned i;
- sm_change *change;
- FOR_EACH_VEC_ELT (m_sm_changes, i, change)
- change->remap_svalue_ids (map);
-}
-
-/* Purge any svalue_ids >= FIRST_UNUSED_SID.
- Return the number of states that were purged. */
-
-int
-state_change::on_svalue_purge (svalue_id first_unused_sid)
-{
- int result = 0;
- unsigned i;
- sm_change *change;
- FOR_EACH_VEC_ELT (m_sm_changes, i, change)
- result += change->on_svalue_purge (first_unused_sid);
- return result;
-}
+ for (svalue_set::iterator iter = src_svalues.begin ();
+ iter != src_svalues.end (); ++iter)
+ {
+ const svalue *sval = (*iter);
+ /* For each sval reachable from SRC_STATE, determine if it is
+ live in DEST_STATE: either explicitly reachable, or implicitly
+ live based on the set of explicitly reachable svalues.
+ Call CTXT->on_svalue_leak on those that have ceased to be live. */
+ if (!sval->live_p (dest_svalues, dest_state.m_region_model))
+ ctxt->on_svalue_leak (sval);
+ }
-/* Assert that this object is sane. */
+ /* Purge dead svals from sm-state. */
+ ctxt->on_liveness_change (dest_svalues, dest_state.m_region_model);
-void
-state_change::validate (const program_state &new_state,
- const extrinsic_state &ext_state) const
-{
- /* Skip this in a release build. */
-#if !CHECKING_P
- return;
-#endif
- unsigned i;
- sm_change *change;
- FOR_EACH_VEC_ELT (m_sm_changes, i, change)
- change->validate (new_state, ext_state);
+ /* Purge dead svals from constraints. */
+ dest_state.m_region_model->get_constraints ()->on_liveness_change
+ (dest_svalues, dest_state.m_region_model);
}
#if CHECKING_P
namespace selftest {
-/* Implementation detail of ASSERT_DUMP_EQ. */
-
-static void
-assert_dump_eq (const location &loc,
- const program_state &state,
- const extrinsic_state &ext_state,
- bool summarize,
- const char *expected)
-{
- auto_fix_quotes sentinel;
- pretty_printer pp;
- pp_format_decoder (&pp) = default_tree_printer;
- state.dump_to_pp (ext_state, summarize, &pp);
- ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected);
-}
-
-/* Assert that STATE.dump_to_pp (SUMMARIZE) is EXPECTED. */
-
-#define ASSERT_DUMP_EQ(STATE, EXT_STATE, SUMMARIZE, EXPECTED) \
- SELFTEST_BEGIN_STMT \
- assert_dump_eq ((SELFTEST_LOCATION), (STATE), (EXT_STATE), (SUMMARIZE), \
- (EXPECTED)); \
- SELFTEST_END_STMT
-
/* Tests for sm_state_map. */
static void
@@ -1294,134 +1050,119 @@ test_sm_state_map ()
tree y = build_global_decl ("y", integer_type_node);
tree z = build_global_decl ("z", integer_type_node);
+ state_machine *sm = make_malloc_state_machine (NULL);
+ auto_delete_vec <state_machine> checkers;
+ checkers.safe_push (sm);
+ extrinsic_state ext_state (checkers);
+
/* Test setting states on svalue_id instances directly. */
{
- region_model model;
- svalue_id sid_x = model.get_rvalue (x, NULL);
- svalue_id sid_y = model.get_rvalue (y, NULL);
- svalue_id sid_z = model.get_rvalue (z, NULL);
+ region_model_manager mgr;
+ region_model model (&mgr);
+ const svalue *x_sval = model.get_rvalue (x, NULL);
+ const svalue *y_sval = model.get_rvalue (y, NULL);
+ const svalue *z_sval = model.get_rvalue (z, NULL);
- sm_state_map map;
+ sm_state_map map (*sm, 0);
ASSERT_TRUE (map.is_empty_p ());
- ASSERT_EQ (map.get_state (sid_x), 0);
+ ASSERT_EQ (map.get_state (x_sval, ext_state), 0);
- map.impl_set_state (sid_x, 42, sid_z);
- ASSERT_EQ (map.get_state (sid_x), 42);
- ASSERT_EQ (map.get_origin (sid_x), sid_z);
- ASSERT_EQ (map.get_state (sid_y), 0);
+ map.impl_set_state (x_sval, 42, z_sval, ext_state);
+ ASSERT_EQ (map.get_state (x_sval, ext_state), 42);
+ ASSERT_EQ (map.get_origin (x_sval, ext_state), z_sval);
+ ASSERT_EQ (map.get_state (y_sval, ext_state), 0);
ASSERT_FALSE (map.is_empty_p ());
- map.impl_set_state (sid_y, 0, sid_z);
- ASSERT_EQ (map.get_state (sid_y), 0);
+ map.impl_set_state (y_sval, 0, z_sval, ext_state);
+ ASSERT_EQ (map.get_state (y_sval, ext_state), 0);
- map.impl_set_state (sid_x, 0, sid_z);
- ASSERT_EQ (map.get_state (sid_x), 0);
+ map.impl_set_state (x_sval, 0, z_sval, ext_state);
+ ASSERT_EQ (map.get_state (x_sval, ext_state), 0);
ASSERT_TRUE (map.is_empty_p ());
}
/* Test setting states via equivalence classes. */
{
- region_model model;
- svalue_id sid_x = model.get_rvalue (x, NULL);
- svalue_id sid_y = model.get_rvalue (y, NULL);
- svalue_id sid_z = model.get_rvalue (z, NULL);
+ region_model_manager mgr;
+ region_model model (&mgr);
+ const svalue *x_sval = model.get_rvalue (x, NULL);
+ const svalue *y_sval = model.get_rvalue (y, NULL);
+ const svalue *z_sval = model.get_rvalue (z, NULL);
- sm_state_map map;
+ sm_state_map map (*sm, 0);
ASSERT_TRUE (map.is_empty_p ());
- ASSERT_EQ (map.get_state (sid_x), 0);
- ASSERT_EQ (map.get_state (sid_y), 0);
+ ASSERT_EQ (map.get_state (x_sval, ext_state), 0);
+ ASSERT_EQ (map.get_state (y_sval, ext_state), 0);
model.add_constraint (x, EQ_EXPR, y, NULL);
/* Setting x to a state should also update y, as they
are in the same equivalence class. */
- map.set_state (&model, sid_x, 5, sid_z);
- ASSERT_EQ (map.get_state (sid_x), 5);
- ASSERT_EQ (map.get_state (sid_y), 5);
- ASSERT_EQ (map.get_origin (sid_x), sid_z);
- ASSERT_EQ (map.get_origin (sid_y), sid_z);
+ map.set_state (&model, x_sval, 5, z_sval, ext_state);
+ ASSERT_EQ (map.get_state (x_sval, ext_state), 5);
+ ASSERT_EQ (map.get_state (y_sval, ext_state), 5);
+ ASSERT_EQ (map.get_origin (x_sval, ext_state), z_sval);
+ ASSERT_EQ (map.get_origin (y_sval, ext_state), z_sval);
}
/* Test equality and hashing. */
{
- region_model model;
- svalue_id sid_y = model.get_rvalue (y, NULL);
- svalue_id sid_z = model.get_rvalue (z, NULL);
+ region_model_manager mgr;
+ region_model model (&mgr);
+ const svalue *y_sval = model.get_rvalue (y, NULL);
+ const svalue *z_sval = model.get_rvalue (z, NULL);
- sm_state_map map0;
- sm_state_map map1;
- sm_state_map map2;
+ sm_state_map map0 (*sm, 0);
+ sm_state_map map1 (*sm, 0);
+ sm_state_map map2 (*sm, 0);
ASSERT_EQ (map0.hash (), map1.hash ());
ASSERT_EQ (map0, map1);
- map1.impl_set_state (sid_y, 5, sid_z);
+ map1.impl_set_state (y_sval, 5, z_sval, ext_state);
ASSERT_NE (map0.hash (), map1.hash ());
ASSERT_NE (map0, map1);
/* Make the same change to map2. */
- map2.impl_set_state (sid_y, 5, sid_z);
+ map2.impl_set_state (y_sval, 5, z_sval, ext_state);
ASSERT_EQ (map1.hash (), map2.hash ());
ASSERT_EQ (map1, map2);
}
/* Equality and hashing shouldn't depend on ordering. */
{
- sm_state_map map0;
- sm_state_map map1;
- sm_state_map map2;
+ sm_state_map map0 (*sm, 0);
+ sm_state_map map1 (*sm, 0);
+ sm_state_map map2 (*sm, 0);
ASSERT_EQ (map0.hash (), map1.hash ());
ASSERT_EQ (map0, map1);
- map1.impl_set_state (svalue_id::from_int (14), 2, svalue_id::null ());
- map1.impl_set_state (svalue_id::from_int (16), 3, svalue_id::null ());
- map1.impl_set_state (svalue_id::from_int (1), 2, svalue_id::null ());
- map1.impl_set_state (svalue_id::from_int (9), 2, svalue_id::null ());
+ region_model_manager mgr;
+ region_model model (&mgr);
+ const svalue *x_sval = model.get_rvalue (x, NULL);
+ const svalue *y_sval = model.get_rvalue (y, NULL);
+ const svalue *z_sval = model.get_rvalue (z, NULL);
- map2.impl_set_state (svalue_id::from_int (1), 2, svalue_id::null ());
- map2.impl_set_state (svalue_id::from_int (16), 3, svalue_id::null ());
- map2.impl_set_state (svalue_id::from_int (14), 2, svalue_id::null ());
- map2.impl_set_state (svalue_id::from_int (9), 2, svalue_id::null ());
+ map1.impl_set_state (x_sval, 2, NULL, ext_state);
+ map1.impl_set_state (y_sval, 3, NULL, ext_state);
+ map1.impl_set_state (z_sval, 2, NULL, ext_state);
+
+ map2.impl_set_state (z_sval, 2, NULL, ext_state);
+ map2.impl_set_state (y_sval, 3, NULL, ext_state);
+ map2.impl_set_state (x_sval, 2, NULL, ext_state);
ASSERT_EQ (map1.hash (), map2.hash ());
ASSERT_EQ (map1, map2);
}
- /* Test sm_state_map::remap_svalue_ids. */
- {
- sm_state_map map;
- svalue_id sid_0 = svalue_id::from_int (0);
- svalue_id sid_1 = svalue_id::from_int (1);
- svalue_id sid_2 = svalue_id::from_int (2);
-
- map.impl_set_state (sid_0, 42, sid_2);
- ASSERT_EQ (map.get_state (sid_0), 42);
- ASSERT_EQ (map.get_origin (sid_0), sid_2);
- ASSERT_EQ (map.get_state (sid_1), 0);
- ASSERT_EQ (map.get_state (sid_2), 0);
-
- /* Apply a remapping to the IDs. */
- svalue_id_map remapping (3);
- remapping.put (sid_0, sid_1);
- remapping.put (sid_1, sid_2);
- remapping.put (sid_2, sid_0);
- map.remap_svalue_ids (remapping);
-
- /* Verify that the IDs have been remapped. */
- ASSERT_EQ (map.get_state (sid_1), 42);
- ASSERT_EQ (map.get_origin (sid_1), sid_0);
- ASSERT_EQ (map.get_state (sid_2), 0);
- ASSERT_EQ (map.get_state (sid_0), 0);
- }
-
// TODO: coverage for purging
}
-/* Verify that program_state::dump_to_pp works as expected. */
+/* Check program_state works as expected. */
static void
-test_program_state_dumping ()
+test_program_state_1 ()
{
/* Create a program_state for a global ptr "p" that has
malloc sm-state, pointing to a region on the heap. */
@@ -1432,83 +1173,44 @@ test_program_state_dumping ()
= sm->get_state_by_name ("unchecked");
auto_delete_vec <state_machine> checkers;
checkers.safe_push (sm);
- extrinsic_state ext_state (checkers);
+ engine eng;
+ extrinsic_state ext_state (checkers, NULL, &eng);
+ region_model_manager *mgr = eng.get_model_manager ();
program_state s (ext_state);
region_model *model = s.m_region_model;
- region_id new_rid = model->add_new_malloc_region ();
- svalue_id ptr_sid
- = model->get_or_create_ptr_svalue (ptr_type_node, new_rid);
+ const svalue *size_in_bytes
+ = mgr->get_or_create_unknown_svalue (integer_type_node);
+ const region *new_reg = model->create_region_for_heap_alloc (size_in_bytes);
+ const svalue *ptr_sval = mgr->get_ptr_svalue (ptr_type_node, new_reg);
model->set_value (model->get_lvalue (p, NULL),
- ptr_sid, NULL);
+ ptr_sval, NULL);
sm_state_map *smap = s.m_checker_states[0];
- smap->impl_set_state (ptr_sid, UNCHECKED_STATE, svalue_id::null ());
- ASSERT_EQ (smap->get_state (ptr_sid), UNCHECKED_STATE);
-
- ASSERT_DUMP_EQ
- (s, ext_state, false,
- "rmodel: r0: {kind: `root', parent: null, sval: null}\n"
- "|-heap: r1: {kind: `heap', parent: r0, sval: null}\n"
- "| `-r2: {kind: `symbolic', parent: r1, sval: null, possibly_null: true}\n"
- "`-globals: r3: {kind: `globals', parent: r0, sval: null, map: {`p': r4}}\n"
- " `-`p': r4: {kind: `primitive', parent: r3, sval: sv0, type: `void *'}\n"
- " |: sval: sv0: {type: `void *', &r2}\n"
- " |: type: `void *'\n"
- "svalues:\n"
- " sv0: {type: `void *', &r2}\n"
- "constraint manager:\n"
- " equiv classes:\n"
- " constraints:\n"
- "malloc: {sv0: unchecked (`p')}\n");
-
- ASSERT_DUMP_EQ (s, ext_state, true,
- "rmodel: p: &r2 malloc: {sv0: unchecked (`p')}");
+ smap->impl_set_state (ptr_sval, UNCHECKED_STATE, NULL, ext_state);
+ ASSERT_EQ (smap->get_state (ptr_sval, ext_state), UNCHECKED_STATE);
}
-/* Verify that program_state::dump_to_pp works for string literals. */
+/* Check that program_state works for string literals. */
static void
-test_program_state_dumping_2 ()
+test_program_state_2 ()
{
- /* Create a program_state for a global ptr "p" that points to
- a string constant. */
+ /* Create a program_state for a global ptr "p" that points to
+ a string constant. */
tree p = build_global_decl ("p", ptr_type_node);
tree string_cst_ptr = build_string_literal (4, "foo");
auto_delete_vec <state_machine> checkers;
- extrinsic_state ext_state (checkers);
+ engine eng;
+ extrinsic_state ext_state (checkers, NULL, &eng);
program_state s (ext_state);
region_model *model = s.m_region_model;
- region_id p_rid = model->get_lvalue (p, NULL);
- svalue_id str_sid = model->get_rvalue (string_cst_ptr, NULL);
- model->set_value (p_rid, str_sid, NULL);
-
- ASSERT_DUMP_EQ
- (s, ext_state, false,
- "rmodel: r0: {kind: `root', parent: null, sval: null}\n"
- "|-globals: r1: {kind: `globals', parent: r0, sval: null, map: {`p': r2}}\n"
- "| `-`p': r2: {kind: `primitive', parent: r1, sval: sv3, type: `void *'}\n"
- "| |: sval: sv3: {type: `void *', &r4}\n"
- "| |: type: `void *'\n"
- "`-r3: {kind: `array', parent: r0, sval: sv0, type: `const char[4]', array: {[0]: r4}}\n"
- " |: sval: sv0: {type: `const char[4]', `\"foo\"'}\n"
- " |: type: `const char[4]'\n"
- " `-[0]: r4: {kind: `primitive', parent: r3, sval: null, type: `const char'}\n"
- " |: type: `const char'\n"
- "svalues:\n"
- " sv0: {type: `const char[4]', `\"foo\"'}\n"
- " sv1: {type: `int', `0'}\n"
- " sv2: {type: `const char *', &r4}\n"
- " sv3: {type: `void *', &r4}\n"
- "constraint manager:\n"
- " equiv classes:\n"
- " constraints:\n");
-
- ASSERT_DUMP_EQ (s, ext_state, true,
- "rmodel: p: &\"foo\"[0]");
+ const region *p_reg = model->get_lvalue (p, NULL);
+ const svalue *str_sval = model->get_rvalue (string_cst_ptr, NULL);
+ model->set_value (p_reg, str_sval, NULL);
}
/* Verify that program_states with identical sm-state can be merged,
@@ -1521,28 +1223,33 @@ test_program_state_merging ()
malloc sm-state, pointing to a region on the heap. */
tree p = build_global_decl ("p", ptr_type_node);
+ program_point point (program_point::origin ());
auto_delete_vec <state_machine> checkers;
checkers.safe_push (make_malloc_state_machine (NULL));
- extrinsic_state ext_state (checkers);
+ engine eng;
+ extrinsic_state ext_state (checkers, NULL, &eng);
+ region_model_manager *mgr = eng.get_model_manager ();
program_state s0 (ext_state);
- impl_region_model_context ctxt (&s0, NULL, ext_state);
+ impl_region_model_context ctxt (&s0, ext_state);
region_model *model0 = s0.m_region_model;
- region_id new_rid = model0->add_new_malloc_region ();
- svalue_id ptr_sid
- = model0->get_or_create_ptr_svalue (ptr_type_node, new_rid);
+ const svalue *size_in_bytes
+ = mgr->get_or_create_unknown_svalue (integer_type_node);
+ const region *new_reg = model0->create_region_for_heap_alloc (size_in_bytes);
+ const svalue *ptr_sval = mgr->get_ptr_svalue (ptr_type_node, new_reg);
model0->set_value (model0->get_lvalue (p, &ctxt),
- ptr_sid, &ctxt);
+ ptr_sval, &ctxt);
sm_state_map *smap = s0.m_checker_states[0];
const state_machine::state_t TEST_STATE = 3;
- smap->impl_set_state (ptr_sid, TEST_STATE, svalue_id::null ());
- ASSERT_EQ (smap->get_state (ptr_sid), TEST_STATE);
+ smap->impl_set_state (ptr_sval, TEST_STATE, NULL, ext_state);
+ ASSERT_EQ (smap->get_state (ptr_sval, ext_state), TEST_STATE);
- model0->canonicalize (&ctxt);
+ model0->canonicalize ();
/* Verify that canonicalization preserves sm-state. */
- ASSERT_EQ (smap->get_state (model0->get_rvalue (p, NULL)), TEST_STATE);
+ ASSERT_EQ (smap->get_state (model0->get_rvalue (p, NULL), ext_state),
+ TEST_STATE);
/* Make a copy of the program_state. */
program_state s1 (s0);
@@ -1552,22 +1259,23 @@ test_program_state_merging ()
with the given sm-state.
They ought to be mergeable, preserving the sm-state. */
program_state merged (ext_state);
- ASSERT_TRUE (s0.can_merge_with_p (s1, ext_state, &merged));
+ ASSERT_TRUE (s0.can_merge_with_p (s1, point, &merged));
merged.validate (ext_state);
/* Verify that the merged state has the sm-state for "p". */
region_model *merged_model = merged.m_region_model;
sm_state_map *merged_smap = merged.m_checker_states[0];
- ASSERT_EQ (merged_smap->get_state (merged_model->get_rvalue (p, NULL)),
+ ASSERT_EQ (merged_smap->get_state (merged_model->get_rvalue (p, NULL),
+ ext_state),
TEST_STATE);
/* Try canonicalizing. */
- impl_region_model_context merged_ctxt (&merged, NULL, ext_state);
- merged.m_region_model->canonicalize (&merged_ctxt);
+ merged.m_region_model->canonicalize ();
merged.validate (ext_state);
/* Verify that the merged state still has the sm-state for "p". */
- ASSERT_EQ (merged_smap->get_state (merged_model->get_rvalue (p, NULL)),
+ ASSERT_EQ (merged_smap->get_state (merged_model->get_rvalue (p, NULL),
+ ext_state),
TEST_STATE);
/* After canonicalization, we ought to have equality with the inputs. */
@@ -1580,6 +1288,7 @@ test_program_state_merging ()
static void
test_program_state_merging_2 ()
{
+ program_point point (program_point::origin ());
auto_delete_vec <state_machine> checkers;
checkers.safe_push (make_signal_state_machine (NULL));
extrinsic_state ext_state (checkers);
@@ -1604,7 +1313,7 @@ test_program_state_merging_2 ()
/* They ought to not be mergeable. */
program_state merged (ext_state);
- ASSERT_FALSE (s0.can_merge_with_p (s1, ext_state, &merged));
+ ASSERT_FALSE (s0.can_merge_with_p (s1, point, &merged));
}
/* Run all of the selftests within this file. */
@@ -1613,8 +1322,8 @@ void
analyzer_program_state_cc_tests ()
{
test_sm_state_map ();
- test_program_state_dumping ();
- test_program_state_dumping_2 ();
+ test_program_state_1 ();
+ test_program_state_2 ();
test_program_state_merging ();
test_program_state_merging_2 ();
}