def check_step(): self._reset_assertions(self.solver) self._add_assertion(self.solver, self.at_time(And(trans, lemma), 0)) self._add_assertion(self.solver, self.at_time(Not(lemma), 1)) if self._solve(self.solver): if Logger.level(2): Logger.log("Lemma \"%s\" failed for L & T -> L'" % lemma, 2) if Logger.level(3): (hr_trace, vcd_trace) = self.print_trace( hts, self._get_model(self.solver), 1, prefix=prefix, map_function=self.config.map_function) if hr_trace or vcd_trace: vcd_msg = "" if vcd_trace: vcd_msg = " and in \"%s\"" % (vcd_trace) Logger.log( "Counterexample stored in \"%s\"%s" % (hr_trace, vcd_msg), 2) else: Logger.log("", 2) return False else: Logger.log("Lemma \"%s\" holds for L & T -> L'" % lemma, 2) return True
def run_passes(self): Logger.log("Running CoreIR passes...", 1) print_level = 3 if not Logger.level(print_level): saved_stdout = suppress_output() self.context.run_passes(PASSES) if not Logger.level(print_level): restore_output(saved_stdout)
def parse_file(self, strfile, config, flags=None): if flags is None: Logger.error("Top module not provided") topmodule = flags[0] absstrfile = os.path.abspath(strfile) directory = "/".join(absstrfile.split("/")[:-1]) filename = absstrfile.split("/")[-1] if self.single_file: files = [absstrfile] else: if self.files_from_dir: files = [ "%s/%s" % (directory, f) for f in os.listdir(directory) if self._get_extension(f) in self.extensions ] else: files = [ "%s/%s" % (directory, f) for f in list( set(self._collect_dependencies(directory, filename))) ] files.append(absstrfile) command = "%s -p \"%s\"" % (CMD, "; ".join(COMMANDS)) command = command.format(FILES=" ".join(files), \ TARGET=topmodule, \ PASSES="; ".join(PASSES), \ BTORFILE=TMPFILE) Logger.log("Command: %s" % command, 2) print_level = 3 if not Logger.level(print_level): saved_stdout = suppress_output() retval = os.system(command) if not Logger.level(print_level): restore_output(saved_stdout) if retval != 0: Logger.error("Error in Verilog conversion") parser = BTOR2Parser() ret = parser.parse_file(TMPFILE, config) if not Logger.level(1): os.remove(TMPFILE) return ret
def parse_file(self, strfile, config, flags=None): if flags is None: Logger.error("Top module not provided") topmodule = flags[0] absstrfile = os.path.abspath(strfile) directory = "/".join(absstrfile.split("/")[:-1]) filename = absstrfile.split("/")[-1] files = [absstrfile] with open(strfile, "r") as f: if topmodule not in f.read(): Logger.error("Module \"%s\" not found"%topmodule) commands = "\n".join(COMMANDS) commands = commands.format(FILES=" ".join(files), \ TARGET=topmodule, \ FILE=TMPFILE) command = "%s -script_file %s"%(CMD, TMPCMDFILE) with open(TMPCMDFILE, "w") as f: f.write(commands) Logger.log("Commands: %s"%commands, 2) print_level = 3 if not Logger.level(print_level): saved_stdout = suppress_output() retval = os.system(command) if not Logger.level(print_level): restore_output(saved_stdout) if retval != 0: Logger.error("Error in SystemVerilog conversion") parser = VerilogHTSParser() ret = parser.parse_file(TMPFILE, config, flags=flags) self.model_info = parser.get_model_info() if (not Logger.level(1)) and (not config.devel): os.remove(TMPFILE) os.remove(TMPCMDFILE) return ret
def _get_param_assignments(self, model, time, parameters, monotonic=True): p_ass = [] fwd = False for p in parameters: p_time = model[TS.get_ptimed(p, 0)] if p.symbol_type() == BOOL: if monotonic: if p_time == TRUE(): p_ass.append(p) else: p_ass.append(p if p_time == TRUE() else Not(p)) else: p_ass.append(EqualsOrIff(p, p_time)) p_ass = And(p_ass) self.region = simplify(Or(self.region, p_ass)) if self.models is None: self.models = [] self.models.append((model, time)) Logger.msg("+", 0, not (Logger.level(1))) self.cs_count += 1 Logger.log( "Found assignment \"%s\"" % (p_ass.serialize(threshold=100)), 1) return (p_ass, False)
def _get_param_assignments(self, model, time, parameters, monotonic=True): p_ass = [] fwd = False for p in parameters: # search the trace for any enabled faults ever_true = False for t in range(time + 1): p_time = model[TS.get_ptimed(p, 0)] if p.symbol_type() == BOOL: if p_time == TRUE(): ever_true = True break if ever_true: p_ass.append(p) elif not monotonic: p_ass.append(EqualsOrIff(p, FALSE())) p_ass = And(p_ass) self.region = simplify(Or(self.region, p_ass)) if self.models is None: self.models = [] self.models.append((model, time)) Logger.msg("+", 0, not (Logger.level(1))) self.cs_count += 1 Logger.log( "Found assignment \"%s\"" % (p_ass.serialize(threshold=100)), 1) return (p_ass, False)
def check_init(): self._reset_assertions(self.solver) self._add_assertion(self.solver, self.at_time(And(init, Not(lemma)), 0), comment="Init check") res = self._solve(self.solver) prefix = None if self.config.prefix is not None: prefix = self.config.prefix + "-ind" if res: if Logger.level(2): Logger.log("Lemma \"%s\" failed for I -> L" % lemma, 2) (hr_trace, vcd_trace) = self.print_trace( hts, self._get_model(self.solver), 0, prefix=prefix, map_function=self.config.map_function) Logger.log("", 2) if hr_trace: Logger.log("Counterexample: \n%s" % (hr_trace), 2) else: Logger.log("", 2) return False else: Logger.log("Lemma \"%s\" holds for I -> L" % lemma, 2) return True
def solve_safety_fwd(self, hts, prop, k, k_min): init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() t = k_min while (t < k+1): self._reset_assertions(self.solver) formula = And(init, invar) formula = self.at_time(formula, 0) Logger.log("Add init and invar", 2) self._add_assertion(self.solver, formula) trans_t = self.unroll(trans, invar, t) self._add_assertion(self.solver, trans_t) propt = self.at_time(Not(prop), t) Logger.log("Add property time %d"%t, 2) self._add_assertion(self.solver, propt) if self._solve(self.solver): Logger.log("Counterexample found with k=%s"%(t), 1) model = self._get_model(self.solver) return (t, model) else: Logger.log("No counterexample found with k=%s"%(t), 1) Logger.msg(".", 0, not(Logger.level(1))) t += 1 return (t-1, None)
def solve_safety_inc_zz(self, hts, prop, k): self._reset_assertions(self.solver) if TS.has_next(prop): Logger.error( "Invariant checking with next variables only supports FWD strategy" ) init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() initt = self.at_time(And(init, invar), 0) Logger.log("Add init at_0", 2) self._add_assertion(self.solver, initt) propt = self.at_ptime(And(Not(prop), invar), -1) Logger.log("Add property pat_%d" % 0, 2) self._add_assertion(self.solver, propt) t = 0 while (t < k + 1): self._push(self.solver) even = (t % 2) == 0 th = int(t / 2) if even: eq = And([ EqualsOrIff(self.at_time(v, th), self.at_ptime(v, th - 1)) for v in hts.vars ]) else: eq = And([ EqualsOrIff(self.at_time(v, th + 1), self.at_ptime(v, th - 1)) for v in hts.vars ]) Logger.log("Add equivalence time %d" % t, 2) self._add_assertion(self.solver, eq) if self._solve(self.solver): Logger.log("Counterexample found with k=%s" % (t), 1) model = self._get_model(self.solver) return (t, model) else: Logger.log("No counterexample found with k=%s" % (t), 1) Logger.msg(".", 0, not (Logger.level(1))) self._pop(self.solver) if even: trans_t = self.unroll(trans, invar, th + 1, th) else: trans_t = self.unroll(trans, invar, th, th + 1) self._add_assertion(self.solver, trans_t) t += 1 return (t - 1, None)
def _add_assertion(self, solver, formula, comment=None): if not self.config.skip_solving: solver.solver.add_assertion(formula) if Logger.level(3): buf = cStringIO() printer = SmtPrinter(buf) printer.printer(formula) print(buf.getvalue() + "\n") if solver.trace_file is not None: if comment: self._write_smt2_comment(solver, "%s: START" % comment) formula_fv = get_free_variables(formula) for v in formula_fv: if v in solver.smt2vars: continue if v.symbol_type() == BOOL: self._write_smt2_log( solver, "(declare-fun %s () Bool)" % (v.symbol_name())) elif v.symbol_type().is_array_type(): st = v.symbol_type() assert st.index_type.is_bv_type(), "Expecting BV indices" assert st.elem_type.is_bv_type(), "Expecting BV elements" self._write_smt2_log( solver, "(declare-fun %s () (Array (_ BitVec %s) (_ BitVec %s)))" % (v.symbol_name(), st.index_type.width, st.elem_type.width)) elif v.symbol_type().is_bv_type(): self._write_smt2_log( solver, "(declare-fun %s () (_ BitVec %s))" % (v.symbol_name(), v.symbol_type().width)) else: Logger.error("Unhandled type in smt2 translation") self._write_smt2_log(solver, "") for v in formula_fv: solver.smt2vars.add(v) if formula.is_and(): for f in conjunctive_partition(formula): buf = cStringIO() printer = SmtPrinter(buf) printer.printer(f) self._write_smt2_log(solver, "(assert %s)" % buf.getvalue()) else: buf = cStringIO() printer = SmtPrinter(buf) printer.printer(formula) self._write_smt2_log(solver, "(assert %s)" % buf.getvalue()) if comment: self._write_smt2_comment(solver, "%s: END" % comment)
def solve_inc(self, hts, prop, k, all_vars=True): if all_vars: relevant_vars = hts.vars else: relevant_vars = hts.state_vars | hts.input_vars | hts.output_vars init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() init = And(init, invar) init_0 = self.at_time(init, 0) nprop = self.enc.to_nnf(Not(prop)) self._reset_assertions(self.solver) self._add_assertion(self.solver, init_0) for t in range(1, k + 1, 1): trans_t = self.unroll(trans, invar, t) self._add_assertion(self.solver, trans_t) lb = self.all_simple_loopbacks(relevant_vars, t) self._push(self.solver) self._push(self.solver) nprop_k = self.enc.encode(nprop, 0, t) self._add_assertion(self.solver, And(nprop_k, Not(Or(lb)))) if self._solve(self.solver): Logger.log("Counterexample (no-loop) found with k=%s" % (t), 1) model = self._get_model(self.solver) return (t, model) nltlprop = [] self._pop(self.solver) for l in range(t + 1): nprop_l = self.enc.encode_l(nprop, 0, t, l) nltlprop.append(And(lb[l], nprop_l)) self._add_assertion(self.solver, Or(nltlprop)) if self._solve(self.solver): Logger.log("Counterexample (with-loop) found with k=%s" % (t), 1) model = self._get_model(self.solver) return (t, model) else: Logger.log("No counterexample found with k=%s" % (t), 1) Logger.msg(".", 0, not (Logger.level(1))) self._pop(self.solver) return (k - 1, None)
def is_available(self): if shutil.which(CMD) is None: return False print_level = 3 if not Logger.level(print_level): saved_stdout = suppress_output() retval = os.system("%s -h"%CMD) if not Logger.level(print_level): restore_output(saved_stdout) if retval != 0: return False return True
def _solve(self, solver): self._write_smt2_log(solver, "(check-sat)") self._write_smt2_log(solver, "") if self.config.skip_solving: return None if Logger.level(2): timer = Logger.start_timer("Solve") r = solver.solver.solve() if Logger.level(2): self.total_time += Logger.get_timer(timer) Logger.log("Total time solve: %.2f sec" % self.total_time, 1) return r
def add_lemmas(self, hts, prop, lemmas): if len(lemmas) == 0: return (hts, False) self._reset_assertions(self.solver) h_init = hts.single_init() h_trans = hts.single_trans() holding_lemmas = [] lindex = 1 nlemmas = len(lemmas) tlemmas = 0 flemmas = 0 for lemma in lemmas: Logger.log("\nChecking Lemma %s/%s" % (lindex, nlemmas), 1) invar = hts.single_invar() init = And(h_init, invar) trans = And(invar, h_trans, TS.to_next(invar)) if self._check_lemma(hts, lemma, init, trans): holding_lemmas.append(lemma) hts.add_assumption(lemma) hts.reset_formulae() Logger.log("Lemma %s holds" % (lindex), 1) tlemmas += 1 if self._suff_lemmas(prop, holding_lemmas): return (hts, True) else: Logger.log("Lemma %s does not hold" % (lindex), 1) flemmas += 1 msg = "%s T:%s F:%s U:%s" % (status_bar( (float(lindex) / float(nlemmas)), False), tlemmas, flemmas, (nlemmas - lindex)) Logger.inline(msg, 0, not (Logger.level(1))) lindex += 1 Logger.clear_inline(0, not (Logger.level(1))) for lemma in holding_lemmas: hts.add_assumption(lemma) return (hts, False)
def solve(self, hts, prop, k, lemmas=None): if lemmas is not None: (hts, res) = self.add_lemmas(hts, prop, lemmas) if res: Logger.log("Lemmas imply the property", 1) Logger.log("", 0, not (Logger.level(1))) return (0, True) hts.reset_formulae() return self.solve_inc(hts, prop, k)
def available(toolname, optiongrep=None): if shutil.which(toolname) is None: return False print_level = 3 if not Logger.level(print_level): saved_status = suppress_output() # assuming there's a -h for the program # yosys and verific have it retval = os.system("{} -h".format(toolname)) if not Logger.level(print_level): restore_output(saved_status) if optiongrep is not None: with open(saved_status[0].name, 'r') as f: output = f.read() if optiongrep not in output: return False return (retval == 0)
def solve_safety(self, hts, prop, k, k_min=0, lemmas=None, processes=1): if lemmas is not None: (hts, res) = self.add_lemmas(hts, prop, lemmas) if res: Logger.log("Lemmas imply the property", 1) Logger.log("", 0, not(Logger.level(1))) return (0, True) hts.reset_formulae() if self.config.incremental: return self.solve_safety_inc(hts, prop, k, k_min, processes) return self.solve_safety_ninc(hts, prop, k, k_min)
def solve_problems(self, problems, config): if len(problems.problems) == 0: Logger.error("No problems defined") if VerificationType.LTL in [ problem.verification for problem in problems.problems ]: ltl_reset_env() # generate systems for each problem configuration systems = {} for si in problems.symbolic_inits: (systems[('hts', si)], _, _) = self.parse_model(problems.relative_path, \ problems.model_file, \ problems.abstract_clock, si, "System 1", \ boolean=problems.boolean, \ no_clock=problems.no_clock, \ run_passes=problems.run_coreir_passes) if problems.equivalence is not None: (systems[('hts2', si)], _, _) = self.parse_model(problems.relative_path, \ problems.equivalence, \ problems.abstract_clock, si, "System 2", \ boolean=problems.boolean, \ no_clock=problems.no_clock, \ run_passes=problems.run_coreir_passes) else: systems[('hts2', si)] = None for problem in problems.problems: problem.hts = systems[('hts', problem.symbolic_init)] problem.hts2 = systems[('hts2', problem.symbolic_init)] problem.abstract_clock = problems.abstract_clock problem.no_clock = problems.no_clock problem.run_coreir_passes = problems.run_coreir_passes problem.relative_path = problems.relative_path if config.time or problems.time: timer_solve = Logger.start_timer("Problem %s" % problem.name, False) try: self.solve_problem(problem, config) Logger.msg(" %s\n" % problem.status, 0, not (Logger.level(1))) if config.time or problems.time: problem.time = Logger.get_timer(timer_solve, False) except KeyboardInterrupt as e: Logger.msg("\b\b Skipped!\n", 0)
def solve_safety_inc_bwd(self, hts, prop, k, assert_property=False): self._reset_assertions(self.solver) if TS.has_next(prop): Logger.error( "Invariant checking with next variables only supports FWD strategy" ) init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() formula = self.at_ptime(And(Not(prop), invar), -1) Logger.log("Add not property at time %d" % 0, 2) self._add_assertion(self.solver, formula) t = 0 while (t < k + 1): self._push(self.solver) pinit = self.at_ptime(init, t - 1) Logger.log("Add init at time %d" % t, 2) self._add_assertion(self.solver, pinit) if self._solve(self.solver): Logger.log("Counterexample found with k=%s" % (t), 1) model = self._get_model(self.solver) return (t, model) else: Logger.log("No counterexample found with k=%s" % (t), 1) Logger.msg(".", 0, not (Logger.level(1))) self._pop(self.solver) trans_t = self.unroll(trans, invar, t, t + 1) self._add_assertion(self.solver, trans_t) if assert_property and t > 0: prop_t = self.unroll(TRUE(), prop, t - 1, t) self._add_assertion(self.solver, prop_t) Logger.log("Add property at time %d" % t, 2) t += 1 return (t - 1, None)
def solve_liveness(self, hts, prop, k, k_min=0, eventually=False, lemmas=None): if lemmas is not None: (hts, res) = self.add_lemmas(hts, prop, lemmas) if res: Logger.log("Lemmas imply the property", 1) Logger.log("", 0, not (Logger.level(1))) return (0, True) if self.config.incremental: return self.solve_liveness_inc(hts, prop, k, k_min, eventually) return self.solve_liveness_fwd(hts, prop, k)
def solve_safety_inc_fwd(self, hts, prop, k, k_min, \ all_vars=False, generalize=None, prove=None): add_unsat_cons = False prove = self.config.prove if prove is None else prove solver_name = "inc_fwd%s"%("_prove" if prove else "") solver = self.solver.copy(solver_name) self._reset_assertions(solver) if prove: solver_ind = self.solver.copy("%s_ind"%solver_name) self._reset_assertions(solver_ind) if all_vars: relevant_vars = hts.vars else: relevant_vars = hts.state_vars | hts.output_vars init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() acc_init = TRUE() acc_prop = TRUE() acc_loop_free = TRUE() trans_t = TRUE() if self.config.simplify: Logger.log("Simplifying the Transition System", 1) if Logger.level(2): timer = Logger.start_timer("Simplify") init = simplify(init) trans = simplify(trans) invar = simplify(invar) if Logger.level(2): Logger.get_timer(timer) n_prop_t = FALSE() init_0 = self.at_time(And(init, invar), 0) Logger.log("Add init and invar", 2) self._add_assertion(solver, init_0) if prove: # add invariants at time 0, but not init self._add_assertion(solver_ind, self.at_time(invar, 0), "invar") next_prop = TS.has_next(prop) if next_prop: if k < 1: Logger.error("Invariant checking with next variables requires at least k=1") k_min = 1 skip_push = False constraints = TRUE() t = k_min for i in range(t): trans_t = self.unroll(trans, invar, i+1, i) self._add_assertion(solver, trans_t) Logger.msg("Unroll and call check-sat without property", 2) # Note: Seems to help a lot to call check-sat here # without this some solvers will run out of memory on large problems # it likely lets them do some internal clean up, and learn some things # about the model self._solve(solver) Logger.msg("_", 0, not(Logger.level(1))) while (t < k+1): if not skip_push: self._push(solver) skip_push = False t_prop = t-1 if next_prop else t if k_min > 0: if (not next_prop) or (next_prop and t>0): if n_prop_t == FALSE(): n_prop_t = self.at_time(Not(prop), t_prop) else: n_prop_t = Or(n_prop_t, self.at_time(Not(prop), t_prop)) else: n_prop_t = self.at_time(Not(prop), t) Logger.log("Add not property at time %d"%t, 2) if not skip_push: self._add_assertion(solver, n_prop_t, "Property") if constraints != TRUE(): self._add_assertion(solver, self.at_time(constraints, t), "Addditional Constraints") if t >= k_min: Logger.log("\nSolving for k=%s"%(t), 1) if self.preferred is not None: try: for (var, val) in self.preferred: for j in range(t+1): solver.solver.set_preferred_var(TS.get_timed(var, j), val) except: Logger.warning("Current solver does not support preferred variables") self.preferred = None if self._solve(solver): Logger.log("Counterexample found with k=%s"%(t), 1) model = self._get_model(solver) if generalize is not None: constr, res = generalize(model, t) if res: return (t, model) constraints = And(constraints, Not(constr)) skip_push = True continue else: return (t, model) else: Logger.log("No counterexample found with k=%s"%(t), 1) Logger.msg(".", 0, not(Logger.level(1))) if add_unsat_cons and prove: self._add_assertion(solver, Implies(self.at_time(And(init, invar), 1), self.at_time(Not(prop), t_prop+1))) self._add_assertion(solver, Not(n_prop_t)) else: Logger.log("\nSkipping solving for k=%s (k_min=%s)"%(t,k_min), 1) Logger.msg("_", 0, not(Logger.level(1))) self._pop(solver) skip_push = False if prove: if t > k_min: loop_free = self.loop_free(relevant_vars, t, t-1) # Checking I & T & loopFree acc_init = And(acc_init, self.at_time(Not(init), t)) acc_loop_free = And(acc_loop_free, loop_free) self._push(solver) self._add_assertion(solver, acc_init) self._add_assertion(solver, acc_loop_free) if self._solve(solver): Logger.log("Induction (I & lF) failed with k=%s"%(t), 1) else: Logger.log("Induction (I & lF) holds with k=%s"%(t), 1) return (t, True) self._pop(solver) # Checking T & loopFree & !P self._add_assertion(solver_ind, trans_t, comment="trans") self._add_assertion(solver_ind, loop_free, comment="loop_free") self._push(solver_ind) self._add_assertion(solver_ind, self.at_time(Not(prop), t_prop)) if self._solve(solver_ind): Logger.log("Induction (lF & !P) failed with k=%s"%(t), 1) else: Logger.log("Induction (lF & !P) holds with k=%s"%(t), 1) return (t, True) self._pop(solver_ind) self._add_assertion(solver_ind, self.at_time(prop, t_prop), "prop") else: if not next_prop: self._add_assertion(solver_ind, self.at_time(prop, t_prop), "prop") else: # add skipped transition self._add_assertion(solver_ind, trans_t, comment="trans") trans_t = self.unroll(trans, invar, t+1, t) self._add_assertion(solver, trans_t) t += 1 return (t-1, None)
def solve_liveness_inc_fwd(self, hts, prop, k, k_min, eventually=False): self._reset_assertions(self.solver) init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() if self.config.simplify: Logger.log("Simplifying the Transition System", 1) if Logger.level(1): timer = Logger.start_timer("Simplify") init = simplify(init) trans = simplify(trans) invar = simplify(invar) if Logger.level(1): Logger.get_timer(timer) heqvar = None if not eventually: heqvar = Symbol(HEQVAR, BOOL) self._init_at_time(hts.vars.union(set([heqvar])), k) if self.config.prove: self.solver_klive = self.solver.copy("klive") self._reset_assertions(self.solver_klive) self._add_assertion(self.solver_klive, self.at_time(invar, 0)) if eventually: self._add_assertion(self.solver_klive, self.at_time(init, 0)) propt = FALSE() formula = And(init, invar) formula = self.at_time(formula, 0) Logger.log("Add init and invar", 2) self._add_assertion(self.solver, formula) next_prop = TS.has_next(prop) if next_prop: if k < 1: Logger.error( "Liveness checking with next variables requires at least k=1" ) k_min = 1 t = 0 while (t < k + 1): self._push(self.solver) loopback = FALSE() if t > 0: loopback = self.all_loopbacks(self.hts.vars, t, heqvar) Logger.log("Add loopbacks at time %d" % t, 2) self._add_assertion(self.solver, loopback) if t >= k_min: self._write_smt2_comment(self.solver, "Solving for k=%s" % (t)) Logger.log("\nSolving for k=%s" % (t), 1) if self._solve(self.solver): Logger.log("Counterexample found with k=%s" % (t), 1) model = self._get_model(self.solver) return (t, model) else: Logger.log("No counterexample found with k=%s" % (t), 1) Logger.msg(".", 0, not (Logger.level(1))) else: Logger.log("Skipping solving for k=%s (k_min=%s)" % (t, k_min), 1) Logger.msg(".", 0, not (Logger.level(1))) self._pop(self.solver) n_prop = Not(prop) if not eventually: n_prop = Or(n_prop, Not(heqvar)) if next_prop: if t > 0: propt = self.at_time(n_prop, t - 1) else: propt = self.at_time(n_prop, t) self._add_assertion(self.solver, propt) if self.config.prove: if t > 0: self._add_assertion(self.solver_klive, trans_t) self._write_smt2_comment(self.solver_klive, "Solving for k=%s" % (t)) if next_prop: if t > 0: propt = self.at_time(Not(prop), t - 1) else: propt = self.at_time(Not(prop), t) self._add_assertion(self.solver_klive, propt) if t >= k_min: if self._solve(self.solver_klive): Logger.log("K-Liveness failed with k=%s" % (t), 1) else: Logger.log("K-Liveness holds with k=%s" % (t), 1) return (t, True) else: self._add_assertion(self.solver_klive, self.at_time(Not(prop), 0)) # self._push(self.solver_klive) # self._add_assertion(self.solver_klive, self.at_time(prop, 0)) # res = self._solve(self.solver_klive) # self._pop(self.solver_klive) # if res: # self._add_assertion(self.solver_klive, self.at_time(prop, 0)) # else: # self._add_assertion(self.solver_klive, self.at_time(Not(prop), 0)) trans_t = self.unroll(trans, invar, t + 1, t) self._add_assertion(self.solver, trans_t) if self.assert_property: prop_t = self.unroll(TRUE(), prop, t, t - 1) self._add_assertion(self.solver, prop_t) Logger.log("Add property at time %d" % t, 2) t += 1 return (t - 1, None)
def parse_string(self, strinput): hts = HTS() ts = TS() nodemap = {} node_covered = set([]) # list of tuples of var and cond_assign_list # cond_assign_list is tuples of (condition, value) # where everything is a pysmt FNode # for btor, the condition is always True ftrans = [] initlist = [] invarlist = [] invar_props = [] ltl_props = [] prop_count = 0 # clean string input, remove special characters from names for sc, rep in special_char_replacements.items(): strinput = strinput.replace(sc, rep) def getnode(nid): node_covered.add(nid) if int(nid) < 0: return Ite(BV2B(nodemap[str(-int(nid))]), BV(0, 1), BV(1, 1)) return nodemap[nid] def binary_op(bvop, bop, left, right): if (get_type(left) == BOOL) and (get_type(right) == BOOL): return bop(left, right) return bvop(B2BV(left), B2BV(right)) def unary_op(bvop, bop, left): if (get_type(left) == BOOL): return bop(left) return bvop(left) for line in strinput.split(NL): linetok = line.split() if len(linetok) == 0: continue if linetok[0] == COM: continue (nid, ntype, *nids) = linetok if ntype == SORT: (stype, *attr) = nids if stype == BITVEC: nodemap[nid] = BVType(int(attr[0])) node_covered.add(nid) if stype == ARRAY: nodemap[nid] = ArrayType(getnode(attr[0]), getnode(attr[1])) node_covered.add(nid) if ntype == WRITE: nodemap[nid] = Store(*[getnode(n) for n in nids[1:4]]) if ntype == READ: nodemap[nid] = Select(getnode(nids[1]), getnode(nids[2])) if ntype == ZERO: nodemap[nid] = BV(0, getnode(nids[0]).width) if ntype == ONE: nodemap[nid] = BV(1, getnode(nids[0]).width) if ntype == ONES: width = getnode(nids[0]).width nodemap[nid] = BV((2**width) - 1, width) if ntype == REDOR: width = get_type(getnode(nids[1])).width zeros = BV(0, width) nodemap[nid] = BVNot(BVComp(getnode(nids[1]), zeros)) if ntype == REDAND: width = get_type(getnode(nids[1])).width ones = BV((2**width) - 1, width) nodemap[nid] = BVComp(getnode(nids[1]), ones) if ntype == CONSTD: width = getnode(nids[0]).width nodemap[nid] = BV(int(nids[1]), width) if ntype == CONST: width = getnode(nids[0]).width try: nodemap[nid] = BV(bin_to_dec(nids[1]), width) except ValueError: if not all([i == 'x' or i == 'z' for i in nids[1]]): raise RuntimeError( "If not a valid number, only support " "all don't cares or high-impedance but got {}". format(nids[1])) # create a fresh variable for this non-deterministic constant nodemap[nid] = Symbol('const_' + nids[1], BVType(width)) ts.add_state_var(nodemap[nid]) Logger.warning( "Creating a fresh symbol for unsupported X/Z constant %s" % nids[1]) if ntype == STATE: if len(nids) > 1: nodemap[nid] = Symbol(nids[1], getnode(nids[0])) else: nodemap[nid] = Symbol((SN % nid), getnode(nids[0])) ts.add_state_var(nodemap[nid]) if ntype == INPUT: if len(nids) > 1: nodemap[nid] = Symbol(nids[1], getnode(nids[0])) else: nodemap[nid] = Symbol((SN % nid), getnode(nids[0])) ts.add_input_var(nodemap[nid]) if ntype == OUTPUT: # unfortunately we need to create an extra symbol just to have the output name # we could be smarter about this, but then this parser can't be greedy original_symbol = B2BV(getnode(nids[0])) output_symbol = Symbol(nids[1], original_symbol.get_type()) nodemap[nid] = EqualsOrIff(output_symbol, original_symbol) invarlist.append(nodemap[nid]) node_covered.add(nid) ts.add_output_var(output_symbol) if ntype == AND: nodemap[nid] = binary_op(BVAnd, And, getnode(nids[1]), getnode(nids[2])) if ntype == CONCAT: nodemap[nid] = BVConcat(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == XOR: nodemap[nid] = binary_op(BVXor, Xor, getnode(nids[1]), getnode(nids[2])) if ntype == XNOR: nodemap[nid] = BVNot( binary_op(BVXor, Xor, getnode(nids[1]), getnode(nids[2]))) if ntype == NAND: bvop = lambda x, y: BVNot(BVAnd(x, y)) bop = lambda x, y: Not(And(x, y)) nodemap[nid] = binary_op(bvop, bop, getnode(nids[1]), getnode(nids[2])) if ntype == IMPLIES: nodemap[nid] = BVOr(BVNot(getnode(nids[1])), getnode(nids[2])) if ntype == NOT: nodemap[nid] = unary_op(BVNot, Not, getnode(nids[1])) if ntype == NEG: nodemap[nid] = unary_op(BVNeg, Not, getnode(nids[1])) if ntype == UEXT: nodemap[nid] = BVZExt(B2BV(getnode(nids[1])), int(nids[2])) if ntype == SEXT: nodemap[nid] = BVSExt(B2BV(getnode(nids[1])), int(nids[2])) if ntype == OR: nodemap[nid] = binary_op(BVOr, Or, getnode(nids[1]), getnode(nids[2])) if ntype == ADD: nodemap[nid] = BVAdd(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == SUB: nodemap[nid] = BVSub(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == UGT: nodemap[nid] = BVUGT(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == UGTE: nodemap[nid] = BVUGE(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == ULT: nodemap[nid] = BVULT(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == ULTE: nodemap[nid] = BVULE(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == SGT: nodemap[nid] = BVSGT(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == SGTE: nodemap[nid] = BVSGE(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == SLT: nodemap[nid] = BVSLT(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == SLTE: nodemap[nid] = BVSLE(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == EQ: nodemap[nid] = BVComp(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == NEQ: nodemap[nid] = BVNot(BVComp(getnode(nids[1]), getnode(nids[2]))) if ntype == MUL: nodemap[nid] = BVMul(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == SLICE: nodemap[nid] = BVExtract(B2BV(getnode(nids[1])), int(nids[3]), int(nids[2])) if ntype == SLL: nodemap[nid] = BVLShl(getnode(nids[1]), getnode(nids[2])) if ntype == SRA: nodemap[nid] = BVAShr(getnode(nids[1]), getnode(nids[2])) if ntype == SRL: nodemap[nid] = BVLShr(getnode(nids[1]), getnode(nids[2])) if ntype == ITE: if (get_type(getnode(nids[2])) == BOOL) or (get_type( getnode(nids[3])) == BOOL): nodemap[nid] = Ite(BV2B(getnode(nids[1])), B2BV(getnode(nids[2])), B2BV(getnode(nids[3]))) else: nodemap[nid] = Ite(BV2B(getnode(nids[1])), getnode(nids[2]), getnode(nids[3])) if ntype == NEXT: if (get_type(getnode(nids[1])) == BOOL) or (get_type( getnode(nids[2])) == BOOL): lval = TS.get_prime(getnode(nids[1])) rval = B2BV(getnode(nids[2])) else: lval = TS.get_prime(getnode(nids[1])) rval = getnode(nids[2]) nodemap[nid] = EqualsOrIff(lval, rval) ftrans.append((lval, [(TRUE(), rval)])) if ntype == INIT: if (get_type(getnode(nids[1])) == BOOL) or (get_type( getnode(nids[2])) == BOOL): nodemap[nid] = EqualsOrIff(BV2B(getnode(nids[1])), BV2B(getnode(nids[2]))) elif get_type(getnode(nids[1])).is_array_type(): _type = get_type(getnode(nids[1])) nodemap[nid] = EqualsOrIff( getnode(nids[1]), Array(_type.index_type, default=getnode(nids[2]))) else: nodemap[nid] = EqualsOrIff(getnode(nids[1]), getnode(nids[2])) initlist.append(getnode(nid)) if ntype == CONSTRAINT: nodemap[nid] = BV2B(getnode(nids[0])) invarlist.append(getnode(nid)) if ntype == BAD: nodemap[nid] = getnode(nids[0]) if len(nids) > 1: assert_name = nids[1] description = "Embedded assertion: {}".format(assert_name) else: assert_name = 'embedded_assertion_%i' % prop_count description = 'Embedded assertion number %i' % prop_count prop_count += 1 # Following problem format (name, description, strformula) invar_props.append( (assert_name, description, Not(BV2B(getnode(nid))))) if nid not in nodemap: Logger.error("Unknown node type \"%s\"" % ntype) # get wirename if it exists if ntype not in {STATE, INPUT, OUTPUT, BAD}: # disregard comments at the end of the line try: symbol_idx = nids.index(';') symbol_idx -= 1 # the symbol should be before the comment except: # the symbol is just the end symbol_idx = -1 # check for wirename, if it's an integer, then it's a node ref try: a = int(nids[symbol_idx]) except: try: name = str(nids[symbol_idx]) # use the exact name, unless it has already been used wire = Symbol(name, getnode(nids[0])) if wire in ts.vars: wire = FreshSymbol(getnode(nids[0]), template=name + "%d") invarlist.append(EqualsOrIff(wire, B2BV(nodemap[nid]))) ts.add_var(wire) except: pass if Logger.level(1): name = lambda x: str(nodemap[x]) if nodemap[x].is_symbol() else x uncovered = [name(x) for x in nodemap if x not in node_covered] uncovered.sort() if len(uncovered) > 0: Logger.warning("Unlinked nodes \"%s\"" % ",".join(uncovered)) if not self.symbolic_init: init = simplify(And(initlist)) else: init = TRUE() invar = simplify(And(invarlist)) # instead of trans, we're using the ftrans format -- see below ts.set_behavior(init, TRUE(), invar) # add ftrans for var, cond_assign_list in ftrans: ts.add_func_trans(var, cond_assign_list) hts.add_ts(ts) return (hts, invar_props, ltl_props)
def solve_safety_inc_bwd(self, hts, prop, k, assert_property=False, generalize=None): solver = self.solver.copy("inc_bwd") self._reset_assertions(solver) has_next = TS.has_next(prop) if has_next: prop = TS.to_prev(prop) init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() formula = self.at_ptime(And(Not(prop), invar), -1) Logger.log("Add not property at time %d"%0, 2) self._add_assertion(solver, formula) skip_push = False constraints = TRUE() models = 0 t = 0 k_min = 1 if has_next else 0 while (t < k+1): if not skip_push: self._push(solver) skip_push = False if not skip_push: pinit = self.at_ptime(init, t-1) Logger.log("Add init at time %d"%t, 2) self._add_assertion(solver, pinit) if constraints != TRUE(): for j in range(t+1): self._add_assertion(solver, self.at_ptime(constraints, j-1), "Addditional Constraints") if self.preferred is not None: try: for (var, val) in self.preferred: solver.solver.set_preferred_var(TS.get_timed(var, t), val) except: Logger.warning("Current solver does not support preferred variables") self.preferred = None if (t >= k_min) and self._solve(solver): Logger.log("Counterexample found with k=%s"%(t), 1) model = self._get_model(solver) models += 1 if models > 20: Logger.msg("R", 0, not(Logger.level(1))) self._reset_solver(solver) models = 0 if generalize is not None: constr, res = generalize(model, t) if res: return (t, model) constraints = And(constraints, Not(constr)) skip_push = True continue else: return (t, model) else: Logger.log("No counterexample found with k=%s"%(t), 1) Logger.msg(".", 0, not(Logger.level(1))) self._pop(solver) skip_push = False trans_t = self.unroll(trans, invar, t, t+1) self._add_assertion(solver, trans_t) if assert_property and t > 0: prop_t = self.unroll(TRUE(), prop, t-1, t) self._add_assertion(solver, prop_t) Logger.log("Add property at time %d"%t, 2) t += 1 return (t-1, None)
def solve_problems(self, problems, config): encoder_config = self.problems2encoder_config(config, problems) self.sparser = StringParser(encoder_config) self.lparser = LTLParser() self.coi = ConeOfInfluence() invar_props = [] ltl_props = [] si = False if len(problems.symbolic_inits) == 0: problems.symbolic_inits.add(si) HTSM = 0 HTS2 = 1 HTSD = (HTSM, si) model_extension = config.model_extension if problems.model_extension is None else problems.model_extension assume_if_true = config.assume_if_true or problems.assume_if_true cache_files = config.cache_files or problems.cache_files clean_cache = config.clean_cache modifier = None if model_extension is not None: modifier = lambda hts: ModelExtension.extend( hts, ModelModifiersFactory.modifier_by_name(model_extension)) # generate systems for each problem configuration systems = {} for si in problems.symbolic_inits: encoder_config.symbolic_init = si (systems[(HTSM, si)], invar_props, ltl_props) = self.parse_model(problems.relative_path, \ problems.model_file, \ encoder_config, \ "System 1", \ modifier, \ cache_files=cache_files, \ clean_cache=clean_cache) if problems.equivalence is not None: (systems[(HTS2, si)], _, _) = self.parse_model(problems.relative_path, \ problems.equivalence, \ encoder_config, \ "System 2", \ cache_files=cache_files, \ clean_cache=clean_cache) else: systems[(HTS2, si)] = None if config.safety or config.problems: for invar_prop in invar_props: inv_prob = problems.new_problem() inv_prob.verification = VerificationType.SAFETY inv_prob.name = invar_prop[0] inv_prob.description = invar_prop[1] inv_prob.formula = invar_prop[2] problems.add_problem(inv_prob) if config.ltl or config.problems: for ltl_prop in ltl_props: ltl_prob = problems.new_problem() ltl_prob.verification = VerificationType.LTL ltl_prob.name = ltl_prop[0] ltl_prob.description = ltl_prop[1] ltl_prob.formula = ltl_prop[2] problems.add_problem(ltl_prob) if HTSD in systems: problems._hts = systems[HTSD] for problem in problems.problems: problem.hts = systems[(HTSM, problem.symbolic_init)] if problems._hts is None: problems._hts = problem.hts problem.hts2 = systems[(HTS2, problem.symbolic_init)] if problems._hts2 is None: problems._hts2 = problem.hts2 problem.vcd = problems.vcd or config.vcd or problem.vcd problem.abstract_clock = problems.abstract_clock or config.abstract_clock problem.add_clock = problems.add_clock or config.add_clock problem.coi = problems.coi or config.coi problem.run_coreir_passes = problems.run_coreir_passes problem.relative_path = problems.relative_path problem.cardinality = max(problems.cardinality, config.cardinality) if not problem.full_trace: problem.full_trace = problems.full_trace if not problem.trace_vars_change: problem.trace_vars_change = problems.trace_vars_change if not problem.trace_all_vars: problem.trace_all_vars = problems.trace_all_vars if not problem.clock_behaviors: clk_bhvs = [ p for p in [problems.clock_behaviors, config.clock_behaviors] if p is not None ] if len(clk_bhvs) > 0: problem.clock_behaviors = ";".join(clk_bhvs) if not problem.generators: problem.generators = config.generators Logger.log( "Solving with abstract_clock=%s, add_clock=%s" % (problem.abstract_clock, problem.add_clock), 2) if problem.trace_prefix is not None: problem.trace_prefix = "".join( [problem.relative_path, problem.trace_prefix]) if config.time or problems.time: timer_solve = Logger.start_timer("Problem %s" % problem.name, False) try: self.__solve_problem(problem, config) if problem.verification is None: Logger.log("Unset verification", 2) continue Logger.msg(" %s\n" % problem.status, 0, not (Logger.level(1))) if (assume_if_true) and \ (problem.status == VerificationStatus.TRUE) and \ (problem.assumptions == None) and \ (problem.verification == VerificationType.SAFETY): ass_ts = TS("Previous assumption from property") if TS.has_next(problem.formula): ass_ts.trans = problem.formula else: ass_ts.invar = problem.formula problem.hts.reset_formulae() problem.hts.add_ts(ass_ts) if config.time or problems.time: problem.time = Logger.get_timer(timer_solve, False) except KeyboardInterrupt as e: Logger.msg("\b\b Skipped!\n", 0)
def parse_model(self, \ relative_path, \ model_files, \ encoder_config, \ name=None, \ modifier=None, \ cache_files=False, \ clean_cache=False): hts = HTS(name if name is not None else "System") invar_props = [] ltl_props = [] models = model_files.split(FILE_SP) for strfile in models: (strfile, flags) = self.get_file_flags(strfile) filetype = strfile.split(".")[-1] strfile = strfile.replace("~", os.path.expanduser("~")) if strfile[0] != "/": strfile = relative_path + strfile parser = None for av_parser in ModelParsersFactory.get_parsers(): assert av_parser.name is not None if filetype in av_parser.get_extensions(): parser = av_parser if not self.parser: self.parser = av_parser if parser is not None: if not os.path.isfile(strfile): Logger.error("File \"%s\" does not exist" % strfile) if cache_files: md5 = self.md5(strfile) cf = "-".join(["1" if encoder_config.abstract_clock else "0", \ "1" if encoder_config.add_clock else "0", \ "1" if encoder_config.boolean else "0"]) cachefile = "%s-%s" % (md5, cf) cachedir = "%s/%s" % ("/".join( strfile.split("/")[:-1]), COSACACHEDIR) if cache_files and self._is_cached(cachedir, cachefile, clean_cache): Logger.msg( "Loading from cache file \"%s\"... " % (strfile), 0) (hts_a, inv_a, ltl_a, model_info) = self._from_cache(cachedir, cachefile, encoder_config, flags) else: Logger.msg("Parsing file \"%s\"... " % (strfile), 0) (hts_a, inv_a, ltl_a) = parser.parse_file(strfile, encoder_config, flags) model_info = parser.get_model_info() if modifier is not None: modifier(hts_a) if cache_files and not clean_cache: self._to_cache(cachedir, cachefile, hts_a, inv_a, ltl_a, model_info) self.model_info.combine(model_info) hts.combine(hts_a) invar_props += inv_a ltl_props += ltl_a Logger.log("DONE", 0) continue Logger.error( "Filetype \"%s\" unsupported or parser is not available" % filetype) if Logger.level(1): print(hts.print_statistics(name, Logger.level(2))) return (hts, invar_props, ltl_props)
def __solve_problem(self, problem, config): if problem.name is not None: Logger.log("\n*** Analyzing problem \"%s\" ***" % (problem), 1) Logger.msg("Solving \"%s\" " % problem.name, 0, not (Logger.level(1))) parsing_defs = [problem.formula, problem.lemmas, problem.assumptions] for i in range(len(parsing_defs)): if parsing_defs[i] is not None: pdef_file = problem.relative_path + parsing_defs[i] if os.path.isfile(pdef_file): with open(pdef_file) as f: parsing_defs[i] = [ p.strip() for p in f.read().strip().split("\n") ] else: parsing_defs[i] = [ p.strip() for p in parsing_defs[i].split(MODEL_SP) ] else: parsing_defs[i] = None [formulae, problem.lemmas, problem.assumptions] = parsing_defs ParametricBehavior.apply_to_problem(problem, self.model_info) assumps = None lemmas = None trace = None traces = None if formulae is None: if problem.verification == VerificationType.SIMULATION: formulae = ["True"] elif (problem.verification is not None) and ( problem.verification != VerificationType.EQUIVALENCE): Logger.error("Property not provided") accepted_ver = False if formulae is not None: problem.formula = formulae[0] precondition = config.precondition if config.precondition is not None else problem.precondition if precondition and problem.verification == VerificationType.SAFETY: problem.formula = "(%s) -> (%s)" % (precondition, problem.formula) if (problem.verification != VerificationType.EQUIVALENCE) and ( problem.formula is not None): assumps = [ t[1] for t in self.sparser.parse_formulae(problem.assumptions) ] lemmas = [ t[1] for t in self.sparser.parse_formulae(problem.lemmas) ] for ass in assumps: problem.hts.add_assumption(ass) for lemma in lemmas: problem.hts.add_lemma(lemma) if problem.verification != VerificationType.LTL: (strprop, prop, types) = self.sparser.parse_formulae([problem.formula])[0] else: (strprop, prop, types) = self.lparser.parse_formulae([problem.formula])[0] problem.formula = prop if problem.verification is None: return problem if problem.coi: if Logger.level(2): timer = Logger.start_timer("COI") problem.hts = self.coi.compute(problem.hts, problem.formula) if Logger.level(2): Logger.get_timer(timer) mc_config = self.problem2mc_config(problem, config) bmc_safety = BMCSafety(problem.hts, mc_config) bmc_parametric = BMCParametric(problem.hts, mc_config) bmc_ltl = BMCLTL(problem.hts, mc_config) res = VerificationStatus.UNC bmc_length = max(problem.bmc_length, config.bmc_length) bmc_length_min = max(problem.bmc_length_min, config.bmc_length_min) if problem.verification == VerificationType.SAFETY: accepted_ver = True Logger.log("Property: %s" % (prop.serialize(threshold=100)), 2) res, trace, _ = bmc_safety.safety(prop, bmc_length, bmc_length_min, config.processes) if problem.verification == VerificationType.LTL: accepted_ver = True res, trace, _ = bmc_ltl.ltl(prop, bmc_length, bmc_length_min) if problem.verification == VerificationType.SIMULATION: accepted_ver = True res, trace = bmc_safety.simulate(prop, bmc_length) if problem.verification == VerificationType.PARAMETRIC: accepted_ver = True Logger.log("Property: %s" % (prop.serialize(threshold=100)), 2) res, traces, problem.region = bmc_parametric.parametric_safety( prop, bmc_length, bmc_length_min, ModelExtension.get_parameters(problem.hts), at_most=problem.cardinality) hts = problem.hts if problem.verification == VerificationType.EQUIVALENCE: accepted_ver = True htseq, miter_out = Miter.combine_systems(problem.hts, \ problem.hts2, \ bmc_length, \ problem.symbolic_init, \ problem.formula, \ True) if problem.assumptions is not None: assumps = [ t[1] for t in self.sparser.parse_formulae(problem.assumptions) ] if problem.lemmas is not None: lemmas = [ t[1] for t in self.sparser.parse_formulae(problem.lemmas) ] if assumps is not None: for assumption in assumps: htseq.add_assumption(assumption) if lemmas is not None: for lemma in lemmas: htseq.add_lemma(lemma) bmcseq = BMCSafety(htseq, mc_config) hts = htseq res, trace, t = bmcseq.safety(miter_out, bmc_length, bmc_length_min) if not accepted_ver: Logger.error("Invalid verification type") problem.status = res if trace is not None: problem.traces = self.__process_trace(hts, trace, config, problem) if traces is not None: problem.traces = [] for trace in traces: problem.traces += self.__process_trace(hts, trace, config, problem) if problem.assumptions is not None: problem.hts.assumptions = None Logger.log("\n*** Problem \"%s\" is %s ***" % (problem, res), 1)
def sim_no_unroll(self, hts, cover, k, all_vars=True, inc=False): init = hts.single_init() invar = hts.single_invar() trans = hts.single_trans() init_0 = self.at_time(init, 0) invar_0 = self.at_time(invar, 0) trans_01 = self.unroll(trans, invar, 1) cover_1 = self.at_time(cover, 1) full_model = {} if all_vars: relevant_vars = hts.vars else: relevant_vars = hts.state_vars | hts.output_vars relevant_vars_0 = [TS.get_timed(v, 0) for v in relevant_vars] relevant_vars_1 = [TS.get_timed(v, 1) for v in relevant_vars] relevant_vars_01 = [(TS.get_timed(v, 0), TS.get_timed(v, 1), v) for v in relevant_vars] self._reset_assertions(self.solver) # Picking Initial State Logger.log("\nSolving for k=0", 1) self._add_assertion(self.solver, And(init_0, invar_0)) if self._solve(self.solver): init_model = self._get_model(self.solver, relevant_vars_0) init_0 = And([EqualsOrIff(v, init_model[v]) for v in relevant_vars_0]) for v in relevant_vars_0: full_model[v] = init_model[v] Logger.msg(".", 0, not(Logger.level(1))) else: return (0, None) self._reset_assertions(self.solver) if inc: self._add_assertion(self.solver, trans_01) self._add_assertion(self.solver, invar_0) init_model = None for t in range(1, k + 1): Logger.log("\nSolving for k=%s"%(t), 1) if not inc: self._reset_assertions(self.solver, True) formula = And(init_0, invar_0) self._add_assertion(self.solver, trans_01) else: formula = init_0 self._push(self.solver) self._add_assertion(self.solver, formula) res_step = self._solve(self.solver) if res_step: Logger.msg(".", 0, not(Logger.level(1))) Logger.log("Able to step forward at k=%s"%(t), 2) if all_vars: init_model = self._get_model(self.solver) else: init_model = self._get_model(self.solver, relevant_vars_1) model = init_model else: Logger.log("System deadlocked at k=%s"%(t), 2) return (-1, full_model) # Use previous model as initial state for next sat call init_0 = [] init_1 = [] for v in relevant_vars_01: if v[1] not in init_model: continue val = init_model[v[1]] full_model[TS.get_timed(v[2], t)] = val init_0.append(EqualsOrIff(v[0], val)) init_1.append(EqualsOrIff(v[1], val)) init_0 = And(init_0) if cover != TRUE(): init_1 = And(init_1) self._add_assertion(self.solver, init_1) self._add_assertion(self.solver, cover_1) res_cont = self._solve(self.solver) if res_cont: Logger.log('Reached cover in no unroll simulation at k=%s'%(t), 2) model = init_model return (t, full_model) else: Logger.log('Cover not reached at k=%s'%t, 2) if inc: self._pop(self.solver) return (t, full_model)
def parse_string(self, strinput): hts = HTS() ts = TS() nodemap = {} node_covered = set([]) # list of tuples of var and cond_assign_list # cond_assign_list is tuples of (condition, value) # where everything is a pysmt FNode # for btor, the condition is always True ftrans = [] initlist = [] invarlist = [] invar_props = [] ltl_props = [] prop_count = 0 # clean string input, remove special characters from names for sc, rep in special_char_replacements.items(): strinput = strinput.replace(sc, rep) def getnode(nid): node_covered.add(nid) if int(nid) < 0: return Ite(BV2B(nodemap[str(-int(nid))]), BV(0,1), BV(1,1)) return nodemap[nid] def binary_op(bvop, bop, left, right): if (get_type(left) == BOOL) and (get_type(right) == BOOL): return bop(left, right) return bvop(B2BV(left), B2BV(right)) def unary_op(bvop, bop, left): if (get_type(left) == BOOL): return bop(left) return bvop(left) for line in strinput.split(NL): linetok = line.split() if len(linetok) == 0: continue if linetok[0] == COM: continue (nid, ntype, *nids) = linetok if ntype == SORT: (stype, *attr) = nids if stype == BITVEC: nodemap[nid] = BVType(int(attr[0])) node_covered.add(nid) if stype == ARRAY: nodemap[nid] = ArrayType(getnode(attr[0]), getnode(attr[1])) node_covered.add(nid) if ntype == WRITE: nodemap[nid] = Store(*[getnode(n) for n in nids[1:4]]) if ntype == READ: nodemap[nid] = Select(getnode(nids[1]), getnode(nids[2])) if ntype == ZERO: nodemap[nid] = BV(0, getnode(nids[0]).width) if ntype == ONE: nodemap[nid] = BV(1, getnode(nids[0]).width) if ntype == ONES: width = getnode(nids[0]).width nodemap[nid] = BV((2**width)-1, width) if ntype == REDOR: width = get_type(getnode(nids[1])).width zeros = BV(0, width) nodemap[nid] = BVNot(BVComp(getnode(nids[1]), zeros)) if ntype == REDXOR: width = get_type(getnode(nids[1])).width nodemap[nid] = BV(0, width) zeros = BV(0, width) for yx_i in range(width): tmp = BV(1 << yx_i, width) tmp_2 = BVAnd(tmp, B2BV(getnode(nids[1]))) tmp_3 = BVZExt(B2BV(BVComp(tmp_2, zeros)), int(width - 1)) nodemap[nid] = BVAdd(tmp_3, nodemap[nid]) nodemap[nid] = BVComp(BVAnd(BV(1, width), nodemap[nid]), BV(1, width)) if ntype == REDAND: width = get_type(getnode(nids[1])).width ones = BV((2**width)-1, width) nodemap[nid] = BVComp(getnode(nids[1]), ones) if ntype == CONSTD: width = getnode(nids[0]).width nodemap[nid] = BV(int(nids[1]), width) if ntype == CONST: width = getnode(nids[0]).width nodemap[nid] = BV(bin_to_dec(nids[1]), width) if ntype == STATE: if len(nids) > 1: nodemap[nid] = Symbol(nids[1], getnode(nids[0])) else: nodemap[nid] = Symbol((SN%nid), getnode(nids[0])) ts.add_state_var(nodemap[nid]) if ntype == INPUT: if len(nids) > 1: nodemap[nid] = Symbol(nids[1], getnode(nids[0])) else: nodemap[nid] = Symbol((SN%nid), getnode(nids[0])) ts.add_input_var(nodemap[nid]) if ntype == OUTPUT: # unfortunately we need to create an extra symbol just to have the output name # we could be smarter about this, but then this parser can't be greedy original_symbol = getnode(nids[0]) output_symbol = Symbol(nids[1], original_symbol.get_type()) nodemap[nid] = EqualsOrIff(output_symbol, original_symbol) invarlist.append(nodemap[nid]) node_covered.add(nid) ts.add_output_var(output_symbol) if ntype == AND: nodemap[nid] = binary_op(BVAnd, And, getnode(nids[1]), getnode(nids[2])) if ntype == CONCAT: nodemap[nid] = BVConcat(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == XOR: nodemap[nid] = binary_op(BVXor, Xor, getnode(nids[1]), getnode(nids[2])) if ntype == XNOR: nodemap[nid] = BVNot(binary_op(BVXor, Xor, getnode(nids[1]), getnode(nids[2]))) if ntype == NAND: bvop = lambda x,y: BVNot(BVAnd(x, y)) bop = lambda x,y: Not(And(x, y)) nodemap[nid] = binary_op(bvop, bop, getnode(nids[1]), getnode(nids[2])) if ntype == IMPLIES: nodemap[nid] = BVOr(BVNot(getnode(nids[1])), getnode(nids[2])) if ntype == NOT: nodemap[nid] = unary_op(BVNot, Not, getnode(nids[1])) if ntype == NEG: nodemap[nid] = unary_op(BVNeg, Not, getnode(nids[1])) if ntype == UEXT: nodemap[nid] = BVZExt(B2BV(getnode(nids[1])), int(nids[2])) if ntype == SEXT: nodemap[nid] = BVSExt(B2BV(getnode(nids[1])), int(nids[2])) if ntype == OR: nodemap[nid] = binary_op(BVOr, Or, getnode(nids[1]), getnode(nids[2])) if ntype == ADD: nodemap[nid] = BVAdd(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == SUB: nodemap[nid] = BVSub(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == UGT: nodemap[nid] = BVUGT(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == UGTE: nodemap[nid] = BVUGE(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == ULT: nodemap[nid] = BVULT(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == ULTE: nodemap[nid] = BVULE(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == SGT: nodemap[nid] = BVSGT(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == SGTE: nodemap[nid] = BVSGE(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == SLT: nodemap[nid] = BVSLT(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == SLTE: nodemap[nid] = BVSLE(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == EQ: nodemap[nid] = BVComp(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == NEQ: nodemap[nid] = BVNot(BVComp(getnode(nids[1]), getnode(nids[2]))) if ntype == MUL: nodemap[nid] = BVMul(B2BV(getnode(nids[1])), B2BV(getnode(nids[2]))) if ntype == SLICE: nodemap[nid] = BVExtract(B2BV(getnode(nids[1])), int(nids[3]), int(nids[2])) if ntype == SLL: nodemap[nid] = BVLShl(getnode(nids[1]), getnode(nids[2])) if ntype == SRA: nodemap[nid] = BVAShr(getnode(nids[1]), getnode(nids[2])) if ntype == SRL: nodemap[nid] = BVLShr(getnode(nids[1]), getnode(nids[2])) if ntype == ITE: if (get_type(getnode(nids[2])) == BOOL) or (get_type(getnode(nids[3])) == BOOL): nodemap[nid] = Ite(BV2B(getnode(nids[1])), B2BV(getnode(nids[2])), B2BV(getnode(nids[3]))) else: nodemap[nid] = Ite(BV2B(getnode(nids[1])), getnode(nids[2]), getnode(nids[3])) if ntype == NEXT: if (get_type(getnode(nids[1])) == BOOL) or (get_type(getnode(nids[2])) == BOOL): lval = TS.get_prime(getnode(nids[1])) rval = BV2B(getnode(nids[2])) else: lval = TS.get_prime(getnode(nids[1])) rval = getnode(nids[2]) nodemap[nid] = EqualsOrIff(lval, rval) ftrans.append( (lval, [(TRUE(), rval)]) ) if ntype == INIT: if (get_type(getnode(nids[1])) == BOOL) or (get_type(getnode(nids[2])) == BOOL): nodemap[nid] = EqualsOrIff(BV2B(getnode(nids[1])), BV2B(getnode(nids[2]))) else: nodemap[nid] = EqualsOrIff(getnode(nids[1]), getnode(nids[2])) initlist.append(getnode(nid)) if ntype == CONSTRAINT: nodemap[nid] = BV2B(getnode(nids[0])) invarlist.append(getnode(nid)) if ntype == BAD: nodemap[nid] = getnode(nids[0]) if ASSERTINFO in line: filename_lineno = os.path.basename(nids[3]) assert_name = 'embedded_assertion_%s'%filename_lineno description = "Embedded assertion at line {1} in {0}".format(*filename_lineno.split(COLON_REP)) else: assert_name = 'embedded_assertion_%i'%prop_count description = 'Embedded assertion number %i'%prop_count prop_count += 1 # Following problem format (name, description, strformula) invar_props.append((assert_name, description, Not(BV2B(getnode(nid))))) if nid not in nodemap: Logger.error("Unknown node type \"%s\""%ntype) # get wirename if it exists if ntype not in {STATE, INPUT, OUTPUT, BAD}: # check for wirename, if it's an integer, then it's a node ref try: a = int(nids[-1]) except: try: wire = Symbol(str(nids[-1]), getnode(nids[0])) invarlist.append(EqualsOrIff(wire, B2BV(nodemap[nid]))) ts.add_var(wire) except: pass if Logger.level(1): name = lambda x: str(nodemap[x]) if nodemap[x].is_symbol() else x uncovered = [name(x) for x in nodemap if x not in node_covered] uncovered.sort() if len(uncovered) > 0: Logger.warning("Unlinked nodes \"%s\""%",".join(uncovered)) if not self.symbolic_init: init = simplify(And(initlist)) else: init = TRUE() invar = simplify(And(invarlist)) # instead of trans, we're using the ftrans format -- see below ts.set_behavior(init, TRUE(), invar) # add ftrans for var, cond_assign_list in ftrans: ts.add_func_trans(var, cond_assign_list) hts.add_ts(ts) return (hts, invar_props, ltl_props)
def solve_safety_inc_int(self, hts, prop, k): init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() solver_proof = self.solver.copy("inc_int_proof") solver = self.solver.copy("inc_int") has_next = TS.has_next(prop) map_10 = dict([(TS.get_timed_name(v.symbol_name(), 1), TS.get_timed_name(v.symbol_name(), 0)) for v in hts.vars]) itp = Interpolator(logic=get_logic(trans)) init = And(init, invar) nprop = Not(prop) def check_overappr(Ri, R): self._reset_assertions(solver_proof) self._add_assertion(solver_proof, And(Ri, Not(R))) if not self._solve(solver_proof): Logger.log("Proof found with k=%s"%(t), 1) return TRUE() Logger.log("Extending initial states (%s)"%int_c, 1) return Or(R, Ri) t = 1 if has_next else 0 trans_t = self.unroll(trans, invar, k+1, gen_list=True) pivot = 2 trans_tA = And(trans_t[:pivot]) init_0 = self.at_time(init, 0) is_sat = True Ri = None self._reset_assertions(solver) while (t < k+1): Logger.log("\nSolving for k=%s"%t, 1) int_c = 0 R = init_0 # trans_t is composed as trans_i, invar_i, trans_i+1, invar_i+1, ... self._add_assertion(solver, trans_t[2*t]) self._add_assertion(solver, trans_t[(2*t)+1]) while True: Logger.log("Add init and invar", 2) self._push(solver) self._add_assertion(solver, R) npropt = self.at_time(nprop, t-1 if has_next else t) Logger.log("Add property time %d"%t, 2) self._add_assertion(solver, npropt) Logger.log("Interpolation at k=%s"%(t), 2) if t > 0: trans_tB = And(trans_t[pivot:(t*2)]) Ri = And(itp.binary_interpolant(And(R, trans_tA), And(trans_tB, npropt))) is_sat = Ri == None if is_sat and self._solve(solver): if R == init_0: Logger.log("Counterexample found with k=%s"%(t), 1) model = self._get_model(solver) return (t, model) else: Logger.log("No counterexample or proof found with k=%s"%(t), 1) Logger.msg(".", 0, not(Logger.level(1))) self._pop(solver) break else: self._pop(solver) if Ri is None: break Ri = substitute(Ri, map_10) res = check_overappr(Ri, R) if res == TRUE(): Logger.log("Proof found with k=%s"%(t), 1) return (t, True) R = res int_c += 1 t += 1 return (t-1, None)