8000 GH-96421: Insert shim frame on entry to interpreter by markshannon · Pull Request #96319 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

GH-96421: Insert shim frame on entry to interpreter #96319

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 39 commits into from
Nov 10, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4d5e9a5
Handle _PyEval_EvalFrameDefault exit in bytecode.
markshannon Aug 24, 2022
cf78d2b
Merge branch 'main' into entry-frame
markshannon Aug 25, 2022
7f5348a
Get entry frame shim working.
markshannon Aug 26, 2022
59e7727
Delay destruction of trampoline code object and make symbol private
markshannon Aug 30, 2022
9f710e6
Make sure that previous never points into the C stack after return.
markshannon Aug 30, 2022
4ee84ab
Use explicit stacksize in _Py_MakeTrampoline. Allow interpreter to cl…
markshannon Aug 30, 2022
f5161e3
Move assert before use.
markshannon Aug 31, 2022
06c2ad6
Use typedef name.
markshannon Aug 31, 2022
cb6134e
Merge branch 'main' into entry-frame
markshannon Sep 5, 2022
11b3b17
Merge branch 'main' into entry-frame
markshannon Sep 7, 2022
053f488
More on-stack C structs into a big struct and add sentinels, as we wa…
markshannon Sep 8, 2022
4dad69a
Skip a test if ASAN is turned on.
markshannon Sep 8, 2022
0db3b25
Remove extra semicolons.
markshannon Sep 9, 2022
05b4f68
Add news
markshannon Sep 9, 2022
6cca61c
Merge branch 'main' into entry-frame
markshannon Sep 9, 2022
5099861
Merge branch 'main' into entry-frame
markshannon Sep 13, 2022
4015c72
Halve the number of writes for shim frame.
markshannon Sep 14, 2022
fb19e94
Remove one more write to shim frame.
markshannon Sep 14, 2022
95ef81b
Fix lltrace
markshannon Sep 14, 2022
972425d
Merge branch 'main' into entry-frame
markshannon Oct 19, 2022
aa8bd73
Remove first_instr local variable.
markshannon Oct 19, 2022
f7072e1
Add news and update frame_layout.md
markshannon Oct 19, 2022
52406d2
Merge branch 'main' into entry-frame
markshannon Oct 19, 2022
bd2b0ee
Remove debugging scaffolding and give C compiler more freedom to layo…
markshannon Oct 20, 2022
10b03c8
Remove typo
markshannon Oct 20, 2022
b62f5f3
Address code review comments.
markshannon Oct 21, 2022
1a0b08f
Generate linetable when creating shim code.
markshannon Oct 21, 2022
76e437a
Bundle shim code definition into single struct.
markshannon Oct 21, 2022
945e26a
Remove incorrect assert.
markshannon Oct 21, 2022
676321a
assert generator is cleared after returning or raising.
markshannon Oct 21, 2022
3cb22e6
Rename pyframe to entry_frame.
markshannon Oct 21, 2022
e5dcbd9
Pass correct length.
markshannon Oct 21, 2022
bd29356
Address review comments
markshannon Nov 7, 2022
9446325
Merge branch 'main' into entry-frame
markshannon Nov 7, 2022
f6a6457
Post merge cleanup
markshannon Nov 8, 2022
d113655
Turn ASAN back on for subprocess test.
markshannon Nov 8, 2022
ed7af1e
Apply suggestions from code review
markshannon Nov 9, 2022
48410d9
Merge branch 'main' into entry-frame
markshannon Nov 9, 2022
7cda3ef
Merge branch 'main' into entry-frame
markshannon Nov 10, 2022
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
Address code review comments.
  • Loading branch information
markshannon committed Oct 21, 2022
commit b62f5f3506b86c4ccb555ad76def3823d79feb3c
2 changes: 1 addition & 1 deletion Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ _PyCode_LineNumberFromArray(PyCodeObject *co, int index)
}

extern PyCodeObject *
_Py_MakeTrampoline(const char *code, int codelen, int stacksize, const char *cname);
_Py_MakeTrampoline(const uint8_t *code, int codelen, int stacksize, const char *cname);


