@@ -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 int
1004+ 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 }
@@ -1218,8 +1224,7 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame,
12181224{
12191225 PyCodeObject * code = _PyFrame_GetCode (frame );
12201226 assert (tstate -> tracing == 0 );
1221- assert (is_version_up_to_date (code , tstate -> interp ));
1222- assert (instrumentation_cross_checks (tstate -> interp , code ));
1227+ assert (debug_check_sanity (tstate -> interp , code ));
12231228 int i = (int )(instr - _PyCode_CODE (code ));
12241229
12251230 _PyCoMonitoringData * monitoring = code -> _co_monitoring ;
@@ -1339,8 +1344,7 @@ int
13391344_Py_call_instrumentation_instruction (PyThreadState * tstate , _PyInterpreterFrame * frame , _Py_CODEUNIT * instr )
13401345{
13411346 PyCodeObject * code = _PyFrame_GetCode (frame );
1342- assert (is_version_up_to_date (code , tstate -> interp ));
1343- assert (instrumentation_cross_checks (tstate -> interp , code ));
1347+ assert (debug_check_sanity (tstate -> interp , code ));
13441348 int offset = (int )(instr - _PyCode_CODE (code ));
13451349 _PyCoMonitoringData * instrumentation_data = code -> _co_monitoring ;
13461350 assert (instrumentation_data -> per_instruction_opcodes );
@@ -1671,9 +1675,11 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp)
16711675 PyErr_NoMemory ();
16721676 return -1 ;
16731677 }
1674- /* This may not be necessary, as we can initialize this memory lazily, but it helps catch errors. */
1678+ // Initialize all of the instructions so if local events change while another thread is executing
1679+ // we know what the original opcode was.
16751680 for (int i = 0 ; i < code_len ; i ++ ) {
1676- code -> _co_monitoring -> per_instruction_opcodes [i ] = 0 ;
1681+ int opcode = _PyCode_CODE (code )[i ].op .code ;
1682+ code -> _co_monitoring -> per_instruction_opcodes [i ] = _PyOpcode_Deopt [opcode ];
16771683 }
16781684 }
16791685 if (multitools && code -> _co_monitoring -> per_instruction_tools == NULL ) {
@@ -1682,7 +1688,6 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp)
16821688 PyErr_NoMemory ();
16831689 return -1 ;
16841690 }
1685- /* This may not be necessary, as we can initialize this memory lazily, but it helps catch errors. */
16861691 for (int i = 0 ; i < code_len ; i ++ ) {
16871692 code -> _co_monitoring -> per_instruction_tools [i ] = 0 ;
16881693 }
@@ -1692,17 +1697,10 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp)
16921697}
16931698
16941699static int
1695- instrument_lock_held (PyCodeObject * code , PyInterpreterState * interp )
1700+ force_instrument_lock_held (PyCodeObject * code , PyInterpreterState * interp )
16961701{
16971702 ASSERT_WORLD_STOPPED_OR_LOCKED (code );
16981703
1699- if (is_version_up_to_date (code , interp )) {
1700- assert (
1701- interp -> ceval .instrumentation_version == 0 ||
1702- instrumentation_cross_checks (interp , code )
1703- );
1704- return 0 ;
1705- }
17061704#ifdef _Py_TIER2
17071705 if (code -> co_executors != NULL ) {
17081706 _PyCode_Clear_Executors (code );
@@ -1769,17 +1767,14 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
17691767
17701768 // GH-103845: We need to remove both the line and instruction instrumentation before
17711769 // adding new ones, otherwise we may remove the newly added instrumentation.
1772-
17731770 uint8_t removed_line_tools = removed_events .tools [PY_MONITORING_EVENT_LINE ];
17741771 uint8_t removed_per_instruction_tools = removed_events .tools [PY_MONITORING_EVENT_INSTRUCTION ];
17751772
17761773 if (removed_line_tools ) {
17771774 _PyCoLineInstrumentationData * line_data = code -> _co_monitoring -> lines ;
17781775 for (int i = code -> _co_firsttraceable ; i < code_len ;) {
17791776 if (line_data [i ].original_opcode ) {
1780- if (removed_line_tools ) {
1781- remove_line_tools (code , i , removed_line_tools );
1782- }
1777+ remove_line_tools (code , i , removed_line_tools );
17831778 }
17841779 i += _PyInstruction_GetLength (code , i );
17851780 }
@@ -1791,25 +1786,22 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
17911786 i += _PyInstruction_GetLength (code , i );
17921787 continue ;
17931788 }
1794- if (removed_per_instruction_tools ) {
1795- remove_per_instruction_tools (code , i , removed_per_instruction_tools );
1796- }
1789+ remove_per_instruction_tools (code , i , removed_per_instruction_tools );
17971790 i += _PyInstruction_GetLength (code , i );
17981791 }
17991792 }
18001793#ifdef INSTRUMENT_DEBUG
18011794 sanity_check_instrumentation (code );
18021795#endif
1796+
18031797 uint8_t new_line_tools = new_events .tools [PY_MONITORING_EVENT_LINE ];
18041798 uint8_t new_per_instruction_tools = new_events .tools [PY_MONITORING_EVENT_INSTRUCTION ];
18051799
18061800 if (new_line_tools ) {
18071801 _PyCoLineInstrumentationData * line_data = code -> _co_monitoring -> lines ;
18081802 for (int i = code -> _co_firsttraceable ; i < code_len ;) {
18091803 if (line_data [i ].original_opcode ) {
1810- if (new_line_tools ) {
1811- add_line_tools (code , i , new_line_tools );
1812- }
1804+ add_line_tools (code , i , new_line_tools );
18131805 }
18141806 i += _PyInstruction_GetLength (code , i );
18151807 }
@@ -1821,12 +1813,11 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
18211813 i += _PyInstruction_GetLength (code , i );
18221814 continue ;
18231815 }
1824- if (new_per_instruction_tools ) {
1825- add_per_instruction_tools (code , i , new_per_instruction_tools );
1826- }
1816+ add_per_instruction_tools (code , i , new_per_instruction_tools );
18271817 i += _PyInstruction_GetLength (code , i );
18281818 }
18291819 }
1820+
18301821done :
18311822 FT_ATOMIC_STORE_UINTPTR_RELEASE (code -> _co_instrumentation_version ,
18321823 global_version (interp ));
@@ -1837,6 +1828,22 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
18371828 return 0 ;
18381829}
18391830
1831+ static int
1832+ instrument_lock_held (PyCodeObject * code , PyInterpreterState * interp )
1833+ {
1834+ ASSERT_WORLD_STOPPED_OR_LOCKED (code );
1835+
1836+ if (is_version_up_to_date (code , interp )) {
1837+ assert (
1838+ interp -> ceval .instrumentation_version == 0 ||
1839+ instrumentation_cross_checks (interp , code )
1840+ );
1841+ return 0 ;
1842+ }
1843+
1844+ return force_instrument_lock_held (code , interp );
1845+ }
1846+
18401847int
18411848_Py_Instrument (PyCodeObject * code , PyInterpreterState * interp )
18421849{
@@ -1983,16 +1990,8 @@ _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEvent
19831990 goto done ;
19841991 }
19851992 set_local_events (local , tool_id , events );
1986- if (is_version_up_to_date (code , interp )) {
1987- /* Force instrumentation update */
1988- code -> _co_instrumentation_version -= MONITORING_VERSION_INCREMENT ;
1989- }
1990-
1991- #ifdef _Py_TIER2
1992- _Py_Executors_InvalidateDependency (interp , code , 1 );
1993- #endif
19941993
1995- res = instrument_lock_held (code , interp );
1994+ res = force_instrument_lock_held (code , interp );
19961995
19971996done :
19981997 UNLOCK_CODE ();
0 commit comments