diff --git a/test_regress/driver.py b/test_regress/driver.py index 3cf46ab10..a5292afe7 100755 --- a/test_regress/driver.py +++ b/test_regress/driver.py @@ -4,6 +4,7 @@ import argparse import collections +import ctypes import glob import hashlib import json @@ -14,6 +15,7 @@ import pickle import platform import pty import re +import resource import runpy import shutil import signal @@ -1366,6 +1368,19 @@ class VlTest: ] self.run(logfile=self.obj_dir + "/pli_compile.log", fails=param['fails'], cmd=cmd) + def timeout(self, seconds): + """Limit the CPU time of the test - this limit is inherited + by all of the spawned child processess""" + # An unprivileged process may set only its soft limit + # to a value in the range from 0 up to the hard limit + _, hardlimit = resource.getrlimit(resource.RLIMIT_CPU) + softlimit = ctypes.c_long(min(seconds, ctypes.c_ulong(hardlimit).value)).value + # Casting is required due to a quirk in Python, + # rlimit values are interpreted as LONG, instead of ULONG + # https://github.com/python/cpython/issues/137044 + rlimit = (softlimit, hardlimit) + resource.setrlimit(resource.RLIMIT_CPU, rlimit) + def execute(self, **kwargs) -> None: """Run simulation executable. Arguments similar to run(); default arguments are from self""" diff --git a/test_regress/t/t_concat_string.py b/test_regress/t/t_concat_string.py index 262a0d4b4..52c58524e 100755 --- a/test_regress/t/t_concat_string.py +++ b/test_regress/t/t_concat_string.py @@ -7,12 +7,11 @@ # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -import signal import vltest_bootstrap test.scenarios('simulator') -signal.alarm(15) # 15s timeout +test.timeout(15) test.compile() diff --git a/test_regress/t/t_const_number_unsized_parse.py b/test_regress/t/t_const_number_unsized_parse.py index 6b8b2c82e..2920c1617 100755 --- a/test_regress/t/t_const_number_unsized_parse.py +++ b/test_regress/t/t_const_number_unsized_parse.py @@ -7,7 +7,6 @@ # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -import signal import vltest_bootstrap test.scenarios('vlt') @@ -20,7 +19,7 @@ with open(test.top_filename, "w", encoding="utf8") as f: f.write(f" int x{i} = 'd{i};\n") f.write("endmodule\n") -signal.alarm(30) # 30s timeout +test.timeout(30) test.lint(verilator_flags2=[f"--max-num-width {2**29}"]) diff --git a/test_regress/t/t_timeout.py b/test_regress/t/t_timeout.py new file mode 100755 index 000000000..bd3c3c3c0 --- /dev/null +++ b/test_regress/t/t_timeout.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('dist') + +test.passes() + +test.timeout(1) + +# Check whether the soft limit is set to 1s +test.run(cmd=["bash", "-c", "\"[ '$(ulimit -St)' -eq 1 ] || exit 1\""]) + +# Set the hard limit to 2s (in case soft limit fails) and run a command which spins until signalled +test.run(cmd=["bash", "-c", "\"trap 'exit 0' SIGXCPU; ulimit -Ht 2; while :; do :; done\""]) + +test.passes()