From: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Date: Sun, 19 Jul 2026 18:59:49 +0000 (+0200) Subject: [3.15] gh-151022: Fix remote debugging linetable reads (GH-151036) (#154187) X-Git-Url: http://git.ipfire.org/index.cgi?a=commitdiff_plain;h=b3ee1902bb57731e9c6d5458c96121bad18dcbdc;p=thirdparty%2FPython%2Fcpython.git [3.15] gh-151022: Fix remote debugging linetable reads (GH-151036) (#154187) --- diff --git a/Lib/test/test_external_inspection.py b/Lib/test/test_external_inspection.py index 6b1529aa173f..910fe96d5e7d 100644 --- a/Lib/test/test_external_inspection.py +++ b/Lib/test/test_external_inspection.py @@ -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 index 000000000000..f16f024c582f --- /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 ab889a130ee4..f83252524b96 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;