forked from stanford-centaur/pono
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pono.cpp
419 lines (374 loc) · 13.7 KB
/
pono.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
/********************* */
/*! \file
** \verbatim
** Top contributors (to current version):
** Makai Mann, Ahmed Irfan
** This file is part of the pono project.
** Copyright (c) 2019 by the authors listed in the file AUTHORS
** in the top-level source directory) and their institutional affiliations.
** All rights reserved. See the file LICENSE in the top-level source
** directory for licensing information.\endverbatim
**
** \brief
**
**
**/
#include <csignal>
#include <iostream>
#include "assert.h"
#ifdef WITH_PROFILING
#include <gperftools/profiler.h>
#endif
#include "smt-switch/boolector_factory.h"
#ifdef WITH_MSAT
#include "smt-switch/msat_factory.h"
#endif
#include "core/fts.h"
#include "frontends/btor2_encoder.h"
#include "frontends/smv_encoder.h"
#include "frontends/vmt_encoder.h"
#include "modifiers/control_signals.h"
#include "modifiers/mod_ts_prop.h"
#include "modifiers/prop_monitor.h"
#include "modifiers/static_coi.h"
#include "options/options.h"
#include "printers/btor2_witness_printer.h"
#include "printers/vcd_witness_printer.h"
#include "smt-switch/logging_solver.h"
#include "smt/available_solvers.h"
#include "utils/logger.h"
#include "utils/timestamp.h"
#include "utils/make_provers.h"
#include "utils/ts_analysis.h"
using namespace pono;
using namespace smt;
using namespace std;
ProverResult check_prop(PonoOptions pono_options,
Term & prop,
TransitionSystem & ts,
const SmtSolver & s,
std::vector<UnorderedTermMap> & cex)
{
// get property name before it is rewritten
const string prop_name = ts.get_name(prop);
logger.log(1, "Solving property: {}", prop_name);
logger.log(3, "INIT:\n{}", ts.init());
logger.log(3, "TRANS:\n{}", ts.trans());
// modify the transition system and property based on options
if (!pono_options.clock_name_.empty()) {
Term clock_symbol = ts.lookup(pono_options.clock_name_);
toggle_clock(ts, clock_symbol);
}
if (!pono_options.reset_name_.empty()) {
std::string reset_name = pono_options.reset_name_;
bool negative_reset = false;
if (reset_name.at(0) == '~') {
reset_name = reset_name.substr(1, reset_name.length() - 1);
negative_reset = true;
}
Term reset_symbol = ts.lookup(reset_name);
if (negative_reset) {
SortKind sk = reset_symbol->get_sort()->get_sort_kind();
reset_symbol = (sk == BV) ? s->make_term(BVNot, reset_symbol)
: s->make_term(Not, reset_symbol);
}
Term reset_done = add_reset_seq(ts, reset_symbol, pono_options.reset_bnd_);
// guard the property with reset_done
prop = ts.solver()->make_term(Implies, reset_done, prop);
}
if (pono_options.static_coi_) {
/* Compute the set of state/input variables related to the
bad-state property. Based on that information, rebuild the
transition relation of the transition system. */
StaticConeOfInfluence coi(ts, { prop }, pono_options.verbosity_);
}
if (pono_options.pseudo_init_prop_) {
ts = pseudo_init_and_prop(ts, prop);
}
if (pono_options.promote_inputvars_) {
ts = promote_inputvars(ts);
assert(!ts.inputvars().size());
}
if (!ts.only_curr(prop)) {
logger.log(1,
"Got next state or input variables in property. "
"Generating a monitor state.");
prop = add_prop_monitor(ts, prop);
}
if (pono_options.assume_prop_) {
// NOTE: crucial that pseudo_init_prop and add_prop_monitor passes are
// before this pass. Can't assume the non-delayed prop and also
// delay it
prop_in_trans(ts, prop);
}
Property p(s, prop, prop_name);
// end modification of the transition system and property
Engine eng = pono_options.engine_;
std::shared_ptr<Prover> prover;
if (pono_options.cegp_abs_vals_) {
prover = make_cegar_values_prover(eng, p, ts, s, pono_options);
} else if (pono_options.ceg_bv_arith_) {
prover = make_cegar_bv_arith_prover(eng, p, ts, s, pono_options);
} else if (pono_options.ceg_prophecy_arrays_) {
prover = make_ceg_proph_prover(eng, p, ts, s, pono_options);
} else {
prover = make_prover(eng, p, ts, s, pono_options);
}
assert(prover);
// TODO: handle this in a more elegant way in the future
// consider calling prover for CegProphecyArrays (so that underlying
// model checker runs prove unbounded) or possibly, have a command line
// flag to pick between the two
ProverResult r;
if (pono_options.engine_ == MSAT_IC3IA)
{
// HACK MSAT_IC3IA does not support check_until
r = prover->prove();
}
else
{
r = prover->check_until(pono_options.bound_);
}
if (r == FALSE && pono_options.witness_) {
bool success = prover->witness(cex);
if (!success) {
logger.log(
0,
"Only got a partial witness from engine. Not suitable for printing.");
}
}
Term invar;
if (r == TRUE && (pono_options.show_invar_ || pono_options.check_invar_)) {
try {
invar = prover->invar();
}
catch (PonoException & e) {
std::cout << "Engine " << pono_options.engine_
<< " does not support getting the invariant." << std::endl;
}
}
if (r == TRUE && pono_options.show_invar_ && invar) {
logger.log(0, "INVAR: {}", invar);
}
if (r == TRUE && pono_options.check_invar_ && invar) {
bool invar_passes = check_invar(ts, p.prop(), invar);
std::cout << "Invariant Check " << (invar_passes ? "PASSED" : "FAILED")
<< std::endl;
if (!invar_passes) {
// shouldn't return true if invariant is incorrect
throw PonoException("Invariant Check FAILED");
}
}
return r;
}
// Note: signal handlers are registered only when profiling is enabled.
void profiling_sig_handler(int sig)
{
std::string signame;
switch (sig) {
case SIGINT: signame = "SIGINT"; break;
case SIGTERM: signame = "SIGTERM"; break;
case SIGALRM: signame = "SIGALRM"; break;
default:
throw PonoException(
"Caught unexpected signal"
"in profiling signal handler.");
}
logger.log(0, "\n Signal {} received\n", signame);
#ifdef WITH_PROFILING
ProfilerFlush();
ProfilerStop();
#endif
// Switch back to default handling for signal 'sig' and raise it.
signal(sig, SIG_DFL);
raise(sig);
}
int main(int argc, char ** argv)
{
auto begin_time_stamp = timestamp();
PonoOptions pono_options;
ProverResult res = pono_options.parse_and_set_options(argc, argv);
if (res == ERROR) return res;
// expected result returned by option parsing and setting is
// 'pono::UNKNOWN', indicating that options were correctly set and
// 'ERROR' otherwise, e.g. wrong command line options or
// incompatible options were passed
assert(res == pono::UNKNOWN);
// set logger verbosity -- can only be set once
logger.set_verbosity(pono_options.verbosity_);
// For profiling: set signal handlers for common signals to abort
// program. This is necessary to gracefully stop profiling when,
// e.g., an external time limit is enforced to stop the program.
if (!pono_options.profiling_log_filename_.empty()) {
signal(SIGINT, profiling_sig_handler);
signal(SIGTERM, profiling_sig_handler);
signal(SIGALRM, profiling_sig_handler);
#ifdef WITH_PROFILING
logger.log(
0, "Profiling log filename: {}", pono_options.profiling_log_filename_);
ProfilerStart(pono_options.profiling_log_filename_.c_str());
#endif
}
#ifdef NDEBUG
try {
#endif
// no logging by default
// could create an option for logging solvers in the future
// HACK bool_model_generation for IC3IA breaks CegProphecyArrays
// longer term fix will use a different solver in CegProphecyArrays,
// but for now just force full model generation in that case
SmtSolver s = create_solver_for(pono_options.smt_solver_,
pono_options.engine_,
false,
pono_options.ceg_prophecy_arrays_);
if (pono_options.logging_smt_solver_) {
s = make_shared<LoggingSolver>(s);
// TODO consider setting base-context-1 for BTOR here
// to allow resetting assertions
}
// limitations with COI
if (pono_options.static_coi_) {
if (pono_options.pseudo_init_prop_) {
// Issue explained here:
// https://github.com/upscale-project/pono/pull/160 will be resolved
// once state variables removed by COI are removed from init then should
// do static-coi BEFORE mod-init-prop
logger.log(0,
"Warning: --mod-init-prop and --static-coi don't work "
"well together currently.");
}
}
// default options for IC3SA
if (pono_options.engine_ == IC3SA_ENGINE) {
// IC3SA expects all state variables
pono_options.promote_inputvars_ = true;
}
// TODO: make this less ugly, just need to keep it in scope if using
// it would be better to have a generic encoder
// and also only create the transition system once
string file_ext = pono_options.filename_.substr(
pono_options.filename_.find_last_of(".") + 1);
if (file_ext == "btor2" || file_ext == "btor") {
logger.log(2, "Parsing BTOR2 file: {}", pono_options.filename_);
FunctionalTransitionSystem fts(s);
BTOR2Encoder btor_enc(pono_options.filename_, fts);
const TermVec & propvec = btor_enc.propvec();
unsigned int num_props = propvec.size();
if (pono_options.prop_idx_ >= num_props) {
throw PonoException(
"Property index " + to_string(pono_options.prop_idx_)
+ " is greater than the number of properties in file "
+ pono_options.filename_ + " (" + to_string(num_props) + ")");
}
Term prop = propvec[pono_options.prop_idx_];
vector<UnorderedTermMap> cex;
res = check_prop(pono_options, prop, fts, s, cex);
// we assume that a prover never returns 'ERROR'
assert(res != ERROR);
// print btor output
if (res == FALSE) {
cout << "sat" << endl;
cout << "b" << pono_options.prop_idx_ << endl;
assert(pono_options.witness_ || !cex.size());
if (cex.size()) {
print_witness_btor(btor_enc, cex, fts);
if (!pono_options.vcd_name_.empty()) {
VCDWitnessPrinter vcdprinter(fts, cex);
vcdprinter.dump_trace_to_file(pono_options.vcd_name_);
}
}
} else if (res == TRUE) {
cout << "unsat" << endl;
cout << "b" << pono_options.prop_idx_ << endl;
} else {
assert(res == pono::UNKNOWN);
cout << "unknown" << endl;
cout << "b" << pono_options.prop_idx_ << endl;
}
} else if (file_ext == "smv" || file_ext == "vmt" || file_ext == "smt2") {
logger.log(2, "Parsing SMV/VMT file: {}", pono_options.filename_);
RelationalTransitionSystem rts(s);
TermVec propvec;
if (file_ext == "smv") {
SMVEncoder smv_enc(pono_options.filename_, rts);
propvec = smv_enc.propvec();
} else {
assert(file_ext == "vmt" || file_ext == "smt2");
VMTEncoder vmt_enc(pono_options.filename_, rts);
propvec = vmt_enc.propvec();
}
unsigned int num_props = propvec.size();
if (pono_options.prop_idx_ >= num_props) {
throw PonoException(
"Property index " + to_string(pono_options.prop_idx_)
+ " is greater than the number of properties in file "
+ pono_options.filename_ + " (" + to_string(num_props) + ")");
}
Term prop = propvec[pono_options.prop_idx_];
// get property name before it is rewritten
std::vector<UnorderedTermMap> cex;
res = check_prop(pono_options, prop, rts, s, cex);
// we assume that a prover never returns 'ERROR'
assert(res != ERROR);
logger.log(
0, "Property {} is {}", pono_options.prop_idx_, to_string(res));
if (res == FALSE) {
cout << "sat" << endl;
assert(pono_options.witness_ || cex.size() == 0);
for (size_t t = 0; t < cex.size(); t++) {
cout << "AT TIME " << t << endl;
for (auto elem : cex[t]) {
cout << "\t" << elem.first << " : " << elem.second << endl;
}
}
assert(pono_options.witness_ || pono_options.vcd_name_.empty());
if (!pono_options.vcd_name_.empty()) {
VCDWitnessPrinter vcdprinter(rts, cex);
vcdprinter.dump_trace_to_file(pono_options.vcd_name_);
}
} else if (res == TRUE) {
cout << "unsat" << endl;
} else {
assert(res == pono::UNKNOWN);
cout << "unknown" << endl;
}
} else {
throw PonoException("Unrecognized file extension " + file_ext
+ " for file " + pono_options.filename_);
}
#ifdef NDEBUG
}
catch (PonoException & ce) {
cout << ce.what() << endl;
cout << "error" << endl;
cout << "b" << pono_options.prop_idx_ << endl;
res = ProverResult::ERROR;
}
catch (SmtException & se) {
cout << se.what() << endl;
cout << "error" << endl;
cout << "b" << pono_options.prop_idx_ << endl;
res = ProverResult::ERROR;
}
catch (std::exception & e) {
cout << "Caught generic exception..." << endl;
cout << e.what() << endl;
cout << "error" << endl;
cout << "b" << pono_options.prop_idx_ << endl;
res = ProverResult::ERROR;
}
#endif
if (!pono_options.profiling_log_filename_.empty()) {
#ifdef WITH_PROFILING
ProfilerFlush();
ProfilerStop();
#endif
}
if (pono_options.print_wall_time_) {
auto end_time_stamp = timestamp();
auto elapsed_time = timestamp_diff(begin_time_stamp, end_time_stamp);
std:cout << "Pono wall clock time (s): " <<
time_duration_to_sec_string(elapsed_time) << std::endl;
}
return res;
}