diff --git a/ddtrace/internal/datadog/profiling/stack_v2/echion/echion/cpython/tasks.h b/ddtrace/internal/datadog/profiling/stack_v2/echion/echion/cpython/tasks.h index 794c526fa4a..b45aeb27799 100644 --- a/ddtrace/internal/datadog/profiling/stack_v2/echion/echion/cpython/tasks.h +++ b/ddtrace/internal/datadog/profiling/stack_v2/echion/echion/cpython/tasks.h @@ -171,7 +171,37 @@ extern "C" #define RESUME_QUICK INSTRUMENTED_RESUME #endif -#if PY_VERSION_HEX >= 0x030b0000 +#if PY_VERSION_HEX >= 0x030d0000 + + inline PyObject* PyGen_yf(PyGenObject* gen, PyObject* frame_addr) + { + if (gen->gi_frame_state != FRAME_SUSPENDED_YIELD_FROM) { + return nullptr; + } + + _PyInterpreterFrame frame; + if (copy_type(frame_addr, frame)) { + return nullptr; + } + + if (frame.stacktop < 1 || frame.stacktop > (1 << 20)) { + return nullptr; + } + + auto localsplus = std::make_unique(frame.stacktop); + + // Calculate the remote address of the localsplus array + auto remote_localsplus = reinterpret_cast(reinterpret_cast(frame_addr) + + offsetof(_PyInterpreterFrame, localsplus)); + if (copy_generic(remote_localsplus, localsplus.get(), (frame.stacktop) * sizeof(PyObject*))) { + return nullptr; + } + + return localsplus[frame.stacktop - 1]; + } + +#elif PY_VERSION_HEX >= 0x030b0000 + inline PyObject* PyGen_yf(PyGenObject* gen, PyObject* frame_addr) { PyObject* yf = NULL; diff --git a/releasenotes/notes/profiling-use-official-pygen-yf-for-313-9e9df6f153fae01f.yaml b/releasenotes/notes/profiling-use-official-pygen-yf-for-313-9e9df6f153fae01f.yaml new file mode 100644 index 00000000000..693f3b32e89 --- /dev/null +++ b/releasenotes/notes/profiling-use-official-pygen-yf-for-313-9e9df6f153fae01f.yaml @@ -0,0 +1,5 @@ +fixes: + - | + profiling: This improves stack unwinding for asyncio workloads running Python 3.13+ by replicating + the official PyGen_yf function from CPython 3.13. Previously, the sampler would use the version from an older + version of CPython, which could lead to incomplete asyncio stacks.