def exit_callback(retcode): if solver_args[0] not in ["avy"]: assert retcode == 0 assert task_status is not None aiw_file.close() job.update_status(task_status) job.log("engine_%d: Status returned by engine: %s" % (engine_idx, task_status)) job.summary.append("engine_%d (%s) returned %s" % (engine_idx, " ".join(engine), task_status)) job.terminate() if task_status == "FAIL" and job.opt_aigsmt != "none": if produced_cex: if mode == "live": task2 = SbyTask(job, "engine_%d" % engine_idx, job.model("smt2"), ("cd %s; %s -g -s %s%s --noprogress --dump-vcd engine_%d/trace.vcd --dump-vlogtb engine_%d/trace_tb.v " + "--dump-smtc engine_%d/trace.smtc --aig model/design_aiger.aim:engine_%d/trace.aiw model/design_smt2.smt2") % (job.workdir, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else " --vlogtb-top %s" % job.opt_tbtop, engine_idx, engine_idx, engine_idx, engine_idx), logfile=open("%s/engine_%d/logfile2.txt" % (job.workdir, engine_idx), "w")) else: task2 = SbyTask(job, "engine_%d" % engine_idx, job.model("smt2"), ("cd %s; %s -s %s%s --noprogress --append %d --dump-vcd engine_%d/trace.vcd --dump-vlogtb engine_%d/trace_tb.v " + "--dump-smtc engine_%d/trace.smtc --aig model/design_aiger.aim:engine_%d/trace.aiw model/design_smt2.smt2") % (job.workdir, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else " --vlogtb-top %s" % job.opt_tbtop, job.opt_append, engine_idx, engine_idx, engine_idx, engine_idx), logfile=open("%s/engine_%d/logfile2.txt" % (job.workdir, engine_idx), "w")) task2_status = None def output_callback2(line): nonlocal task2_status match = re.match(r"^## [0-9: ]+ Status: FAILED", line) if match: task2_status = "FAIL" match = re.match(r"^## [0-9: ]+ Status: PASSED", line) if match: task2_status = "PASS" return line def exit_callback2(line): assert task2_status is not None if mode == "live": assert task2_status == "PASS" else: assert task2_status == "FAIL" job.summary.append("counterexample trace: %s/engine_%d/trace.vcd" % (job.workdir, engine_idx)) task2.output_callback = output_callback2 task2.exit_callback = exit_callback2 else: job.log("engine_%d: Engine did not produce a counter example." % engine_idx)
def exit_callback(retcode): assert retcode == 0 assert task_status is not None job.update_status(task_status) job.log("engine_{}: Status returned by engine: {}".format( engine_idx, task_status)) job.summary.append("engine_{} ({}) returned {}".format( engine_idx, " ".join(engine), task_status)) job.terminate() if task_status == "FAIL" and job.opt_aigsmt != "none": task2 = SbyTask( job, "engine_{}".format(engine_idx), job.model("smt2"), ("cd {}; {} -s {}{} --noprogress --append {} --dump-vcd engine_{i}/trace.vcd --dump-vlogtb engine_{i}/trace_tb.v " + "--dump-smtc engine_{i}/trace.smtc --aig model/design_aiger.aim:engine_{i}/trace.aiw --aig-noheader model/design_smt2.smt2" ).format(job.workdir, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else " --vlogtb-top {}".format(job.opt_tbtop), job.opt_append, i=engine_idx), logfile=open( "{}/engine_{}/logfile2.txt".format(job.workdir, engine_idx), "w")) task2_status = None def output_callback2(line): nonlocal task2_status match = re.match(r"^## [0-9: ]+ Status: FAILED", line) if match: task2_status = "FAIL" match = re.match(r"^## [0-9: ]+ Status: PASSED", line) if match: task2_status = "PASS" return line def exit_callback2(line): assert task2_status is not None assert task2_status == "FAIL" if os.path.exists("{}/engine_{}/trace.vcd".format( job.workdir, engine_idx)): job.summary.append( "counterexample trace: {}/engine_{}/trace.vcd".format( job.workdir, engine_idx)) task2.output_callback = output_callback2 task2.exit_callback = exit_callback2
def run(mode, job, engine_idx, engine): smtbmc_opts = [] nomem_opt = False presat_opt = True unroll_opt = None syn_opt = False stbv_opt = False stdt_opt = False dumpsmt2 = False progress = False basecase_only = False induction_only = False opts, args = getopt.getopt(engine[1:], "", [ "nomem", "syn", "stbv", "stdt", "presat", "nopresat", "unroll", "nounroll", "dumpsmt2", "progress", "basecase", "induction" ]) for o, a in opts: if o == "--nomem": nomem_opt = True elif o == "--syn": syn_opt = True elif o == "--stbv": stbv_opt = True elif o == "--stdt": stdt_opt = True elif o == "--presat": presat_opt = True elif o == "--nopresat": presat_opt = False elif o == "--unroll": unroll_opt = True elif o == "--nounroll": unroll_opt = False elif o == "--dumpsmt2": dumpsmt2 = True elif o == "--progress": progress = True elif o == "--basecase": if induction_only: job.error( "smtbmc options --basecase and --induction are exclusive.") basecase_only = True elif o == "--induction": if basecase_only: job.error( "smtbmc options --basecase and --induction are exclusive.") induction_only = True else: job.error("Invalid smtbmc options %s." % o) for i, a in enumerate(args): if i == 0 and a == "z3" and unroll_opt is None: unroll_opt = False smtbmc_opts += ["-s" if i == 0 else "-S", a] if presat_opt: smtbmc_opts += ["--presat"] if unroll_opt is None or unroll_opt: smtbmc_opts += ["--unroll"] if job.opt_smtc is not None: smtbmc_opts += ["--smtc", "src/%s" % job.opt_smtc] if job.opt_tbtop is not None: smtbmc_opts += ["--vlogtb-top", job.opt_tbtop] model_name = "smt2" if syn_opt: model_name += "_syn" if nomem_opt: model_name += "_nomem" if stbv_opt: model_name += "_stbv" if stdt_opt: model_name += "_stdt" if mode == "prove": if not induction_only: run("prove_basecase", job, engine_idx, engine) if not basecase_only: run("prove_induction", job, engine_idx, engine) return taskname = "engine_%d" % engine_idx trace_prefix = "engine_%d/trace" % engine_idx logfile_prefix = "%s/engine_%d/logfile" % (job.workdir, engine_idx) if mode == "prove_basecase": taskname += ".basecase" logfile_prefix += "_basecase" if mode == "prove_induction": taskname += ".induction" trace_prefix += "_induct" logfile_prefix += "_induction" smtbmc_opts.append("-i") if mode == "cover": smtbmc_opts.append("-c") trace_prefix += "%" if dumpsmt2: smtbmc_opts += ["--dump-smt2", trace_prefix.replace("%", "") + ".smt2"] if not progress: smtbmc_opts.append("--noprogress") task = SbyTask( job, taskname, job.model(model_name), "cd %s; %s %s -t %d --append %d --dump-vcd %s.vcd --dump-vlogtb %s_tb.v --dump-smtc %s.smtc model/design_%s.smt2" % (job.workdir, job.exe_paths["smtbmc"], " ".join(smtbmc_opts), job.opt_depth, job.opt_append, trace_prefix, trace_prefix, trace_prefix, model_name), logfile=open(logfile_prefix + ".txt", "w"), logstderr=(not progress)) if mode == "prove_basecase": job.basecase_tasks.append(task) if mode == "prove_induction": job.induction_tasks.append(task) task_status = None def output_callback(line): nonlocal task_status match = re.match(r"^## [0-9: ]+ Status: FAILED", line) if match: task_status = "FAIL" match = re.match(r"^## [0-9: ]+ Status: PASSED", line) if match: task_status = "PASS" return line def exit_callback(retcode): if task_status is None: job.error("engine_%d: Engine terminated without status." % engine_idx) if mode == "bmc" or mode == "cover": job.update_status(task_status) job.log("engine_%d: Status returned by engine: %s" % (engine_idx, task_status)) job.summary.append("engine_%d (%s) returned %s" % (engine_idx, " ".join(engine), task_status)) if task_status == "FAIL" and mode != "cover": job.summary.append( "counterexample trace: %s/engine_%d/trace.vcd" % (job.workdir, engine_idx)) job.terminate() elif mode in ["prove_basecase", "prove_induction"]: job.log("engine_%d: Status returned by engine for %s: %s" % (engine_idx, mode.split("_")[1], task_status)) job.summary.append("engine_%d (%s) returned %s for %s" % (engine_idx, " ".join(engine), task_status, mode.split("_")[1])) if mode == "prove_basecase": for task in job.basecase_tasks: task.terminate() if task_status == "PASS": job.basecase_pass = True else: job.update_status(task_status) job.summary.append( "counterexample trace: %s/engine_%d/trace.vcd" % (job.workdir, engine_idx)) job.terminate() elif mode == "prove_induction": for task in job.induction_tasks: task.terminate() if task_status == "PASS": job.induction_pass = True else: assert False if job.basecase_pass and job.induction_pass: job.update_status("PASS") job.summary.append("successful proof by k-induction.") job.terminate() else: assert False task.output_callback = output_callback task.exit_callback = exit_callback
def run(mode, job, engine_idx, engine): opts, solver_args = getopt.getopt(engine[1:], "", []) if len(solver_args) == 0: job.error("Missing solver command.") for o, a in opts: job.error("Unexpected AIGER engine options.") if solver_args[0] == "suprove": if mode == "live" and (len(solver_args) == 1 or solver_args[1][0] != "+"): solver_args.insert(1, "+simple_liveness") solver_cmd = " ".join([job.exe_paths["suprove"]] + solver_args[1:]) elif solver_args[0] == "avy": solver_cmd = " ".join([job.exe_paths["avy"], "--cex", "-"] + solver_args[1:]) elif solver_args[0] == "aigbmc": solver_cmd = " ".join([job.exe_paths["aigbmc"]] + solver_args[1:]) else: job.error("Invalid solver command {}.".format(solver_args[0])) task = SbyTask( job, "engine_{}".format(engine_idx), job.model("aig"), "cd {}; {} model/design_aiger.aig".format(job.workdir, solver_cmd), logfile=open( "{}/engine_{}/logfile.txt".format(job.workdir, engine_idx), "w")) task_status = None produced_cex = False end_of_cex = False aiw_file = open("{}/engine_{}/trace.aiw".format(job.workdir, engine_idx), "w") def output_callback(line): nonlocal task_status nonlocal produced_cex nonlocal end_of_cex if task_status is not None: if not end_of_cex and not produced_cex and line.isdigit(): produced_cex = True if not end_of_cex: print(line, file=aiw_file) if line == ".": end_of_cex = True return None if line.startswith("u"): return "No CEX up to depth {}.".format(int(line[1:]) - 1) if line in ["0", "1", "2"]: print(line, file=aiw_file) if line == "0": task_status = "PASS" if line == "1": task_status = "FAIL" if line == "2": task_status = "UNKNOWN" return None def exit_callback(retcode): if solver_args[0] not in ["avy"]: assert retcode == 0 assert task_status is not None aiw_file.close() job.update_status(task_status) job.log("engine_{}: Status returned by engine: {}".format( engine_idx, task_status)) job.summary.append("engine_{} ({}) returned {}".format( engine_idx, " ".join(engine), task_status)) job.terminate() if task_status == "FAIL" and job.opt_aigsmt != "none": if produced_cex: if mode == "live": task2 = SbyTask( job, "engine_{}".format(engine_idx), job.model("smt2"), ("cd {}; {} -g -s {}{} --noprogress --dump-vcd engine_{i}/trace.vcd --dump-vlogtb engine_{i}/trace_tb.v " + "--dump-smtc engine_{i}/trace.smtc --aig model/design_aiger.aim:engine_{i}/trace.aiw model/design_smt2.smt2" ).format(job.workdir, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else " --vlogtb-top {}".format(job.opt_tbtop), i=engine_idx), logfile=open( "{}/engine_{}/logfile2.txt".format( job.workdir, engine_idx), "w")) else: task2 = SbyTask( job, "engine_{}".format(engine_idx), job.model("smt2"), ("cd {}; {} -s {}{} --noprogress --append {} --dump-vcd engine_{i}/trace.vcd --dump-vlogtb engine_{i}/trace_tb.v " + "--dump-smtc engine_{i}/trace.smtc --aig model/design_aiger.aim:engine_{i}/trace.aiw model/design_smt2.smt2" ).format(job.workdir, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else " --vlogtb-top {}".format(job.opt_tbtop), job.opt_append, i=engine_idx), logfile=open( "{}/engine_{}/logfile2.txt".format( job.workdir, engine_idx), "w")) task2_status = None def output_callback2(line): nonlocal task2_status match = re.match(r"^## [0-9: ]+ Status: FAILED", line) if match: task2_status = "FAIL" match = re.match(r"^## [0-9: ]+ Status: PASSED", line) if match: task2_status = "PASS" return line def exit_callback2(line): assert task2_status is not None if mode == "live": assert task2_status == "PASS" else: assert task2_status == "FAIL" if os.path.exists("{}/engine_{}/trace.vcd".format( job.workdir, engine_idx)): job.summary.append( "counterexample trace: {}/engine_{}/trace.vcd". format(job.workdir, engine_idx)) task2.output_callback = output_callback2 task2.exit_callback = exit_callback2 else: job.log("engine_{}: Engine did not produce a counter example.". format(engine_idx)) task.output_callback = output_callback task.exit_callback = exit_callback
def run(mode, job, engine_idx, engine): opts, solver_args = getopt.getopt(engine[1:], "", []) if len(solver_args) == 0: job.error("Missing solver command.") for o, a in opts: job.error("Unexpected BTOR engine options.") if solver_args[0] == "btormc": solver_cmd = job.exe_paths[ "btormc"] + " --stop-first {} -v 1 -kmax {}".format( 0 if mode == "cover" else 1, job.opt_depth - 1) if job.opt_skip is not None: solver_cmd += " -kmin {}".format(job.opt_skip) solver_cmd += " ".join([""] + solver_args[1:]) elif solver_args[0] == "cosa2": solver_cmd = job.exe_paths["cosa2"] + " -v 1 -e bmc -k {}".format( job.opt_depth - 1) else: job.error("Invalid solver command {}.".format(solver_args[0])) common_state = SimpleNamespace() common_state.solver_status = None common_state.produced_cex = 0 common_state.expected_cex = 1 common_state.wit_file = None common_state.assert_fail = False common_state.produced_traces = [] common_state.print_traces_max = 5 common_state.running_tasks = 0 def print_traces_and_terminate(): if mode == "cover": if common_state.assert_fail: task_status = "FAIL" elif common_state.expected_cex == 0: task_status = "pass" elif common_state.solver_status == "sat": task_status = "pass" elif common_state.solver_status == "unsat": task_status = "FAIL" else: job.error( "engine_{}: Engine terminated without status.".format( engine_idx)) else: if common_state.expected_cex == 0: task_status = "pass" elif common_state.solver_status == "sat": task_status = "FAIL" elif common_state.solver_status == "unsat": task_status = "pass" else: job.error( "engine_{}: Engine terminated without status.".format( engine_idx)) job.update_status(task_status.upper()) job.log("engine_{}: Status returned by engine: {}".format( engine_idx, task_status)) job.summary.append("engine_{} ({}) returned {}".format( engine_idx, " ".join(engine), task_status)) if len(common_state.produced_traces) == 0: job.log("engine_{}: Engine did not produce a{}example.".format( engine_idx, " counter" if mode != "cover" else "n ")) elif len( common_state.produced_traces) <= common_state.print_traces_max: job.summary.extend(common_state.produced_traces) else: job.summary.extend( common_state.produced_traces[:common_state.print_traces_max]) excess_traces = len( common_state.produced_traces) - common_state.print_traces_max job.summary.append("and {} further trace{}".format( excess_traces, "s" if excess_traces > 1 else "")) job.terminate() if mode == "cover": def output_callback2(line): match = re.search(r"Assert failed in test", line) if match: common_state.assert_fail = True return line else: def output_callback2(line): return line def make_exit_callback(suffix): def exit_callback2(retcode): assert retcode == 0 vcdpath = "{}/engine_{}/trace{}.vcd".format( job.workdir, engine_idx, suffix) if os.path.exists(vcdpath): common_state.produced_traces.append("{}trace: {}".format( "" if mode == "cover" else "counterexample ", vcdpath)) common_state.running_tasks -= 1 if (common_state.running_tasks == 0): print_traces_and_terminate() return exit_callback2 def output_callback(line): if mode == "cover": if solver_args[0] == "btormc": match = re.search(r"calling BMC on ([0-9]+) properties", line) if match: common_state.expected_cex = int(match[1]) assert common_state.produced_cex == 0 else: job.error( "engine_{}: BTOR solver '{}' is currently not supported in cover mode." .format(solver_args[0])) if (common_state.produced_cex < common_state.expected_cex) and line == "sat": assert common_state.wit_file == None if common_state.expected_cex == 1: common_state.wit_file = open( "{}/engine_{}/trace.wit".format(job.workdir, engine_idx), "w") else: common_state.wit_file = open( "{}/engine_{}/trace{}.wit".format( job.workdir, engine_idx, common_state.produced_cex), "w") if solver_args[0] != "btormc": task.log("Found satisfiability witness.") if common_state.wit_file: print(line, file=common_state.wit_file) if line == ".": if common_state.expected_cex == 1: suffix = "" else: suffix = common_state.produced_cex task2 = SbyTask( job, "engine_{}_{}".format(engine_idx, common_state.produced_cex), job.model("btor"), "cd {dir} ; btorsim -c --vcd engine_{idx}/trace{i}.vcd --hierarchical-symbols --info model/design_btor.info model/design_btor.btor engine_{idx}/trace{i}.wit" .format(dir=job.workdir, idx=engine_idx, i=suffix), logfile=open( "{dir}/engine_{idx}/logfile2.txt".format( dir=job.workdir, idx=engine_idx), "w")) task2.output_callback = output_callback2 task2.exit_callback = make_exit_callback(suffix) task2.checkretcode = True common_state.running_tasks += 1 common_state.produced_cex += 1 common_state.wit_file.close() common_state.wit_file = None if common_state.produced_cex == common_state.expected_cex: common_state.solver_status = "sat" else: if solver_args[0] == "btormc": if "calling BMC on" in line: return line if "SATISFIABLE" in line: return line if "bad state properties at bound" in line: return line if "deleting model checker:" in line: if common_state.solver_status is None: common_state.solver_status = "unsat" return line elif solver_args[0] == "cosa2": if line == "unknown": if common_state.solver_status is None: common_state.solver_status = "unsat" return "No CEX found." if line not in ["b0"]: return line print(line, file=task.logfile) return None def exit_callback(retcode): if solver_args[0] == "cosa2": assert retcode in [1, 2] else: assert retcode == 0 if common_state.expected_cex != 0: assert common_state.solver_status is not None if common_state.solver_status == "unsat": if common_state.expected_cex == 1: with open( "{}/engine_{}/trace.wit".format( job.workdir, engine_idx), "w") as wit_file: print("unsat", file=wit_file) else: for i in range(common_state.produced_cex, common_state.expected_cex): with open( "{}/engine_{}/trace{}.wit".format( job.workdir, engine_idx, i), "w") as wit_file: print("unsat", file=wit_file) common_state.running_tasks -= 1 if (common_state.running_tasks == 0): print_traces_and_terminate() task = SbyTask( job, "engine_{}".format(engine_idx), job.model("btor"), "cd {}; {} model/design_btor.btor".format(job.workdir, solver_cmd), logfile=open( "{}/engine_{}/logfile.txt".format(job.workdir, engine_idx), "w")) task.output_callback = output_callback task.exit_callback = exit_callback common_state.running_tasks += 1
def output_callback(line): if mode == "cover": if solver_args[0] == "btormc": match = re.search(r"calling BMC on ([0-9]+) properties", line) if match: common_state.expected_cex = int(match[1]) assert common_state.produced_cex == 0 else: job.error( "engine_{}: BTOR solver '{}' is currently not supported in cover mode." .format(solver_args[0])) if (common_state.produced_cex < common_state.expected_cex) and line == "sat": assert common_state.wit_file == None if common_state.expected_cex == 1: common_state.wit_file = open( "{}/engine_{}/trace.wit".format(job.workdir, engine_idx), "w") else: common_state.wit_file = open( "{}/engine_{}/trace{}.wit".format( job.workdir, engine_idx, common_state.produced_cex), "w") if solver_args[0] != "btormc": task.log("Found satisfiability witness.") if common_state.wit_file: print(line, file=common_state.wit_file) if line == ".": if common_state.expected_cex == 1: suffix = "" else: suffix = common_state.produced_cex task2 = SbyTask( job, "engine_{}_{}".format(engine_idx, common_state.produced_cex), job.model("btor"), "cd {dir} ; btorsim -c --vcd engine_{idx}/trace{i}.vcd --hierarchical-symbols --info model/design_btor.info model/design_btor.btor engine_{idx}/trace{i}.wit" .format(dir=job.workdir, idx=engine_idx, i=suffix), logfile=open( "{dir}/engine_{idx}/logfile2.txt".format( dir=job.workdir, idx=engine_idx), "w")) task2.output_callback = output_callback2 task2.exit_callback = make_exit_callback(suffix) task2.checkretcode = True common_state.running_tasks += 1 common_state.produced_cex += 1 common_state.wit_file.close() common_state.wit_file = None if common_state.produced_cex == common_state.expected_cex: common_state.solver_status = "sat" else: if solver_args[0] == "btormc": if "calling BMC on" in line: return line if "SATISFIABLE" in line: return line if "bad state properties at bound" in line: return line if "deleting model checker:" in line: if common_state.solver_status is None: common_state.solver_status = "unsat" return line elif solver_args[0] == "cosa2": if line == "unknown": if common_state.solver_status is None: common_state.solver_status = "unsat" return "No CEX found." if line not in ["b0"]: return line print(line, file=task.logfile) return None
def run(mode, job, engine_idx, engine): random_seed = None opts, solver_args = getopt.getopt(engine[1:], "", ["seed="]) if len(solver_args) == 0: job.error("Missing solver command.") for o, a in opts: if o == "--seed": random_seed = a else: job.error("Unexpected BTOR engine options.") if solver_args[0] == "btormc": solver_cmd = "" if random_seed: solver_cmd += f"BTORSEED={random_seed} " solver_cmd += job.exe_paths[ "btormc"] + f""" --stop-first {0 if mode == "cover" else 1} -v 1 -kmax {job.opt_depth - 1}""" if job.opt_skip is not None: solver_cmd += f" -kmin {job.opt_skip}" solver_cmd += " ".join([""] + solver_args[1:]) elif solver_args[0] == "pono": if random_seed: job.error( "Setting the random seed is not available for the pono solver." ) solver_cmd = job.exe_paths[ "pono"] + f" -v 1 -e bmc -k {job.opt_depth - 1}" else: job.error(f"Invalid solver command {solver_args[0]}.") common_state = SimpleNamespace() common_state.solver_status = None common_state.produced_cex = 0 common_state.expected_cex = 1 common_state.wit_file = None common_state.assert_fail = False common_state.produced_traces = [] common_state.print_traces_max = 5 common_state.running_tasks = 0 def print_traces_and_terminate(): if mode == "cover": if common_state.assert_fail: task_status = "FAIL" elif common_state.expected_cex == 0: task_status = "pass" elif common_state.solver_status == "sat": task_status = "pass" elif common_state.solver_status == "unsat": task_status = "FAIL" else: job.error( f"engine_{engine_idx}: Engine terminated without status.") else: if common_state.expected_cex == 0: task_status = "pass" elif common_state.solver_status == "sat": task_status = "FAIL" elif common_state.solver_status == "unsat": task_status = "pass" else: job.error( f"engine_{engine_idx}: Engine terminated without status.") job.update_status(task_status.upper()) job.log( f"engine_{engine_idx}: Status returned by engine: {task_status}") job.summary.append( f"""engine_{engine_idx} ({" ".join(engine)}) returned {task_status}""" ) if len(common_state.produced_traces) == 0: job.log( f"""engine_{engine_idx}: Engine did not produce a{" counter" if mode != "cover" else "n "}example.""" ) elif len( common_state.produced_traces) <= common_state.print_traces_max: job.summary.extend(common_state.produced_traces) else: job.summary.extend( common_state.produced_traces[:common_state.print_traces_max]) excess_traces = len( common_state.produced_traces) - common_state.print_traces_max job.summary.append( f"""and {excess_traces} further trace{"s" if excess_traces > 1 else ""}""" ) job.terminate() if mode == "cover": def output_callback2(line): match = re.search(r"Assert failed in test", line) if match: common_state.assert_fail = True return line else: def output_callback2(line): return line def make_exit_callback(suffix): def exit_callback2(retcode): assert retcode == 0 vcdpath = f"{job.workdir}/engine_{engine_idx}/trace{suffix}.vcd" if os.path.exists(vcdpath): common_state.produced_traces.append( f"""{"" if mode == "cover" else "counterexample "}trace: {vcdpath}""" ) common_state.running_tasks -= 1 if (common_state.running_tasks == 0): print_traces_and_terminate() return exit_callback2 def output_callback(line): if mode == "cover": if solver_args[0] == "btormc": match = re.search(r"calling BMC on ([0-9]+) properties", line) if match: common_state.expected_cex = int(match[1]) assert common_state.produced_cex == 0 else: job.error( f"engine_{engine_idx}: BTOR solver '{solver_args[0]}' is currently not supported in cover mode." ) if (common_state.produced_cex < common_state.expected_cex) and line == "sat": assert common_state.wit_file == None if common_state.expected_cex == 1: common_state.wit_file = open( f"{job.workdir}/engine_{engine_idx}/trace.wit", "w") else: common_state.wit_file = open( f"""{job.workdir}/engine_{engine_idx}/trace{common_state.produced_cex}.wit""", "w") if solver_args[0] != "btormc": task.log("Found satisfiability witness.") if common_state.wit_file: print(line, file=common_state.wit_file) if line == ".": if common_state.expected_cex == 1: suffix = "" else: suffix = common_state.produced_cex task2 = SbyTask( job, f"engine_{engine_idx}_{common_state.produced_cex}", job.model("btor"), "cd {dir} ; btorsim -c --vcd engine_{idx}/trace{i}.vcd --hierarchical-symbols --info model/design_btor.info model/design_btor.btor engine_{idx}/trace{i}.wit" .format(dir=job.workdir, idx=engine_idx, i=suffix), logfile=open( f"{job.workdir}/engine_{engine_idx}/logfile2.txt", "w")) task2.output_callback = output_callback2 task2.exit_callback = make_exit_callback(suffix) task2.checkretcode = True common_state.running_tasks += 1 common_state.produced_cex += 1 common_state.wit_file.close() common_state.wit_file = None if common_state.produced_cex == common_state.expected_cex: common_state.solver_status = "sat" else: if solver_args[0] == "btormc": if "calling BMC on" in line: return line if "SATISFIABLE" in line: return line if "bad state properties at bound" in line: return line if "deleting model checker:" in line: if common_state.solver_status is None: common_state.solver_status = "unsat" return line elif solver_args[0] == "pono": if line == "unknown": if common_state.solver_status is None: common_state.solver_status = "unsat" return "No CEX found." if line not in ["b0"]: return line print(line, file=task.logfile) return None def exit_callback(retcode): if solver_args[0] == "pono": assert retcode in [ 0, 1, 255 ] # UNKNOWN = -1, FALSE = 0, TRUE = 1, ERROR = 2 else: assert retcode == 0 if common_state.expected_cex != 0: assert common_state.solver_status is not None if common_state.solver_status == "unsat": if common_state.expected_cex == 1: with open(f"""{job.workdir}/engine_{engine_idx}/trace.wit""", "w") as wit_file: print("unsat", file=wit_file) else: for i in range(common_state.produced_cex, common_state.expected_cex): with open( f"{job.workdir}/engine_{engine_idx}/trace{i}.wit", "w") as wit_file: print("unsat", file=wit_file) common_state.running_tasks -= 1 if (common_state.running_tasks == 0): print_traces_and_terminate() task = SbyTask(job, f"engine_{engine_idx}", job.model("btor"), f"cd {job.workdir}; {solver_cmd} model/design_btor.btor", logfile=open( f"{job.workdir}/engine_{engine_idx}/logfile.txt", "w")) task.output_callback = output_callback task.exit_callback = exit_callback common_state.running_tasks += 1
def run(mode, job, engine_idx, engine): smtbmc_opts = [] nomem_opt = False presat_opt = True unroll_opt = None syn_opt = False stbv_opt = False stdt_opt = False dumpsmt2 = False progress = False basecase_only = False induction_only = False random_seed = None opts, args = getopt.getopt(engine[1:], "", [ "nomem", "syn", "stbv", "stdt", "presat", "nopresat", "unroll", "nounroll", "dumpsmt2", "progress", "basecase", "induction", "seed=" ]) for o, a in opts: if o == "--nomem": nomem_opt = True elif o == "--syn": syn_opt = True elif o == "--stbv": stbv_opt = True elif o == "--stdt": stdt_opt = True elif o == "--presat": presat_opt = True elif o == "--nopresat": presat_opt = False elif o == "--unroll": unroll_opt = True elif o == "--nounroll": unroll_opt = False elif o == "--dumpsmt2": dumpsmt2 = True elif o == "--progress": progress = True elif o == "--basecase": if induction_only: job.error( "smtbmc options --basecase and --induction are exclusive.") basecase_only = True elif o == "--induction": if basecase_only: job.error( "smtbmc options --basecase and --induction are exclusive.") induction_only = True elif o == "--seed": random_seed = a else: job.error(f"Invalid smtbmc options {o}.") xtra_opts = False for i, a in enumerate(args): if i == 0 and a == "z3" and unroll_opt is None: unroll_opt = False if a == "--": xtra_opts = True continue if xtra_opts: smtbmc_opts.append(a) else: smtbmc_opts += ["-s" if i == 0 else "-S", a] if presat_opt: smtbmc_opts += ["--presat"] if unroll_opt is None or unroll_opt: smtbmc_opts += ["--unroll"] if job.opt_smtc is not None: smtbmc_opts += ["--smtc", f"src/{job.opt_smtc}"] if job.opt_tbtop is not None: smtbmc_opts += ["--vlogtb-top", job.opt_tbtop] model_name = "smt2" if syn_opt: model_name += "_syn" if nomem_opt: model_name += "_nomem" if stbv_opt: model_name += "_stbv" if stdt_opt: model_name += "_stdt" if mode == "prove": if not induction_only: run("prove_basecase", job, engine_idx, engine) if not basecase_only: run("prove_induction", job, engine_idx, engine) return taskname = f"engine_{engine_idx}" trace_prefix = f"engine_{engine_idx}/trace" logfile_prefix = f"{job.workdir}/engine_{engine_idx}/logfile" if mode == "prove_basecase": taskname += ".basecase" logfile_prefix += "_basecase" if mode == "prove_induction": taskname += ".induction" trace_prefix += "_induct" logfile_prefix += "_induction" smtbmc_opts.append("-i") if mode == "cover": smtbmc_opts.append("-c") trace_prefix += "%" if dumpsmt2: smtbmc_opts += ["--dump-smt2", trace_prefix.replace("%", "") + ".smt2"] if not progress: smtbmc_opts.append("--noprogress") if job.opt_skip is not None: t_opt = "{}:{}".format(job.opt_skip, job.opt_depth) else: t_opt = "{}".format(job.opt_depth) random_seed = f"--info \"(set-option :random-seed {random_seed})\"" if random_seed else "" task = SbyTask( job, taskname, job.model(model_name), f"""cd {job.workdir}; {job.exe_paths["smtbmc"]} {" ".join(smtbmc_opts)} -t {t_opt} {random_seed} --append {job.opt_append} --dump-vcd {trace_prefix}.vcd --dump-vlogtb {trace_prefix}_tb.v --dump-smtc {trace_prefix}.smtc model/design_{model_name}.smt2""", logfile=open(logfile_prefix + ".txt", "w"), logstderr=(not progress)) if mode == "prove_basecase": job.basecase_tasks.append(task) if mode == "prove_induction": job.induction_tasks.append(task) task_status = None def output_callback(line): nonlocal task_status match = re.match(r"^## [0-9: ]+ Status: FAILED", line) if match: task_status = "FAIL" return line.replace("FAILED", "failed") match = re.match(r"^## [0-9: ]+ Status: PASSED", line) if match: task_status = "PASS" return line.replace("PASSED", "passed") match = re.match(r"^## [0-9: ]+ Status: PREUNSAT", line) if match: task_status = "ERROR" return line match = re.match(r"^## [0-9: ]+ Unexpected response from solver:", line) if match: task_status = "ERROR" return line return line def exit_callback(retcode): if task_status is None: job.error( f"engine_{engine_idx}: Engine terminated without status.") if mode == "bmc" or mode == "cover": job.update_status(task_status) task_status_lower = task_status.lower( ) if task_status == "PASS" else task_status job.log( f"engine_{engine_idx}: Status returned by engine: {task_status_lower}" ) job.summary.append( f"""engine_{engine_idx} ({" ".join(engine)}) returned {task_status_lower}""" ) if task_status == "FAIL" and mode != "cover": if os.path.exists( f"{job.workdir}/engine_{engine_idx}/trace.vcd"): job.summary.append( f"counterexample trace: {job.workdir}/engine_{engine_idx}/trace.vcd" ) elif task_status == "PASS" and mode == "cover": print_traces_max = 5 for i in range(print_traces_max): if os.path.exists( f"{job.workdir}/engine_{engine_idx}/trace{i}.vcd"): job.summary.append( f"trace: {job.workdir}/engine_{engine_idx}/trace{i}.vcd" ) else: break else: excess_traces = 0 while os.path.exists( f"{job.workdir}/engine_{engine_idx}/trace{print_traces_max + excess_traces}.vcd" ): excess_traces += 1 if excess_traces > 0: job.summary.append( f"""and {excess_traces} further trace{"s" if excess_traces > 1 else ""}""" ) job.terminate() elif mode in ["prove_basecase", "prove_induction"]: task_status_lower = task_status.lower( ) if task_status == "PASS" else task_status job.log( f"""engine_{engine_idx}: Status returned by engine for {mode.split("_")[1]}: {task_status_lower}""" ) job.summary.append( f"""engine_{engine_idx} ({" ".join(engine)}) returned {task_status_lower} for {mode.split("_")[1]}""" ) if mode == "prove_basecase": for task in job.basecase_tasks: task.terminate() if task_status == "PASS": job.basecase_pass = True else: job.update_status(task_status) if os.path.exists( f"{job.workdir}/engine_{engine_idx}/trace.vcd"): job.summary.append( f"counterexample trace: {job.workdir}/engine_{engine_idx}/trace.vcd" ) job.terminate() elif mode == "prove_induction": for task in job.induction_tasks: task.terminate() if task_status == "PASS": job.induction_pass = True else: assert False if job.basecase_pass and job.induction_pass: job.update_status("PASS") job.summary.append("successful proof by k-induction.") job.terminate() else: assert False task.output_callback = output_callback task.exit_callback = exit_callback
def run(mode, job, engine_idx, engine): abc_opts, abc_command = getopt.getopt(engine[1:], "", []) if len(abc_command) == 0: job.error("Missing ABC command.") for o, a in abc_opts: job.error("Unexpected ABC engine options.") if abc_command[0] == "bmc3": if mode != "bmc": job.error("ABC command 'bmc3' is only valid in bmc mode.") abc_command[0] += " -F %d -v" % job.opt_depth elif abc_command[0] == "sim3": if mode != "bmc": job.error("ABC command 'sim3' is only valid in bmc mode.") abc_command[0] += " -F %d -v" % job.opt_depth elif abc_command[0] == "pdr": if mode != "prove": job.error("ABC command 'pdr' is only valid in prove mode.") else: job.error("Invalid ABC command %s." % abc_command[0]) task = SbyTask(job, "engine_%d" % engine_idx, job.model("aig"), ("cd %s; %s -c 'read_aiger model/design_aiger.aig; fold; strash; %s; write_cex -a engine_%d/trace.aiw'") % (job.workdir, job.exe_paths["abc"], " ".join(abc_command), engine_idx), logfile=open("%s/engine_%d/logfile.txt" % (job.workdir, engine_idx), "w")) task.noprintregex = re.compile(r"^\.+$") task_status = None def output_callback(line): nonlocal task_status match = re.match(r"^Output [0-9]+ of miter .* was asserted in frame [0-9]+.", line) if match: task_status = "FAIL" match = re.match(r"^Simulation of [0-9]+ frames for [0-9]+ rounds with [0-9]+ restarts did not assert POs.", line) if match: task_status = "UNKNOWN" match = re.match(r"^Stopping BMC because all 2\^[0-9]+ reachable states are visited.", line) if match: task_status = "PASS" match = re.match(r"^No output asserted in [0-9]+ frames.", line) if match: task_status = "PASS" match = re.match(r"^Property proved.", line) if match: task_status = "PASS" return line def exit_callback(retcode): assert retcode == 0 assert task_status is not None job.update_status(task_status) job.log("engine_%d: Status returned by engine: %s" % (engine_idx, task_status)) job.summary.append("engine_%d (%s) returned %s" % (engine_idx, " ".join(engine), task_status)) job.terminate() if task_status == "FAIL" and job.opt_aigsmt != "none": task2 = SbyTask(job, "engine_%d" % engine_idx, job.model("smt2"), ("cd %s; %s -s %s%s --noprogress --append %d --dump-vcd engine_%d/trace.vcd --dump-vlogtb engine_%d/trace_tb.v " + "--dump-smtc engine_%d/trace.smtc --aig model/design_aiger.aim:engine_%d/trace.aiw --aig-noheader model/design_smt2.smt2") % (job.workdir, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else " --vlogtb-top %s" % job.opt_tbtop, job.opt_append, engine_idx, engine_idx, engine_idx, engine_idx), logfile=open("%s/engine_%d/logfile2.txt" % (job.workdir, engine_idx), "w")) task2_status = None def output_callback2(line): nonlocal task2_status match = re.match(r"^## [0-9: ]+ Status: FAILED", line) if match: task2_status = "FAIL" match = re.match(r"^## [0-9: ]+ Status: PASSED", line) if match: task2_status = "PASS" return line def exit_callback2(line): assert task2_status is not None assert task2_status == "FAIL" job.summary.append("counterexample trace: %s/engine_%d/trace.vcd" % (job.workdir, engine_idx)) task2.output_callback = output_callback2 task2.exit_callback = exit_callback2 task.output_callback = output_callback task.exit_callback = exit_callback
def run(mode, job, engine_idx, engine): opts, solver_args = getopt.getopt(engine[1:], "", []) assert len(solver_args) > 0 for o, a in opts: assert False if solver_args[0] == "suprove": if mode == "live" and (len(solver_args) == 1 or solver_args[1][0] != "+"): solver_args.insert(1, "+simple_liveness") solver_cmd = " ".join([job.exe_paths["suprove"]] + solver_args[1:]) elif solver_args[0] == "avy": solver_cmd = " ".join([job.exe_paths["avy"], "--cex", "-"] + solver_args[1:]) elif solver_args[0] == "aigbmc": solver_cmd = " ".join([job.exe_paths["aigbmc"]] + solver_args[1:]) else: assert False task = SbyTask(job, "engine_%d" % engine_idx, job.model("aig"), "cd %s; %s model/design_aiger.aig" % (job.workdir, solver_cmd), logfile=open("%s/engine_%d/logfile.txt" % (job.workdir, engine_idx), "w")) task_status = None produced_cex = False end_of_cex = False aiw_file = open("%s/engine_%d/trace.aiw" % (job.workdir, engine_idx), "w") def output_callback(line): nonlocal task_status nonlocal produced_cex nonlocal end_of_cex if task_status is not None: if not end_of_cex and not produced_cex and line.isdigit(): produced_cex = True if not end_of_cex: print(line, file=aiw_file) if line == ".": end_of_cex = True return None if line.startswith("u"): return "No CEX up to depth %d." % (int(line[1:])-1) if line in ["0", "1", "2"]: print(line, file=aiw_file) if line == "0": task_status = "PASS" if line == "1": task_status = "FAIL" if line == "2": task_status = "UNKNOWN" return None def exit_callback(retcode): if solver_args[0] not in ["avy"]: assert retcode == 0 assert task_status is not None aiw_file.close() job.update_status(task_status) job.log("engine_%d: Status returned by engine: %s" % (engine_idx, task_status)) job.summary.append("engine_%d (%s) returned %s" % (engine_idx, " ".join(engine), task_status)) job.terminate() if task_status == "FAIL" and job.opt_aigsmt != "none": if produced_cex: if mode == "live": task2 = SbyTask(job, "engine_%d" % engine_idx, job.model("smt2"), ("cd %s; %s -g -s %s%s --noprogress --dump-vcd engine_%d/trace.vcd --dump-vlogtb engine_%d/trace_tb.v " + "--dump-smtc engine_%d/trace.smtc --aig model/design_aiger.aim:engine_%d/trace.aiw model/design_smt2.smt2") % (job.workdir, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else " --vlogtb-top %s" % job.opt_tbtop, engine_idx, engine_idx, engine_idx, engine_idx), logfile=open("%s/engine_%d/logfile2.txt" % (job.workdir, engine_idx), "w")) else: task2 = SbyTask(job, "engine_%d" % engine_idx, job.model("smt2"), ("cd %s; %s -s %s%s --noprogress --append %d --dump-vcd engine_%d/trace.vcd --dump-vlogtb engine_%d/trace_tb.v " + "--dump-smtc engine_%d/trace.smtc --aig model/design_aiger.aim:engine_%d/trace.aiw model/design_smt2.smt2") % (job.workdir, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else " --vlogtb-top %s" % job.opt_tbtop, job.opt_append, engine_idx, engine_idx, engine_idx, engine_idx), logfile=open("%s/engine_%d/logfile2.txt" % (job.workdir, engine_idx), "w")) task2_status = None def output_callback2(line): nonlocal task2_status match = re.match(r"^## [0-9: ]+ Status: FAILED", line) if match: task2_status = "FAIL" match = re.match(r"^## [0-9: ]+ Status: PASSED", line) if match: task2_status = "PASS" return line def exit_callback2(line): assert task2_status is not None if mode == "live": assert task2_status == "PASS" else: assert task2_status == "FAIL" job.summary.append("counterexample trace: %s/engine_%d/trace.vcd" % (job.workdir, engine_idx)) task2.output_callback = output_callback2 task2.exit_callback = exit_callback2 else: job.log("engine_%d: Engine did not produce a counter example." % engine_idx) task.output_callback = output_callback task.exit_callback = exit_callback
def exit_callback(retcode): if solver_args[0] not in ["avy"]: assert retcode == 0 assert task_status is not None aiw_file.close() job.update_status(task_status) job.log( f"engine_{engine_idx}: Status returned by engine: {task_status}") job.summary.append( f"""engine_{engine_idx} ({" ".join(engine)}) returned {task_status}""" ) job.terminate() if task_status == "FAIL" and job.opt_aigsmt != "none": if produced_cex: if mode == "live": task2 = SbyTask( job, f"engine_{engine_idx}", job.model("smt2"), ("cd {}; {} -g -s {}{} --noprogress --dump-vcd engine_{i}/trace.vcd --dump-vlogtb engine_{i}/trace_tb.v " + "--dump-smtc engine_{i}/trace.smtc --aig model/design_aiger.aim:engine_{i}/trace.aiw model/design_smt2.smt2" ).format(job.workdir, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else f" --vlogtb-top {job.opt_tbtop}", i=engine_idx), logfile=open( f"{job.workdir}/engine_{engine_idx}/logfile2.txt", "w")) else: task2 = SbyTask( job, f"engine_{engine_idx}", job.model("smt2"), ("cd {}; {} -s {}{} --noprogress --append {} --dump-vcd engine_{i}/trace.vcd --dump-vlogtb engine_{i}/trace_tb.v " + "--dump-smtc engine_{i}/trace.smtc --aig model/design_aiger.aim:engine_{i}/trace.aiw model/design_smt2.smt2" ).format(job.workdir, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else f" --vlogtb-top {job.opt_tbtop}", job.opt_append, i=engine_idx), logfile=open( f"{job.workdir}/engine_{engine_idx}/logfile2.txt", "w")) task2_status = None def output_callback2(line): nonlocal task2_status match = re.match(r"^## [0-9: ]+ Status: FAILED", line) if match: task2_status = "FAIL" match = re.match(r"^## [0-9: ]+ Status: PASSED", line) if match: task2_status = "PASS" return line def exit_callback2(line): assert task2_status is not None if mode == "live": assert task2_status == "PASS" else: assert task2_status == "FAIL" if os.path.exists( f"{job.workdir}/engine_{engine_idx}/trace.vcd"): job.summary.append( f"counterexample trace: {job.workdir}/engine_{engine_idx}/trace.vcd" ) task2.output_callback = output_callback2 task2.exit_callback = exit_callback2 else: job.log( f"engine_{engine_idx}: Engine did not produce a counter example." )
def exit_callback(retcode): assert retcode == 0 assert task_status is not None if task_status == "PASS": print("unsat", file=wit_file) wit_file.close() job.update_status(task_status) job.log("engine_{}: Status returned by engine: {}".format( engine_idx, task_status)) job.summary.append("engine_{} ({}) returned {}".format( engine_idx, " ".join(engine), task_status)) job.terminate() if task_status == "FAIL" and job.opt_aigsmt != "none": if produced_cex: has_arrays = False with open("{}/model/design_btor.btor".format(job.workdir), "r") as f: for line in f: line = line.split() if len(line) == 5 and line[1] == "sort" and line[ 2] == "array": has_arrays = True break if has_arrays: setupcmd = "cd {};".format(job.workdir) finalwit = "engine_{}/trace.wit".format(engine_idx) else: setupcmd = "cd {}; {{ echo sat; btorsim --states model/design_btor.btor engine_{i}/trace.wit; }} > engine_{i}/simtrace.wit &&".format( job.workdir, i=engine_idx) finalwit = "engine_{}/simtrace.wit".format(engine_idx) if mode == "live": task2 = SbyTask( job, "engine_{}".format(engine_idx), job.model("smt2"), ("{} {} -g -s {}{} --noprogress --dump-vcd engine_{i}/trace.vcd --dump-vlogtb engine_{i}/trace_tb.v " + "--dump-smtc engine_{i}/trace.smtc --btorwit {} model/design_smt2.smt2" ).format(setupcmd, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else " --vlogtb-top {}".format(job.opt_tbtop), finalwit, i=engine_idx), logfile=open( "{}/engine_{}/logfile2.txt".format( job.workdir, engine_idx), "w")) else: task2 = SbyTask( job, "engine_{}".format(engine_idx), job.model("smt2"), ("{} {} -s {}{} --noprogress --append {} --dump-vcd engine_{i}/trace.vcd --dump-vlogtb engine_{i}/trace_tb.v " + "--dump-smtc engine_{i}/trace.smtc --btorwit {} model/design_smt2.smt2" ).format(setupcmd, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else " --vlogtb-top {}".format(job.opt_tbtop), job.opt_append, finalwit, i=engine_idx), logfile=open( "{}/engine_{}/logfile2.txt".format( job.workdir, engine_idx), "w")) task2_status = None def output_callback2(line): nonlocal task2_status match = re.match(r"^## [0-9: ]+ Status: FAILED", line) if match: task2_status = "FAIL" match = re.match(r"^## [0-9: ]+ Status: PASSED", line) if match: task2_status = "PASS" return line def exit_callback2(line): assert task2_status is not None if mode == "live": assert task2_status == "PASS" else: assert task2_status == "FAIL" if os.path.exists("{}/engine_{}/trace.vcd".format( job.workdir, engine_idx)): job.summary.append( "counterexample trace: {}/engine_{}/trace.vcd". format(job.workdir, engine_idx)) task2.output_callback = output_callback2 task2.exit_callback = exit_callback2 else: job.log("engine_{}: Engine did not produce a counter example.". format(engine_idx))
def run(mode, job, engine_idx, engine): opts, solver_args = getopt.getopt(engine[1:], "", []) if len(solver_args) == 0: job.error("Missing solver command.") for o, a in opts: job.error("Unexpected BTOR engine options.") if solver_args[0] == "btormc": solver_cmd = job.exe_paths[ "btormc"] + " --stop-first -v 1 -kmax {}".format(job.opt_depth - 1) if job.opt_skip is not None: solver_cmd += " -kmin {}".format(job.opt_skip) solver_cmd += " ".join([""] + solver_args[1:]) else: job.error("Invalid solver command {}.".format(solver_args[0])) task = SbyTask( job, "engine_{}".format(engine_idx), job.model("btor"), "cd {}; {} model/design_btor.btor".format(job.workdir, solver_cmd), logfile=open( "{}/engine_{}/logfile.txt".format(job.workdir, engine_idx), "w")) task_status = None produced_cex = False end_of_cex = False wit_file = open("{}/engine_{}/trace.wit".format(job.workdir, engine_idx), "w") def output_callback(line): nonlocal task_status nonlocal produced_cex nonlocal end_of_cex if not end_of_cex and not produced_cex and line == "sat": produced_cex = True task_status = "FAIL" if produced_cex and not end_of_cex: print(line, file=wit_file) if line == ".": end_of_cex = True if line.startswith("u"): return "No CEX up to depth {}.".format(int(line[1:]) - 1) if solver_args[0] == "btormc": if "calling BMC on" in line: return line if "SATISFIABLE" in line: return line if "bad state properties at bound" in line: return line if "deleting model checker:" in line: if task_status is None: task_status = "PASS" return line if not produced_cex or end_of_cex: print(line, file=task.logfile) return None def exit_callback(retcode): assert retcode == 0 assert task_status is not None if task_status == "PASS": print("unsat", file=wit_file) wit_file.close() job.update_status(task_status) job.log("engine_{}: Status returned by engine: {}".format( engine_idx, task_status)) job.summary.append("engine_{} ({}) returned {}".format( engine_idx, " ".join(engine), task_status)) job.terminate() if task_status == "FAIL" and job.opt_aigsmt != "none": if produced_cex: has_arrays = False with open("{}/model/design_btor.btor".format(job.workdir), "r") as f: for line in f: line = line.split() if len(line) == 5 and line[1] == "sort" and line[ 2] == "array": has_arrays = True break if has_arrays: setupcmd = "cd {};".format(job.workdir) finalwit = "engine_{}/trace.wit".format(engine_idx) else: setupcmd = "cd {}; {{ echo sat; btorsim --states model/design_btor.btor engine_{i}/trace.wit; }} > engine_{i}/simtrace.wit &&".format( job.workdir, i=engine_idx) finalwit = "engine_{}/simtrace.wit".format(engine_idx) if mode == "live": task2 = SbyTask( job, "engine_{}".format(engine_idx), job.model("smt2"), ("{} {} -g -s {}{} --noprogress --dump-vcd engine_{i}/trace.vcd --dump-vlogtb engine_{i}/trace_tb.v " + "--dump-smtc engine_{i}/trace.smtc --btorwit {} model/design_smt2.smt2" ).format(setupcmd, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else " --vlogtb-top {}".format(job.opt_tbtop), finalwit, i=engine_idx), logfile=open( "{}/engine_{}/logfile2.txt".format( job.workdir, engine_idx), "w")) else: task2 = SbyTask( job, "engine_{}".format(engine_idx), job.model("smt2"), ("{} {} -s {}{} --noprogress --append {} --dump-vcd engine_{i}/trace.vcd --dump-vlogtb engine_{i}/trace_tb.v " + "--dump-smtc engine_{i}/trace.smtc --btorwit {} model/design_smt2.smt2" ).format(setupcmd, job.exe_paths["smtbmc"], job.opt_aigsmt, "" if job.opt_tbtop is None else " --vlogtb-top {}".format(job.opt_tbtop), job.opt_append, finalwit, i=engine_idx), logfile=open( "{}/engine_{}/logfile2.txt".format( job.workdir, engine_idx), "w")) task2_status = None def output_callback2(line): nonlocal task2_status match = re.match(r"^## [0-9: ]+ Status: FAILED", line) if match: task2_status = "FAIL" match = re.match(r"^## [0-9: ]+ Status: PASSED", line) if match: task2_status = "PASS" return line def exit_callback2(line): assert task2_status is not None if mode == "live": assert task2_status == "PASS" else: assert task2_status == "FAIL" if os.path.exists("{}/engine_{}/trace.vcd".format( job.workdir, engine_idx)): job.summary.append( "counterexample trace: {}/engine_{}/trace.vcd". format(job.workdir, engine_idx)) task2.output_callback = output_callback2 task2.exit_callback = exit_callback2 else: job.log("engine_{}: Engine did not produce a counter example.". format(engine_idx)) task.output_callback = output_callback task.exit_callback = exit_callback
def run(mode, job, engine_idx, engine): smtbmc_opts = [] nomem_opt = False presat_opt = True unroll_opt = None syn_opt = False stbv_opt = False stdt_opt = False dumpsmt2 = False progress = False basecase_only = False induction_only = False opts, args = getopt.getopt(engine[1:], "", [ "nomem", "syn", "stbv", "stdt", "presat", "nopresat", "unroll", "nounroll", "dumpsmt2", "progress", "basecase", "induction" ]) for o, a in opts: if o == "--nomem": nomem_opt = True elif o == "--syn": syn_opt = True elif o == "--stbv": stbv_opt = True elif o == "--stdt": stdt_opt = True elif o == "--presat": presat_opt = True elif o == "--nopresat": presat_opt = False elif o == "--unroll": unroll_opt = True elif o == "--nounroll": unroll_opt = False elif o == "--dumpsmt2": dumpsmt2 = True elif o == "--progress": progress = True elif o == "--basecase": if induction_only: job.error( "smtbmc options --basecase and --induction are exclusive.") basecase_only = True elif o == "--induction": if basecase_only: job.error( "smtbmc options --basecase and --induction are exclusive.") induction_only = True else: job.error("Invalid smtbmc options {}.".format(o)) xtra_opts = False for i, a in enumerate(args): if i == 0 and a == "z3" and unroll_opt is None: unroll_opt = False if a == "--": xtra_opts = True continue if xtra_opts: smtbmc_opts.append(a) else: smtbmc_opts += ["-s" if i == 0 else "-S", a] if presat_opt: smtbmc_opts += ["--presat"] if unroll_opt is None or unroll_opt: smtbmc_opts += ["--unroll"] if job.opt_smtc is not None: smtbmc_opts += ["--smtc", "src/{}".format(job.opt_smtc)] if job.opt_tbtop is not None: smtbmc_opts += ["--vlogtb-top", job.opt_tbtop] model_name = "smt2" if syn_opt: model_name += "_syn" if nomem_opt: model_name += "_nomem" if stbv_opt: model_name += "_stbv" if stdt_opt: model_name += "_stdt" if mode == "prove": if not induction_only: run("prove_basecase", job, engine_idx, engine) if not basecase_only: run("prove_induction", job, engine_idx, engine) return taskname = "engine_{}".format(engine_idx) trace_prefix = "engine_{}/trace".format(engine_idx) logfile_prefix = "{}/engine_{}/logfile".format(job.workdir, engine_idx) if mode == "prove_basecase": taskname += ".basecase" logfile_prefix += "_basecase" if mode == "prove_induction": taskname += ".induction" trace_prefix += "_induct" logfile_prefix += "_induction" smtbmc_opts.append("-i") if mode == "cover": smtbmc_opts.append("-c") trace_prefix += "%" if dumpsmt2: smtbmc_opts += ["--dump-smt2", trace_prefix.replace("%", "") + ".smt2"] if not progress: smtbmc_opts.append("--noprogress") if job.opt_skip is not None: t_opt = "{}:{}".format(job.opt_skip, job.opt_depth) else: t_opt = "{}".format(job.opt_depth) task = SbyTask( job, taskname, job.model(model_name), "cd {}; {} {} -t {} --append {} --dump-vcd {p}.vcd --dump-vlogtb {p}_tb.v --dump-smtc {p}.smtc model/design_{}.smt2" .format(job.workdir, job.exe_paths["smtbmc"], " ".join(smtbmc_opts), t_opt, job.opt_append, model_name, p=trace_prefix), logfile=open(logfile_prefix + ".txt", "w"), logstderr=(not progress)) if mode == "prove_basecase": job.basecase_tasks.append(task) if mode == "prove_induction": job.induction_tasks.append(task) task_status = None def output_callback(line): nonlocal task_status match = re.match(r"^## [0-9: ]+ Status: FAILED", line) if match: task_status = "FAIL" return line.replace("FAILED", "failed") match = re.match(r"^## [0-9: ]+ Status: PASSED", line) if match: task_status = "PASS" return line.replace("PASSED", "passed") match = re.match(r"^## [0-9: ]+ Status: PREUNSAT", line) if match: task_status = "ERROR" return line return line def exit_callback(retcode): if task_status is None: job.error("engine_{}: Engine terminated without status.".format( engine_idx)) if mode == "bmc" or mode == "cover": job.update_status(task_status) task_status_lower = task_status.lower( ) if task_status == "PASS" else task_status job.log("engine_{}: Status returned by engine: {}".format( engine_idx, task_status_lower)) job.summary.append("engine_{} ({}) returned {}".format( engine_idx, " ".join(engine), task_status_lower)) if task_status == "FAIL" and mode != "cover": if os.path.exists("{}/engine_{}/trace.vcd".format( job.workdir, engine_idx)): job.summary.append( "counterexample trace: {}/engine_{}/trace.vcd".format( job.workdir, engine_idx)) job.terminate() elif mode in ["prove_basecase", "prove_induction"]: task_status_lower = task_status.lower( ) if task_status == "PASS" else task_status job.log("engine_{}: Status returned by engine for {}: {}".format( engine_idx, mode.split("_")[1], task_status_lower)) job.summary.append("engine_{} ({}) returned {} for {}".format( engine_idx, " ".join(engine), task_status_lower, mode.split("_")[1])) if mode == "prove_basecase": for task in job.basecase_tasks: task.terminate() if task_status == "PASS": job.basecase_pass = True else: job.update_status(task_status) if os.path.exists("{}/engine_{}/trace.vcd".format( job.workdir, engine_idx)): job.summary.append( "counterexample trace: {}/engine_{}/trace.vcd". format(job.workdir, engine_idx)) job.terminate() elif mode == "prove_induction": for task in job.induction_tasks: task.terminate() if task_status == "PASS": job.induction_pass = True else: assert False if job.basecase_pass and job.induction_pass: job.update_status("PASS") job.summary.append("successful proof by k-induction.") job.terminate() else: assert False task.output_callback = output_callback task.exit_callback = exit_callback