mirror of
https://github.com/python/cpython.git
synced 2026-06-19 08:02:29 +00:00
gh-151436: Fix missing tstate->last_profiled_frame updates (#151437)
This commit is contained in:
parent
eff805b7a7
commit
a8d74c062f
8 changed files with 33 additions and 8 deletions
|
|
@ -287,6 +287,20 @@ _PyThreadState_GetFrame(PyThreadState *tstate)
|
|||
return _PyFrame_GetFirstComplete(tstate->current_frame);
|
||||
}
|
||||
|
||||
// Update last_profiled_frame for remote profiler frame caching.
|
||||
// Only update if we're removing the exact frame that was last profiled.
|
||||
// This avoids corrupting the cache when transient frames (called and returned
|
||||
// between profiler samples) update last_profiled_frame to addresses the
|
||||
// profiler never saw.
|
||||
#define _PyThreadState_UpdateLastProfiledFrame(tstate, frame, previous) \
|
||||
do { \
|
||||
PyThreadState *tstate_ = (tstate); \
|
||||
_PyInterpreterFrame *frame_ = (frame); \
|
||||
if (tstate_->last_profiled_frame == frame_) { \
|
||||
tstate_->last_profiled_frame = (previous); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* For use by _PyFrame_GetFrameObject
|
||||
Do not call directly. */
|
||||
PyAPI_FUNC(PyFrameObject *)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
3
Modules/_testinternalcapi/test_cases.c.h
generated
3
Modules/_testinternalcapi/test_cases.c.h
generated
|
|
@ -7939,6 +7939,7 @@
|
|||
gen->gi_exc_state.previous_item = NULL;
|
||||
_Py_LeaveRecursiveCallPy(tstate);
|
||||
_PyInterpreterFrame *gen_frame = frame;
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, gen_frame, gen_frame->previous);
|
||||
frame = tstate->current_frame = frame->previous;
|
||||
gen_frame->previous = NULL;
|
||||
((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD;
|
||||
|
|
@ -11000,6 +11001,7 @@
|
|||
gen_frame->owner = FRAME_OWNED_BY_GENERATOR;
|
||||
_Py_LeaveRecursiveCallPy(tstate);
|
||||
_PyInterpreterFrame *prev = frame->previous;
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, frame, prev);
|
||||
_PyThreadState_PopFrame(tstate, frame);
|
||||
frame = tstate->current_frame = prev;
|
||||
LOAD_IP(frame->return_offset);
|
||||
|
|
@ -13029,6 +13031,7 @@
|
|||
gen->gi_exc_state.previous_item = NULL;
|
||||
_Py_LeaveRecursiveCallPy(tstate);
|
||||
_PyInterpreterFrame *gen_frame = frame;
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, gen_frame, gen_frame->previous);
|
||||
frame = tstate->current_frame = frame->previous;
|
||||
gen_frame->previous = NULL;
|
||||
((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD;
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ gen_clear_frame(PyGenObject *gen)
|
|||
{
|
||||
assert(FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state) == FRAME_CLEARED);
|
||||
_PyInterpreterFrame *frame = &gen->gi_iframe;
|
||||
_PyThreadState_UpdateLastProfiledFrame(_PyThreadState_GET(), frame, frame->previous);
|
||||
frame->previous = NULL;
|
||||
_PyFrame_ClearExceptCode(frame);
|
||||
_PyErr_ClearExcState(&gen->gi_exc_state);
|
||||
|
|
@ -681,6 +682,7 @@ _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);
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, frame, prev);
|
||||
tstate->current_frame = prev;
|
||||
frame->previous = NULL;
|
||||
}
|
||||
|
|
@ -701,6 +703,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
|
|||
frame->previous = prev;
|
||||
tstate->current_frame = frame;
|
||||
ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL);
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, frame, prev);
|
||||
tstate->current_frame = prev;
|
||||
frame->previous = NULL;
|
||||
Py_DECREF(meth);
|
||||
|
|
|
|||
|
|
@ -1860,6 +1860,7 @@ dummy_func(
|
|||
gen->gi_exc_state.previous_item = NULL;
|
||||
_Py_LeaveRecursiveCallPy(tstate);
|
||||
_PyInterpreterFrame *gen_frame = frame;
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, gen_frame, gen_frame->previous);
|
||||
frame = tstate->current_frame = frame->previous;
|
||||
gen_frame->previous = NULL;
|
||||
((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD;
|
||||
|
|
@ -5874,6 +5875,7 @@ dummy_func(
|
|||
gen_frame->owner = FRAME_OWNED_BY_GENERATOR;
|
||||
_Py_LeaveRecursiveCallPy(tstate);
|
||||
_PyInterpreterFrame *prev = frame->previous;
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, frame, prev);
|
||||
_PyThreadState_PopFrame(tstate, frame);
|
||||
frame = tstate->current_frame = prev;
|
||||
LOAD_IP(frame->return_offset);
|
||||
|
|
|
|||
|
|
@ -1974,15 +1974,8 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame)
|
|||
void
|
||||
_PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame)
|
||||
{
|
||||
// Update last_profiled_frame for remote profiler frame caching.
|
||||
// By this point, tstate->current_frame is already set to the parent frame.
|
||||
// Only update if we're popping the exact frame that was last profiled.
|
||||
// This avoids corrupting the cache when transient frames (called and returned
|
||||
// between profiler samples) update last_profiled_frame to addresses the
|
||||
// profiler never saw.
|
||||
if (tstate->last_profiled_frame != NULL && tstate->last_profiled_frame == frame) {
|
||||
tstate->last_profiled_frame = tstate->current_frame;
|
||||
}
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, frame, tstate->current_frame);
|
||||
|
||||
if (frame->owner == FRAME_OWNED_BY_THREAD) {
|
||||
clear_thread_frame(tstate, frame);
|
||||
|
|
@ -2008,6 +2001,7 @@ _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);
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, frame, tstate->current_frame);
|
||||
clear_thread_frame(tstate, frame);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
2
Python/executor_cases.c.h
generated
2
Python/executor_cases.c.h
generated
|
|
@ -9340,6 +9340,7 @@
|
|||
gen->gi_exc_state.previous_item = NULL;
|
||||
_Py_LeaveRecursiveCallPy(tstate);
|
||||
_PyInterpreterFrame *gen_frame = frame;
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, gen_frame, gen_frame->previous);
|
||||
frame = tstate->current_frame = frame->previous;
|
||||
gen_frame->previous = NULL;
|
||||
((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD;
|
||||
|
|
@ -20346,6 +20347,7 @@
|
|||
gen_frame->owner = FRAME_OWNED_BY_GENERATOR;
|
||||
_Py_LeaveRecursiveCallPy(tstate);
|
||||
_PyInterpreterFrame *prev = frame->previous;
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, frame, prev);
|
||||
_PyThreadState_PopFrame(tstate, frame);
|
||||
frame = tstate->current_frame = prev;
|
||||
LOAD_IP(frame->return_offset);
|
||||
|
|
|
|||
3
Python/generated_cases.c.h
generated
3
Python/generated_cases.c.h
generated
|
|
@ -7938,6 +7938,7 @@
|
|||
gen->gi_exc_state.previous_item = NULL;
|
||||
_Py_LeaveRecursiveCallPy(tstate);
|
||||
_PyInterpreterFrame *gen_frame = frame;
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, gen_frame, gen_frame->previous);
|
||||
frame = tstate->current_frame = frame->previous;
|
||||
gen_frame->previous = NULL;
|
||||
((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD;
|
||||
|
|
@ -10997,6 +10998,7 @@
|
|||
gen_frame->owner = FRAME_OWNED_BY_GENERATOR;
|
||||
_Py_LeaveRecursiveCallPy(tstate);
|
||||
_PyInterpreterFrame *prev = frame->previous;
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, frame, prev);
|
||||
_PyThreadState_PopFrame(tstate, frame);
|
||||
frame = tstate->current_frame = prev;
|
||||
LOAD_IP(frame->return_offset);
|
||||
|
|
@ -13026,6 +13028,7 @@
|
|||
gen->gi_exc_state.previous_item = NULL;
|
||||
_Py_LeaveRecursiveCallPy(tstate);
|
||||
_PyInterpreterFrame *gen_frame = frame;
|
||||
_PyThreadState_UpdateLastProfiledFrame(tstate, gen_frame, gen_frame->previous);
|
||||
frame = tstate->current_frame = frame->previous;
|
||||
gen_frame->previous = NULL;
|
||||
((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue