5454#include <internal/pycore_debug_offsets.h> // _Py_DebugOffsets
5555#include <internal/pycore_frame.h> // FRAME_SUSPENDED_YIELD_FROM
5656#include <internal/pycore_interpframe.h> // FRAME_OWNED_BY_CSTACK
57+ #include <internal/pycore_llist.h> // struct llist_node
5758#include <internal/pycore_stackref.h> // Py_TAG_BITS
5859
5960#ifndef HAVE_PROCESS_VM_READV
@@ -68,11 +69,17 @@ struct _Py_AsyncioModuleDebugOffsets {
6869 uint64_t task_is_task ;
6970 uint64_t task_awaited_by_is_set ;
7071 uint64_t task_coro ;
72+ uint64_t task_node ;
7173 } asyncio_task_object ;
74+ struct _asyncio_interpreter_state {
75+ uint64_t size ;
76+ uint64_t asyncio_tasks_head ;
77+ } asyncio_interpreter_state ;
7278 struct _asyncio_thread_state {
7379 uint64_t size ;
7480 uint64_t asyncio_running_loop ;
7581 uint64_t asyncio_running_task ;
82+ uint64_t asyncio_tasks_head ;
7683 } asyncio_thread_state ;
7784};
7885
@@ -1464,6 +1471,240 @@ find_running_task(
14641471 return 0 ;
14651472}
14661473
1474+ static int
1475+ append_awaited_by_for_thread (
1476+ int pid ,
1477+ uintptr_t head_addr ,
1478+ struct _Py_DebugOffsets * debug_offsets ,
1479+ struct _Py_AsyncioModuleDebugOffsets * async_offsets ,
1480+ PyObject * result
1481+ ) {
1482+ struct llist_node task_node ;
1483+
1484+ if (0 > read_memory (
1485+ pid ,
1486+ head_addr ,
1487+ sizeof (task_node ),
1488+ & task_node ))
1489+ {
1490+ return -1 ;
1491+ }
1492+
1493+ while ((uintptr_t )task_node .next != head_addr ) {
1494+ uintptr_t task_addr = (uintptr_t )task_node .next
1495+ - async_offsets -> asyncio_task_object .task_node ;
1496+
1497+ PyObject * tn = parse_task_name (
1498+ pid ,
1499+ debug_offsets ,
1500+ async_offsets ,
1501+ task_addr );
1502+ if (tn == NULL ) {
1503+ return -1 ;
1504+ }
1505+
1506+ PyObject * current_awaited_by = PyList_New (0 );
1507+ if (current_awaited_by == NULL ) {
1508+ Py_DECREF (tn );
1509+ return -1 ;
1510+ }
1511+
1512+ PyObject * result_item = PyTuple_New (2 );
1513+ if (result_item == NULL ) {
1514+ Py_DECREF (tn );
1515+ Py_DECREF (current_awaited_by );
1516+ return -1 ;
1517+ }
1518+
1519+ PyTuple_SET_ITEM (result_item , 0 , tn ); // steals ref
1520+ PyTuple_SET_ITEM (result_item , 1 , current_awaited_by ); // steals ref
1521+ if (PyList_Append (result , result_item )) {
1522+ Py_DECREF (result_item );
1523+ return -1 ;
1524+ }
1525+ Py_DECREF (result_item );
1526+
1527+ if (parse_task_awaited_by (pid , debug_offsets , async_offsets ,
1528+ task_addr , current_awaited_by ))
1529+ {
1530+ return -1 ;
1531+ }
1532+
1533+ // onto the next one...
1534+ if (0 > read_memory (
1535+ pid ,
1536+ (uintptr_t )task_node .next ,
1537+ sizeof (task_node ),
1538+ & task_node ))
1539+ {
1540+ return -1 ;
1541+ }
1542+ }
1543+
1544+ return 0 ;
1545+ }
1546+
1547+ static int
1548+ append_awaited_by (
1549+ int pid ,
1550+ unsigned long tid ,
1551+ uintptr_t head_addr ,
1552+ struct _Py_DebugOffsets * debug_offsets ,
1553+ struct _Py_AsyncioModuleDebugOffsets * async_offsets ,
1554+ PyObject * result )
1555+ {
1556+ PyObject * tid_py = PyLong_FromUnsignedLong (tid );
1557+ if (tid_py == NULL ) {
1558+ return -1 ;
1559+ }
1560+
1561+ PyObject * result_item = PyTuple_New (2 );
1562+ if (result_item == NULL ) {
1563+ Py_DECREF (tid_py );
1564+ return -1 ;
1565+ }
1566+
1567+ PyObject * awaited_by_for_thread = PyList_New (0 );
1568+ if (awaited_by_for_thread == NULL ) {
1569+ Py_DECREF (tid_py );
1570+ Py_DECREF (result_item );
1571+ return -1 ;
1572+ }
1573+
1574+ PyTuple_SET_ITEM (result_item , 0 , tid_py ); // steals ref
1575+ PyTuple_SET_ITEM (result_item , 1 , awaited_by_for_thread ); // steals ref
1576+ if (PyList_Append (result , result_item )) {
1577+ Py_DECREF (result_item );
1578+ return -1 ;
1579+ }
1580+ Py_DECREF (result_item );
1581+
1582+ if (append_awaited_by_for_thread (
1583+ pid ,
1584+ head_addr ,
1585+ debug_offsets ,
1586+ async_offsets ,
1587+ awaited_by_for_thread ))
1588+ {
1589+ return -1 ;
1590+ }
1591+
1592+ return 0 ;
1593+ }
1594+
1595+ static PyObject *
1596+ get_all_awaited_by (PyObject * self , PyObject * args )
1597+ {
1598+ #if (!defined(__linux__ ) && !defined(__APPLE__ )) || \
1599+ (defined(__linux__ ) && !HAVE_PROCESS_VM_READV )
1600+ PyErr_SetString (
1601+ PyExc_RuntimeError ,
1602+ "get_all_awaited_by is not implemented on this platform" );
1603+ return NULL ;
1604+ #endif
1605+
1606+ int pid ;
1607+
1608+ if (!PyArg_ParseTuple (args , "i" , & pid )) {
1609+ return NULL ;
1610+ }
1611+
1612+ uintptr_t runtime_start_addr = get_py_runtime (pid );
1613+ if (runtime_start_addr == 0 ) {
1614+ if (!PyErr_Occurred ()) {
1615+ PyErr_SetString (
1616+ PyExc_RuntimeError , "Failed to get .PyRuntime address" );
1617+ }
1618+ return NULL ;
1619+ }
1620+ struct _Py_DebugOffsets local_debug_offsets ;
1621+
1622+ if (read_offsets (pid , & runtime_start_addr , & local_debug_offsets )) {
1623+ return NULL ;
1624+ }
1625+
1626+ struct _Py_AsyncioModuleDebugOffsets local_async_debug ;
1627+ if (read_async_debug (pid , & local_async_debug )) {
1628+ return NULL ;
1629+ }
1630+
1631+ PyObject * result = PyList_New (0 );
1632+ if (result == NULL ) {
1633+ return NULL ;
1634+ }
1635+
1636+ off_t interpreter_state_list_head =
1637+ local_debug_offsets .runtime_state .interpreters_head ;
1638+
1639+ uintptr_t interpreter_state_addr ;
1640+ if (0 > read_memory (
1641+ pid ,
1642+ runtime_start_addr + interpreter_state_list_head ,
1643+ sizeof (void * ),
1644+ & interpreter_state_addr ))
1645+ {
1646+ goto result_err ;
1647+ }
1648+
1649+ uintptr_t thread_state_addr ;
1650+ unsigned long tid = 0 ;
1651+ if (0 > read_memory (
1652+ pid ,
1653+ interpreter_state_addr
1654+ + local_debug_offsets .interpreter_state .threads_head ,
1655+ sizeof (void * ),
1656+ & thread_state_addr ))
1657+ {
1658+ goto result_err ;
1659+ }
1660+
1661+ uintptr_t head_addr ;
1662+ while (thread_state_addr != 0 ) {
1663+ if (0 > read_memory (
1664+ pid ,
1665+ thread_state_addr
1666+ + local_debug_offsets .thread_state .native_thread_id ,
1667+ sizeof (tid ),
1668+ & tid ))
1669+ {
1670+ goto result_err ;
1671+ }
1672+
1673+ head_addr = thread_state_addr
1674+ + local_async_debug .asyncio_thread_state .asyncio_tasks_head ;
1675+
1676+ if (append_awaited_by (pid , tid , head_addr , & local_debug_offsets ,
1677+ & local_async_debug , result ))
1678+ {
1679+ goto result_err ;
1680+ }
1681+
1682+ if (0 > read_memory (
1683+ pid ,
1684+ thread_state_addr + local_debug_offsets .thread_state .next ,
1685+ sizeof (void * ),
1686+ & thread_state_addr ))
1687+ {
1688+ goto result_err ;
1689+ }
1690+ }
1691+
1692+ head_addr = interpreter_state_addr
1693+ + local_async_debug .asyncio_interpreter_state .asyncio_tasks_head ;
1694+
1695+ if (append_awaited_by (pid , 0 , head_addr , & local_debug_offsets ,
1696+ & local_async_debug , result ))
1697+ {
1698+ goto result_err ;
1699+ }
1700+
1701+ return result ;
1702+
1703+ result_err :
1704+ Py_DECREF (result );
1705+ return NULL ;
1706+ }
1707+
14671708static PyObject *
14681709get_stack_trace (PyObject * self , PyObject * args )
14691710{
@@ -1686,6 +1927,8 @@ static PyMethodDef methods[] = {
16861927 "Get the Python stack from a given PID" },
16871928 {"get_async_stack_trace" , get_async_stack_trace , METH_VARARGS ,
16881929 "Get the asyncio stack from a given PID" },
1930+ {"get_all_awaited_by" , get_all_awaited_by , METH_VARARGS ,
1931+ "Get all tasks and their awaited_by from a given PID" },
16891932 {NULL , NULL , 0 , NULL },
16901933};
16911934
0 commit comments