#ifdef __cplusplus
Expand Down
13 changes: 6 additions & 7 deletions Include/internal/pycore_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ enum _frameowner {

typedef struct _PyInterpreterFrame {
/* "Specials" section */
PyObject *f_funcobj; /* Strong reference. Only valid for Python frames */
PyObject *f_globals; /* Borrowed reference. Only valid for Python frames */
PyObject *f_builtins; /* Borrowed reference. Only valid for Python frames */
PyObject *f_locals; /* Strong reference, may be NULL. Only valid for Python frames */
PyObject *f_funcobj; /* Strong reference. Only valid if not on C stack */
PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */
PyObject *f_builtins; /* Borrowed reference. Only valid if not on C stack */
PyObject *f_locals; /* Strong reference, may be NULL. Only valid if not on C stack */
PyCodeObject *f_code; /* Strong reference */
PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid for Python frames */
PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid if not on C stack */
/* Linkage section */
struct _PyInterpreterFrame *previous;
// NOTE: This is not necessarily the last instruction started in the given
Expand All @@ -62,8 +62,7 @@ typedef struct _PyInterpreterFrame {
// over, or (in the case of a newly-created frame) a totally invalid value:
_Py_CODEUNIT *prev_instr;
int stacktop; /* Offset of TOS from localsplus */
int yield_offset: 28;
int owner: 4;
char owner;
/* Locals and stack */
PyObject *localsplus[1];
} _PyInterpreterFrame;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
On entry to ``PyEval_EvalFrameDefault()`` an additional, internal, shim
frame is pushed. This simplifies the return and yield bytecode instructions.
Only debuggers and profiles that inspect the stack of frames should be
impacted.
When calling into Python code from C code, though ``PyEval_EvalFrame`` or
related C-API function, a shim frame in inserted into the call stack.
This occurs in the ``_PyEval_EvalFrameDefault()`` function.
The extra frame should be invisible to all Python and most C extensions,
but out-of-process debuggers, profilers and debuggers need to be aware of
it.
These shim frames can be detected by checking
``frame->owner == FRAME_OWNED_BY_CSTACK``.

Extensions implementing their own interpreters using PEP 523 need to be
aware of this shim frame and the changes to the semantics of
``RETURN_VALUE``, ``YIELD_VALUE``, and ``RETURN_GENERATOR``,
which now clear the frame.

2 changes: 1 addition & 1 deletion Objects/codeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2270,7 +2270,7 @@ _PyStaticCode_InternStrings(PyCodeObject *co)
}

PyCodeObject *
_Py_MakeTrampoline(const char *code, int codelen, int stacksize, const char *cname)
_Py_MakeTrampoline(const uint8_t *code, int codelen, int stacksize, const char *cname)
{
PyObject *name = NULL;
PyObject *co_code = NULL;
Expand Down
2 changes: 0 additions & 2 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
pyframe.prev_instr = code;
pyframe.stacktop = 0;
pyframe.owner = FRAME_OWNED_BY_CSTACK;
pyframe.yield_offset = 0;
/* Push frame */
pyframe.previous = prev_cframe->current_frame;
frame->previous = &pyframe;
Expand Down Expand Up @@ -2095,7 +2094,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
_PyInterpreterFrame *gen_frame = frame;
frame = cframe.current_frame = gen_frame->previous;
gen_frame->previous = NULL;
frame->prev_instr += frame->yield_offset;
_PyFrame_StackPush(frame, retval);
goto resume_frame;
}
Expand Down
3 changes: 0 additions & 3 deletions Python/frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ _PyFrame_Clear(_PyInterpreterFrame *frame)
* to have cleared the enclosing generator, if any. */
assert(frame->owner != FRAME_OWNED_BY_GENERATOR ||
_PyFrame_GetGenerator(frame)->gi_frame_state == FRAME_CLEARED);
assert(frame->owner != FRAME_CLEARED);
if (frame->frame_obj) {
PyFrameObject *f = frame->frame_obj;
frame->frame_obj = NULL;
Expand All @@ -142,12 +141,10 @@ _PyFrame_Clear(_PyInterpreterFrame *frame)
for (int i = 0; i < frame->stacktop; i++) {
Py_XDECREF(frame->localsplus[i]);
}
frame->stacktop = 0;
Py_XDECREF(frame->frame_obj);
Py_XDECREF(frame->f_locals);
Py_DECREF(frame->f_funcobj);
Py_DECREF(frame->f_code);
frame->owner = FRAME_CLEARED;
}

int
Expand Down
11 changes: 8 additions & 3 deletions Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -752,9 +752,12 @@ pycore_init_types(PyInterpreterState *interp)
return _PyStatus_OK();
}

