CBMC
solver_hardness.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: measure and track the complexity of solver queries
4 
5 Author: Diffblue Ltd.
6 
7 \*******************************************************************/
8 
9 #include "solver_hardness.h"
10 
11 #include <util/format_expr.h>
12 #include <util/format_type.h>
13 #include <util/json_irep.h>
14 #include <util/json_stream.h>
15 #include <util/std_code.h>
16 
17 #include <fstream>
18 #include <iomanip>
19 
22 {
23  clauses += other.clauses;
24  literals += other.literals;
25  variables.insert(other.variables.begin(), other.variables.end());
26  clause_set.insert(
27  clause_set.end(), other.clause_set.begin(), other.clause_set.end());
28  return *this;
29 }
30 
33 {
34  if(ssa_expression != other.ssa_expression)
35  return false;
36  return pc->source_location().as_string() ==
37  other.pc->source_location().as_string();
38 }
39 
41 {
42  return pcs.empty();
43 }
44 
46  std::size_t ssa_index,
47  const exprt &ssa_expression,
49 {
50  PRECONDITION(ssa_index < hardness_stats.size());
51 
52  current_ssa_key.ssa_expression = expr2string(ssa_expression);
53  current_ssa_key.pc = pc;
54  auto pre_existing =
55  hardness_stats[ssa_index].insert({current_ssa_key, current_hardness});
56  if(!pre_existing.second)
57  { // there already was an element with the same key
58  pre_existing.first->second += current_hardness;
59  }
60  if(hardness_stats[ssa_index].size() > max_ssa_set_size)
61  max_ssa_set_size = hardness_stats[ssa_index].size();
62  current_ssa_key = {};
63  current_hardness = {};
64 }
65 
67 {
68  // do not shrink
69  if(size <= hardness_stats.size())
70  return;
71 
72  hardness_stats.resize(size);
73 }
74 
76  const exprt &ssa_expression,
77  const std::vector<goto_programt::const_targett> &pcs)
78 {
80  {
81  assertion_stats.ssa_expression = expr2string(ssa_expression);
82  assertion_stats.pcs = pcs;
83  }
84 
86  current_ssa_key = {};
87  current_hardness = {};
88 }
89 
91  const bvt &bv,
92  const bvt &cnf,
93  const size_t cnf_clause_index,
94  bool register_cnf)
95 {
97  current_hardness.literals += bv.size();
98 
99  for(const auto &literal : bv)
100  {
101  current_hardness.variables.insert(literal.var_no());
102  }
103 
104  if(register_cnf)
105  {
106 #ifdef DEBUG
107  std::cout << cnf_clause_index << ": ";
108  for(const auto &literal : cnf)
109  std::cout << literal.dimacs() << " ";
110  std::cout << "0\n";
111 #endif
112  current_hardness.clause_set.push_back(cnf_clause_index);
113  }
114 }
115 
116 void solver_hardnesst::set_outfile(const std::string &file_name)
117 {
118  outfile = file_name;
119 }
120 
122 {
123  PRECONDITION(!outfile.empty());
124 
125  // The SSA steps and indexed internally (by the position in the SSA equation)
126  // but if the `--paths` option is used, there are multiple equations, some
127  // sharing SSA steps. We only store the unique ones in a set but now we want
128  // to identify them by a single number. So we shift the SSA index to make room
129  // for the set index.
130  std::size_t ssa_set_bit_offset = 0;
131  const std::size_t one = 1;
132  while((one << ssa_set_bit_offset) < max_ssa_set_size)
133  ++ssa_set_bit_offset;
134 
135  std::ofstream out{outfile};
136  json_stream_arrayt json_stream_array{out};
137 
138  for(std::size_t i = 0; i < hardness_stats.size(); i++)
139  {
140  const auto &ssa_step_hardness = hardness_stats[i];
141  if(ssa_step_hardness.empty())
142  continue;
143 
144  std::size_t j = 0;
145  for(const auto &key_value_pair : ssa_step_hardness)
146  {
147  auto const &ssa = key_value_pair.first;
148  auto const &hardness = key_value_pair.second;
149  auto hardness_stats_json = json_objectt{};
150  hardness_stats_json["SSA_expr"] = json_stringt{ssa.ssa_expression};
151  hardness_stats_json["GOTO"] =
153 
154  // It might be desirable to collect all SAT hardness statistics pertaining
155  // to a particular GOTO instruction, since there may be a number of SSA
156  // steps per GOTO instruction. The following JSON contains a unique
157  // identifier for each one.
158  hardness_stats_json["GOTO_ID"] =
159  json_numbert{std::to_string((i << ssa_set_bit_offset) + j)};
160  hardness_stats_json["Source"] = json(ssa.pc->source_location());
161 
162  auto sat_hardness_json = json_objectt{};
163  sat_hardness_json["Clauses"] =
164  json_numbert{std::to_string(hardness.clauses)};
165  sat_hardness_json["Literals"] =
166  json_numbert{std::to_string(hardness.literals)};
167  sat_hardness_json["Variables"] =
168  json_numbert{std::to_string(hardness.variables.size())};
169 
170  json_arrayt sat_hardness_clause_set_json;
171  for(auto const &clause : hardness.clause_set)
172  {
173  sat_hardness_clause_set_json.push_back(
174  json_numbert{std::to_string(clause)});
175  }
176  sat_hardness_json["ClauseSet"] = sat_hardness_clause_set_json;
177 
178  hardness_stats_json["SAT_hardness"] = sat_hardness_json;
179 
180  json_stream_array.push_back(hardness_stats_json);
181  ++j;
182  }
183  }
184 
185  if(!assertion_stats.empty())
186  {
187  auto assertion_stats_json = json_objectt{};
188  assertion_stats_json["SSA_expr"] =
190 
191  auto assertion_stats_pcs_json = json_arrayt{};
192  for(const auto &pc : assertion_stats.pcs)
193  {
194  auto assertion_stats_pc_json = json_objectt{};
195  assertion_stats_pc_json["GOTO"] =
197  assertion_stats_pc_json["Source"] = json(pc->source_location());
198  assertion_stats_pcs_json.push_back(assertion_stats_pc_json);
199  }
200  assertion_stats_json["Sources"] = assertion_stats_pcs_json;
201 
202  auto assertion_hardness_json = json_objectt{};
203  assertion_hardness_json["Clauses"] =
205  assertion_hardness_json["Literals"] =
207  assertion_hardness_json["Variables"] = json_numbert{
209 
210  json_arrayt assertion_sat_hardness_clause_set_json;
211  for(auto const &clause : assertion_stats.sat_hardness.clause_set)
212  {
213  assertion_sat_hardness_clause_set_json.push_back(
214  json_numbert{std::to_string(clause)});
215  }
216  assertion_hardness_json["ClauseSet"] =
217  assertion_sat_hardness_clause_set_json;
218 
219  assertion_stats_json["SAT_hardness"] = assertion_hardness_json;
220 
221  json_stream_array.push_back(assertion_stats_json);
222  }
223 }
224 
225 std::string
227 {
228  std::stringstream out;
229  auto instruction = *pc;
230 
231  if(!instruction.labels.empty())
232  {
233  out << " // Labels:";
234  for(const auto &label : instruction.labels)
235  out << " " << label;
236  }
237 
238  if(instruction.is_target())
239  out << std::setw(6) << instruction.target_number << ": ";
240 
241  switch(instruction.type())
242  {
243  case NO_INSTRUCTION_TYPE:
244  out << "NO INSTRUCTION TYPE SET";
245  break;
246 
247  case GOTO:
248  case INCOMPLETE_GOTO:
249  if(!instruction.condition().is_true())
250  {
251  out << "IF " << format(instruction.condition()) << " THEN ";
252  }
253 
254  out << "GOTO ";
255 
256  if(instruction.is_incomplete_goto())
257  {
258  out << "(incomplete)";
259  }
260  else
261  {
262  for(auto gt_it = instruction.targets.begin();
263  gt_it != instruction.targets.end();
264  gt_it++)
265  {
266  if(gt_it != instruction.targets.begin())
267  out << ", ";
268  out << (*gt_it)->target_number;
269  }
270  }
271  break;
272 
273  case SET_RETURN_VALUE:
274  out << "SET RETURN VALUE" << format(instruction.return_value());
275  break;
276 
277  case OTHER:
278  case DECL:
279  case DEAD:
280  case FUNCTION_CALL:
281  case ASSIGN:
282  out << format(instruction.code());
283  break;
284 
285  case ASSUME:
286  case ASSERT:
287  if(instruction.is_assume())
288  out << "ASSUME ";
289  else
290  out << "ASSERT ";
291 
292  out << format(instruction.condition());
293  break;
294 
295  case SKIP:
296  out << "SKIP";
297  break;
298 
299  case END_FUNCTION:
300  out << "END_FUNCTION";
301  break;
302 
303  case LOCATION:
304  out << "LOCATION";
305  break;
306 
307  case THROW:
308  out << "THROW";
309 
310  {
311  const irept::subt &exception_list =
312  instruction.code().find(ID_exception_list).get_sub();
313 
314  for(const auto &ex : exception_list)
315  out << " " << ex.id();
316  }
317 
318  if(instruction.code().operands().size() == 1)
319  out << ": " << format(instruction.code().op0());
320 
321  break;
322 
323  case CATCH:
324  {
325  if(instruction.code().get_statement() == ID_exception_landingpad)
326  {
327  const auto &landingpad = to_code_landingpad(instruction.code());
328  out << "EXCEPTION LANDING PAD (" << format(landingpad.catch_expr().type())
329  << ' ' << format(landingpad.catch_expr()) << ")";
330  }
331  else if(instruction.code().get_statement() == ID_push_catch)
332  {
333  out << "CATCH-PUSH ";
334 
335  unsigned i = 0;
336  const irept::subt &exception_list =
337  instruction.code().find(ID_exception_list).get_sub();
339  instruction.targets.size() == exception_list.size(),
340  "unexpected discrepancy between sizes of instruction"
341  "targets and exception list");
342  for(auto gt_it = instruction.targets.begin();
343  gt_it != instruction.targets.end();
344  gt_it++, i++)
345  {
346  if(gt_it != instruction.targets.begin())
347  out << ", ";
348  out << exception_list[i].id() << "->" << (*gt_it)->target_number;
349  }
350  }
351  else if(instruction.code().get_statement() == ID_pop_catch)
352  {
353  out << "CATCH-POP";
354  }
355  else
356  {
357  UNREACHABLE;
358  }
359  break;
360  }
361 
362  case ATOMIC_BEGIN:
363  out << "ATOMIC_BEGIN";
364  break;
365 
366  case ATOMIC_END:
367  out << "ATOMIC_END";
368  break;
369 
370  case START_THREAD:
371  out << "START THREAD " << instruction.get_target()->target_number;
372  break;
373 
374  case END_THREAD:
375  out << "END THREAD";
376  break;
377  }
378 
379  return out.str();
380 }
381 
382 std::string solver_hardnesst::expr2string(const exprt &expr)
383 {
384  std::stringstream ss;
385  ss << format(expr);
386  return ss.str();
387 }
Base class for all expressions.
Definition: expr.h:56
instructionst::const_iterator const_targett
Definition: goto_program.h:615
jsont & push_back(const jsont &json)
Definition: json.h:212
Provides methods for streaming JSON arrays.
Definition: json_stream.h:93
static format_containert< T > format(const T &o)
Definition: format.h:37
@ FUNCTION_CALL
Definition: goto_program.h:49
@ ATOMIC_END
Definition: goto_program.h:44
@ DEAD
Definition: goto_program.h:48
@ END_FUNCTION
Definition: goto_program.h:42
@ ASSIGN
Definition: goto_program.h:46
@ ASSERT
Definition: goto_program.h:36
@ SET_RETURN_VALUE
Definition: goto_program.h:45
@ ATOMIC_BEGIN
Definition: goto_program.h:43
@ CATCH
Definition: goto_program.h:51
@ END_THREAD
Definition: goto_program.h:40
@ SKIP
Definition: goto_program.h:38
@ NO_INSTRUCTION_TYPE
Definition: goto_program.h:33
@ START_THREAD
Definition: goto_program.h:39
@ THROW
Definition: goto_program.h:50
@ DECL
Definition: goto_program.h:47
@ OTHER
Definition: goto_program.h:37
@ GOTO
Definition: goto_program.h:34
@ INCOMPLETE_GOTO
Definition: goto_program.h:52
std::vector< literalt > bvt
Definition: literal.h:201
static void json(json_objectT &result, const irep_idt &property_id, const property_infot &property_info)
Definition: properties.cpp:120
#define UNREACHABLE
This should be used to mark dead code.
Definition: invariant.h:525
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition: invariant.h:534
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
static code_landingpadt & to_code_landingpad(codet &code)
Definition: std_code.h:1972
std::string to_string(const string_not_contains_constraintt &expr)
Used for debug printing.
std::vector< goto_programt::const_targett > pcs
goto_programt::const_targett pc
bool operator==(const hardness_ssa_keyt &other) const
sat_hardnesst & operator+=(const sat_hardnesst &other)
std::vector< size_t > clause_set
std::unordered_set< size_t > variables
sat_hardnesst current_hardness
static std::string expr2string(const exprt &expr)
assertion_statst assertion_stats
void set_outfile(const std::string &file_name)
void register_clause(const bvt &bv, const bvt &cnf, const size_t cnf_clause_index, bool register_cnf)
Called e.g.
hardness_ssa_keyt current_ssa_key
void register_ssa(std::size_t ssa_index, const exprt &ssa_expression, goto_programt::const_targett pc)
Called from the symtex_target_equationt::convert_*, this function associates an SSA step to all the s...
std::string outfile
void register_assertion_ssas(const exprt &ssa_expression, const std::vector< goto_programt::const_targett > &pcs)
Called from the symtex_target_equationt::convert_assertions, this function associates the disjunction...
std::vector< std::unordered_map< hardness_ssa_keyt, sat_hardnesst > > hardness_stats
std::size_t max_ssa_set_size
void produce_report()
Print the statistics to a JSON file (specified via command-line option).
static std::string goto_instruction2string(goto_programt::const_targett pc)
void register_ssa_size(std::size_t size)