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
19 changes: 19 additions & 0 deletions Lib/test/test_external_inspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,25 @@ 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_running_task_reports_its_call_stack(self):
# gh-158443
async def inner():
return [
[frame.funcname.rpartition(".")[2] for frame in coro.call_stack]
for info in RemoteUnwinder(os.getpid()).get_all_awaited_by()
for task in info.awaited_by
for coro in task.coroutine_stack
]

async def middle():
return await inner()

async def main():
return await middle()

self.assertEqual(asyncio.run(main()), [["inner", "middle", "main"]])

@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 ``python -m asyncio ps`` showing a task that is running as a single
frame instead of its call stack. Patch by Timofei Ivankov.
2 changes: 2 additions & 0 deletions Modules/_remote_debugging/_remote_debugging.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,8 @@ typedef struct {
pid_t *thread_tids; // Reusable buffer for thread IDs
size_t thread_tids_capacity; // Current capacity of thread_tids buffer
#endif
// Thread whose task list is being walked, 0 when there is none
uintptr_t walked_thread_addr;
} RemoteUnwinderObject;

#define RemoteUnwinder_CAST(op) ((RemoteUnwinderObject *)(op))
Expand Down
17 changes: 16 additions & 1 deletion Modules/_remote_debugging/asyncio.c
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,17 @@ parse_coro_chain(
return 0;
}

if (frame_state == FRAME_EXECUTING && unwinder->walked_thread_addr) {
// gh-158443: a running coroutine keeps its inner frames on the thread stack
Py_DECREF(name);
if (parse_async_frame_chain(unwinder, render_to,
unwinder->walked_thread_addr,
address_of_code_object) < 0) {
return -1;
}
return PyList_Reverse(render_to);
}

if (PyList_Append(render_to, name)) {
Py_DECREF(name);
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append frame to coro chain");
Expand Down Expand Up @@ -930,7 +941,11 @@ process_thread_for_awaited_by(
) {
PyObject *result = (PyObject *)context;
uintptr_t head_addr = thread_state_addr + (uintptr_t)unwinder->async_debug_offsets.asyncio_thread_state.asyncio_tasks_head;
return append_awaited_by(unwinder, tid, head_addr, result);

unwinder->walked_thread_addr = thread_state_addr;
int res = append_awaited_by(unwinder, tid, head_addr, result);
unwinder->walked_thread_addr = 0;
return res;
}

static int
Expand Down
Loading