Skip to content

Commit 3cd1a5d

Browse files
authoredJun 14, 2022
GH-93516: Store offset of first traceable instruction in code object (GH-93769)
1 parent 2bf7475 commit 3cd1a5d

File tree

6 files changed

+54
-53
lines changed

6 files changed

+54
-53
lines changed
 

‎Include/cpython/code.h

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ typedef uint16_t _Py_CODEUNIT;
8989
PyObject *co_linetable; /* bytes object that holds location info */ \
9090
PyObject *co_weakreflist; /* to support weakrefs to code objects */ \
9191
void *_co_code; /* cached co_code object/attribute */ \
92+
int _co_firsttraceable; /* index of first traceable instruction */ \
9293
/* Scratch space for extra data relating to the code object. \
9394
Type is a void* to keep the format private in codeobject.c to force \
9495
people to go through the proper APIs. */ \

‎Lib/opcode.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def jabs_op(name, op):
177177
hasfree.append(148)
178178
def_op('COPY_FREE_VARS', 149)
179179
def_op('YIELD_VALUE', 150)
180-
def_op('RESUME', 151)
180+
def_op('RESUME', 151) # This must be kept in sync with deepfreeze.py
181181
def_op('MATCH_CLASS', 152)
182182

183183
def_op('FORMAT_VALUE', 155)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Store offset of first traceable instruction in code object to avoid having
2+
to recompute it for each instruction when tracing.

‎Objects/codeobject.c

+6
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,12 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
339339
co->co_warmup = QUICKENING_INITIAL_WARMUP_VALUE;
340340
memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code),
341341
PyBytes_GET_SIZE(con->code));
342+
int entry_point = 0;
343+
while (entry_point < Py_SIZE(co) &&
344+
_Py_OPCODE(_PyCode_CODE(co)[entry_point]) != RESUME) {
345+
entry_point++;
346+
}
347+
co->_co_firsttraceable = entry_point;
342348
}
343349

344350
static int

‎Python/ceval.c

+37-52
Original file line numberDiff line numberDiff line change
@@ -5568,57 +5568,47 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
55685568
case DO_TRACING:
55695569
#endif
55705570
{
5571-
if (tstate->tracing == 0) {
5571+
if (tstate->tracing == 0 &&
5572+
INSTR_OFFSET() >= frame->f_code->_co_firsttraceable
5573+
) {
55725574
int instr_prev = _PyInterpreterFrame_LASTI(frame);
55735575
frame->prev_instr = next_instr;
55745576
TRACING_NEXTOPARG();
5575-
switch(opcode) {
5576-
case COPY_FREE_VARS:
5577-
case MAKE_CELL:
5578-
case RETURN_GENERATOR:
5579-
/* Frame not fully initialized */
5580-
break;
5581-
case RESUME:
5582-
if (oparg < 2) {
5583-
CHECK_EVAL_BREAKER();
5584-
}
5585-
/* Call tracing */
5586-
TRACE_FUNCTION_ENTRY();
5587-
DTRACE_FUNCTION_ENTRY();
5588-
break;
5589-
case POP_TOP:
5590-
if (_Py_OPCODE(next_instr[-1]) == RETURN_GENERATOR) {
5591-
/* Frame not fully initialized */
5592-
break;
5593-
}
5594-
/* fall through */
5595-
default:
5596-
/* line-by-line tracing support */
5597-
if (PyDTrace_LINE_ENABLED()) {
5598-
maybe_dtrace_line(frame, &tstate->trace_info, instr_prev);
5599-
}
5600-
5601-
if (cframe.use_tracing &&
5602-
tstate->c_tracefunc != NULL && !tstate->tracing) {
5603-
int err;
5604-
/* see maybe_call_line_trace()
5605-
for expository comments */
5606-
_PyFrame_SetStackPointer(frame, stack_pointer);
5607-
5608-
err = maybe_call_line_trace(tstate->c_tracefunc,
5609-
tstate->c_traceobj,
5610-
tstate, frame, instr_prev);
5611-
if (err) {
5612-
/* trace function raised an exception */
5613-
next_instr++;
5614-
goto error;
5615-
}
5616-
/* Reload possibly changed frame fields */
5617-
next_instr = frame->prev_instr;
5577+
if (opcode == RESUME) {
5578+
if (oparg < 2) {
5579+
CHECK_EVAL_BREAKER();
5580+
}
5581+
/* Call tracing */
5582+
TRACE_FUNCTION_ENTRY();
5583+
DTRACE_FUNCTION_ENTRY();
5584+
}
5585+
else {
5586+
/* line-by-line tracing support */
5587+
if (PyDTrace_LINE_ENABLED()) {
5588+
maybe_dtrace_line(frame, &tstate->trace_info, instr_prev);
5589+
}
56185590

5619-
stack_pointer = _PyFrame_GetStackPointer(frame);
5620-
frame->stacktop = -1;
5591+
if (cframe.use_tracing &&
5592+
tstate->c_tracefunc != NULL && !tstate->tracing) {
5593+
int err;
5594+
/* see maybe_call_line_trace()
5595+
for expository comments */
5596+
_PyFrame_SetStackPointer(frame, stack_pointer);
5597+
5598+
err = maybe_call_line_trace(tstate->c_tracefunc,
5599+
tstate->c_traceobj,
5600+
tstate, frame, instr_prev);
5601+
if (err) {
5602+
/* trace function raised an exception */
5603+
next_instr++;
5604+
goto error;
56215605
}
5606+
/* Reload possibly changed frame fields */
5607+
next_instr = frame->prev_instr;
5608+
5609+
stack_pointer = _PyFrame_GetStackPointer(frame);
5610+
frame->stacktop = -1;
5611+
}
56225612
}
56235613
}
56245614
TRACING_NEXTOPARG();
@@ -6855,13 +6845,8 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
68556845
then call the trace function if we're tracing source lines.
68566846
*/
68576847
initialize_trace_info(&tstate->trace_info, frame);
6858-
int entry_point = 0;
6859-
_Py_CODEUNIT *code = _PyCode_CODE(frame->f_code);
6860-
while (_PyOpcode_Deopt[_Py_OPCODE(code[entry_point])] != RESUME) {
6861-
entry_point++;
6862-
}
68636848
int lastline;
6864-
if (instr_prev <= entry_point) {
6849+
if (instr_prev <= frame->f_code->_co_firsttraceable) {
68656850
lastline = -1;
68666851
}
68676852
else {

‎Tools/scripts/deepfreeze.py

+7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
verbose = False
2121
identifiers, strings = get_identifiers_and_strings()
2222

23+
# This must be kept in sync with opcode.py
24+
RESUME = 151
25+
2326
def isprintable(b: bytes) -> bool:
2427
return all(0x20 <= c < 0x7f for c in b)
2528

@@ -267,6 +270,10 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
267270
self.write(f".co_qualname = {co_qualname},")
268271
self.write(f".co_linetable = {co_linetable},")
269272
self.write(f".co_code_adaptive = {co_code_adaptive},")
273+
for i, op in enumerate(code.co_code[::2]):
274+
if op == RESUME:
275+
self.write(f"._co_firsttraceable = {i},")
276+
break
270277
name_as_code = f"(PyCodeObject *)&{name}"
271278
self.deallocs.append(f"_PyStaticCode_Dealloc({name_as_code});")
272279
self.interns.append(f"_PyStaticCode_InternStrings({name_as_code})")

0 commit comments

Comments
 (0)
Please sign in to comment.