From 9b0f3baa4d222664c133bd49aae28e4b079290f2 Mon Sep 17 00:00:00 2001 From: goutamadwant Date: Sat, 6 Jun 2026 15:05:35 -0700 Subject: [PATCH] gh-151022: Fix remote debugging linetable reads --- Lib/test/test_external_inspection.py | 35 +++++++++++++++++++ ...-06-06-15-04-37.gh-issue-151022.1Aw8Tk.rst | 2 ++ Modules/_remote_debugging/code_objects.c | 7 ++-- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-06-15-04-37.gh-issue-151022.1Aw8Tk.rst diff --git a/Lib/test/test_external_inspection.py b/Lib/test/test_external_inspection.py index 6b1529aa173f01..3dbb5189214a60 100644 --- a/Lib/test/test_external_inspection.py +++ b/Lib/test/test_external_inspection.py @@ -438,6 +438,41 @@ def _extract_coroutine_stacks_lineno_only(self, stack_trace): # ============================================================================ +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) + ) + source = ( + f"{assignments}\\n" + "_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) + exec(code) + """) + + 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 00000000000000..f16f024c582fb3 --- /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 ab889a130ee4e7..b5c8d5d9bc0b7b 100644 --- a/Modules/_remote_debugging/code_objects.c +++ b/Modules/_remote_debugging/code_objects.c @@ -7,6 +7,9 @@ #include "_remote_debugging.h" +#define MAX_LINETABLE_SIZE (1024 * 1024) +#define MAX_LINETABLE_ENTRIES 65536 + /* ============================================================================ * TLBC CACHING FUNCTIONS (Py_GIL_DISABLED only) * ============================================================================ */ @@ -186,7 +189,6 @@ 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) { @@ -387,7 +389,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;