[3.15] gh-151022: Fix remote debugging linetable reads (GH-151036) (#154187)

pablogsal <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/b3ee1902bb57731e9c6d5458c96121bad18dcbdc
commit: b3ee1902bb57731e9c6d5458c96121bad18dcbdc
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-07-19T18:59:49Z
summary:

[3.15] gh-151022: Fix remote debugging linetable reads (GH-151036) (#154187)

files:
A Misc/NEWS.d/next/Library/2026-06-06-15-04-37.gh-issue-151022.1Aw8Tk.rst
M Lib/test/test_external_inspection.py
M Modules/_remote_debugging/code_objects.c

diff --git a/Lib/test/test_external_inspection.py b/Lib/test/test_external_inspection.py
index 6b1529aa173f01c..910fe96d5e7d81e 100644
--- a/Lib/test/test_external_inspection.py
+++ b/Lib/test/test_external_inspection.py
@@ -438,6 +438,56 @@ def _extract_coroutine_stacks_lineno_only(self, stack_trace):
 # ============================================================================
 
 
+@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
index 000000000000000..f16f024c582fb3c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-06-06-15-04-37.gh-issue-151022.1Aw8Tk.rst
@@ -0,0 +1,2 @@
+Fix ``_remote_debugging`` stack traces for code objects with large line
+tables.
diff --git a/Modules/_remote_debugging/code_objects.c b/Modules/_remote_debugging/code_objects.c
index ab889a130ee4e7e..f83252524b96ff4 100644
--- a/Modules/_remote_debugging/code_objects.c
+++ b/Modules/_remote_debugging/code_objects.c
@@ -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;

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.