]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.15] gh-151022: Fix remote debugging linetable reads (GH-151036) (#154187)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Sun, 19 Jul 2026 18:59:49 +0000 (20:59 +0200)
committerGitHub <noreply@github.com>
Sun, 19 Jul 2026 18:59:49 +0000 (18:59 +0000)
Lib/test/test_external_inspection.py
Misc/NEWS.d/next/Library/2026-06-06-15-04-37.gh-issue-151022.1Aw8Tk.rst [new file with mode: 0644]
Modules/_remote_debugging/code_objects.c

index 6b1529aa173f01c25bf7515a05d41744dbcd4568..910fe96d5e7d81e7023e1890aff8eef99ac330c9 100644 (file)
@@ -438,6 +438,56 @@ class RemoteInspectionTestBase(unittest.TestCase):
 # ============================================================================
 
 
+@requires_remote_subprocess_debugging()
+class TestSelfStackTrace(RemoteInspectionTestBase):
+    @skip_if_not_supported
+    @unittest.skipIf(
+        sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED,
+        "Test only runs on Linux with process_vm_readv support",
+    )
+    def test_self_trace_with_large_linetable(self):
+        script = textwrap.dedent("""\
+            import os
+            import _remote_debugging
+
+            assignments = "\\n".join(
+                f"value_{i} = {i}" for i in range(1000)
+            )
+            expected_lineno = len(assignments.splitlines()) + 1
+            source = (
+                f"{assignments}\\n"
+                "stack_trace = "
+                "_remote_debugging.RemoteUnwinder(os.getpid()).get_stack_trace()\\n"
+            )
+            code = compile(source, "large_linetable.py", "exec")
+            assert len(code.co_linetable) > 4096, len(code.co_linetable)
+            namespace = {"os": os, "_remote_debugging": _remote_debugging}
+            exec(code, namespace)
+            large_linetable_frames = [
+                frame
+                for interpreter in namespace["stack_trace"]
+                for thread in interpreter.threads
+                for frame in thread.frame_info
+                if frame.filename == "large_linetable.py"
+            ]
+            assert len(large_linetable_frames) == 1, large_linetable_frames
+            assert large_linetable_frames[0].location.lineno == expected_lineno, (
+                large_linetable_frames[0]
+            )
+            """)
+
+        result = subprocess.run(
+            [sys.executable, "-c", script],
+            capture_output=True,
+            text=True,
+            timeout=SHORT_TIMEOUT,
+        )
+        self.assertEqual(
+            result.returncode, 0,
+            f"stdout: {result.stdout}\nstderr: {result.stderr}"
+        )
+
+
 @requires_remote_subprocess_debugging()
 class TestGetStackTrace(RemoteInspectionTestBase):
     @skip_if_not_supported
diff --git a/Misc/NEWS.d/next/Library/2026-06-06-15-04-37.gh-issue-151022.1Aw8Tk.rst b/Misc/NEWS.d/next/Library/2026-06-06-15-04-37.gh-issue-151022.1Aw8Tk.rst
new file mode 100644 (file)
index 0000000..f16f024
--- /dev/null
@@ -0,0 +1,2 @@
+Fix ``_remote_debugging`` stack traces for code objects with large line
+tables.
index ab889a130ee4e7ec8de604a27f7dd610258f846d..f83252524b96ff49ef3cab5c57b232a3d9a8d1a3 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "_remote_debugging.h"
 
+#define MAX_LINETABLE_SIZE (64 * 1024)
+
 /* ============================================================================
  * TLBC CACHING FUNCTIONS (Py_GIL_DISABLED only)
  * ============================================================================ */
@@ -186,11 +188,7 @@ parse_linetable(const uintptr_t addrq, const char* linetable, Py_ssize_t linetab
     int computed_line = firstlineno;  // Running accumulator, separate from output
     int val;  // Temporary for varint results
     uint8_t byte;  // Temporary for byte reads
-    const size_t MAX_LINETABLE_ENTRIES = 65536;
-    size_t entry_count = 0;
-
-    while (ptr < end && *ptr != '\0' && entry_count < MAX_LINETABLE_ENTRIES) {
-        entry_count++;
+    while (ptr < end && *ptr != '\0') {
         uint8_t first_byte = *(ptr++);
         uint8_t code = (first_byte >> 3) & 15;
         size_t length = (first_byte & 7) + 1;
@@ -387,7 +385,8 @@ parse_code_object(RemoteUnwinderObject *unwinder,
         }
 
         linetable = read_py_bytes(unwinder,
-            GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.linetable), 4096);
+            GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.linetable),
+            MAX_LINETABLE_SIZE);
         if (!linetable) {
             set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read linetable from code object");
             goto error;