Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Lib/test/test_external_inspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,21 @@ async def main():
self.assertIn(main_name, names)
self.assertEqual([len(n) for n in names if n.startswith("x")], [255])

@skip_if_not_supported
def test_recursive_coroutine_stack_is_not_truncated(self):
# gh-158522
async def rec(n):
if n:
return await rec(n - 1)
return [
frame.funcname.rpartition(".")[2]
for task in RemoteUnwinder(os.getpid()).get_async_stack_trace()[0].awaited_by
for coro in task.coroutine_stack
for frame in coro.call_stack
]

self.assertEqual(asyncio.run(rec(3)), ["rec"] * 4)

@skip_if_not_supported
@unittest.skipIf(
sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix the sampling profiler showing a single frame for a task whose coroutine
recurses. Patch by Timofei Ivankov.
6 changes: 3 additions & 3 deletions Modules/_remote_debugging/_remote_debugging.h
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ extern int parse_async_frame_chain(
RemoteUnwinderObject *unwinder,
PyObject *calls,
uintptr_t address_of_thread,
uintptr_t running_task_code_obj
uintptr_t running_task_frame
);

/* Set iteration */
Expand Down Expand Up @@ -777,10 +777,10 @@ extern int find_running_task_in_thread(
uintptr_t *running_task_addr
);

extern int get_task_code_object(
extern int get_task_frame(
RemoteUnwinderObject *unwinder,
uintptr_t task_addr,
uintptr_t *code_obj_addr
uintptr_t *frame_addr
);

extern int append_awaited_by(
Expand Down
31 changes: 10 additions & 21 deletions Modules/_remote_debugging/asyncio.c
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ find_running_task_in_thread(
}

int
get_task_code_object(RemoteUnwinderObject *unwinder, uintptr_t task_addr, uintptr_t *code_obj_addr) {
get_task_frame(RemoteUnwinderObject *unwinder, uintptr_t task_addr, uintptr_t *frame_addr) {
uintptr_t running_coro_addr = 0;

if(read_py_ptr(
Expand All @@ -741,21 +741,7 @@ get_task_code_object(RemoteUnwinderObject *unwinder, uintptr_t task_addr, uintpt
return -1;
}

// note: genobject's gi_iframe is an embedded struct so the address to
// the offset leads directly to its first field: f_executable
if (read_py_ptr(
unwinder,
running_coro_addr + (uintptr_t)unwinder->debug_offsets.gen_object.gi_iframe, code_obj_addr) < 0) {
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read running task code object");
return -1;
}

if (*code_obj_addr == 0) {
PyErr_SetString(PyExc_RuntimeError, "Running task code object is NULL");
set_exception_cause(unwinder, PyExc_RuntimeError, "Running task code object address is NULL");
return -1;
}

*frame_addr = running_coro_addr + (uintptr_t)unwinder->debug_offsets.gen_object.gi_iframe;
return 0;
}

Expand All @@ -768,7 +754,7 @@ parse_async_frame_chain(
RemoteUnwinderObject *unwinder,
PyObject *calls,
uintptr_t address_of_thread,
uintptr_t running_task_code_obj
uintptr_t running_task_frame
) {
uintptr_t address_of_current_frame;
if (find_running_frame(unwinder, address_of_thread, &address_of_current_frame) < 0) {
Expand All @@ -779,6 +765,8 @@ parse_async_frame_chain(
while ((void*)address_of_current_frame != NULL) {
PyObject* frame_info = NULL;
uintptr_t address_of_code_object;

uintptr_t this_frame = address_of_current_frame;
int res = parse_frame_object(
unwinder,
&frame_info,
Expand All @@ -804,7 +792,8 @@ parse_async_frame_chain(

Py_DECREF(frame_info);

if (address_of_code_object == running_task_code_obj) {
// gh-158522: compare frame instead of code-object
if (this_frame == running_task_frame) {
break;
}
}
Expand Down Expand Up @@ -940,8 +929,8 @@ process_running_task_chain(
uintptr_t thread_state_addr,
PyObject *result
) {
uintptr_t running_task_code_obj = 0;
if(get_task_code_object(unwinder, running_task_addr, &running_task_code_obj) < 0) {
uintptr_t running_task_frame = 0;
if(get_task_frame(unwinder, running_task_addr, &running_task_frame) < 0) {
return -1;
}

Expand Down Expand Up @@ -973,7 +962,7 @@ process_running_task_chain(
}

// Add the chain from the current frame to this task
if (parse_async_frame_chain(unwinder, frame_chain, thread_state_addr, running_task_code_obj) < 0) {
if (parse_async_frame_chain(unwinder, frame_chain, thread_state_addr, running_task_frame) < 0) {
return -1;
}

Expand Down
Loading