SSA trace: don't concretise steps that will subsequently be dropped

Previously the SSA trace did all concretisation (taking the solver's output
and turning it into concrete expressions to be output in the result trace) before
sorting the steps by time and determining what belongs in the final trace. This
was generally harmless, but could result in much wasted time and potentially
memory exhaustion when concretising very large arrays and strings.

Therefore, we now perform the time-order sort and figure out which steps are
to be kept first, *then* concretise them.
This commit is contained in:
Chris Smowton 2018-07-25 12:02:18 +01:00
parent f3630f0b6a
commit 501546ae4d
5 changed files with 170 additions and 134 deletions

View File

@ -41,10 +41,7 @@ void bmc_all_propertiest::goal_covered(const cover_goalst::goalt &)
if(solver.l_get(cond).is_false())
{
g.second.status=goalt::statust::FAILURE;
symex_target_equationt::SSA_stepst::iterator next=c;
next++; // include the assertion
build_goto_trace(bmc.equation, next, solver, bmc.ns,
g.second.goto_trace);
build_goto_trace(bmc.equation, c, solver, bmc.ns, g.second.goto_trace);
break;
}
}

View File

@ -144,6 +144,13 @@ protected:
bmct &bmc;
};
static bool is_failed_assumption_step(
symex_target_equationt::SSA_stepst::const_iterator step,
const prop_convt &prop_conv)
{
return step->is_assume() && prop_conv.l_get(step->cond_literal).is_false();
}
void bmc_covert::satisfying_assignment()
{
tests.push_back(testt());
@ -173,21 +180,8 @@ void bmc_covert::satisfying_assignment()
}
}
build_goto_trace(bmc.equation, bmc.equation.SSA_steps.end(),
solver, bmc.ns, test.goto_trace);
goto_tracet &goto_trace=test.goto_trace;
// Now delete anything after first failed assumption
for(goto_tracet::stepst::iterator
s_it1=goto_trace.steps.begin();
s_it1!=goto_trace.steps.end();
s_it1++)
if(s_it1->is_assume() && !s_it1->cond_value)
{
goto_trace.steps.erase(++s_it1, goto_trace.steps.end());
break;
}
build_goto_trace(
bmc.equation, is_failed_assumption_step, solver, bmc.ns, test.goto_trace);
}
bool bmc_covert::operator()()

View File

