diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index 608b12bb6f2a38..2cf63330366aed 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -164,6 +164,7 @@ def __init__(self, **kwargs) -> None: self.match_tests: TestFilter = [] self.pgo = False self.pgo_extended = False + self.tsan = False self.worker_json = None self.start = None self.timeout = None @@ -333,6 +334,8 @@ def _create_parser(): help='enable Profile Guided Optimization (PGO) training') group.add_argument('--pgo-extended', action='store_true', help='enable extended PGO training (slower training)') + group.add_argument('--tsan', dest='tsan', action='store_true', + help='enable TSAN test') group.add_argument('--fail-env-changed', action='store_true', help='if a test file alters the environment, mark ' 'the test as failed') diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 126daca388fd7f..28f3c5abd0bc2e 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -18,6 +18,7 @@ from .runtests import RunTests, HuntRefleak from .setup import setup_process, setup_test_dir from .single import run_single_test, PROGRESS_MIN_TIME +from .tsan import setup_tsan_tests from .utils import ( StrPath, StrJSON, TestName, TestList, TestTuple, TestFilter, strip_py_suffix, count, format_duration, @@ -56,6 +57,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): self.quiet: bool = ns.quiet self.pgo: bool = ns.pgo self.pgo_extended: bool = ns.pgo_extended + self.tsan: bool = ns.tsan # Test results self.results: TestResults = TestResults() @@ -87,7 +89,11 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): # Workers if ns.use_mp is None: - num_workers = 0 # run sequentially + if ns.tsan: + # For TSAN tests, use number of CPU of the current process. + num_workers = os.process_cpu_count() + else: + num_workers = 0 # run sequentially elif ns.use_mp <= 0: num_workers = -1 # use the number of CPUs else: @@ -183,6 +189,9 @@ def find_tests(self, tests: TestList | None = None) -> tuple[TestTuple, TestList # add default PGO tests if no tests are specified setup_pgo_tests(self.cmdline_args, self.pgo_extended) + if self.tsan: + setup_tsan_tests(self.cmdline_args) + exclude_tests = set() if self.exclude: for arg in self.cmdline_args: diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py new file mode 100644 index 00000000000000..53c7957b5efed2 --- /dev/null +++ b/Lib/test/libregrtest/tsan.py @@ -0,0 +1,43 @@ +# Set of tests run by default if --tsan is specified. The tests below were +# chosen to run a thread sanitizer in a reasonable executable time. + +TSAN_TESTS = [ + 'test_asyncio', + 'test_capi', + 'test_code', + 'test_compilerall', + 'test_concurrent_futures', + 'test_enum', + 'test_fork1', + 'test_functools', + 'test_httpservers', + 'test_imaplib', + 'test_import', + 'test_importlib', + 'test_io', + 'test_logging', + 'test_multiprocessing_forkserver', + 'test_multiprocessing_spawn', + 'test_pickle', + 'test_queue', + 'test_list', + 'test_dict', + 'test_set', + 'test_tuple', + 'test_smtpnet', + 'test_socketserver', + 'test_ssl', + 'test_syslog', + 'test_thread', + 'test_threadedtempfile', + 'test_threading', + 'test_threading_local', + 'test_threadsignals', + 'test_urllib2net', + 'test_weakref' +] + + +def setup_tsan_tests(cmdline_args): + if not cmdline_args: + cmdline_args[:] = TSAN_TESTS[:] diff --git a/Tools/tsan/supressions.txt b/Tools/tsan/supressions.txt new file mode 100644 index 00000000000000..b70dcc30d3aaf5 --- /dev/null +++ b/Tools/tsan/supressions.txt @@ -0,0 +1,3 @@ +## reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions +race:get_allocator_unlocked +race:set_allocator_unlocked