From 4a8e2fa69b98177d23eca93b0c784550e3f54945 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 13 Jun 2022 13:56:30 +0100 Subject: [PATCH 1/6] Store offset of first traceable instruction to avoid having to recompute it all the time when tracing. --- Include/cpython/code.h | 1 + ...2-06-13-13-55-34.gh-issue-93516.HILrDl.rst | 2 + Objects/codeobject.c | 5 ++ Python/ceval.c | 89 ++++++++----------- Tools/scripts/deepfreeze.py | 8 ++ 5 files changed, 53 insertions(+), 52 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-06-13-13-55-34.gh-issue-93516.HILrDl.rst diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 66cf4eccb8fcc9..ef8c6422046dc7 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -91,6 +91,7 @@ typedef uint16_t _Py_CODEUNIT; PyObject *co_weakreflist; /* to support weakrefs to code objects */ \ PyObject *_co_code; /* cached co_code object/attribute */ \ char *_co_linearray; /* array of line offsets */ \ + int _co_firsttraceable; /* index of first traceable instruction */ \ /* Scratch space for extra data relating to the code object. \ Type is a void* to keep the format private in codeobject.c to force \ people to go through the proper APIs. */ \ diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-13-13-55-34.gh-issue-93516.HILrDl.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-13-13-55-34.gh-issue-93516.HILrDl.rst new file mode 100644 index 00000000000000..a324c2dbcbe8a6 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-06-13-13-55-34.gh-issue-93516.HILrDl.rst @@ -0,0 +1,2 @@ +Store offset of first traceable instruction in code object to avoid having +to recompute it for each instruction when tracing. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 8b9ca890431c6c..658295de6140bc 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -341,6 +341,11 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->_co_linearray = NULL; memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code), PyBytes_GET_SIZE(con->code)); + int entry_point = 0; + while (_Py_OPCODE(_PyCode_CODE(co)[entry_point]) != RESUME) { + entry_point++; + } + co->_co_firsttraceable = entry_point; } static int diff --git a/Python/ceval.c b/Python/ceval.c index 763187a8317af4..00fb02718d8a37 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5608,57 +5608,47 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int case DO_TRACING: #endif { - if (tstate->tracing == 0) { + if (tstate->tracing == 0 && + INSTR_OFFSET() >= frame->f_code->_co_firsttraceable + ) { int instr_prev = _PyInterpreterFrame_LASTI(frame); frame->prev_instr = next_instr; TRACING_NEXTOPARG(); - switch(opcode) { - case COPY_FREE_VARS: - case MAKE_CELL: - case RETURN_GENERATOR: - /* Frame not fully initialized */ - break; - case RESUME: - if (oparg < 2) { - CHECK_EVAL_BREAKER(); - } - /* Call tracing */ - TRACE_FUNCTION_ENTRY(); - DTRACE_FUNCTION_ENTRY(); - break; - case POP_TOP: - if (_Py_OPCODE(next_instr[-1]) == RETURN_GENERATOR) { - /* Frame not fully initialized */ - break; - } - /* fall through */ - default: - /* line-by-line tracing support */ - if (PyDTrace_LINE_ENABLED()) { - maybe_dtrace_line(frame, &tstate->trace_info, instr_prev); - } - - if (cframe.use_tracing && - tstate->c_tracefunc != NULL && !tstate->tracing) { - int err; - /* see maybe_call_line_trace() - for expository comments */ - _PyFrame_SetStackPointer(frame, stack_pointer); - - err = maybe_call_line_trace(tstate->c_tracefunc, - tstate->c_traceobj, - tstate, frame, instr_prev); - if (err) { - /* trace function raised an exception */ - next_instr++; - goto error; - } - /* Reload possibly changed frame fields */ - next_instr = frame->prev_instr; + if (opcode == RESUME) { + if (oparg < 2) { + CHECK_EVAL_BREAKER(); + } + /* Call tracing */ + TRACE_FUNCTION_ENTRY(); + DTRACE_FUNCTION_ENTRY(); + } + else { + /* line-by-line tracing support */ + if (PyDTrace_LINE_ENABLED()) { + maybe_dtrace_line(frame, &tstate->trace_info, instr_prev); + } - stack_pointer = _PyFrame_GetStackPointer(frame); - frame->stacktop = -1; + if (cframe.use_tracing && + tstate->c_tracefunc != NULL && !tstate->tracing) { + int err; + /* see maybe_call_line_trace() + for expository comments */ + _PyFrame_SetStackPointer(frame, stack_pointer); + + err = maybe_call_line_trace(tstate->c_tracefunc, + tstate->c_traceobj, + tstate, frame, instr_prev); + if (err) { + /* trace function raised an exception */ + next_instr++; + goto error; } + /* Reload possibly changed frame fields */ + next_instr = frame->prev_instr; + + stack_pointer = _PyFrame_GetStackPointer(frame); + frame->stacktop = -1; + } } } TRACING_NEXTOPARG(); @@ -6896,13 +6886,8 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, if (_PyCode_InitLineArray(frame->f_code)) { return -1; } - int entry_point = 0; - _Py_CODEUNIT *code = _PyCode_CODE(frame->f_code); - while (_PyOpcode_Deopt[_Py_OPCODE(code[entry_point])] != RESUME) { - entry_point++; - } int lastline; - if (instr_prev <= entry_point) { + if (instr_prev <= frame->f_code->_co_firsttraceable) { lastline = -1; } else { diff --git a/Tools/scripts/deepfreeze.py b/Tools/scripts/deepfreeze.py index 5aca79a722e11e..b02e423f5e9da6 100644 --- a/Tools/scripts/deepfreeze.py +++ b/Tools/scripts/deepfreeze.py @@ -11,6 +11,7 @@ import collections import contextlib import os +import opcode import re import time import types @@ -22,6 +23,9 @@ verbose = False identifiers, strings = get_identifiers_and_strings() +RESUME = opcode.opmap["RESUME"] +del opcode + def isprintable(b: bytes) -> bool: return all(0x20 <= c < 0x7f for c in b) @@ -284,6 +288,10 @@ def generate_code(self, name: str, code: types.CodeType) -> str: self.write(f"._co_code = NULL,") self.write("._co_linearray = NULL,") self.write(f".co_code_adaptive = {co_code_adaptive},") + for i, op in enumerate(code.co_code[::2]): + if op == RESUME: + self.write(f"._co_firsttraceable = {i},") + break name_as_code = f"(PyCodeObject *)&{name}" self.deallocs.append(f"_PyStaticCode_Dealloc({name_as_code});") self.interns.append(f"_PyStaticCode_InternStrings({name_as_code})") From 71dd6c84fa29f75f2cd62075d5baf7b9df5b3f03 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 13 Jun 2022 18:13:38 +0100 Subject: [PATCH 2/6] Hardcode RESUME opcode in deepfreeze, to break dependency on exact version of Python used. --- Lib/opcode.py | 2 +- Tools/scripts/deepfreeze.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/opcode.py b/Lib/opcode.py index 310582874dc8f0..bc3c02af2bccb3 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -175,7 +175,7 @@ def jabs_op(name, op): hasfree.append(148) def_op('COPY_FREE_VARS', 149) -def_op('RESUME', 151) +def_op('RESUME', 151) # This must be kept in sync with deepfreeze.py def_op('MATCH_CLASS', 152) def_op('FORMAT_VALUE', 155) diff --git a/Tools/scripts/deepfreeze.py b/Tools/scripts/deepfreeze.py index b02e423f5e9da6..5a64c1ec2e61f7 100644 --- a/Tools/scripts/deepfreeze.py +++ b/Tools/scripts/deepfreeze.py @@ -11,7 +11,6 @@ import collections import contextlib import os -import opcode import re import time import types @@ -23,8 +22,8 @@ verbose = False identifiers, strings = get_identifiers_and_strings() -RESUME = opcode.opmap["RESUME"] -del opcode +# This must be kept in sync with opcode.py +RESUME = 151 def isprintable(b: bytes) -> bool: return all(0x20 <= c < 0x7f for c in b) From 588d5feb791a82045cb1f59e4cc9e0ee9b0f9c43 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 13 Jun 2022 18:33:05 +0100 Subject: [PATCH 3/6] Handle empty code objects, and other invalid code, gracefully. --- Objects/codeobject.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 658295de6140bc..99ca72f9822115 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -342,7 +342,8 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code), PyBytes_GET_SIZE(con->code)); int entry_point = 0; - while (_Py_OPCODE(_PyCode_CODE(co)[entry_point]) != RESUME) { + while (_Py_OPCODE(_PyCode_CODE(co)[entry_point]) != RESUME && + entry_point < Py_SIZE(co)) { entry_point++; } co->_co_firsttraceable = entry_point; From 643a6146509315ef583f4dbcb61628bf5ad88f84 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 14 Jun 2022 10:29:36 +0100 Subject: [PATCH 4/6] Check bounds before data --- Objects/codeobject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 99ca72f9822115..fddb42425ba068 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -342,8 +342,8 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code), PyBytes_GET_SIZE(con->code)); int entry_point = 0; - while (_Py_OPCODE(_PyCode_CODE(co)[entry_point]) != RESUME && - entry_point < Py_SIZE(co)) { + while (entry_point < Py_SIZE(co) && + _Py_OPCODE(_PyCode_CODE(co)[entry_point]) != RESUME) { entry_point++; } co->_co_firsttraceable = entry_point; From fe6a1d3ce5d81d41635ad4dab4577d14cd8f98a9 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 28 Jun 2022 13:52:28 +0100 Subject: [PATCH 5/6] Assert that _co_firsttraceable is a RESUME --- Python/ceval.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Python/ceval.c b/Python/ceval.c index 00fb02718d8a37..2d794837ce3d7e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5611,6 +5611,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int if (tstate->tracing == 0 && INSTR_OFFSET() >= frame->f_code->_co_firsttraceable ) { + assert( + _PyOpcode_Deopt[first_instr[frame->f_code->_co_firsttraceable]] + == RESUME + ); int instr_prev = _PyInterpreterFrame_LASTI(frame); frame->prev_instr = next_instr; TRACING_NEXTOPARG(); From a0ced7127d4ca6dc9bcf79f3ed9c33720a0e1b08 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 28 Jun 2022 14:08:58 +0100 Subject: [PATCH 6/6] Regen ABI doc. --- Doc/data/python3.11.abi | 317 ++++++++++++++++++++-------------------- 1 file changed, 160 insertions(+), 157 deletions(-) diff --git a/Doc/data/python3.11.abi b/Doc/data/python3.11.abi index 79b3ca3995c246..98cdaf9524bbc3 100644 --- a/Doc/data/python3.11.abi +++ b/Doc/data/python3.11.abi @@ -5858,122 +5858,125 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + - + - + - + - + - + - + - + - + - + @@ -7454,93 +7457,93 @@ - - - + + + - - + + - - - - + + + + - - - - + + + + - - - - - - - + + + + + + + - - - + + + - - - + + + - - - - + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + @@ -7594,8 +7597,8 @@ - - + + @@ -11172,32 +11175,32 @@ - + - - + + - - + + - + - - - + + + - + - + @@ -11211,33 +11214,33 @@ - - + + - + - + - - + + - + - + - + - - - + + + @@ -11363,43 +11366,43 @@ - - - - + + + + - - - + + + - - - - + + + + - + - + - - - - - - - - - - - - + + + + + + + + + + + + @@ -14919,7 +14922,7 @@ - +