GH-120024: Use pointer for stack pointer by markshannon · Pull Request #121923 · python/cpython
taegyunkim added a commit to DataDog/dd-trace-py that referenced this pull request
## Description This PR adds support for Python 3.14 in the profiler by updating it to handle CPython internal changes. ### Key CPython changes addressed **`_PyInterpreterFrame` Structure Changes** 1. Moved from `Include/internal/pycore_frame.h` to `Include/internal/pycore_interpframe_structs.h` 2. `PyObject *f_executable` and `PyObject *f_funcobj` changed to `_PyStackRef` type. Profilers like us now need to clear the LSB of these fields to get the `PyObject*`. See python/cpython#123923 for details 3. `int stacktop` field removed, replaced with `_PyStackRef *stackpointer` pointer. See python/cpython#121923 (GH-120024) for details 4. `PyObject *localsplus[1]` changed to `_PyStackRef localsplus[1]`. See python/cpython#118450 (gh-117139) for details **`FutureObj`/`TaskObj` Changes** 1. Added fields: `awaited_by`, `is_task`, `awaited_by_is_set` in `FutureObj_HEAD` macro 2. Added `struct llist_node_task_node` field for linked-list storage **Asyncio Task Storage Changes** Prior to Python 3.14, - All tasks are stored in `_scheduled_tasks` WeakSet ([exported](https://github.com/python/cpython/blob/e96367da1fdc1e1cf17ca523e93a127b1961b443/Modules/_asynciomodule.c#L3738) from C extension) - Eager tasks are stored in `_eager_tasks` set ([exported](https://github.com/python/cpython/blob/e96367da1fdc1e1cf17ca523e93a127b1961b443/Modules/_asynciomodule.c#L3742) from C extension) From Python 3.14, - Native `asyncio.Tasks` are stored in a linked-list (`struct llist_node`) per thread and per interpreter - [Per-thread](https://github.com/python/cpython/blob/0114178911f8713bfcb935ff5542fe61b4a5d551/Include/internal/pycore_tstate.h#L46): `tstate->asyncio_tasks_head` (in `_PyThreadStateImpl`) - [Per-interpreter](https://github.com/python/cpython/blob/0114178911f8713bfcb935ff5542fe61b4a5d551/Include/internal/pycore_interp_structs.h#L897): `interp->asyncio_tasks_head` (for lingering tasks) - Each `TaskObj` has a `task_node` field with `next` and `prev` pointers - Third-party tasks: Still stored in Python-level `_scheduled_tasks` WeakSet (now Python-only, not exported from C extension) - Eager tasks: Still stored in Python-level `_eager_tasks` set ### Implementation Summary - **Frame reading** (`frame.h`, `frame.cc`): Updated header includes to use `pycore_interpframe_structs.h` for Python 3.14+. Implemented tagged pointer handling: clear LSB of `f_executable` to recover `PyObject*` (per gh-123923). Replaced `stacktop` field access with `stackpointer` pointer arithmetic for stack depth calculation. Updated `PyGen_yf()` to use `_PyStackRef` and `stackpointer[-1]` instead of `localsplus[stacktop-1]`. Added handling for `FRAME_OWNED_BY_INTERPRETER` frame type (introduced in 3.14). - **Task structures** (`cpython/tasks.h`): Added Python 3.14+ `FutureObj_HEAD` macro with new fields: `awaited_by`, `is_task`, `awaited_by_is_set`. Added `struct llist_node task_node` field to `TaskObj` for linked-list storage. Updated `PyGen_yf()` implementation to handle `_PyStackRef` and `stackpointer` instead of `stacktop`. - **Asyncio discovery** (`tasks.h`, `threads.h`): Implemented `get_tasks_from_linked_list()` to safely iterate over circular linked-lists with iteration limits (`MAX_ITERATIONS = 2 << 15`). Added `get_tasks_from_thread_linked_list()` to read tasks from `_PyThreadStateImpl.asyncio_tasks_head` (per-thread active tasks). Added `get_tasks_from_interpreter_linked_list()` to read lingering tasks from `PyInterpreterState.asyncio_tasks_head` (per-interpreter). Updated `get_all_tasks()` to handle both linked-list (native `asyncio.Task` instances) and WeakSet (third-party tasks). - **Python integration** (`_asyncio.py`): Added compatibility handling for `BaseDefaultEventLoopPolicy` → `_BaseDefaultEventLoopPolicy` rename in 3.14. Updated `_scheduled_tasks` access to handle Python-only WeakSet (no longer exported from C extension in 3.14+). ## Testing All existing tests pass except for tests/profiling/collector/test_memalloc.py which needed some edits. ## Risks <!-- Note any risks associated with this change, or "None" if no risks --> ## Additional Notes --------- Co-authored-by: Brett Langdon <brett.langdon@datadoghq.com>