CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
solver_hardness.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: measure and track the complexity of solver queries
4
5Author: 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{
51
54 auto pre_existing =
56 if(!pre_existing.second)
57 { // there already was an element with the same key
58 pre_existing.first->second += current_hardness;
59 }
62 current_ssa_key = {};
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 {
82 assertion_stats.pcs = pcs;
83 }
84
86 current_ssa_key = {};
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
113 }
114}
115
116void 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)
134
135 std::ofstream out{outfile};
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;
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
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
171 for(auto const &clause : hardness.clause_set)
172 {
174 json_numbert{std::to_string(clause)});
175 }
177
178 hardness_stats_json["SAT_hardness"] = sat_hardness_json;
179
181 ++j;
182 }
183 }
184
186 {
188 assertion_stats_json["SSA_expr"] =
190
192 for(const auto &pc : assertion_stats.pcs)
193 {
197 assertion_stats_pc_json["Source"] = json(pc->source_location());
199 }
201
203 assertion_hardness_json["Clauses"] =
205 assertion_hardness_json["Literals"] =
208 std::to_string(assertion_stats.sat_hardness.variables.size())};
209
211 for(auto const &clause : assertion_stats.sat_hardness.clause_set)
212 {
214 json_numbert{std::to_string(clause)});
215 }
216 assertion_hardness_json["ClauseSet"] =
218
220
222 }
223}
224
225std::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 {
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 {
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
382std::string solver_hardnesst::expr2string(const exprt &expr)
383{
384 std::stringstream ss;
385 ss << format(expr);
386 return ss.str();
387}
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
Base class for all expressions.
Definition expr.h:56
instructionst::const_iterator const_targett
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
@ ATOMIC_END
@ DEAD
@ LOCATION
@ END_FUNCTION
@ ASSIGN
@ ASSERT
@ SET_RETURN_VALUE
@ ATOMIC_BEGIN
@ CATCH
@ END_THREAD
@ SKIP
@ NO_INSTRUCTION_TYPE
@ START_THREAD
@ THROW
@ DECL
@ OTHER
@ GOTO
@ INCOMPLETE_GOTO
@ ASSUME
std::vector< literalt > bvt
Definition literal.h:201
static void json(json_objectT &result, const irep_idt &property_id, const property_infot &property_info)
#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::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...
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)