Skip to content

Commit b3ee190

Browse files
[3.15] gh-151022: Fix remote debugging linetable reads (GH-151036) (#154187)
1 parent 9bb1271 commit b3ee190

3 files changed

Lines changed: 57 additions & 6 deletions

File tree

Lib/test/test_external_inspection.py

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

440440

441+
@requires_remote_subprocess_debugging()
442+
class TestSelfStackTrace(RemoteInspectionTestBase):
443+
@skip_if_not_supported
444+
@unittest.skipIf(
445+
sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED,
446+
"Test only runs on Linux with process_vm_readv support",
447+
)
448+
def test_self_trace_with_large_linetable(self):
449+
script = textwrap.dedent("""\
450+
import os
451+
import _remote_debugging
452+
453+
assignments = "\\n".join(
454+
f"value_{i} = {i}" for i in range(1000)
455+
)
456+
expected_lineno = len(assignments.splitlines()) + 1
457+
source = (
458+
f"{assignments}\\n"
459+
"stack_trace = "
460+
"_remote_debugging.RemoteUnwinder(os.getpid()).get_stack_trace()\\n"
461+
)
462+
code = compile(source, "large_linetable.py", "exec")
463+
assert len(code.co_linetable) > 4096, len(code.co_linetable)
464+
namespace = {"os": os, "_remote_debugging": _remote_debugging}
465+
exec(code, namespace)
466+
large_linetable_frames = [
467+
frame
468+
for interpreter in namespace["stack_trace"]
469+
for thread in interpreter.threads
470+
for frame in thread.frame_info
471+
if frame.filename == "large_linetable.py"
472+
]
473+
assert len(large_linetable_frames) == 1, large_linetable_frames
474+
assert large_linetable_frames[0].location.lineno == expected_lineno, (
475+
large_linetable_frames[0]
476+
)
477+
""")
478+
479+
result = subprocess.run(
480+
[sys.executable, "-c", script],
481+
capture_output=True,
482+
text=True,
483+
timeout=SHORT_TIMEOUT,
484+
)
485+
self.assertEqual(
486+
result.returncode, 0,
487+
f"stdout: {result.stdout}\nstderr: {result.stderr}"
488+
)
489+
490+
441491
@requires_remote_subprocess_debugging()
442492
class TestGetStackTrace(RemoteInspectionTestBase):
443493
@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 & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include "_remote_debugging.h"
99

10+
#define MAX_LINETABLE_SIZE (64 * 1024)
11+
1012
/* ============================================================================
1113
* TLBC CACHING FUNCTIONS (Py_GIL_DISABLED only)
1214
* ============================================================================ */
@@ -186,11 +188,7 @@ parse_linetable(const uintptr_t addrq, const char* linetable, Py_ssize_t linetab
186188
int computed_line = firstlineno; // Running accumulator, separate from output
187189
int val; // Temporary for varint results
188190
uint8_t byte; // Temporary for byte reads
189-
const size_t MAX_LINETABLE_ENTRIES = 65536;
190-
size_t entry_count = 0;
191-
192-
while (ptr < end && *ptr != '\0' && entry_count < MAX_LINETABLE_ENTRIES) {
193-
entry_count++;
191+
while (ptr < end && *ptr != '\0') {
194192
uint8_t first_byte = *(ptr++);
195193
uint8_t code = (first_byte >> 3) & 15;
196194
size_t length = (first_byte & 7) + 1;
@@ -387,7 +385,8 @@ parse_code_object(RemoteUnwinderObject *unwinder,
387385
}
388386

389387
linetable = read_py_bytes(unwinder,
390-
GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.linetable), 4096);
388+
GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.linetable),
389+
MAX_LINETABLE_SIZE);
391390
if (!linetable) {
392391
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read linetable from code object");
393392
goto error;

0 commit comments

Comments
 (0)