Skip to content

Commit e45f749

Browse files
committed
review changes
1 parent 94e332d commit e45f749

2 files changed

Lines changed: 36 additions & 23 deletions

File tree

‎Lib/test/test_external_inspection.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from unittest.mock import ANY
1515
from test.support import (
1616
os_helper,
17+
threading_helper,
1718
SHORT_TIMEOUT,
1819
busy_retry,
1920
requires_gil_enabled,
@@ -639,6 +640,36 @@ async def main():
639640
self.assertIn([["sleep", "child"], ["main"]], stacks)
640641
self.assertIn([[], ["main"]], stacks)
641642

643+
@skip_if_not_supported
644+
def test_running_task_frame_not_in_thread_stack(self):
645+
# gh-159645
646+
ready = threading.Event()
647+
release = threading.Event()
648+
649+
async def child():
650+
ready.set()
651+
release.wait()
652+
await asyncio.sleep(0)
653+
654+
async def main():
655+
loop = asyncio.get_running_loop()
656+
coro = child()
657+
task = asyncio.create_task(coro, name="child")
658+
thread = threading.Thread(target=coro.send, args=(None,))
659+
with threading_helper.start_threads([thread], unlock=release.set):
660+
self.assertTrue(ready.wait(SHORT_TIMEOUT))
661+
self.assertTrue(coro.cr_running)
662+
previous = tasks._swap_current_task(loop, task)
663+
try:
664+
trace = RemoteUnwinder(os.getpid()).get_async_stack_trace()
665+
finally:
666+
tasks._swap_current_task(loop, previous)
667+
await task
668+
return trace
669+
670+
stacks = self._extract_coroutine_funcnames(asyncio.run(main()))
671+
self.assertEqual(stacks["child"], ["child"])
672+
642673
@skip_if_not_supported
643674
def test_running_task_with_non_native_coroutine(self):
644675
# gh-159645

‎Modules/_remote_debugging/asyncio.c‎

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -770,20 +770,6 @@ get_task_frame(RemoteUnwinderObject *unwinder, uintptr_t task_addr, uintptr_t *f
770770
return 0;
771771
}
772772

773-
int8_t frame_state;
774-
if (_Py_RemoteDebug_PagedReadRemoteMemory(
775-
&unwinder->handle,
776-
running_coro_addr + (uintptr_t)unwinder->debug_offsets.gen_object.gi_frame_state,
777-
sizeof(frame_state),
778-
&frame_state) < 0) {
779-
set_exception_cause(unwinder, PyExc_RuntimeError, "Running task coro frame state read failed");
780-
return -1;
781-
}
782-
if (frame_state != FRAME_EXECUTING) {
783-
*frame_addr = 0;
784-
return 0;
785-
}
786-
787773
*frame_addr = running_coro_addr + (uintptr_t)unwinder->debug_offsets.gen_object.gi_iframe;
788774
return 0;
789775
}
@@ -799,6 +785,7 @@ parse_async_frame_chain(
799785
uintptr_t address_of_thread,
800786
uintptr_t running_task_frame
801787
) {
788+
Py_ssize_t coro_chain_size = PyList_GET_SIZE(calls);
802789
uintptr_t address_of_current_frame;
803790
if (find_running_frame(unwinder, address_of_thread, &address_of_current_frame) < 0) {
804791
set_exception_cause(unwinder, PyExc_RuntimeError, "Running frame search failed in async chain");
@@ -844,11 +831,12 @@ parse_async_frame_chain(
844831
// Stop at the task's own frame. Code objects are shared by
845832
// recursive calls, so they cannot identify it.
846833
if (this_frame == running_task_frame) {
847-
break;
834+
return PyList_SetSlice(calls, 0, coro_chain_size, NULL);
848835
}
849836
}
850837

851-
return 0;
838+
// gh-159645: keep the coroutine chain if the task's frame was not found.
839+
return PyList_SetSlice(calls, coro_chain_size, PY_SSIZE_T_MAX, NULL);
852840
}
853841

854842
/* ============================================================================
@@ -990,7 +978,7 @@ process_running_task_chain(
990978
return -1;
991979
}
992980

993-
// gh-159645: the coroutine isn't running, so keep its await chain.
981+
// gh-159645: non-native coroutines have no frame to look for.
994982
if (running_task_frame == 0) {
995983
return process_task_waiters(unwinder, result);
996984
}
@@ -1010,12 +998,6 @@ process_running_task_chain(
1010998
PyObject *frame_chain = PyStructSequence_GET_ITEM(coro_info, 0);
1011999
assert(frame_chain != NULL);
10121000

1013-
// Clear the coro_chain
1014-
if (PyList_Clear(frame_chain) < 0) {
1015-
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to clear coroutine chain");
1016-
return -1;
1017-
}
1018-
10191001
// Add the chain from the current frame to this task
10201002
if (parse_async_frame_chain(unwinder, frame_chain, thread_state_addr, running_task_frame) < 0) {
10211003
return -1;

0 commit comments

Comments
 (0)