Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4dd6f49
let me declare single limit
maurycy Jul 8, 2026
e71aaa5
use our new limit in process_frame_chain()
maurycy Jul 8, 2026
116da45
add it in parse_async_frame_chain()
maurycy Jul 8, 2026
3e12939
parse_coro_chain()
maurycy Jul 8, 2026
c46810c
NEWS
maurycy Jul 8, 2026
dd6e19a
async in the message?
maurycy Jul 8, 2026
0972e00
test
maurycy Jul 8, 2026
3c5f78b
no race
maurycy Jul 8, 2026
78c18f9
process_task_awaited_by
maurycy Jul 10, 2026
21d764e
process_task_awaited_by limit test
maurycy Jul 10, 2026
000287e
NEWS
maurycy Jul 10, 2026
353f815
MAX_TASK_WAITER_CHAIN_DEPTH
maurycy Jul 10, 2026
f66866b
TASK_WAITER_CHAIN_DEPTH in test
maurycy Jul 10, 2026
c9f1ebd
TASK_WAITER_CHAIN_DEPTH 256
maurycy Jul 10, 2026
7236ca2
prevent the drift with the comment
maurycy Jul 11, 2026
35fc6c6
better naming, better style
maurycy Jul 11, 2026
10149e9
MAX_TASK_WAITER_CHAIN_DEPTH comment
maurycy Jul 11, 2026
ef51563
task-waiter iterative bfs walk
maurycy Jul 12, 2026
b5022ba
iterative coro-walk
maurycy Jul 13, 2026
73b8674
nicer news
maurycy Jul 13, 2026
e0607dc
1 << 14
maurycy Jul 25, 2026
ed731eb
comment
maurycy Jul 25, 2026
75a8d8e
unused read_Py_ssize_t
maurycy Jul 25, 2026
c0ce737
fix tombstones
maurycy Jul 25, 2026
f3c80d4
simplify
maurycy Jul 25, 2026
4fd4027
correct msg
maurycy Jul 25, 2026
f2530ec
better test
maurycy Jul 25, 2026
0ab3342
news for tombstones
maurycy Jul 25, 2026
2a444ec
left-over from when testing buggy version
maurycy Jul 25, 2026
ed95a2e
redundant new line
maurycy Jul 25, 2026
a1221fd
Merge branch 'main' into tachyon-parse_async_frame_chain-limit
maurycy Oct 4, 2026
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
Prev Previous commit
Next Next commit
iterative coro-walk
  • Loading branch information
maurycy committed Jul 13, 2026
commit b5022ba82443cc608030c5730bd1965d30178e97
7 changes: 0 additions & 7 deletions Modules/_remote_debugging/_remote_debugging.h
Original file line number Diff line number Diff line change
Expand Up @@ -725,13 +725,6 @@ extern int parse_task(
PyObject *render_to
);

extern int parse_coro_chain(
RemoteUnwinderObject *unwinder,
uintptr_t coro_address,
PyObject *render_to,
size_t depth
);

