diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 7c662907475..b3f480c7204 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -233,6 +233,9 @@ extern void _PyLineTable_InitAddressRange( extern int _PyLineTable_NextAddressRange(PyCodeAddressRange *range); extern int _PyLineTable_PreviousAddressRange(PyCodeAddressRange *range); +/** API for executors */ +extern void _PyCode_Clear_Executors(PyCodeObject *code); + #define ENABLE_SPECIALIZATION 1 /* Specialization functions */ diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py index 95fe8d03b0b..8a7d300b734 100644 --- a/Lib/test/test_monitoring.py +++ b/Lib/test/test_monitoring.py @@ -1718,3 +1718,31 @@ class TestRegressions(MonitoringTestBase, unittest.TestCase): make_foo_optimized_then_set_event() finally: sys.monitoring.set_events(TEST_TOOL, 0) + + +class TestOptimizer(MonitoringTestBase, unittest.TestCase): + + def setUp(self): + import _testinternalcapi + self.old_opt = _testinternalcapi.get_optimizer() + opt = _testinternalcapi.get_counter_optimizer() + _testinternalcapi.set_optimizer(opt) + super(TestOptimizer, self).setUp() + + def tearDown(self): + import _testinternalcapi + super(TestOptimizer, self).tearDown() + _testinternalcapi.set_optimizer(self.old_opt) + + def test_for_loop(self): + def test_func(x): + i = 0 + while i < x: + i += 1 + + code = test_func.__code__ + sys.monitoring.set_local_events(TEST_TOOL, code, E.PY_START) + self.assertEqual(sys.monitoring.get_local_events(TEST_TOOL, code), E.PY_START) + test_func(1000) + sys.monitoring.set_local_events(TEST_TOOL, code, 0) + self.assertEqual(sys.monitoring.get_local_events(TEST_TOOL, code), 0) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 58306075cad..1443027ff29 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1479,6 +1479,23 @@ clear_executors(PyCodeObject *co) co->co_executors = NULL; } +void +_PyCode_Clear_Executors(PyCodeObject *code) { + int code_len = (int)Py_SIZE(code); + for (int i = 0; i < code_len; i += _PyInstruction_GetLength(code, i)) { + _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; + uint8_t opcode = instr->op.code; + uint8_t oparg = instr->op.arg; + if (opcode == ENTER_EXECUTOR) { + _PyExecutorObject *exec = code->co_executors->executors[oparg]; + assert(exec->vm_data.opcode != ENTER_EXECUTOR); + instr->op.code = exec->vm_data.opcode; + instr->op.arg = exec->vm_data.oparg; + } + } + clear_executors(code); +} + static void deopt_code(PyCodeObject *code, _Py_CODEUNIT *instructions) { diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 9065043f55d..acc3278d50a 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -3,6 +3,7 @@ #include "opcode_ids.h" #include "pycore_call.h" +#include "pycore_code.h" // _PyCode_Clear_Executors() #include "pycore_frame.h" #include "pycore_interp.h" #include "pycore_long.h" @@ -583,13 +584,7 @@ de_instrument(PyCodeObject *code, int i, int event) _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; uint8_t *opcode_ptr = &instr->op.code; int opcode = *opcode_ptr; - if (opcode == ENTER_EXECUTOR) { - int oparg = instr->op.arg; - _PyExecutorObject *exec = code->co_executors->executors[oparg]; - opcode_ptr = &exec->vm_data.opcode; - opcode = *opcode_ptr; - assert(opcode != ENTER_EXECUTOR); - } + assert(opcode != ENTER_EXECUTOR); if (opcode == INSTRUMENTED_LINE) { opcode_ptr = &code->_co_monitoring->lines[i].original_opcode; opcode = *opcode_ptr; @@ -734,22 +729,7 @@ remove_tools(PyCodeObject * code, int offset, int event, int tools) assert(event != PY_MONITORING_EVENT_LINE); assert(event != PY_MONITORING_EVENT_INSTRUCTION); assert(PY_MONITORING_IS_INSTRUMENTED_EVENT(event)); - #ifndef NDEBUG - _Py_CODEUNIT co_instr = _PyCode_CODE(code)[offset]; - uint8_t opcode = co_instr.op.code; - uint8_t oparg = co_instr.op.arg; - if (opcode == ENTER_EXECUTOR) { - _PyExecutorObject *exec = code->co_executors->executors[oparg]; - assert(exec->vm_data.opcode != ENTER_EXECUTOR); - opcode = _PyOpcode_Deopt[exec->vm_data.opcode]; - opcode = exec->vm_data.oparg; - } - else { - opcode = _Py_GetBaseOpcode(code, offset); - } - assert(opcode != ENTER_EXECUTOR); - assert(opcode_has_event(opcode)); - #endif + assert(opcode_has_event(_Py_GetBaseOpcode(code, offset))); _PyCoMonitoringData *monitoring = code->_co_monitoring; if (monitoring && monitoring->tools) { monitoring->tools[offset] &= ~tools; @@ -1315,16 +1295,10 @@ initialize_tools(PyCodeObject *code) for (int i = 0; i < code_len; i++) { _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; int opcode = instr->op.code; - int oparg = instr->op.arg; - if (opcode == ENTER_EXECUTOR) { - _PyExecutorObject *exec = code->co_executors->executors[oparg]; - opcode = exec->vm_data.opcode; - oparg = exec->vm_data.oparg; - } - else if (opcode == INSTRUMENTED_LINE) { + assert(opcode != ENTER_EXECUTOR); + if (opcode == INSTRUMENTED_LINE) { opcode = code->_co_monitoring->lines[i].original_opcode; } - assert(opcode != ENTER_EXECUTOR); bool instrumented = is_instrumented(opcode); if (instrumented) { opcode = DE_INSTRUMENT[opcode]; @@ -1335,7 +1309,7 @@ initialize_tools(PyCodeObject *code) if (instrumented) { int8_t event; if (opcode == RESUME) { - event = oparg != 0; + event = instr->op.arg != 0; } else { event = EVENT_FOR_OPCODE[opcode]; @@ -1588,6 +1562,9 @@ _Py_Instrument(PyCodeObject *code, PyInterpreterState *interp) ); return 0; } + if (code->co_executors != NULL) { + _PyCode_Clear_Executors(code); + } int code_len = (int)Py_SIZE(code); /* code->_co_firsttraceable >= code_len indicates * that no instrumentation can be inserted. @@ -1629,7 +1606,9 @@ _Py_Instrument(PyCodeObject *code, PyInterpreterState *interp) for (int i = code->_co_firsttraceable; i < code_len; i+= _PyInstruction_GetLength(code, i)) { _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; CHECK(instr->op.code != 0); + assert(instr->op.code != ENTER_EXECUTOR); int base_opcode = _Py_GetBaseOpcode(code, i); + assert(base_opcode != ENTER_EXECUTOR); if (opcode_has_event(base_opcode)) { int8_t event; if (base_opcode == RESUME) {