mirror of
https://github.com/YosysHQ/sby.git
synced 2025-08-24 05:37:54 +00:00
Merge 34a2139a21
into 66a458958d
This commit is contained in:
commit
3960fd3e8c
9 changed files with 129 additions and 99 deletions
|
@ -20,6 +20,8 @@ import os, re, sys, signal
|
||||||
if os.name == "posix":
|
if os.name == "posix":
|
||||||
import resource, fcntl
|
import resource, fcntl
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import asyncio
|
||||||
|
from functools import partial
|
||||||
from shutil import copyfile, rmtree
|
from shutil import copyfile, rmtree
|
||||||
from select import select
|
from select import select
|
||||||
from time import time, localtime, sleep
|
from time import time, localtime, sleep
|
||||||
|
@ -82,17 +84,12 @@ class SbyTask:
|
||||||
self.job.tasks_pending.append(self)
|
self.job.tasks_pending.append(self)
|
||||||
|
|
||||||
for dep in self.deps:
|
for dep in self.deps:
|
||||||
dep.register_dep(self)
|
if not dep.finished:
|
||||||
|
dep.notify.append(self)
|
||||||
|
|
||||||
self.output_callback = None
|
self.output_callback = None
|
||||||
self.exit_callback = None
|
self.exit_callback = None
|
||||||
|
|
||||||
def register_dep(self, next_task):
|
|
||||||
if self.finished:
|
|
||||||
next_task.poll()
|
|
||||||
else:
|
|
||||||
self.notify.append(next_task)
|
|
||||||
|
|
||||||
def log(self, line):
|
def log(self, line):
|
||||||
if line is not None and (self.noprintregex is None or not self.noprintregex.match(line)):
|
if line is not None and (self.noprintregex is None or not self.noprintregex.match(line)):
|
||||||
if self.logfile is not None:
|
if self.logfile is not None:
|
||||||
|
@ -120,17 +117,39 @@ class SbyTask:
|
||||||
if self.running:
|
if self.running:
|
||||||
if not self.silent:
|
if not self.silent:
|
||||||
self.job.log("{}: terminating process".format(self.info))
|
self.job.log("{}: terminating process".format(self.info))
|
||||||
if os.name == "posix":
|
if os.name != "posix":
|
||||||
|
# self.p.terminate does not actually terminate underlying
|
||||||
|
# processes on Windows, so send ctrl+break to the process
|
||||||
|
# group we created. This for some reason does
|
||||||
|
# not cause the associated future (self.fut) to complete
|
||||||
|
# until it is awaited on one last time.
|
||||||
|
|
||||||
|
os.kill(self.p.pid, signal.CTRL_BREAK_EVENT)
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
os.killpg(self.p.pid, signal.SIGTERM)
|
os.killpg(self.p.pid, signal.SIGTERM)
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
pass
|
pass
|
||||||
self.p.terminate()
|
self.p.terminate()
|
||||||
self.job.tasks_running.remove(self)
|
self.job.tasks_running.remove(self)
|
||||||
|
self.job.tasks_retired.append(self)
|
||||||
all_tasks_running.remove(self)
|
all_tasks_running.remove(self)
|
||||||
self.terminated = True
|
self.terminated = True
|
||||||
|
|
||||||
def poll(self):
|
async def output(self):
|
||||||
|
while True:
|
||||||
|
outs = await self.p.stdout.readline()
|
||||||
|
await asyncio.sleep(0) # https://bugs.python.org/issue24532
|
||||||
|
outs = outs.decode("utf-8")
|
||||||
|
if len(outs) == 0: break
|
||||||
|
if outs[-1] != '\n':
|
||||||
|
self.linebuffer += outs
|
||||||
|
break
|
||||||
|
outs = (self.linebuffer + outs).strip()
|
||||||
|
self.linebuffer = ""
|
||||||
|
self.handle_output(outs)
|
||||||
|
|
||||||
|
async def maybe_spawn(self):
|
||||||
if self.finished or self.terminated:
|
if self.finished or self.terminated:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -141,45 +160,34 @@ class SbyTask:
|
||||||
|
|
||||||
if not self.silent:
|
if not self.silent:
|
||||||
self.job.log("{}: starting process \"{}\"".format(self.info, self.cmdline))
|
self.job.log("{}: starting process \"{}\"".format(self.info, self.cmdline))
|
||||||
|
|
||||||
if os.name == "posix":
|
if os.name == "posix":
|
||||||
def preexec_fn():
|
def preexec_fn():
|
||||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
os.setpgrp()
|
os.setpgrp()
|
||||||
|
|
||||||
self.p = subprocess.Popen(["/usr/bin/env", "bash", "-c", self.cmdline], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,
|
subp_kwargs = { "preexec_fn" : preexec_fn }
|
||||||
stderr=(subprocess.STDOUT if self.logstderr else None), preexec_fn=preexec_fn)
|
|
||||||
|
|
||||||
fl = fcntl.fcntl(self.p.stdout, fcntl.F_GETFL)
|
|
||||||
fcntl.fcntl(self.p.stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.p = subprocess.Popen(self.cmdline, shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,
|
subp_kwargs = { "creationflags" : subprocess.CREATE_NEW_PROCESS_GROUP }
|
||||||
stderr=(subprocess.STDOUT if self.logstderr else None))
|
self.p = await asyncio.create_subprocess_shell(self.cmdline, stdin=asyncio.subprocess.DEVNULL,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=(asyncio.subprocess.STDOUT if self.logstderr else None),
|
||||||
|
**subp_kwargs)
|
||||||
self.job.tasks_pending.remove(self)
|
self.job.tasks_pending.remove(self)
|
||||||
self.job.tasks_running.append(self)
|
self.job.tasks_running.append(self)
|
||||||
all_tasks_running.append(self)
|
all_tasks_running.append(self)
|
||||||
self.running = True
|
self.running = True
|
||||||
return
|
asyncio.ensure_future(self.output())
|
||||||
|
self.fut = asyncio.ensure_future(self.p.wait())
|
||||||
|
|
||||||
while True:
|
async def shutdown_and_notify(self):
|
||||||
outs = self.p.stdout.readline().decode("utf-8")
|
|
||||||
if len(outs) == 0: break
|
|
||||||
if outs[-1] != '\n':
|
|
||||||
self.linebuffer += outs
|
|
||||||
break
|
|
||||||
outs = (self.linebuffer + outs).strip()
|
|
||||||
self.linebuffer = ""
|
|
||||||
self.handle_output(outs)
|
|
||||||
|
|
||||||
if self.p.poll() is not None:
|
|
||||||
if not self.silent:
|
if not self.silent:
|
||||||
self.job.log("{}: finished (returncode={})".format(self.info, self.p.returncode))
|
self.job.log("{}: finished (returncode={})".format(self.info, self.p.returncode))
|
||||||
self.job.tasks_running.remove(self)
|
self.job.tasks_running.remove(self)
|
||||||
all_tasks_running.remove(self)
|
self.job.tasks_retired.append(self)
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
|
self.handle_exit(self.p.returncode)
|
||||||
|
|
||||||
if self.p.returncode == 127:
|
if self.p.returncode == 127:
|
||||||
self.job.status = "ERROR"
|
self.job.status = "ERROR"
|
||||||
if not self.silent:
|
if not self.silent:
|
||||||
|
@ -188,8 +196,6 @@ class SbyTask:
|
||||||
self.job.terminate()
|
self.job.terminate()
|
||||||
return
|
return
|
||||||
|
|
||||||
self.handle_exit(self.p.returncode)
|
|
||||||
|
|
||||||
if self.checkretcode and self.p.returncode != 0:
|
if self.checkretcode and self.p.returncode != 0:
|
||||||
self.job.status = "ERROR"
|
self.job.status = "ERROR"
|
||||||
if not self.silent:
|
if not self.silent:
|
||||||
|
@ -200,10 +206,9 @@ class SbyTask:
|
||||||
|
|
||||||
self.finished = True
|
self.finished = True
|
||||||
for next_task in self.notify:
|
for next_task in self.notify:
|
||||||
next_task.poll()
|
await next_task.maybe_spawn()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
class SbyAbort(BaseException):
|
class SbyAbort(BaseException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -237,6 +242,7 @@ class SbyJob:
|
||||||
|
|
||||||
self.tasks_running = []
|
self.tasks_running = []
|
||||||
self.tasks_pending = []
|
self.tasks_pending = []
|
||||||
|
self.tasks_retired = []
|
||||||
|
|
||||||
self.start_clock_time = time()
|
self.start_clock_time = time()
|
||||||
|
|
||||||
|
@ -257,36 +263,60 @@ class SbyJob:
|
||||||
print(line, file=f)
|
print(line, file=f)
|
||||||
|
|
||||||
def taskloop(self):
|
def taskloop(self):
|
||||||
for task in self.tasks_pending:
|
if os.name != "posix":
|
||||||
task.poll()
|
loop = asyncio.ProactorEventLoop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
poll_fut = asyncio.ensure_future(self.task_poller())
|
||||||
|
loop.run_until_complete(poll_fut)
|
||||||
|
|
||||||
while len(self.tasks_running):
|
async def timekeeper(self):
|
||||||
fds = []
|
|
||||||
for task in self.tasks_running:
|
|
||||||
if task.running:
|
|
||||||
fds.append(task.p.stdout)
|
|
||||||
|
|
||||||
if os.name == "posix":
|
|
||||||
try:
|
|
||||||
select(fds, [], [], 1.0) == ([], [], [])
|
|
||||||
except InterruptedError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
sleep(0.1)
|
|
||||||
|
|
||||||
for task in self.tasks_running:
|
|
||||||
task.poll()
|
|
||||||
|
|
||||||
for task in self.tasks_pending:
|
|
||||||
task.poll()
|
|
||||||
|
|
||||||
if self.opt_timeout is not None:
|
|
||||||
total_clock_time = int(time() - self.start_clock_time)
|
total_clock_time = int(time() - self.start_clock_time)
|
||||||
if total_clock_time > self.opt_timeout:
|
|
||||||
|
try:
|
||||||
|
while total_clock_time <= self.opt_timeout:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
total_clock_time = int(time() - self.start_clock_time)
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def timeout(self, fut):
|
||||||
self.log("Reached TIMEOUT ({} seconds). Terminating all tasks.".format(self.opt_timeout))
|
self.log("Reached TIMEOUT ({} seconds). Terminating all tasks.".format(self.opt_timeout))
|
||||||
self.status = "TIMEOUT"
|
self.status = "TIMEOUT"
|
||||||
self.terminate(timeout=True)
|
self.terminate(timeout=True)
|
||||||
|
|
||||||
|
async def task_poller(self):
|
||||||
|
if self.opt_timeout is not None:
|
||||||
|
timer_fut = asyncio.ensure_future(self.timekeeper())
|
||||||
|
done_cb = partial(SbyJob.timeout, self)
|
||||||
|
timer_fut.add_done_callback(done_cb)
|
||||||
|
|
||||||
|
for task in self.tasks_pending:
|
||||||
|
await task.maybe_spawn()
|
||||||
|
|
||||||
|
while len(self.tasks_running):
|
||||||
|
task_futs = []
|
||||||
|
for task in self.tasks_running:
|
||||||
|
if task.running:
|
||||||
|
task_futs.append(task.fut)
|
||||||
|
(done, pending) = await asyncio.wait(task_futs, return_when=asyncio.FIRST_COMPLETED)
|
||||||
|
|
||||||
|
for task in self.tasks_running:
|
||||||
|
if task.fut in done:
|
||||||
|
await task.shutdown_and_notify()
|
||||||
|
|
||||||
|
if self.opt_timeout is not None:
|
||||||
|
timer_fut.remove_done_callback(done_cb)
|
||||||
|
timer_fut.cancel()
|
||||||
|
|
||||||
|
# Required on Windows. I am unsure why, but subprocesses that were
|
||||||
|
# terminated will not have their futures complete until awaited on
|
||||||
|
# one last time.
|
||||||
|
if os.name != "posix":
|
||||||
|
for t in self.tasks_retired:
|
||||||
|
if not t.fut.done():
|
||||||
|
await t.fut
|
||||||
|
|
||||||
def log(self, logmessage):
|
def log(self, logmessage):
|
||||||
tm = localtime()
|
tm = localtime()
|
||||||
print("SBY {:2d}:{:02d}:{:02d} [{}] {}".format(tm.tm_hour, tm.tm_min, tm.tm_sec, self.workdir, logmessage), flush=True)
|
print("SBY {:2d}:{:02d}:{:02d} [{}] {}".format(tm.tm_hour, tm.tm_min, tm.tm_sec, self.workdir, logmessage), flush=True)
|
||||||
|
@ -641,19 +671,19 @@ class SbyJob:
|
||||||
|
|
||||||
if self.opt_mode == "bmc":
|
if self.opt_mode == "bmc":
|
||||||
import sby_mode_bmc
|
import sby_mode_bmc
|
||||||
sby_mode_bmc.run(self)
|
sby_mode_bmc.init(self)
|
||||||
|
|
||||||
elif self.opt_mode == "prove":
|
elif self.opt_mode == "prove":
|
||||||
import sby_mode_prove
|
import sby_mode_prove
|
||||||
sby_mode_prove.run(self)
|
sby_mode_prove.init(self)
|
||||||
|
|
||||||
elif self.opt_mode == "live":
|
elif self.opt_mode == "live":
|
||||||
import sby_mode_live
|
import sby_mode_live
|
||||||
sby_mode_live.run(self)
|
sby_mode_live.init(self)
|
||||||
|
|
||||||
elif self.opt_mode == "cover":
|
elif self.opt_mode == "cover":
|
||||||
import sby_mode_cover
|
import sby_mode_cover
|
||||||
sby_mode_cover.run(self)
|
sby_mode_cover.init(self)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import re, os, getopt
|
import re, os, getopt
|
||||||
from sby_core import SbyTask
|
from sby_core import SbyTask
|
||||||
|
|
||||||
def run(mode, job, engine_idx, engine):
|
def init(mode, job, engine_idx, engine):
|
||||||
abc_opts, abc_command = getopt.getopt(engine[1:], "", [])
|
abc_opts, abc_command = getopt.getopt(engine[1:], "", [])
|
||||||
|
|
||||||
if len(abc_command) == 0:
|
if len(abc_command) == 0:
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import re, os, getopt
|
import re, os, getopt
|
||||||
from sby_core import SbyTask
|
from sby_core import SbyTask
|
||||||
|
|
||||||
def run(mode, job, engine_idx, engine):
|
def init(mode, job, engine_idx, engine):
|
||||||
opts, solver_args = getopt.getopt(engine[1:], "", [])
|
opts, solver_args = getopt.getopt(engine[1:], "", [])
|
||||||
|
|
||||||
if len(solver_args) == 0:
|
if len(solver_args) == 0:
|
||||||
|
|
|
@ -20,7 +20,7 @@ import re, os, getopt
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from sby_core import SbyTask
|
from sby_core import SbyTask
|
||||||
|
|
||||||
def run(mode, job, engine_idx, engine):
|
def init(mode, job, engine_idx, engine):
|
||||||
random_seed = None
|
random_seed = None
|
||||||
|
|
||||||
opts, solver_args = getopt.getopt(engine[1:], "", ["seed="])
|
opts, solver_args = getopt.getopt(engine[1:], "", ["seed="])
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import re, os, getopt
|
import re, os, getopt
|
||||||
from sby_core import SbyTask
|
from sby_core import SbyTask
|
||||||
|
|
||||||
def run(mode, job, engine_idx, engine):
|
def init(mode, job, engine_idx, engine):
|
||||||
smtbmc_opts = []
|
smtbmc_opts = []
|
||||||
nomem_opt = False
|
nomem_opt = False
|
||||||
presat_opt = True
|
presat_opt = True
|
||||||
|
@ -102,9 +102,9 @@ def run(mode, job, engine_idx, engine):
|
||||||
|
|
||||||
if mode == "prove":
|
if mode == "prove":
|
||||||
if not induction_only:
|
if not induction_only:
|
||||||
run("prove_basecase", job, engine_idx, engine)
|
init("prove_basecase", job, engine_idx, engine)
|
||||||
if not basecase_only:
|
if not basecase_only:
|
||||||
run("prove_induction", job, engine_idx, engine)
|
init("prove_induction", job, engine_idx, engine)
|
||||||
return
|
return
|
||||||
|
|
||||||
taskname = "engine_{}".format(engine_idx)
|
taskname = "engine_{}".format(engine_idx)
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import re, os, getopt
|
import re, os, getopt
|
||||||
from sby_core import SbyTask
|
from sby_core import SbyTask
|
||||||
|
|
||||||
def run(job):
|
def init(job):
|
||||||
job.handle_int_option("depth", 20)
|
job.handle_int_option("depth", 20)
|
||||||
job.handle_int_option("append", 0)
|
job.handle_int_option("append", 0)
|
||||||
job.handle_str_option("aigsmt", "yices")
|
job.handle_str_option("aigsmt", "yices")
|
||||||
|
@ -33,15 +33,15 @@ def run(job):
|
||||||
|
|
||||||
if engine[0] == "smtbmc":
|
if engine[0] == "smtbmc":
|
||||||
import sby_engine_smtbmc
|
import sby_engine_smtbmc
|
||||||
sby_engine_smtbmc.run("bmc", job, engine_idx, engine)
|
sby_engine_smtbmc.init("bmc", job, engine_idx, engine)
|
||||||
|
|
||||||
elif engine[0] == "abc":
|
elif engine[0] == "abc":
|
||||||
import sby_engine_abc
|
import sby_engine_abc
|
||||||
sby_engine_abc.run("bmc", job, engine_idx, engine)
|
sby_engine_abc.init("bmc", job, engine_idx, engine)
|
||||||
|
|
||||||
elif engine[0] == "btor":
|
elif engine[0] == "btor":
|
||||||
import sby_engine_btor
|
import sby_engine_btor
|
||||||
sby_engine_btor.run("bmc", job, engine_idx, engine)
|
sby_engine_btor.init("bmc", job, engine_idx, engine)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
job.error("Invalid engine '{}' for bmc mode.".format(engine[0]))
|
job.error("Invalid engine '{}' for bmc mode.".format(engine[0]))
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import re, os, getopt
|
import re, os, getopt
|
||||||
from sby_core import SbyTask
|
from sby_core import SbyTask
|
||||||
|
|
||||||
def run(job):
|
def init(job):
|
||||||
job.handle_int_option("depth", 20)
|
job.handle_int_option("depth", 20)
|
||||||
job.handle_int_option("append", 0)
|
job.handle_int_option("append", 0)
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ def run(job):
|
||||||
|
|
||||||
if engine[0] == "smtbmc":
|
if engine[0] == "smtbmc":
|
||||||
import sby_engine_smtbmc
|
import sby_engine_smtbmc
|
||||||
sby_engine_smtbmc.run("cover", job, engine_idx, engine)
|
sby_engine_smtbmc.init("cover", job, engine_idx, engine)
|
||||||
|
|
||||||
elif engine[0] == "btor":
|
elif engine[0] == "btor":
|
||||||
import sby_engine_btor
|
import sby_engine_btor
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import re, os, getopt
|
import re, os, getopt
|
||||||
from sby_core import SbyTask
|
from sby_core import SbyTask
|
||||||
|
|
||||||
def run(job):
|
def init(job):
|
||||||
job.handle_str_option("aigsmt", "yices")
|
job.handle_str_option("aigsmt", "yices")
|
||||||
|
|
||||||
job.status = "UNKNOWN"
|
job.status = "UNKNOWN"
|
||||||
|
@ -33,7 +33,7 @@ def run(job):
|
||||||
|
|
||||||
if engine[0] == "aiger":
|
if engine[0] == "aiger":
|
||||||
import sby_engine_aiger
|
import sby_engine_aiger
|
||||||
sby_engine_aiger.run("live", job, engine_idx, engine)
|
sby_engine_aiger.init("live", job, engine_idx, engine)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
job.error("Invalid engine '{}' for live mode.".format(engine[0]))
|
job.error("Invalid engine '{}' for live mode.".format(engine[0]))
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import re, os, getopt
|
import re, os, getopt
|
||||||
from sby_core import SbyTask
|
from sby_core import SbyTask
|
||||||
|
|
||||||
def run(job):
|
def init(job):
|
||||||
job.handle_int_option("depth", 20)
|
job.handle_int_option("depth", 20)
|
||||||
job.handle_int_option("append", 0)
|
job.handle_int_option("append", 0)
|
||||||
job.handle_str_option("aigsmt", "yices")
|
job.handle_str_option("aigsmt", "yices")
|
||||||
|
@ -40,15 +40,15 @@ def run(job):
|
||||||
|
|
||||||
if engine[0] == "smtbmc":
|
if engine[0] == "smtbmc":
|
||||||
import sby_engine_smtbmc
|
import sby_engine_smtbmc
|
||||||
sby_engine_smtbmc.run("prove", job, engine_idx, engine)
|
sby_engine_smtbmc.init("prove", job, engine_idx, engine)
|
||||||
|
|
||||||
elif engine[0] == "aiger":
|
elif engine[0] == "aiger":
|
||||||
import sby_engine_aiger
|
import sby_engine_aiger
|
||||||
sby_engine_aiger.run("prove", job, engine_idx, engine)
|
sby_engine_aiger.init("prove", job, engine_idx, engine)
|
||||||
|
|
||||||
elif engine[0] == "abc":
|
elif engine[0] == "abc":
|
||||||
import sby_engine_abc
|
import sby_engine_abc
|
||||||
sby_engine_abc.run("prove", job, engine_idx, engine)
|
sby_engine_abc.init("prove", job, engine_idx, engine)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
job.error("Invalid engine '{}' for prove mode.".format(engine[0]))
|
job.error("Invalid engine '{}' for prove mode.".format(engine[0]))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue