🌐 AI搜索 & 代理 主页
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
ReuseFRAME_OWNED_BY_INTERPRETER
  • Loading branch information
pablogsal committed Nov 27, 2025
commit e14dc8ebb19d3c6c1bd60995266149fe3b2697cc
1 change: 0 additions & 1 deletion Include/internal/pycore_interpframe_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ enum _frameowner {
FRAME_OWNED_BY_GENERATOR = 1,
FRAME_OWNED_BY_FRAME_OBJECT = 2,
FRAME_OWNED_BY_INTERPRETER = 3,
FRAME_OWNED_BY_THREAD_STATE = 4, /* Sentinel base frame in thread state */
};

struct _PyInterpreterFrame {
Expand Down
4 changes: 2 additions & 2 deletions InternalDocs/frames.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ instruction which cleans up the shim frame and returns.
Each thread state contains an embedded `_PyInterpreterFrame` called the "base frame"
that serves as a sentinel at the bottom of the frame stack. This frame is allocated
in `_PyThreadStateImpl` (the internal extension of `PyThreadState`) and initialized
when the thread state is created. The `owner` field is set to `FRAME_OWNED_BY_THREAD_STATE`.
when the thread state is created. The `owner` field is set to `FRAME_OWNED_BY_INTERPRETER`.

External profilers and sampling tools can validate that they have successfully unwound
the complete call stack by checking that the frame chain terminates at the base frame.
Expand All @@ -135,7 +135,7 @@ See the initialization in `new_threadstate()` in [Python/pystate.c](../Python/py

External profilers should read `tstate->base_frame` before walking the stack, then
walk from `tstate->current_frame` following `frame->previous` pointers until reaching
a frame with `owner == FRAME_OWNED_BY_THREAD_STATE`. After the walk, verify that the
a frame with `owner == FRAME_OWNED_BY_INTERPRETER`. After the walk, verify that the
last frame address matches `base_frame`. If not, discard the sample as incomplete
since the frame chain may have been in an inconsistent state due to concurrent updates.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Add incomplete sample detection to prevent corrupted profiling data. Each
thread state now contains an embedded base frame (sentinel at the bottom of
the frame stack) with owner type ``FRAME_OWNED_BY_THREAD_STATE``. The profiler
the frame stack) with owner type ``FRAME_OWNED_BY_INTERPRETER``. The profiler
validates that stack unwinding terminates at this sentinel frame. Samples that
fail to reach the base frame (due to race conditions, memory corruption, or
other errors) are now rejected rather than being included as spurious data.
6 changes: 1 addition & 5 deletions Modules/_remote_debugging/frames.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,7 @@ is_frame_valid(

char owner = GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner);
if (owner == FRAME_OWNED_BY_INTERPRETER) {
return 0; // C frame
}

if (owner == FRAME_OWNED_BY_THREAD_STATE) {
return 0; // Sentinel base frame - end of stack
return 0; // C frame or sentinel base frame
}

if (owner != FRAME_OWNED_BY_GENERATOR && owner != FRAME_OWNED_BY_THREAD) {
Expand Down
2 changes: 1 addition & 1 deletion Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1493,7 +1493,7 @@ init_threadstate(_PyThreadStateImpl *_tstate,
_tstate->base_frame.instr_ptr = NULL;
_tstate->base_frame.stackpointer = _tstate->base_frame.localsplus;
_tstate->base_frame.return_offset = 0;
_tstate->base_frame.owner = FRAME_OWNED_BY_THREAD_STATE;
_tstate->base_frame.owner = FRAME_OWNED_BY_INTERPRETER;
_tstate->base_frame.visited = 0;
#ifdef Py_DEBUG
_tstate->base_frame.lltrace = 0;
Expand Down
3 changes: 1 addition & 2 deletions Python/traceback.c
Original file line number Diff line number Diff line change
Expand Up @@ -1035,8 +1035,7 @@ _Py_DumpWideString(int fd, wchar_t *str)
static int
dump_frame(int fd, _PyInterpreterFrame *frame)
{
if (frame->owner == FRAME_OWNED_BY_INTERPRETER ||
frame->owner == FRAME_OWNED_BY_THREAD_STATE) {
if (frame->owner == FRAME_OWNED_BY_INTERPRETER) {
/* Ignore trampoline frames and base frame sentinel */
return 0;
}
Expand Down
Loading