https://github.com/python/cpython/commit/fcb230180faff828aa67ca2ee1aa94fc522e6daa
commit: fcb230180faff828aa67ca2ee1aa94fc522e6daa
branch: 3.12
author: Antoine Pitrou <anto...@python.org>
committer: pitrou <pit...@free.fr>
date: 2024-03-18T10:22:19+01:00
summary:

[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...@python.org>

files:
A Lib/test/libregrtest/tsan.py
A Misc/NEWS.d/next/Tests/2024-03-11-23-20-28.gh-issue-112536.Qv1RrX.rst
A Tools/tsan/supressions.txt
M Lib/test/libregrtest/cmdline.py
M Lib/test/libregrtest/main.py
M Lib/test/support/script_helper.py
M Lib/test/test_threading.py
M Modules/_testcapi/mem.c

diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py
index 23ca3565662146..ccf5c2e7d77184 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='run a subset of test cases that are proper for 
the 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 a9725fa967370a..1dcf7474163787 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()
@@ -182,6 +184,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..99cef88e6801a3
--- /dev/null
+++ b/Lib/test/libregrtest/tsan.py
@@ -0,0 +1,31 @@
+# Set of tests run by default if --tsan is specified.  The tests below were
+# chosen because they use threads and run in a reasonable amount of time.
+
+TSAN_TESTS = [
+    'test_capi',
+    'test_code',
+    'test_enum',
+    'test_functools',
+    'test_httpservers',
+    'test_imaplib',
+    'test_importlib',
+    'test_io',
+    'test_logging',
+    'test_queue',
+    'test_signal',
+    'test_socket',
+    'test_sqlite3',
+    'test_ssl',
+    'test_syslog',
+    'test_thread',
+    'test_threadedtempfile',
+    'test_threading',
+    'test_threading_local',
+    'test_threadsignals',
+    'test_weakref',
+]
+
+
+def setup_tsan_tests(cmdline_args):
+    if not cmdline_args:
+        cmdline_args[:] = TSAN_TESTS[:]
diff --git a/Lib/test/support/script_helper.py 
b/Lib/test/support/script_helper.py
index c2b43f4060eb55..565f3b54a04be5 100644
--- a/Lib/test/support/script_helper.py
+++ b/Lib/test/support/script_helper.py
@@ -64,8 +64,8 @@ class 
_PythonRunResult(collections.namedtuple("_PythonRunResult",
     """Helper for reporting Python subprocess run results"""
     def fail(self, cmd_line):
         """Provide helpful details about failed subcommand runs"""
-        # Limit to 80 lines to ASCII characters
-        maxlen = 80 * 100
+        # Limit to 300 lines of ASCII characters
+        maxlen = 300 * 100
         out, err = self.out, self.err
         if len(out) > maxlen:
             out = b'(... truncated stdout ...)' + out[-maxlen:]
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index 00d9e591c764ea..119b41f39245ca 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -47,6 +47,8 @@ def skip_unless_reliable_fork(test):
         return unittest.skip("due to known OS bug related to 
thread+fork")(test)
     if support.HAVE_ASAN_FORK_BUG:
         return unittest.skip("libasan has a pthread_create() dead lock related 
to thread+fork")(test)
+    if support.check_sanitizer(thread=True):
+        return unittest.skip("TSAN doesn't support threads after fork")
     return test
 
 
@@ -384,6 +386,10 @@ def test_finalize_running_thread(self):
         # Issue 1402: the PyGILState_Ensure / _Release functions may be called
         # very late on python exit: on deallocation of a running thread for
         # example.
+        if support.check_sanitizer(thread=True):
+            # the thread running `time.sleep(100)` below will still be alive
+            # at process exit
+            self.skipTest("TSAN would report thread leak")
         import_module("ctypes")
 
         rc, out, err = assert_python_failure("-c", """if 1:
@@ -416,6 +422,11 @@ def waitingThread():
     def test_finalize_with_trace(self):
         # Issue1733757
         # Avoid a deadlock when sys.settrace steps into threading._shutdown
+        if support.check_sanitizer(thread=True):
+            # the thread running `time.sleep(2)` below will still be alive
+            # at process exit
+            self.skipTest("TSAN would report thread leak")
+
         assert_python_ok("-c", """if 1:
             import sys, threading
 
@@ -1223,6 +1234,11 @@ def test_4_daemon_threads(self):
         # Check that a daemon thread cannot crash the interpreter on shutdown
         # by manipulating internal structures that are being disposed of in
         # the main thread.
+        if support.check_sanitizer(thread=True):
+            # some of the threads running `random_io` below will still be alive
+            # at process exit
+            self.skipTest("TSAN would report thread leak")
+
         script = """if True:
             import os
             import random
diff --git 
a/Misc/NEWS.d/next/Tests/2024-03-11-23-20-28.gh-issue-112536.Qv1RrX.rst 
b/Misc/NEWS.d/next/Tests/2024-03-11-23-20-28.gh-issue-112536.Qv1RrX.rst
new file mode 100644
index 00000000000000..de9e1c557b093c
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2024-03-11-23-20-28.gh-issue-112536.Qv1RrX.rst
@@ -0,0 +1,2 @@
+Add --tsan to test.regrtest for running TSAN tests in reasonable execution
+times. Patch by Donghee Na.
diff --git a/Modules/_testcapi/mem.c b/Modules/_testcapi/mem.c
index af32e9668dda2d..545cc6b669c010 100644
--- a/Modules/_testcapi/mem.c
+++ b/Modules/_testcapi/mem.c
@@ -580,8 +580,8 @@ check_pyobject_forbidden_bytes_is_freed(PyObject *self,
 static PyObject *
 check_pyobject_freed_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
 {
-    /* This test would fail if run with the address sanitizer */
-#ifdef _Py_ADDRESS_SANITIZER
+    /* ASan or TSan would report an use-after-free error */
+#if defined(_Py_ADDRESS_SANITIZER) || defined(_Py_THREAD_SANITIZER)
     Py_RETURN_NONE;
 #else
     PyObject *op = PyObject_CallNoArgs((PyObject *)&PyBaseObject_Type);
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

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to