CBMC
build_goto_trace.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Traces of GOTO Programs
4 
5 Author: Daniel Kroening
6 
7  Date: July 2005
8 
9 \*******************************************************************/
10 
13 
14 #include "build_goto_trace.h"
15 
16 #include <util/arith_tools.h>
17 #include <util/byte_operators.h>
18 #include <util/namespace.h>
19 #include <util/simplify_expr.h>
20 #include <util/symbol.h>
21 
24 
26 
28 
30  const decision_proceduret &decision_procedure,
31  const namespacet &ns,
32  const exprt &src_original, // original identifiers
33  const exprt &src_ssa) // renamed identifiers
34 {
35  if(src_ssa.id()!=src_original.id())
36  return src_original;
37 
38  const irep_idt id=src_original.id();
39 
40  if(id==ID_index)
41  {
42  // get index value from src_ssa
43  exprt index_value = decision_procedure.get(to_index_expr(src_ssa).index());
44 
45  if(index_value.is_not_nil())
46  {
47  simplify(index_value, ns);
48  index_exprt tmp=to_index_expr(src_original);
49  tmp.index()=index_value;
50  tmp.array() = build_full_lhs_rec(
51  decision_procedure,
52  ns,
53  to_index_expr(src_original).array(),
54  to_index_expr(src_ssa).array());
55  return std::move(tmp);
56  }
57 
58  return src_original;
59  }
60  else if(id==ID_member)
61  {
62  member_exprt tmp=to_member_expr(src_original);
64  decision_procedure,
65  ns,
66  to_member_expr(src_original).struct_op(),
67  to_member_expr(src_ssa).struct_op());
68  }
69  else if(id==ID_if)
70  {
71  if_exprt tmp2=to_if_expr(src_original);
72 
74  decision_procedure,
75  ns,
76  tmp2.false_case(),
77  to_if_expr(src_ssa).false_case());
78 
80  decision_procedure,
81  ns,
82  tmp2.true_case(),
83  to_if_expr(src_ssa).true_case());
84 
85  exprt tmp = decision_procedure.get(to_if_expr(src_ssa).cond());
86 
87  if(tmp.is_true())
88  return tmp2.true_case();
89  else if(tmp.is_false())
90  return tmp2.false_case();
91  else
92  return std::move(tmp2);
93  }
94  else if(id==ID_typecast)
95  {
96  typecast_exprt tmp=to_typecast_expr(src_original);
97  tmp.op() = build_full_lhs_rec(
98  decision_procedure,
99  ns,
100  to_typecast_expr(src_original).op(),
101  to_typecast_expr(src_ssa).op());
102  return std::move(tmp);
103  }
104  else if(id==ID_byte_extract_little_endian ||
105  id==ID_byte_extract_big_endian)
106  {
107  byte_extract_exprt tmp = to_byte_extract_expr(src_original);
108  tmp.op() = build_full_lhs_rec(
109  decision_procedure, ns, tmp.op(), to_byte_extract_expr(src_ssa).op());
110 
111  // re-write into big case-split
112  }
113 
114  return src_original;
115 }
116 
120  const exprt &expr,
121  goto_trace_stept &goto_trace_step,
122  const namespacet &ns)
123 {
124  if(expr.id()==ID_symbol)
125  {
126  const auto &type = expr.type();
127  if(type.id() != ID_code && type.id() != ID_mathematical_function)
128  {
129  const irep_idt &id = to_ssa_expr(expr).get_original_name();
130  const symbolt *symbol;
131  if(!ns.lookup(id, symbol))
132  {
133  bool result = symbol->type.get_bool(ID_C_dynamic);
134  if(result)
135  goto_trace_step.internal = true;
136  }
137  }
138  }
139  else
140  {
141  for(const auto &op : expr.operands())
142  set_internal_dynamic_object(op, goto_trace_step, ns);
143  }
144 }
145 
149  const SSA_stept &SSA_step,
150  goto_trace_stept &goto_trace_step,
151  const namespacet &ns)
152 {
153  // set internal for dynamic_object in both lhs and rhs expressions
154  set_internal_dynamic_object(SSA_step.ssa_lhs, goto_trace_step, ns);
155  set_internal_dynamic_object(SSA_step.ssa_rhs, goto_trace_step, ns);
156 
157  // set internal field to CPROVER functions (e.g., __CPROVER_initialize)
158  if(SSA_step.is_function_call())
159  {
160  if(SSA_step.source.pc->source_location().as_string().empty())
161  goto_trace_step.internal=true;
162  }
163 
164  // set internal field to input and output steps
165  if(goto_trace_step.type==goto_trace_stept::typet::OUTPUT ||
166  goto_trace_step.type==goto_trace_stept::typet::INPUT)
167  {
168  goto_trace_step.internal=true;
169  }
170 
171  // set internal field to _start function-return step
173  {
174  // "__CPROVER_*" function calls in __CPROVER_start are already marked as
175  // internal. Don't mark any other function calls (i.e. "main"), function
176  // arguments or any other parts of a code_function_callt as internal.
177  if(SSA_step.source.pc->code().get_statement() != ID_function_call)
178  goto_trace_step.internal=true;
179  }
180 }
181 
184 static void
186 {
187  if(type.id() == ID_array)
188  {
189  array_typet &array_type = to_array_type(type);
190  array_type.size() = solver.get(array_type.size());
191  }
192 
193  if(type.has_subtype())
195 }
196 
199 static void
201 {
203  for(auto &sub : expr.operands())
205 }
206 
208  const symex_target_equationt &target,
209  ssa_step_predicatet is_last_step_to_keep,
210  const decision_proceduret &decision_procedure,
211  const namespacet &ns,
212  goto_tracet &goto_trace)
213 {
214  // We need to re-sort the steps according to their clock.
215  // Furthermore, read-events need to occur before write
216  // events with the same clock.
217 
218  typedef symex_target_equationt::SSA_stepst::const_iterator ssa_step_iteratort;
219  typedef std::map<mp_integer, std::vector<ssa_step_iteratort>> time_mapt;
220  time_mapt time_map;
221 
222  mp_integer current_time=0;
223  const bool has_threads = target.has_threads();
224 
225  ssa_step_iteratort last_step_to_keep = target.SSA_steps.end();
226  bool last_step_was_kept = false;
227 
228  // First sort the SSA steps by time, in the process dropping steps
229  // we definitely don't want to retain in the final trace:
230 
231  for(ssa_step_iteratort it = target.SSA_steps.begin();
232  it != target.SSA_steps.end();
233  it++)
234  {
235  if(
236  last_step_to_keep == target.SSA_steps.end() &&
237  is_last_step_to_keep(it, decision_procedure))
238  {
239  last_step_to_keep = it;
240  }
241 
242  const SSA_stept &SSA_step = *it;
243 
244  if(!decision_procedure.get(SSA_step.guard_handle).is_true())
245  continue;
246 
247  if(it->is_constraint() ||
248  it->is_spawn())
249  continue;
250  else if(it->is_atomic_begin())
251  {
252  // for atomic sections the timing can only be determined once we see
253  // a shared read or write (if there is none, the time will be
254  // reverted to the time before entering the atomic section); we thus
255  // use a temporary negative time slot to gather all events
256  current_time*=-1;
257  continue;
258  }
259  else if(it->is_shared_read() || it->is_shared_write() ||
260  it->is_atomic_end())
261  {
262  if(!has_threads)
263  continue;
264 
265  mp_integer time_before=current_time;
266 
267  if(it->is_shared_read() || it->is_shared_write())
268  {
269  // these are just used to get the time stamp -- the clock type is
270  // computed to be of the minimal necessary size, but we don't need to
271  // know it to get the value so just use typeless
272  exprt clock_value = decision_procedure.get(
274 
275  const auto cv = numeric_cast<mp_integer>(clock_value);
276  if(cv.has_value())
277  current_time = *cv;
278  else
279  current_time = 0;
280  }
281  else if(it->is_atomic_end() && current_time<0)
282  current_time*=-1;
283 
284  INVARIANT(current_time >= 0, "time keeping inconsistency");
285  // move any steps gathered in an atomic section
286 
287  if(time_before<0)
288  {
289  time_mapt::const_iterator time_before_steps_it =
290  time_map.find(time_before);
291 
292  if(time_before_steps_it != time_map.end())
293  {
294  std::vector<ssa_step_iteratort> &current_time_steps =
295  time_map[current_time];
296 
297  current_time_steps.insert(
298  current_time_steps.end(),
299  time_before_steps_it->second.begin(),
300  time_before_steps_it->second.end());
301 
302  time_map.erase(time_before_steps_it);
303  }
304  }
305 
306  continue;
307  }
308 
309  // drop PHI and GUARD assignments altogether
310  if(it->is_assignment() &&
311  (SSA_step.assignment_type==
313  SSA_step.assignment_type==
315  {
316  continue;
317  }
318 
319  if(it == last_step_to_keep)
320  {
321  last_step_was_kept = true;
322  }
323 
324  time_map[current_time].push_back(it);
325  }
326 
327  INVARIANT(
328  last_step_to_keep == target.SSA_steps.end() || last_step_was_kept,
329  "last step in SSA trace to keep must not be filtered out as a sync "
330  "instruction, not-taken branch, PHI node, or similar");
331 
332  // Now build the GOTO trace, ordered by time, then by SSA trace order.
333 
334  // produce the step numbers
335  unsigned step_nr = 0;
336 
337  for(const auto &time_and_ssa_steps : time_map)
338  {
339  for(const auto &ssa_step_it : time_and_ssa_steps.second)
340  {
341  const auto &SSA_step = *ssa_step_it;
342  goto_trace.steps.push_back(goto_trace_stept());
343  goto_trace_stept &goto_trace_step = goto_trace.steps.back();
344 
345  goto_trace_step.step_nr = ++step_nr;
346 
347  goto_trace_step.thread_nr = SSA_step.source.thread_nr;
348  goto_trace_step.pc = SSA_step.source.pc;
349  goto_trace_step.function_id = SSA_step.source.function_id;
350  if(SSA_step.is_assert())
351  {
352  goto_trace_step.comment = SSA_step.comment;
353  goto_trace_step.property_id = SSA_step.property_id;
354  }
355  goto_trace_step.type = SSA_step.type;
356  goto_trace_step.hidden = SSA_step.hidden;
357  goto_trace_step.format_string = SSA_step.format_string;
358  goto_trace_step.io_id = SSA_step.io_id;
359  goto_trace_step.formatted = SSA_step.formatted;
360  goto_trace_step.called_function = SSA_step.called_function;
361 
362  for(const auto &arg : SSA_step.converted_function_arguments)
363  {
364  goto_trace_step.function_arguments.push_back(
365  simplify_expr(decision_procedure.get(arg), ns));
366  }
367 
368  // update internal field for specific variables in the counterexample
369  update_internal_field(SSA_step, goto_trace_step, ns);
370 
371  goto_trace_step.assignment_type =
372  (SSA_step.is_assignment() &&
373  (SSA_step.assignment_type ==
375  SSA_step.assignment_type ==
379 
380  if(SSA_step.original_full_lhs.is_not_nil())
381  {
382  goto_trace_step.full_lhs = simplify_expr(
384  decision_procedure,
385  ns,
386  SSA_step.original_full_lhs,
387  SSA_step.ssa_full_lhs),
388  ns);
389  replace_nondet_in_type(goto_trace_step.full_lhs, decision_procedure);
390  restore_union(goto_trace_step.full_lhs, ns);
391  }
392 
393  if(SSA_step.ssa_full_lhs.is_not_nil())
394  {
395  goto_trace_step.full_lhs_value =
396  decision_procedure.get(SSA_step.ssa_full_lhs);
397  simplify(goto_trace_step.full_lhs_value, ns);
399  goto_trace_step.full_lhs_value, decision_procedure);
400  }
401 
402  for(const auto &j : SSA_step.converted_io_args)
403  {
404  goto_trace_step.io_args.push_back(
405  simplify_expr(decision_procedure.get(j), ns));
406  }
407 
408  if(SSA_step.is_assert() || SSA_step.is_assume() || SSA_step.is_goto())
409  {
410  goto_trace_step.cond_expr = SSA_step.cond_expr;
411 
412  goto_trace_step.cond_value =
413  decision_procedure.get(SSA_step.cond_handle).is_true();
414  }
415 
416  if(SSA_step.source.pc->is_assert() || SSA_step.source.pc->is_assume())
417  {
418  goto_trace_step.original_condition = SSA_step.source.pc->condition();
419  restore_union(goto_trace_step.original_condition, ns);
420  }
421 
422  if(ssa_step_it == last_step_to_keep)
423  return;
424  }
425  }
426 }
427 
429  const symex_target_equationt &target,
430  symex_target_equationt::SSA_stepst::const_iterator last_step_to_keep,
431  const decision_proceduret &decision_procedure,
432  const namespacet &ns,
433  goto_tracet &goto_trace)
434 {
435  const auto is_last_step_to_keep =
436  [last_step_to_keep](
437  symex_target_equationt::SSA_stepst::const_iterator it,
438  const decision_proceduret &) { return last_step_to_keep == it; };
439 
440  return build_goto_trace(
441  target, is_last_step_to_keep, decision_procedure, ns, goto_trace);
442 }
443 
445  symex_target_equationt::SSA_stepst::const_iterator step,
446  const decision_proceduret &decision_procedure)
447 {
448  return step->is_assert() &&
449  decision_procedure.get(step->cond_handle).is_false();
450 }
451 
453  const symex_target_equationt &target,
454  const decision_proceduret &decision_procedure,
455  const namespacet &ns,
456  goto_tracet &goto_trace)
457 {
459  target, is_failed_assertion_step, decision_procedure, ns, goto_trace);
460 }
static exprt build_full_lhs_rec(const decision_proceduret &decision_procedure, const namespacet &ns, const exprt &src_original, const exprt &src_ssa)
static void set_internal_dynamic_object(const exprt &expr, goto_trace_stept &goto_trace_step, const namespacet &ns)
set internal field for variable assignment related to dynamic_object[0-9] and dynamic_[0-9]_array.
static void update_internal_field(const SSA_stept &SSA_step, goto_trace_stept &goto_trace_step, const namespacet &ns)
set internal for variables assignments related to dynamic_object and CPROVER internal functions (e....
static bool is_failed_assertion_step(symex_target_equationt::SSA_stepst::const_iterator step, const decision_proceduret &decision_procedure)
static void replace_nondet_in_type(typet &type, const decision_proceduret &solver)
Replace nondet values that appear in type by their values as found by solver.
void build_goto_trace(const symex_target_equationt &target, ssa_step_predicatet is_last_step_to_keep, const decision_proceduret &decision_procedure, const namespacet &ns, goto_tracet &goto_trace)
Build a trace by going through the steps of target and stopping after the step matching a given condi...
Traces of GOTO Programs.
std::function< bool(symex_target_equationt::SSA_stepst::const_iterator, const decision_proceduret &)> ssa_step_predicatet
Expression classes for byte-level operators.
const byte_extract_exprt & to_byte_extract_expr(const exprt &expr)
Single SSA step in the equation.
Definition: ssa_step.h:47
exprt guard_handle
Definition: ssa_step.h:136
symex_targett::assignment_typet assignment_type
Definition: ssa_step.h:142
exprt ssa_rhs
Definition: ssa_step.h:141
symex_targett::sourcet source
Definition: ssa_step.h:49
bool is_function_call() const
Definition: ssa_step.h:92
ssa_exprt ssa_lhs
Definition: ssa_step.h:139
Arrays with given size.
Definition: std_types.h:807
const exprt & size() const
Definition: std_types.h:840
Expression of type type extracted from some object op starting at position offset (given in number of...
An interface for a decision procedure for satisfiability problems.
virtual exprt get(const exprt &) const =0
Return expr with variables replaced by values from satisfying assignment if available.
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition: dstring.h:38
Base class for all expressions.
Definition: expr.h:56
bool is_true() const
Return whether the expression is a constant representing true.
Definition: expr.cpp:27
bool is_false() const
Return whether the expression is a constant representing false.
Definition: expr.cpp:34
typet & type()
Return the type of the expression.
Definition: expr.h:84
operandst & operands()
Definition: expr.h:94
static irep_idt entry_point()
Get the identifier of the entry point to a goto model.
Step of the trace of a GOTO program.
Definition: goto_trace.h:50
std::vector< exprt > function_arguments
Definition: goto_trace.h:145
irep_idt format_string
Definition: goto_trace.h:136
exprt original_condition
Definition: goto_trace.h:120
std::string comment
Definition: goto_trace.h:124
exprt full_lhs_value
Definition: goto_trace.h:133
goto_programt::const_targett pc
Definition: goto_trace.h:112
irep_idt function_id
Definition: goto_trace.h:111
io_argst io_args
Definition: goto_trace.h:138
unsigned thread_nr
Definition: goto_trace.h:115
irep_idt property_id
Definition: goto_trace.h:123
irep_idt called_function
Definition: goto_trace.h:142
assignment_typet assignment_type
Definition: goto_trace.h:107
std::size_t step_nr
Number of the step in the trace.
Definition: goto_trace.h:53
irep_idt io_id
Definition: goto_trace.h:136
Trace of a GOTO program.
Definition: goto_trace.h:177
stepst steps
Definition: goto_trace.h:180
The trinary if-then-else operator.
Definition: std_expr.h:2375
exprt & true_case()
Definition: std_expr.h:2402
exprt & false_case()
Definition: std_expr.h:2412
Array index operator.
Definition: std_expr.h:1470
exprt & array()
Definition: std_expr.h:1500
exprt & index()
Definition: std_expr.h:1510
bool get_bool(const irep_idt &name) const
Definition: irep.cpp:57
bool is_not_nil() const
Definition: irep.h:372
const irep_idt & id() const
Definition: irep.h:388
Extract member of struct or union.
Definition: std_expr.h:2849
const exprt & struct_op() const
Definition: std_expr.h:2887
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition: namespace.h:94
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
Definition: namespace.cpp:148
static irep_idt rw_clock_id(event_it e, axiomt axiom=AX_PROPAGATION)
Build identifier for the read/write clock variable.
const irep_idt get_original_name() const
Definition: ssa_expr.h:49
static symbol_exprt typeless(const irep_idt &id)
Generate a symbol_exprt without a proper type.
Definition: std_expr.h:150
Symbol table entry.
Definition: symbol.h:28
typet type
Type of symbol.
Definition: symbol.h:31
Inheriting the interface of symex_targett this class represents the SSA form of the input program as ...
Semantic type conversion.
Definition: std_expr.h:2073
The type of an expression, extends irept.
Definition: type.h:29
bool has_subtype() const
Definition: type.h:64
const exprt & op() const
Definition: std_expr.h:391
Decision Procedure Interface.
Goto Programs with Functions.
Add constraints to equation encoding partial orders on events.
void restore_union(exprt &expr, const namespacet &ns)
Undo the union access -> byte_extract replacement that rewrite_union did for the purpose of displayin...
Symbolic Execution.
bool simplify(exprt &expr, const namespacet &ns)
exprt simplify_expr(exprt src, const namespacet &ns)
BigInt mp_integer
Definition: smt_terms.h:17
void solver(std::vector< framet > &frames, const std::unordered_set< symbol_exprt, irep_hash > &address_taken, const solver_optionst &solver_options, const namespacet &ns, std::vector< propertyt > &properties, std::size_t property_index)
Definition: solver.cpp:44
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition: invariant.h:423
const ssa_exprt & to_ssa_expr(const exprt &expr)
Cast a generic exprt to an ssa_exprt.
Definition: ssa_expr.h:145
const if_exprt & to_if_expr(const exprt &expr)
Cast an exprt to an if_exprt.
Definition: std_expr.h:2455
const typecast_exprt & to_typecast_expr(const exprt &expr)
Cast an exprt to a typecast_exprt.
Definition: std_expr.h:2107
const member_exprt & to_member_expr(const exprt &expr)
Cast an exprt to a member_exprt.
Definition: std_expr.h:2941
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition: std_expr.h:1538
const array_typet & to_array_type(const typet &type)
Cast a typet to an array_typet.
Definition: std_types.h:888
goto_programt::const_targett pc
Definition: symex_target.h:42
Symbol table entry.
const type_with_subtypet & to_type_with_subtype(const typet &type)
Definition: type.h:208