Skip to content

Commit 9b0f3ba

Browse files
committed
gh-151022: Fix remote debugging linetable reads
1 parent ab93017 commit 9b0f3ba

3 files changed

Lines changed: 42 additions & 2 deletions

File tree

Lib/test/test_external_inspection.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,41 @@ def _extract_coroutine_stacks_lineno_only(self, stack_trace):
438438
# ============================================================================
439439

440440

441+
class TestSelfStackTrace(RemoteInspectionTestBase):
442+
@skip_if_not_supported
443+
@unittest.skipIf(
444+
sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED,
445+
"Test only runs on Linux with process_vm_readv support",
446+
)
447+
def test_self_trace_with_large_linetable(self):
448+
script = textwrap.dedent("""\
449+
import os
450+
import _remote_debugging
451+
452+
assignments = "\\n".join(
453+
f"value_{i} = {i}" for i in range(1000)
454+
)
455+
source = (
456+
f"{assignments}\\n"
457+
"_remote_debugging.RemoteUnwinder(os.getpid()).get_stack_trace()\\n"
458+
)
459+
code = compile(source, "large_linetable.py", "exec")
460+
assert len(code.co_linetable) > 4096, len(code.co_linetable)
461+
exec(code)
462+
""")
463+
464+
result = subprocess.run(
465+
[sys.executable, "-c", script],
466+
capture_output=True,
467+
text=True,
468+
timeout=SHORT_TIMEOUT,
469+
)
470+
self.assertEqual(
471+
result.returncode, 0,
472+
f"stdout: {result.stdout}\nstderr: {result.stderr}"
473+
)
474+
475+
441476
@requires_remote_subprocess_debugging()
442477
class TestGetStackTrace(RemoteInspectionTestBase):
443478
@skip_if_not_supported
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix ``_remote_debugging`` stack traces for code objects with large line
2+
tables.

Modules/_remote_debugging/code_objects.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
#include "_remote_debugging.h"
99

10+
#define MAX_LINETABLE_SIZE (1024 * 1024)
11+
#define MAX_LINETABLE_ENTRIES 65536
12+
1013
/* ============================================================================
1114
* TLBC CACHING FUNCTIONS (Py_GIL_DISABLED only)
1215
* ============================================================================ */
@@ -186,7 +189,6 @@ parse_linetable(const uintptr_t addrq, const char* linetable, Py_ssize_t linetab
186189
int computed_line = firstlineno; // Running accumulator, separate from output
187190
int val; // Temporary for varint results
188191
uint8_t byte; // Temporary for byte reads
189-
const size_t MAX_LINETABLE_ENTRIES = 65536;
190192
size_t entry_count = 0;
191193

192194
while (ptr < end && *ptr != '\0' && entry_count < MAX_LINETABLE_ENTRIES) {
@@ -387,7 +389,8 @@ parse_code_object(RemoteUnwinderObject *unwinder,
387389
}
388390

389391
linetable = read_py_bytes(unwinder,
390-
GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.linetable), 4096);
392+
GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.linetable),
393+
MAX_LINETABLE_SIZE);
391394
if (!linetable) {
392395
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read linetable from code object");
393396
goto error;

0 commit comments

Comments
 (0)