From 4eab5a0bf7b98409d5544cfbf78f6e1fae82d601 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 13 Jun 2026 11:42:54 +0200 Subject: [PATCH 1/2] up last_profiled_frame --- Modules/_testinternalcapi/test_cases.c.h | 9 +++++++++ Objects/genobject.c | 6 ++++++ Python/bytecodes.c | 6 ++++++ Python/ceval.c | 3 +++ Python/executor_cases.c.h | 6 ++++++ Python/generated_cases.c.h | 9 +++++++++ 6 files changed, 39 insertions(+) diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 503f566c9ae86a5..d3ac8e6f96b4193 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -7939,6 +7939,9 @@ gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *gen_frame = frame; + if (tstate->last_profiled_frame == gen_frame) { + tstate->last_profiled_frame = gen_frame->previous; + } frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; @@ -11000,6 +11003,9 @@ gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *prev = frame->previous; + if (tstate->last_profiled_frame == frame) { + tstate->last_profiled_frame = prev; + } _PyThreadState_PopFrame(tstate, frame); frame = tstate->current_frame = prev; LOAD_IP(frame->return_offset); @@ -13029,6 +13035,9 @@ gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *gen_frame = frame; + if (tstate->last_profiled_frame == gen_frame) { + tstate->last_profiled_frame = gen_frame->previous; + } frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; diff --git a/Objects/genobject.c b/Objects/genobject.c index 38d493343454fce..68249fc9a6a77de 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -681,6 +681,9 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, 'yield from' or awaiting on with 'await'. */ ret = _gen_throw((PyGenObject *)yf, close_on_genexit, typ, val, tb); + if (tstate->last_profiled_frame == frame) { + tstate->last_profiled_frame = prev; + } tstate->current_frame = prev; frame->previous = NULL; } @@ -701,6 +704,9 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, frame->previous = prev; tstate->current_frame = frame; ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); + if (tstate->last_profiled_frame == frame) { + tstate->last_profiled_frame = prev; + } tstate->current_frame = prev; frame->previous = NULL; Py_DECREF(meth); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index e368092b300f864..08c09edd0e7a0a5 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1860,6 +1860,9 @@ dummy_func( gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *gen_frame = frame; + if (tstate->last_profiled_frame == gen_frame) { + tstate->last_profiled_frame = gen_frame->previous; + } frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; @@ -5874,6 +5877,9 @@ dummy_func( gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *prev = frame->previous; + if (tstate->last_profiled_frame == frame) { + tstate->last_profiled_frame = prev; + } _PyThreadState_PopFrame(tstate, frame); frame = tstate->current_frame = prev; LOAD_IP(frame->return_offset); diff --git a/Python/ceval.c b/Python/ceval.c index a9b31affca9890a..b7d8afa1b122f22 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2006,6 +2006,9 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, _PyStackRef func, _PyFrame_Initialize(tstate, frame, func, locals, code, 0, previous); if (initialize_locals(tstate, func_obj, frame->localsplus, args, argcount, kwnames)) { assert(frame->owner == FRAME_OWNED_BY_THREAD); + if (tstate->last_profiled_frame == frame) { + tstate->last_profiled_frame = tstate->current_frame; + } clear_thread_frame(tstate, frame); return NULL; } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 7973c75e1a60ad2..0589b860716c2e7 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -9340,6 +9340,9 @@ gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *gen_frame = frame; + if (tstate->last_profiled_frame == gen_frame) { + tstate->last_profiled_frame = gen_frame->previous; + } frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; @@ -20346,6 +20349,9 @@ gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *prev = frame->previous; + if (tstate->last_profiled_frame == frame) { + tstate->last_profiled_frame = prev; + } _PyThreadState_PopFrame(tstate, frame); frame = tstate->current_frame = prev; LOAD_IP(frame->return_offset); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 5adcdcb4521baf5..3a7265ecbe31f41 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -7938,6 +7938,9 @@ gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *gen_frame = frame; + if (tstate->last_profiled_frame == gen_frame) { + tstate->last_profiled_frame = gen_frame->previous; + } frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; @@ -10997,6 +11000,9 @@ gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *prev = frame->previous; + if (tstate->last_profiled_frame == frame) { + tstate->last_profiled_frame = prev; + } _PyThreadState_PopFrame(tstate, frame); frame = tstate->current_frame = prev; LOAD_IP(frame->return_offset); @@ -13026,6 +13032,9 @@ gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *gen_frame = frame; + if (tstate->last_profiled_frame == gen_frame) { + tstate->last_profiled_frame = gen_frame->previous; + } frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; From 4c946536e9ba30767d36410d0527b0dc055071d9 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 13 Jun 2026 11:58:17 +0200 Subject: [PATCH 2/2] news --- .../Library/2026-06-13-11-57-48.gh-issue-151436.UEDowO.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-06-13-11-57-48.gh-issue-151436.UEDowO.rst diff --git a/Misc/NEWS.d/next/Library/2026-06-13-11-57-48.gh-issue-151436.UEDowO.rst b/Misc/NEWS.d/next/Library/2026-06-13-11-57-48.gh-issue-151436.UEDowO.rst new file mode 100644 index 000000000000000..1d1aadbf57be485 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-13-11-57-48.gh-issue-151436.UEDowO.rst @@ -0,0 +1,4 @@ +Fix skewed stack trackes in the Tachyon profiler when caching is enabled and +when generators and coroutines are profiled, by updating +``tstate->last_profiled_frame`` at every frame-removal site. The issue resulted +in total erasure of some callers. Patch by Maurycy Pawłowski-Wieroński.