@@ -459,7 +459,6 @@ dump_instrumentation_data(PyCodeObject *code, int star, FILE*out)
459459}
460460
461461#define CHECK (test ) do { \
462- ASSERT_WORLD_STOPPED_OR_LOCKED(code); \
463462 if (!(test)) { \
464463 dump_instrumentation_data(code, i, stderr); \
465464 } \
@@ -516,10 +515,6 @@ sanity_check_instrumentation(PyCodeObject *code)
516515 if (!is_instrumented (opcode )) {
517516 CHECK (_PyOpcode_Deopt [opcode ] == opcode );
518517 }
519- if (data -> per_instruction_tools ) {
520- uint8_t tools = active_monitors .tools [PY_MONITORING_EVENT_INSTRUCTION ];
521- CHECK ((tools & data -> per_instruction_tools [i ]) == data -> per_instruction_tools [i ]);
522- }
523518 }
524519 if (opcode == INSTRUMENTED_LINE ) {
525520 CHECK (data -> lines );
@@ -677,8 +672,6 @@ de_instrument_per_instruction(PyCodeObject *code, int i)
677672 }
678673 assert (* opcode_ptr != INSTRUMENTED_INSTRUCTION );
679674 assert (instr -> op .code != INSTRUMENTED_INSTRUCTION );
680- /* Keep things clean for sanity check */
681- code -> _co_monitoring -> per_instruction_opcodes [i ] = 0 ;
682675}
683676
684677
@@ -992,18 +985,32 @@ set_global_version(PyThreadState *tstate, uint32_t version)
992985static bool
993986is_version_up_to_date (PyCodeObject * code , PyInterpreterState * interp )
994987{
988+ ASSERT_WORLD_STOPPED_OR_LOCKED (code );
995989 return global_version (interp ) == code -> _co_instrumentation_version ;
996990}
997991
998992#ifndef NDEBUG
999993static bool
1000994instrumentation_cross_checks (PyInterpreterState * interp , PyCodeObject * code )
1001995{
996+ ASSERT_WORLD_STOPPED_OR_LOCKED (code );
1002997 _Py_LocalMonitors expected = local_union (
1003998 interp -> monitors ,
1004999 code -> _co_monitoring -> local_monitors );
10051000 return monitors_equals (code -> _co_monitoring -> active_monitors , expected );
10061001}
1002+
1003+ static inline
1004+ int debug_check_sanity (PyInterpreterState * interp , PyCodeObject * code )
1005+ {
1006+ int res ;
1007+ LOCK_CODE (code );
1008+ res = is_version_up_to_date (code , interp ) &&
1009+ instrumentation_cross_checks (interp , code );
1010+ UNLOCK_CODE ();
1011+ return res ;
1012+ }
1013+
10071014#endif
10081015
10091016static inline uint8_t
@@ -1018,8 +1025,7 @@ get_tools_for_instruction(PyCodeObject *code, PyInterpreterState *interp, int i,
10181025 event = PY_MONITORING_EVENT_CALL ;
10191026 }
10201027 if (PY_MONITORING_IS_INSTRUMENTED_EVENT (event )) {
1021- CHECK (is_version_up_to_date (code , interp ));
1022- CHECK (instrumentation_cross_checks (interp , code ));
1028+ CHECK (debug_check_sanity (interp , code ));
10231029 if (code -> _co_monitoring -> tools ) {
10241030 tools = code -> _co_monitoring -> tools [i ];
10251031 }
@@ -1217,8 +1223,7 @@ int
12171223_Py_call_instrumentation_line (PyThreadState * tstate , _PyInterpreterFrame * frame , _Py_CODEUNIT * instr , _Py_CODEUNIT * prev )
12181224{
12191225 PyCodeObject * code = _PyFrame_GetCode (frame );
1220- assert (is_version_up_to_date (code , tstate -> interp ));
1221- assert (instrumentation_cross_checks (tstate -> interp , code ));
1226+ assert (debug_check_sanity (tstate -> interp , code ));
12221227 int i = (int )(instr - _PyCode_CODE (code ));
12231228
12241229 _PyCoMonitoringData * monitoring = code -> _co_monitoring ;
@@ -1341,8 +1346,7 @@ int
13411346_Py_call_instrumentation_instruction (PyThreadState * tstate , _PyInterpreterFrame * frame , _Py_CODEUNIT * instr )
13421347{
13431348 PyCodeObject * code = _PyFrame_GetCode (frame );
1344- assert (is_version_up_to_date (code , tstate -> interp ));
1345- assert (instrumentation_cross_checks (tstate -> interp , code ));
1349+ assert (debug_check_sanity (tstate -> interp , code ));
13461350 int offset = (int )(instr - _PyCode_CODE (code ));
13471351 _PyCoMonitoringData * instrumentation_data = code -> _co_monitoring ;
13481352 assert (instrumentation_data -> per_instruction_opcodes );
@@ -1673,9 +1677,11 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp)
16731677 PyErr_NoMemory ();
16741678 return -1 ;
16751679 }
1676- /* This may not be necessary, as we can initialize this memory lazily, but it helps catch errors. */
1680+ // Initialize all of the instructions so if local events change while another thread is executing
1681+ // we know what the original opcode was.
16771682 for (int i = 0 ; i < code_len ; i ++ ) {
1678- code -> _co_monitoring -> per_instruction_opcodes [i ] = 0 ;
1683+ int opcode = _PyCode_CODE (code )[i ].op .code ;
1684+ code -> _co_monitoring -> per_instruction_opcodes [i ] = _PyOpcode_Deopt [opcode ];
16791685 }
16801686 }
16811687 if (multitools && code -> _co_monitoring -> per_instruction_tools == NULL ) {
@@ -1684,27 +1690,22 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp)
16841690 PyErr_NoMemory ();
16851691 return -1 ;
16861692 }
1687- /* This may not be necessary, as we can initialize this memory lazily, but it helps catch errors. */
1693+ // Initialize all of the instructions so if local events change while another thread is executing
1694+ // we know what the original opcode was.
16881695 for (int i = 0 ; i < code_len ; i ++ ) {
1689- code -> _co_monitoring -> per_instruction_tools [i ] = 0 ;
1696+ int opcode = _PyCode_CODE (code )[i ].op .code ;
1697+ code -> _co_monitoring -> per_instruction_tools [i ] = _PyOpcode_Deopt [opcode ];
16901698 }
16911699 }
16921700 }
16931701 return 0 ;
16941702}
16951703
16961704static int
1697- instrument_lock_held (PyCodeObject * code , PyInterpreterState * interp )
1705+ force_instrument_lock_held (PyCodeObject * code , PyInterpreterState * interp )
16981706{
16991707 ASSERT_WORLD_STOPPED_OR_LOCKED (code );
17001708
1701- if (is_version_up_to_date (code , interp )) {
1702- assert (
1703- interp -> ceval .instrumentation_version == 0 ||
1704- instrumentation_cross_checks (interp , code )
1705- );
1706- return 0 ;
1707- }
17081709#ifdef _Py_TIER2
17091710 if (code -> co_executors != NULL ) {
17101711 _PyCode_Clear_Executors (code );
@@ -1771,17 +1772,14 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
17711772
17721773 // GH-103845: We need to remove both the line and instruction instrumentation before
17731774 // adding new ones, otherwise we may remove the newly added instrumentation.
1774-
17751775 uint8_t removed_line_tools = removed_events .tools [PY_MONITORING_EVENT_LINE ];
17761776 uint8_t removed_per_instruction_tools = removed_events .tools [PY_MONITORING_EVENT_INSTRUCTION ];
17771777
17781778 if (removed_line_tools ) {
17791779 _PyCoLineInstrumentationData * line_data = code -> _co_monitoring -> lines ;
17801780 for (int i = code -> _co_firsttraceable ; i < code_len ;) {
17811781 if (line_data [i ].original_opcode ) {
1782- if (removed_line_tools ) {
1783- remove_line_tools (code , i , removed_line_tools );
1784- }
1782+ remove_line_tools (code , i , removed_line_tools );
17851783 }
17861784 i += _PyInstruction_GetLength (code , i );
17871785 }
@@ -1793,25 +1791,22 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
17931791 i += _PyInstruction_GetLength (code , i );
17941792 continue ;
17951793 }
1796- if (removed_per_instruction_tools ) {
1797- remove_per_instruction_tools (code , i , removed_per_instruction_tools );
1798- }
1794+ remove_per_instruction_tools (code , i , removed_per_instruction_tools );
17991795 i += _PyInstruction_GetLength (code , i );
18001796 }
18011797 }
18021798#ifdef INSTRUMENT_DEBUG
18031799 sanity_check_instrumentation (code );
18041800#endif
1801+
18051802 uint8_t new_line_tools = new_events .tools [PY_MONITORING_EVENT_LINE ];
18061803 uint8_t new_per_instruction_tools = new_events .tools [PY_MONITORING_EVENT_INSTRUCTION ];
18071804
18081805 if (new_line_tools ) {
18091806 _PyCoLineInstrumentationData * line_data = code -> _co_monitoring -> lines ;
18101807 for (int i = code -> _co_firsttraceable ; i < code_len ;) {
18111808 if (line_data [i ].original_opcode ) {
1812- if (new_line_tools ) {
1813- add_line_tools (code , i , new_line_tools );
1814- }
1809+ add_line_tools (code , i , new_line_tools );
18151810 }
18161811 i += _PyInstruction_GetLength (code , i );
18171812 }
@@ -1823,12 +1818,11 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
18231818 i += _PyInstruction_GetLength (code , i );
18241819 continue ;
18251820 }
1826- if (new_per_instruction_tools ) {
1827- add_per_instruction_tools (code , i , new_per_instruction_tools );
1828- }
1821+ add_per_instruction_tools (code , i , new_per_instruction_tools );
18291822 i += _PyInstruction_GetLength (code , i );
18301823 }
18311824 }
1825+
18321826done :
18331827 FT_ATOMIC_STORE_UINTPTR_RELEASE (code -> _co_instrumentation_version ,
18341828 global_version (interp ));
@@ -1839,6 +1833,22 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
18391833 return 0 ;
18401834}
18411835
1836+ static int
1837+ instrument_lock_held (PyCodeObject * code , PyInterpreterState * interp )
1838+ {
1839+ ASSERT_WORLD_STOPPED_OR_LOCKED (code );
1840+
1841+ if (is_version_up_to_date (code , interp )) {
1842+ assert (
1843+ interp -> ceval .instrumentation_version == 0 ||
1844+ instrumentation_cross_checks (interp , code )
1845+ );
1846+ return 0 ;
1847+ }
1848+
1849+ return force_instrument_lock_held (code , interp );
1850+ }
1851+
18421852int
18431853_Py_Instrument (PyCodeObject * code , PyInterpreterState * interp )
18441854{
@@ -1985,16 +1995,8 @@ _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEvent
19851995 goto done ;
19861996 }
19871997 set_local_events (local , tool_id , events );
1988- if (is_version_up_to_date (code , interp )) {
1989- /* Force instrumentation update */
1990- code -> _co_instrumentation_version -= MONITORING_VERSION_INCREMENT ;
1991- }
1992-
1993- #ifdef _Py_TIER2
1994- _Py_Executors_InvalidateDependency (interp , code , 1 );
1995- #endif
19961998
1997- res = instrument_lock_held (code , interp );
1999+ res = force_instrument_lock_held (code , interp );
19982000
19992001done :
20002002 UNLOCK_CODE ();
0 commit comments