@ -336,10 +336,8 @@ void fault_localizationt::goal_covered(
if(solver.l_get(cond).is_false())
{
goal_pair.second.status=goalt::statust::FAILURE;
symex_target_equationt::SSA_stepst::iterator next=inst;
next++; // include the assertion
build_goto_trace(
bmc.equation, next, solver, bmc.ns, goal_pair.second.goto_trace);
bmc.equation, inst, solver, bmc.ns, goal_pair.second.goto_trace);
// localize faults
run(goal_pair.first);

View File

@ -164,7 +164,7 @@ void update_internal_field(
void build_goto_trace(
const symex_target_equationt &target,
symex_target_equationt::SSA_stepst::const_iterator end_step,
ssa_step_predicatet is_last_step_to_keep,
const prop_convt &prop_conv,
const namespacet &ns,
goto_tracet &goto_trace)
@ -173,21 +173,28 @@ void build_goto_trace(
// Furthermore, read-events need to occur before write
// events with the same clock.
typedef std::map<mp_integer, goto_tracet::stepst> time_mapt;
typedef symex_target_equationt::SSA_stepst::const_iterator ssa_step_iteratort;
typedef std::map<mp_integer, std::vector<ssa_step_iteratort>> time_mapt;
time_mapt time_map;
mp_integer current_time=0;
const goto_trace_stept *end_ptr=nullptr;
bool end_step_seen=false;
ssa_step_iteratort last_step_to_keep = target.SSA_steps.end();
bool last_step_was_kept = false;
for(symex_target_equationt::SSA_stepst::const_iterator
it=target.SSA_steps.begin();
it!=target.SSA_steps.end();
// First sort the SSA steps by time, in the process dropping steps
// we definitely don't want to retain in the final trace:
for(ssa_step_iteratort it = target.SSA_steps.begin();
it != target.SSA_steps.end();
it++)
{
if(it==end_step)
end_step_seen=true;
if(
last_step_to_keep == target.SSA_steps.end() &&
is_last_step_to_keep(it, prop_conv))
{
last_step_to_keep = it;
}
const symex_target_equationt::SSA_stept &SSA_step=*it;
@ -227,12 +234,21 @@ void build_goto_trace(
if(time_before<0)
{
time_mapt::iterator entry=
time_map.insert(std::make_pair(
current_time,
goto_tracet::stepst())).first;
entry->second.splice(entry->second.end(), time_map[time_before]);
time_map.erase(time_before);
time_mapt::const_iterator time_before_steps_it =
time_map.find(time_before);
if(time_before_steps_it != time_map.end())
{
std::vector<ssa_step_iteratort> &current_time_steps =
time_map[current_time];
current_time_steps.insert(
current_time_steps.end(),
time_before_steps_it->second.begin(),
time_before_steps_it->second.end());
time_map.erase(time_before_steps_it);
}
}
continue;
@ -248,97 +264,128 @@ void build_goto_trace(
continue;
}
goto_tracet::stepst &steps=time_map[current_time];
steps.push_back(goto_trace_stept());
goto_trace_stept &goto_trace_step=steps.back();
if(!end_step_seen)
end_ptr=&goto_trace_step;
goto_trace_step.thread_nr=SSA_step.source.thread_nr;
goto_trace_step.pc=SSA_step.source.pc;
goto_trace_step.comment=SSA_step.comment;
if(SSA_step.ssa_lhs.is_not_nil())
goto_trace_step.lhs_object=
ssa_exprt(SSA_step.ssa_lhs.get_original_expr());
else
goto_trace_step.lhs_object.make_nil();
goto_trace_step.type=SSA_step.type;
goto_trace_step.hidden=SSA_step.hidden;
goto_trace_step.format_string=SSA_step.format_string;
goto_trace_step.io_id=SSA_step.io_id;
goto_trace_step.formatted=SSA_step.formatted;
goto_trace_step.identifier=SSA_step.identifier;
// update internal field for specific variables in the counterexample
update_internal_field(SSA_step, goto_trace_step, ns);
goto_trace_step.assignment_type=
(it->is_assignment()&&
(SSA_step.assignment_type==
symex_targett::assignment_typet::VISIBLE_ACTUAL_PARAMETER ||
SSA_step.assignment_type==
symex_targett::assignment_typet::HIDDEN_ACTUAL_PARAMETER))?
goto_trace_stept::assignment_typet::ACTUAL_PARAMETER:
goto_trace_stept::assignment_typet::STATE;
if(SSA_step.original_full_lhs.is_not_nil())
goto_trace_step.full_lhs=
build_full_lhs_rec(
prop_conv, ns, SSA_step.original_full_lhs, SSA_step.ssa_full_lhs);
if(SSA_step.ssa_lhs.is_not_nil())
goto_trace_step.lhs_object_value=prop_conv.get(SSA_step.ssa_lhs);
if(SSA_step.ssa_full_lhs.is_not_nil())
if(it == last_step_to_keep)
{
goto_trace_step.full_lhs_value=prop_conv.get(SSA_step.ssa_full_lhs);
simplify(goto_trace_step.full_lhs_value, ns);
last_step_was_kept = true;
}
for(const auto &j : SSA_step.converted_io_args)
{
if(j.is_constant() ||
j.id()==ID_string_constant)
goto_trace_step.io_args.push_back(j);
else
{
exprt tmp=prop_conv.get(j);
goto_trace_step.io_args.push_back(tmp);
}
}
if(SSA_step.is_assert() ||
SSA_step.is_assume() ||
SSA_step.is_goto())
{
goto_trace_step.cond_expr=SSA_step.cond_expr;
goto_trace_step.cond_value=
prop_conv.l_get(SSA_step.cond_literal).is_true();
}
time_map[current_time].push_back(it);
}
// Now assemble into a single goto_trace.
// This exploits sorted-ness of the map.
for(auto &t_it : time_map)
goto_trace.steps.splice(goto_trace.steps.end(), t_it.second);
INVARIANT(
last_step_to_keep == target.SSA_steps.end() || last_step_was_kept,
"last step in SSA trace to keep must not be filtered out as a sync "
"instruction, not-taken branch, PHI node, or similar");
// cut off the trace at the desired end
for(goto_tracet::stepst::iterator
s_it1=goto_trace.steps.begin();
s_it1!=goto_trace.steps.end();
++s_it1)
if(end_step_seen && end_ptr==&(*s_it1))
{
goto_trace.trim_after(s_it1);
break;
}
// Now build the GOTO trace, ordered by time, then by SSA trace order.
// produce the step numbers
unsigned step_nr=0;
unsigned step_nr = 0;
for(auto &s_it : goto_trace.steps)
s_it.step_nr=++step_nr;
for(const auto &time_and_ssa_steps : time_map)
{
for(const auto ssa_step_it : time_and_ssa_steps.second)
{
const auto &SSA_step = *ssa_step_it;
goto_trace.steps.push_back(goto_trace_stept());
goto_trace_stept &goto_trace_step = goto_trace.steps.back();
goto_trace_step.step_nr = ++step_nr;
goto_trace_step.thread_nr = SSA_step.source.thread_nr;
goto_trace_step.pc = SSA_step.source.pc;
goto_trace_step.comment = SSA_step.comment;
if(SSA_step.ssa_lhs.is_not_nil())
{
goto_trace_step.lhs_object =
ssa_exprt(SSA_step.ssa_lhs.get_original_expr());
}
else
{
goto_trace_step.lhs_object.make_nil();
}
goto_trace_step.type = SSA_step.type;
goto_trace_step.hidden = SSA_step.hidden;
goto_trace_step.format_string = SSA_step.format_string;
goto_trace_step.io_id = SSA_step.io_id;
goto_trace_step.formatted = SSA_step.formatted;
goto_trace_step.identifier = SSA_step.identifier;
// update internal field for specific variables in the counterexample
update_internal_field(SSA_step, goto_trace_step, ns);
goto_trace_step.assignment_type =
(SSA_step.is_assignment() &&
(SSA_step.assignment_type ==
symex_targett::assignment_typet::VISIBLE_ACTUAL_PARAMETER ||
SSA_step.assignment_type ==
symex_targett::assignment_typet::HIDDEN_ACTUAL_PARAMETER))
? goto_trace_stept::assignment_typet::ACTUAL_PARAMETER
: goto_trace_stept::assignment_typet::STATE;
if(SSA_step.original_full_lhs.is_not_nil())
{
goto_trace_step.full_lhs = build_full_lhs_rec(
prop_conv, ns, SSA_step.original_full_lhs, SSA_step.ssa_full_lhs);
}
if(SSA_step.ssa_lhs.is_not_nil())
goto_trace_step.lhs_object_value = prop_conv.get(SSA_step.ssa_lhs);
if(SSA_step.ssa_full_lhs.is_not_nil())
{
goto_trace_step.full_lhs_value = prop_conv.get(SSA_step.ssa_full_lhs);
simplify(goto_trace_step.full_lhs_value, ns);
}
for(const auto &j : SSA_step.converted_io_args)
{
if(j.is_constant() || j.id() == ID_string_constant)
{
goto_trace_step.io_args.push_back(j);
}
else
{
exprt tmp = prop_conv.get(j);
goto_trace_step.io_args.push_back(tmp);
}
}
if(SSA_step.is_assert() || SSA_step.is_assume() || SSA_step.is_goto())
{
goto_trace_step.cond_expr = SSA_step.cond_expr;
goto_trace_step.cond_value =
prop_conv.l_get(SSA_step.cond_literal).is_true();
}
if(ssa_step_it == last_step_to_keep)
return;
}
}
}
void build_goto_trace(
const symex_target_equationt &target,
symex_target_equationt::SSA_stepst::const_iterator last_step_to_keep,
const prop_convt &prop_conv,
const namespacet &ns,
goto_tracet &goto_trace)
{
const auto is_last_step_to_keep = [last_step_to_keep](
symex_target_equationt::SSA_stepst::const_iterator it, const prop_convt &) {
return last_step_to_keep == it;
};
return build_goto_trace(
target, is_last_step_to_keep, prop_conv, ns, goto_trace);
}
static bool is_failed_assertion_step(
symex_target_equationt::SSA_stepst::const_iterator step,
const prop_convt &prop_conv)
{
return step->is_assert() && prop_conv.l_get(step->cond_literal).is_false();
}
void build_goto_trace(
@ -347,17 +394,5 @@ void build_goto_trace(
const namespacet &ns,
goto_tracet &goto_trace)
{
build_goto_trace(
target, target.SSA_steps.end(), prop_conv, ns, goto_trace);
// Now delete anything after first failed assertion
for(goto_tracet::stepst::iterator
s_it1=goto_trace.steps.begin();
s_it1!=goto_trace.steps.end();
s_it1++)
if(s_it1->is_assert() && !s_it1->cond_value)
{
goto_trace.trim_after(s_it1);
break;
}
build_goto_trace(target, is_failed_assertion_step, prop_conv, ns, goto_trace);
}

View File

@ -24,10 +24,22 @@ void build_goto_trace(
const namespacet &ns,
goto_tracet &goto_trace);
// builds a trace that stops after given step
// builds a trace that stops after the given step
void build_goto_trace(
const symex_target_equationt &target,
symex_target_equationt::SSA_stepst::const_iterator stop,
symex_target_equationt::SSA_stepst::const_iterator last_step_to_keep,
const prop_convt &prop_conv,
const namespacet &ns,
goto_tracet &goto_trace);
typedef std::function<
bool(symex_target_equationt::SSA_stepst::const_iterator, const prop_convt &)>
ssa_step_predicatet;
// builds a trace that stops after the step matching a given condition
void build_goto_trace(
const symex_target_equationt &target,
ssa_step_predicatet stop_after_predicate,
const prop_convt &prop_conv,
const namespacet &ns,
goto_tracet &goto_trace);