static const char INTERPRETER_TRAMPOLINE_CODE[] = {
0, 0,
static const uint8_t INTERPRETER_TRAMPOLINE_CODE[] = {
/* Put a NOP at the start, so that the IP points into
* the code, rather than before it */
NOP, 0,
INTERPRETER_EXIT, 0,
/* RESUME at end makes sure that the frame appears incomplete */
RESUME, 0
};

Expand Down Expand Up @@ -792,7 +795,9 @@ pycore_init_builtins(PyThreadState *tstate)
assert(object__getattribute__);
interp->callable_cache.object__getattribute__ = object__getattribute__;
interp->interpreter_trampoline = _Py_MakeTrampoline(
INTERPRETER_TRAMPOLINE_CODE, sizeof(INTERPRETER_TRAMPOLINE_CODE), 1, "<interpreter trampoline>");
INTERPRETER_TRAMPOLINE_CODE,
sizeof(INTERPRETER_TRAMPOLINE_CODE),
1, "<interpreter trampoline>");
if (interp->interpreter_trampoline == NULL) {
return _PyStatus_ERR("failed to create interpreter trampoline.");
}
Expand Down
2 changes: 2 additions & 0 deletions Python/traceback.c
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,8 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
if (frame == NULL) {
break;
}
/* Can't have more than one shim frame in a row */
assert(frame->owner != FRAME_OWNED_BY_CSTACK);
depth++;
}
}
Expand Down
12 changes: 7 additions & 5 deletions Tools/gdb/libpython.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ def _sizeof_void_p():
Py_TPFLAGS_BASE_EXC_SUBCLASS = (1 << 30)
Py_TPFLAGS_TYPE_SUBCLASS = (1 << 31)

#From pycore_frame.h
FRAME_OWNED_BY_CSTACK = 3

MAX_OUTPUT_LEN=1024

Expand Down Expand Up @@ -1077,8 +1079,8 @@ def _f_lasti(self):
first_instr = self._f_code().field("co_code_adaptive").cast(codeunit_p)
return int(prev_instr - first_instr)

def is_entry(self):
return self._f_special("owner", int) == 3
def is_shim(self):
return self._f_special("owner", int) == FRAME_OWNED_BY_CSTACK

def previous(self):
return self._f_special("previous", PyFramePtr)
Expand Down Expand Up @@ -1821,7 +1823,7 @@ def print_summary(self):
interp_frame = self.get_pyop()
while True:
if interp_frame:
if interp_frame.is_entry():
if interp_frame.is_shim():
break
line = interp_frame.get_truncated_repr(MAX_OUTPUT_LEN)
sys.stdout.write('#%i %s\n' % (self.get_index(), line))
Expand All @@ -1845,7 +1847,7 @@ def print_traceback(self):
interp_frame = self.get_pyop()
while True:
if interp_frame:
if interp_frame.is_entry():
if interp_frame.is_shim():
break
interp_frame.print_traceback()
if not interp_frame.is_optimized_out():
Expand Down Expand Up @@ -2106,7 +2108,7 @@ def invoke(self, args, from_tty):
while True:
if not pyop_frame:
print(UNABLE_READ_INFO_PYTHON_FRAME)
if pyop_frame.is_entry():
if pyop_frame.is_shim():
break

sys.stdout.write('Locals for %s\n' % (pyop_frame.co_name.proxyval(set())))
Expand Down
0