Skip to content

Commit fcb2301

Browse files
pitroucorona10
andauthored
[3.12] gh-112536: Add --tsan test for reasonable TSAN execution times. (gh-116601) (#116929)
(cherry picked from commit ebf29b3) Co-authored-by: Donghee Na <donghee.na@python.org>
1 parent 2ac1b48 commit fcb2301

File tree

8 files changed

+64
-4
lines changed

8 files changed

+64
-4
lines changed

Lib/test/libregrtest/cmdline.py

+3
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ def __init__(self, **kwargs) -> None:
164164
self.match_tests: TestFilter = []
165165
self.pgo = False
166166
self.pgo_extended = False
167+
self.tsan = False
167168
self.worker_json = None
168169
self.start = None
169170
self.timeout = None
@@ -333,6 +334,8 @@ def _create_parser():
333334
help='enable Profile Guided Optimization (PGO) training')
334335
group.add_argument('--pgo-extended', action='store_true',
335336
help='enable extended PGO training (slower training)')
337+
group.add_argument('--tsan', dest='tsan', action='store_true',
338+
help='run a subset of test cases that are proper for the TSAN test')
336339
group.add_argument('--fail-env-changed', action='store_true',
337340
help='if a test file alters the environment, mark '
338341
'the test as failed')

Lib/test/libregrtest/main.py

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from .runtests import RunTests, HuntRefleak
1919
from .setup import setup_process, setup_test_dir
2020
from .single import run_single_test, PROGRESS_MIN_TIME
21+
from .tsan import setup_tsan_tests
2122
from .utils import (
2223
StrPath, StrJSON, TestName, TestList, TestTuple, TestFilter,
2324
strip_py_suffix, count, format_duration,
@@ -56,6 +57,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False):
5657
self.quiet: bool = ns.quiet
5758
self.pgo: bool = ns.pgo
5859
self.pgo_extended: bool = ns.pgo_extended
60+
self.tsan: bool = ns.tsan
5961

6062
# Test results
6163
self.results: TestResults = TestResults()
@@ -182,6 +184,9 @@ def find_tests(self, tests: TestList | None = None) -> tuple[TestTuple, TestList
182184
# add default PGO tests if no tests are specified
183185
setup_pgo_tests(self.cmdline_args, self.pgo_extended)
184186

187+
if self.tsan:
188+
setup_tsan_tests(self.cmdline_args)
189+
185190
exclude_tests = set()
186191
if self.exclude:
187192
for arg in self.cmdline_args:

Lib/test/libregrtest/tsan.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Set of tests run by default if --tsan is specified. The tests below were
2+
# chosen because they use threads and run in a reasonable amount of time.
3+
4+
TSAN_TESTS = [
5+
'test_capi',
6+
'test_code',
7+
'test_enum',
8+
'test_functools',
9+
'test_httpservers',
10+
'test_imaplib',
11+
'test_importlib',
12+
'test_io',
13+
'test_logging',
14+
'test_queue',
15+
'test_signal',
16+
'test_socket',
17+
'test_sqlite3',
18+
'test_ssl',
19+
'test_syslog',
20+
'test_thread',
21+
'test_threadedtempfile',
22+
'test_threading',
23+
'test_threading_local',
24+
'test_threadsignals',
25+
'test_weakref',
26+
]
27+
28+
29+
def setup_tsan_tests(cmdline_args):
30+
if not cmdline_args:
31+
cmdline_args[:] = TSAN_TESTS[:]

Lib/test/support/script_helper.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ class _PythonRunResult(collections.namedtuple("_PythonRunResult",
6464
"""Helper for reporting Python subprocess run results"""
6565
def fail(self, cmd_line):
6666
"""Provide helpful details about failed subcommand runs"""
67-
# Limit to 80 lines to ASCII characters
68-
maxlen = 80 * 100
67+
# Limit to 300 lines of ASCII characters
68+
maxlen = 300 * 100
6969
out, err = self.out, self.err
7070
if len(out) > maxlen:
7171
out = b'(... truncated stdout ...)' + out[-maxlen:]

Lib/test/test_threading.py

+16
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ def skip_unless_reliable_fork(test):
4747
return unittest.skip("due to known OS bug related to thread+fork")(test)
4848
if support.HAVE_ASAN_FORK_BUG:
4949
return unittest.skip("libasan has a pthread_create() dead lock related to thread+fork")(test)
50+
if support.check_sanitizer(thread=True):
51+
return unittest.skip("TSAN doesn't support threads after fork")
5052
return test
5153

5254

@@ -384,6 +386,10 @@ def test_finalize_running_thread(self):
384386
# Issue 1402: the PyGILState_Ensure / _Release functions may be called
385387
# very late on python exit: on deallocation of a running thread for
386388
# example.
389+
if support.check_sanitizer(thread=True):
390+
# the thread running `time.sleep(100)` below will still be alive
391+
# at process exit
392+
self.skipTest("TSAN would report thread leak")
387393
import_module("ctypes")
388394

389395
rc, out, err = assert_python_failure("-c", """if 1:
@@ -416,6 +422,11 @@ def waitingThread():
416422
def test_finalize_with_trace(self):
417423
# Issue1733757
418424
# Avoid a deadlock when sys.settrace steps into threading._shutdown
425+
if support.check_sanitizer(thread=True):
426+
# the thread running `time.sleep(2)` below will still be alive
427+
# at process exit
428+
self.skipTest("TSAN would report thread leak")
429+
419430
assert_python_ok("-c", """if 1:
420431
import sys, threading
421432
@@ -1223,6 +1234,11 @@ def test_4_daemon_threads(self):
12231234
# Check that a daemon thread cannot crash the interpreter on shutdown
12241235
# by manipulating internal structures that are being disposed of in
12251236
# the main thread.
1237+
if support.check_sanitizer(thread=True):
1238+
# some of the threads running `random_io` below will still be alive
1239+
# at process exit
1240+
self.skipTest("TSAN would report thread leak")
1241+
12261242
script = """if True:
12271243
import os
12281244
import random
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add --tsan to test.regrtest for running TSAN tests in reasonable execution
2+
times. Patch by Donghee Na.

Modules/_testcapi/mem.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -580,8 +580,8 @@ check_pyobject_forbidden_bytes_is_freed(PyObject *self,
580580
static PyObject *
581581
check_pyobject_freed_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
582582
{
583-
/* This test would fail if run with the address sanitizer */
584-
#ifdef _Py_ADDRESS_SANITIZER
583+
/* ASan or TSan would report an use-after-free error */
584+
#if defined(_Py_ADDRESS_SANITIZER) || defined(_Py_THREAD_SANITIZER)
585585
Py_RETURN_NONE;
586586
#else
587587
PyObject *op = PyObject_CallNoArgs((PyObject *)&PyBaseObject_Type);

Tools/tsan/supressions.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions
2+
race:get_allocator_unlocked
3+
race:set_allocator_unlocked

0 commit comments

Comments
 (0)