extern int parse_async_frame_chain(
RemoteUnwinderObject *unwinder,
PyObject *calls,
Expand Down
113 changes: 57 additions & 56 deletions Modules/_remote_debugging/asyncio.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,14 @@ parse_task_name(
* ============================================================================ */

static int
handle_yield_from_frame(
get_awaited_coro_address(
RemoteUnwinderObject *unwinder,
uintptr_t gi_iframe_addr,
uintptr_t gen_type_addr,
PyObject *render_to,
size_t depth
uintptr_t *next_coro
) {
*next_coro = 0;

// Read the entire interpreter frame at once
char iframe[SIZEOF_INTERP_FRAME];
int err = _Py_RemoteDebug_PagedReadRemoteMemory(
Expand Down Expand Up @@ -300,80 +301,80 @@ handle_yield_from_frame(
doesn't match the type of whatever it points to
in its cr_await.
*/
err = parse_coro_chain(unwinder, gi_await_addr, render_to,
depth + 1);
if (err) {
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse coroutine chain in yield_from");
return -1;
}
*next_coro = gi_await_addr;
}
}
}

return 0;
}

int
static int
parse_coro_chain(
Comment thread
maurycy marked this conversation as resolved.
RemoteUnwinderObject *unwinder,
uintptr_t coro_address,
PyObject *render_to,
size_t depth
PyObject *render_to
) {
assert((void*)coro_address != NULL);

if (depth >= MAX_FRAME_CHAIN_DEPTH) {
PyErr_SetString(PyExc_RuntimeError,
"Too many coroutine frames (possible infinite loop)");
set_exception_cause(unwinder, PyExc_RuntimeError,
"Coroutine chain depth limit exceeded");
return -1;
}
for (size_t depth = 0; (void*)coro_address != NULL; depth++) {
if (depth >= MAX_FRAME_CHAIN_DEPTH) {
PyErr_SetString(PyExc_RuntimeError,
"Too many coroutine frames (possible infinite loop)");
set_exception_cause(unwinder, PyExc_RuntimeError,
"Coroutine chain depth limit exceeded");
return -1;
}

// Read the entire generator object at once
char gen_object[SIZEOF_GEN_OBJ];
int err = _Py_RemoteDebug_PagedReadRemoteMemory(
&unwinder->handle,
coro_address,
SIZEOF_GEN_OBJ,
gen_object);
if (err < 0) {
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read generator object in coro chain");
return -1;
}
// Read the entire generator object at once
char gen_object[SIZEOF_GEN_OBJ];
int err = _Py_RemoteDebug_PagedReadRemoteMemory(
&unwinder->handle,
coro_address,
SIZEOF_GEN_OBJ,
gen_object);
if (err < 0) {
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read generator object in coro chain");
return -1;
}

int8_t frame_state = GET_MEMBER(int8_t, gen_object, unwinder->debug_offsets.gen_object.gi_frame_state);
if (frame_state == FRAME_CLEARED) {
return 0;
}
int8_t frame_state = GET_MEMBER(int8_t, gen_object, unwinder->debug_offsets.gen_object.gi_frame_state);
if (frame_state == FRAME_CLEARED) {
return 0;
}

uintptr_t gen_type_addr = GET_MEMBER(uintptr_t, gen_object, unwinder->debug_offsets.pyobject.ob_type);
uintptr_t gen_type_addr = GET_MEMBER(uintptr_t, gen_object, unwinder->debug_offsets.pyobject.ob_type);

PyObject* name = NULL;
PyObject* name = NULL;

// Parse the previous frame using the gi_iframe from local copy
uintptr_t prev_frame;
uintptr_t gi_iframe_addr = coro_address + (uintptr_t)unwinder->debug_offsets.gen_object.gi_iframe;
uintptr_t address_of_code_object = 0;
if (parse_frame_object(unwinder, &name, gi_iframe_addr, &address_of_code_object, &prev_frame) < 0) {
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse frame object in coro chain");
return -1;
}
// Parse the previous frame using the gi_iframe from local copy
uintptr_t prev_frame;
uintptr_t gi_iframe_addr = coro_address + (uintptr_t)unwinder->debug_offsets.gen_object.gi_iframe;
uintptr_t address_of_code_object = 0;
if (parse_frame_object(unwinder, &name, gi_iframe_addr, &address_of_code_object, &prev_frame) < 0) {
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse frame object in coro chain");
return -1;
}

if (!name) {
return 0;
}
if (!name) {
return 0;
}

if (PyList_Append(render_to, name)) {
if (PyList_Append(render_to, name)) {
Py_DECREF(name);
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append frame to coro chain");
return -1;
}
Py_DECREF(name);
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append frame to coro chain");
return -1;
}
Py_DECREF(name);

if (frame_state == FRAME_SUSPENDED_YIELD_FROM) {
return handle_yield_from_frame(unwinder, gi_iframe_addr, gen_type_addr,
render_to, depth);
if (frame_state != FRAME_SUSPENDED_YIELD_FROM) {
return 0;
}

if (get_awaited_coro_address(unwinder, gi_iframe_addr, gen_type_addr,
&coro_address) < 0) {
return -1;
}
}

return 0;
Expand Down Expand Up @@ -419,7 +420,7 @@ create_task_result(
coro_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_coro);

if ((void*)coro_addr != NULL) {
if (parse_coro_chain(unwinder, coro_addr, call_stack, 0) < 0) {
if (parse_coro_chain(unwinder, coro_addr, call_stack) < 0) {
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse coroutine chain");
goto error;
}
Expand Down
Loading