Пример #1
0
    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
Пример #2
0
    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
Пример #3
0
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
Пример #4
0
def run_task(taskname):
    my_opt_tmpdir = opt_tmpdir
    my_workdir = None

    if workdir is not None:
        my_workdir = workdir
    elif workdir_prefix is not None:
        my_workdir = workdir_prefix + "_" + taskname

    if my_workdir is None and sbyfile is not None and not my_opt_tmpdir:
        my_workdir = sbyfile[:-4]
        if taskname is not None:
            my_workdir += "_" + taskname

    if my_workdir is not None:
        if opt_backup:
            backup_idx = 0
            while os.path.exists("{}.bak{:03d}".format(my_workdir,
                                                       backup_idx)):
                backup_idx += 1
            early_log(
                my_workdir, "Moving directory '{}' to '{}'.".format(
                    my_workdir, "{}.bak{:03d}".format(my_workdir, backup_idx)))
            shutil.move(my_workdir,
                        "{}.bak{:03d}".format(my_workdir, backup_idx))

        if opt_force and not reusedir:
            early_log(my_workdir,
                      f"Removing directory '{os.path.abspath(my_workdir)}'.")
            if sbyfile:
                shutil.rmtree(my_workdir, ignore_errors=True)

        if reusedir:
            pass
        elif os.path.isdir(my_workdir):
            print(f"ERROR: Directory '{my_workdir}' already exists.")
            sys.exit(1)
        else:
            os.makedirs(my_workdir)

    else:
        my_opt_tmpdir = True
        my_workdir = tempfile.mkdtemp()

    junit_ts_name = os.path.basename(
        sbyfile[:-4]
    ) if sbyfile is not None else workdir if workdir is not None else "stdin"
    junit_tc_name = taskname if taskname is not None else "default"

    if reusedir:
        junit_filename = os.path.basename(my_workdir)
    elif sbyfile is not None:
        junit_filename = os.path.basename(sbyfile[:-4])
        if taskname is not None:
            junit_filename += "_" + taskname
    elif taskname is not None:
        junit_filename = taskname
    else:
        junit_filename = "junit"

    sbyconfig, _, _, _ = read_sbyconfig(sbydata, taskname)
    task = SbyTask(sbyconfig, my_workdir, early_logmsgs, reusedir)

    for k, v in exe_paths.items():
        task.exe_paths[k] = v

    if throw_err:
        task.run(setupmode)
    else:
        try:
            task.run(setupmode)
        except SbyAbort:
            pass

    if my_opt_tmpdir:
        task.log(f"Removing directory '{my_workdir}'.")
        shutil.rmtree(my_workdir, ignore_errors=True)

    if setupmode:
        task.log(f"SETUP COMPLETE (rc={task.retcode})")
    else:
        task.log(f"DONE ({task.status}, rc={task.retcode})")
    task.logfile.close()

    if not my_opt_tmpdir and not setupmode:
        with open("{}/{}.xml".format(task.workdir, junit_filename), "w") as f:
            junit_errors = 1 if task.retcode == 16 else 0
            junit_failures = 1 if task.retcode != 0 and junit_errors == 0 else 0
            print('<?xml version="1.0" encoding="UTF-8"?>', file=f)
            print(
                f'<testsuites disabled="0" errors="{junit_errors}" failures="{junit_failures}" tests="1" time="{task.total_time}">',
                file=f)
            print(
                f'<testsuite disabled="0" errors="{junit_errors}" failures="{junit_failures}" name="{junit_ts_name}" skipped="0" tests="1" time="{task.total_time}">',
                file=f)
            print('<properties>', file=f)
            print(f'<property name="os" value="{os.name}"/>', file=f)
            print('</properties>', file=f)
            print(
                f'<testcase classname="{junit_ts_name}" name="{junit_tc_name}" status="{task.status}" time="{task.total_time}">',
                file=f)
            if junit_errors:
                print(f'<error message="{task.status}" type="{task.status}"/>',
                      file=f)
            if junit_failures:
                print(
                    f'<failure message="{task.status}" type="{task.status}"/>',
                    file=f)
            print('<system-out>', end="", file=f)
            with open(f"{task.workdir}/logfile.txt", "r") as logf:
                for line in logf:
                    print(line.replace("&",
                                       "&amp;").replace("<", "&lt;").replace(
                                           ">",
                                           "&gt;").replace("\"", "&quot;"),
                          end="",
                          file=f)
            print('</system-out></testcase></testsuite></testsuites>', file=f)
        with open(f"{task.workdir}/status", "w") as f:
            print(f"{task.status} {task.retcode} {task.total_time}", file=f)

    return task.retcode
Пример #5
0
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
Пример #6
0
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
Пример #7
0
    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
Пример #8
0
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
Пример #9
0
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
Пример #10
0
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
Пример #11
0
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
Пример #12
0
    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."
                )
Пример #13
0
    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))
Пример #14
0
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
Пример #15
0
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