Skip to content

Commit 608eba1

Browse files
authored
Merge pull request #2356 from willend/main
Add timers for maximal compile / runtime in mctest
2 parents 4279405 + b37ce5a commit 608eba1

2 files changed

Lines changed: 50 additions & 17 deletions

File tree

tools/Python/mccodelib/utils.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -772,24 +772,43 @@ def get_file_contents(filepath):
772772
else:
773773
return ''
774774

775-
def run_subtool_noread(cmd, cwd=None):
776-
''' run subtool to completion in a excessive pipe-output robust way (millions of lines) '''
775+
def run_subtool_noread(cmd, cwd=None, timeout=None):
776+
"""Run external command without reading output; kill if it runs longer than timeout (seconds).
777+
Returns (returncode, timed_out: bool).
778+
"""
779+
777780
if not cwd:
778781
cwd = os.getcwd()
782+
779783
try:
780-
process = subprocess.Popen(cmd,
781-
stdout=subprocess.PIPE,
782-
stderr=subprocess.PIPE,
783-
stdin=subprocess.PIPE,
784-
shell=True,
785-
universal_newlines=True,
786-
cwd=cwd)
787-
process.communicate()
788-
return process.returncode
784+
process = subprocess.Popen(
785+
cmd,
786+
stdout=subprocess.PIPE,
787+
stderr=subprocess.PIPE,
788+
stdin=subprocess.PIPE,
789+
shell=True,
790+
universal_newlines=True,
791+
cwd=cwd,
792+
)
793+
794+
try:
795+
# communicate supports timeout (Python 3.3+)
796+
process.communicate(timeout=timeout)
797+
return process.returncode, False
798+
except subprocess.TimeoutExpired:
799+
# force kill if still alive
800+
try:
801+
process.kill() # SIGKILL on Unix
802+
except Exception:
803+
pass
804+
process.wait()
805+
806+
return process.returncode if process.returncode is not None else -1, True
807+
789808
except Exception as e:
790-
''' unicode read error safe-guard '''
809+
# unicode/read error safe-guard
791810
print("run_subtool_noread (cmd=%s) error: %s" % (cmd, str(e)))
792-
return -1
811+
return -1, False
793812

794813
def run_subtool_to_completion(cmd, cwd=None, stdout_cb=None, stderr_cb=None):
795814
'''

tools/Python/mctest/mctest.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def mccode_test(branchdir, testdir, limitinstrs=None, instrfilter=None, compfilt
232232

233233

234234
# compile, record time
235-
global ncount, mpi, openacc, suffix, nexus, lint, permissive
235+
global ncount, mpi, openacc, suffix, nexus, lint, permissive, compilemax, runmax
236236
logging.info("")
237237
if not lint:
238238
logging.info("Compiling instruments [seconds]...")
@@ -271,7 +271,7 @@ def mccode_test(branchdir, testdir, limitinstrs=None, instrfilter=None, compfilt
271271
cmd = cmd + " --mpi=1 "
272272
cmd = cmd + " --verbose -c -n0 %s > compile_stdout.txt 2>&1" % test.instrname
273273

274-
utils.run_subtool_noread(cmd, cwd=join(testdir, test.instrname))
274+
utils.run_subtool_noread(cmd, cwd=join(testdir, test.instrname), timeout=compilemax)
275275
t2 = time.time()
276276
test.compiled = os.path.exists(binfile)
277277
test.compiletime = t2 - t1
@@ -339,7 +339,7 @@ def mccode_test(branchdir, testdir, limitinstrs=None, instrfilter=None, compfilt
339339
if version:
340340
cmd = cmd + " --override-config=" + join(os.path.dirname(__file__), mccode_config.configuration["MCCODE"] + "-test",version)
341341
cmd = cmd + " -s 1000 %s %s -n%s -d%d > run_stdout_%d.txt 2>&1" % (test.instrname, test.parvals, ncount, test.testnb, test.testnb)
342-
retcode = utils.run_subtool_noread(cmd, cwd=join(testdir, test.instrname))
342+
retcode = utils.run_subtool_noread(cmd, cwd=join(testdir, test.instrname),timeout=runmax)
343343
t2 = time.time()
344344
didwrite = os.path.exists(join(testdir, test.instrname, str(test.testnb), "mccode.sim"))
345345
didwrite_nexus = os.path.exists(join(testdir, test.instrname, str(test.testnb), "mccode.h5"))
@@ -614,6 +614,8 @@ def get_config_files(configfltr):
614614
lint = None
615615
permissive = None
616616
runLocal = None
617+
runmax = None
618+
compilemax = None
617619

618620
def main(args):
619621
# mutually excusive main branches
@@ -672,7 +674,7 @@ def main(args):
672674
quit(1)
673675
logging.debug("")
674676

675-
global ncount, mpi, skipnontest, openacc, nexus, lint, permissive, runLocal
677+
global ncount, mpi, skipnontest, openacc, nexus, lint, permissive, runLocal, compilemax, runmax
676678
if args.ncount:
677679
ncount = args.ncount[0]
678680
elif args.n:
@@ -719,6 +721,16 @@ def main(args):
719721
lint = True
720722
suffix = '_lint' + suffix
721723
logging.info("c-linting enabled")
724+
if args.runmax:
725+
runmax=int(args.runmax[0])
726+
else:
727+
runmax=3600
728+
if args.compilemax:
729+
compilemax=int(args.compilemax[0])
730+
if lint:
731+
compilemax=100*compilemax
732+
else:
733+
compilemax=600
722734
if args.permissive:
723735
permissive = True
724736
logging.info("Permissive mode, tool will not report failure on failed instruments")
@@ -746,6 +758,8 @@ def main(args):
746758
parser.add_argument('--suffix', nargs=1, help='Add suffix to test directory name, e.g. 3.x-dev_suffix')
747759
parser.add_argument('--nexus', action='store_true', help='Compile for / use NeXus output format everywhere')
748760
parser.add_argument('--lint', action='store_true', help='Just run the c-linter')
761+
parser.add_argument('--compilemax', nargs=1, help='Maximum time (s) allowed pr. compilation (x100 with --lint)')
762+
parser.add_argument('--runmax', nargs=1, help='Maximum time (s) allowed pr. test Example run')
749763
parser.add_argument('--permissive', action='store_true', help='Use zero return-value even if some tests fail. Useful for full test con systems that are only partially functional.')
750764
parser.add_argument('--local', help='Instruments to test are NOT picked up from MCCODE installation, instead from --local=DIR')
751765
args = parser.parse_args()

0 commit comments

Comments
 (0)