Skip to content

Commit 664d439

Browse files
committed
Add note to the docs
1 parent 111e70c commit 664d439

File tree

1 file changed

+29
-0
lines changed

1 file changed

+29
-0
lines changed

InternalDocs/frames.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,35 @@ The shim frame points to a special code object containing the `INTERPRETER_EXIT`
111111
instruction which cleans up the shim frame and returns.
112112

113113

114+
### Base frame
115+
116+
Each thread state contains an embedded `_PyInterpreterFrame` called the "base frame"
117+
that serves as a sentinel at the bottom of the frame stack. This frame is allocated
118+
in `_PyThreadStateImpl` (the internal extension of `PyThreadState`) and initialized
119+
when the thread state is created. The `owner` field is set to `FRAME_OWNED_BY_THREAD_STATE`.
120+
121+
External profilers and sampling tools can validate that they have successfully unwound
122+
the complete call stack by checking that the frame chain terminates at the base frame.
123+
The `PyThreadState.base_frame` pointer provides the expected address to compare against.
124+
If a stack walk doesn't reach this frame, the sample is incomplete (possibly due to a
125+
race condition) and should be discarded.
126+
127+
The base frame is embedded in `_PyThreadStateImpl` rather than `PyThreadState` because
128+
`_PyInterpreterFrame` is defined in internal headers that cannot be exposed in the
129+
public API. A pointer (`PyThreadState.base_frame`) is provided for profilers to access
130+
the address without needing internal headers.
131+
132+
See the initialization in `new_threadstate()` in [Python/pystate.c](../Python/pystate.c).
133+
134+
#### How profilers should use the base frame
135+
136+
External profilers should read `tstate->base_frame` before walking the stack, then
137+
walk from `tstate->current_frame` following `frame->previous` pointers until reaching
138+
a frame with `owner == FRAME_OWNED_BY_THREAD_STATE`. After the walk, verify that the
139+
last frame address matches `base_frame`. If not, discard the sample as incomplete
140+
since the frame chain may have been in an inconsistent state due to concurrent updates.
141+
142+
114143
### The Instruction Pointer
115144

116145
`_PyInterpreterFrame` has two fields which are used to maintain the instruction

0 commit comments

Comments
 (0)