# ============================================================================
+@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
#include "_remote_debugging.h"
+#define MAX_LINETABLE_SIZE (64 * 1024)
+
/* ============================================================================
* TLBC CACHING FUNCTIONS (Py_GIL_DISABLED only)
* ============================================================================ */
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;
}
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;