summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorBrett Simmers <swtaarrs@users.noreply.github.com>2024-03-04 16:29:39 (GMT)
committerGitHub <noreply@github.com>2024-03-04 16:29:39 (GMT)
commit0adfa8482d369899e9963206a3307f423309e10c (patch)
treec1f71c0f65b0f01581d37eafa55c26b769915299 /Lib/test
parent15dc2979bc1b24269177f0e150495abb7f3eb546 (diff)
downloadcpython-0adfa8482d369899e9963206a3307f423309e10c.zip
cpython-0adfa8482d369899e9963206a3307f423309e10c.tar.gz
cpython-0adfa8482d369899e9963206a3307f423309e10c.tar.bz2
gh-115832: Fix instrumentation version mismatch during interpreter shutdown (#115856)
A previous commit introduced a bug to `interpreter_clear()`: it set `interp->ceval.instrumentation_version` to 0, without making the corresponding change to `tstate->eval_breaker` (which holds a thread-local copy of the version). After this happens, Python code can still run due to object finalizers during a GC, and the version check in bytecodes.c will see a different result than the one in instrumentation.c causing an infinite loop. The fix itself is straightforward: clear `tstate->eval_breaker` when clearing `interp->ceval.instrumentation_version`.
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/_test_monitoring_shutdown.py30
-rw-r--r--Lib/test/test_monitoring.py12
2 files changed, 41 insertions, 1 deletions
diff --git a/Lib/test/_test_monitoring_shutdown.py b/Lib/test/_test_monitoring_shutdown.py
new file mode 100644
index 0000000..3d0fbec
--- /dev/null
+++ b/Lib/test/_test_monitoring_shutdown.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+
+# gh-115832: An object destructor running during the final GC of interpreter
+# shutdown triggered an infinite loop in the instrumentation code.
+
+import sys
+
+class CallableCycle:
+ def __init__(self):
+ self._cycle = self
+
+ def __del__(self):
+ pass
+
+ def __call__(self, code, instruction_offset):
+ pass
+
+def tracefunc(frame, event, arg):
+ pass
+
+def main():
+ tool_id = sys.monitoring.PROFILER_ID
+ event_id = sys.monitoring.events.PY_START
+
+ sys.monitoring.use_tool_id(tool_id, "test profiler")
+ sys.monitoring.set_events(tool_id, event_id)
+ sys.monitoring.register_callback(tool_id, event_id, CallableCycle())
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py
index b027959..1e77eb6 100644
--- a/Lib/test/test_monitoring.py
+++ b/Lib/test/test_monitoring.py
@@ -9,7 +9,8 @@ import textwrap
import types
import unittest
import asyncio
-from test.support import requires_specialization
+from test import support
+from test.support import requires_specialization, script_helper
PAIR = (0,1)
@@ -1858,3 +1859,12 @@ class TestTier2Optimizer(CheckEvents):
sys.monitoring.register_callback(TEST_TOOL, E.LINE, None)
sys.monitoring.set_events(TEST_TOOL, 0)
self.assertGreater(len(events), 250)
+
+class TestMonitoringAtShutdown(unittest.TestCase):
+
+ def test_monitoring_live_at_shutdown(self):
+ # gh-115832: An object destructor running during the final GC of
+ # interpreter shutdown triggered an infinite loop in the
+ # instrumentation code.
+ script = support.findfile("_test_monitoring_shutdown.py")
+ script_helper.run_test_script(script)