@@ -459,7 +459,6 @@ dump_instrumentation_data(PyCodeObject *code, int star, FILE*out)
459
459
}
460
460
461
461
#define CHECK (test ) do { \
462
- ASSERT_WORLD_STOPPED_OR_LOCKED(code); \
463
462
if (!(test)) { \
464
463
dump_instrumentation_data(code, i, stderr); \
465
464
} \
@@ -516,10 +515,6 @@ sanity_check_instrumentation(PyCodeObject *code)
516
515
if (!is_instrumented (opcode )) {
517
516
CHECK (_PyOpcode_Deopt [opcode ] == opcode );
518
517
}
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
- }
523
518
}
524
519
if (opcode == INSTRUMENTED_LINE ) {
525
520
CHECK (data -> lines );
@@ -677,8 +672,6 @@ de_instrument_per_instruction(PyCodeObject *code, int i)
677
672
}
678
673
assert (* opcode_ptr != INSTRUMENTED_INSTRUCTION );
679
674
assert (instr -> op .code != INSTRUMENTED_INSTRUCTION );
680
- /* Keep things clean for sanity check */
681
- code -> _co_monitoring -> per_instruction_opcodes [i ] = 0 ;
682
675
}
683
676
684
677
@@ -992,18 +985,32 @@ set_global_version(PyThreadState *tstate, uint32_t version)
992
985
static bool
993
986
is_version_up_to_date (PyCodeObject * code , PyInterpreterState * interp )
994
987
{
988
+ ASSERT_WORLD_STOPPED_OR_LOCKED (code );
995
989
return global_version (interp ) == code -> _co_instrumentation_version ;
996
990
}
997
991
998
992
#ifndef NDEBUG
999
993
static bool
1000
994
instrumentation_cross_checks (PyInterpreterState * interp , PyCodeObject * code )
1001
995
{
996
+ ASSERT_WORLD_STOPPED_OR_LOCKED (code );
1002
997
_Py_LocalMonitors expected = local_union (
1003
998
interp -> monitors ,
1004
999
code -> _co_monitoring -> local_monitors );
1005
1000
return monitors_equals (code -> _co_monitoring -> active_monitors , expected );
1006
1001
}
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
+
1007
1014
#endif
1008
1015
1009
1016
static inline uint8_t
@@ -1018,8 +1025,7 @@ get_tools_for_instruction(PyCodeObject *code, PyInterpreterState *interp, int i,
1018
1025
event = PY_MONITORING_EVENT_CALL ;
1019
1026
}
1020
1027
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 ));
1023
1029
if (code -> _co_monitoring -> tools ) {
1024
1030
tools = code -> _co_monitoring -> tools [i ];
1025
1031
}
@@ -1218,8 +1224,7 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame,
1218
1224
{
1219
1225
PyCodeObject * code = _PyFrame_GetCode (frame );
1220
1226
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 ));
1223
1228
int i = (int )(instr - _PyCode_CODE (code ));
1224
1229
1225
1230
_PyCoMonitoringData * monitoring = code -> _co_monitoring ;
@@ -1339,8 +1344,7 @@ int
1339
1344
_Py_call_instrumentation_instruction (PyThreadState * tstate , _PyInterpreterFrame * frame , _Py_CODEUNIT * instr )
1340
1345
{
1341
1346
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 ));
1344
1348
int offset = (int )(instr - _PyCode_CODE (code ));
1345
1349
_PyCoMonitoringData * instrumentation_data = code -> _co_monitoring ;
1346
1350
assert (instrumentation_data -> per_instruction_opcodes );
@@ -1671,9 +1675,11 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp)
1671
1675
PyErr_NoMemory ();
1672
1676
return -1 ;
1673
1677
}
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.
1675
1680
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 ];
1677
1683
}
1678
1684
}
1679
1685
if (multitools && code -> _co_monitoring -> per_instruction_tools == NULL ) {
@@ -1682,7 +1688,6 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp)
1682
1688
PyErr_NoMemory ();
1683
1689
return -1 ;
1684
1690
}
1685
- /* This may not be necessary, as we can initialize this memory lazily, but it helps catch errors. */
1686
1691
for (int i = 0 ; i < code_len ; i ++ ) {
1687
1692
code -> _co_monitoring -> per_instruction_tools [i ] = 0 ;
1688
1693
}
@@ -1692,17 +1697,10 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp)
1692
1697
}
1693
1698
1694
1699
static int
1695
- instrument_lock_held (PyCodeObject * code , PyInterpreterState * interp )
1700
+ force_instrument_lock_held (PyCodeObject * code , PyInterpreterState * interp )
1696
1701
{
1697
1702
ASSERT_WORLD_STOPPED_OR_LOCKED (code );
1698
1703
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
- }
1706
1704
#ifdef _Py_TIER2
1707
1705
if (code -> co_executors != NULL ) {
1708
1706
_PyCode_Clear_Executors (code );
@@ -1769,17 +1767,14 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
1769
1767
1770
1768
// GH-103845: We need to remove both the line and instruction instrumentation before
1771
1769
// adding new ones, otherwise we may remove the newly added instrumentation.
1772
-
1773
1770
uint8_t removed_line_tools = removed_events .tools [PY_MONITORING_EVENT_LINE ];
1774
1771
uint8_t removed_per_instruction_tools = removed_events .tools [PY_MONITORING_EVENT_INSTRUCTION ];
1775
1772
1776
1773
if (removed_line_tools ) {
1777
1774
_PyCoLineInstrumentationData * line_data = code -> _co_monitoring -> lines ;
1778
1775
for (int i = code -> _co_firsttraceable ; i < code_len ;) {
1779
1776
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 );
1783
1778
}
1784
1779
i += _PyInstruction_GetLength (code , i );
1785
1780
}
@@ -1791,25 +1786,22 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
1791
1786
i += _PyInstruction_GetLength (code , i );
1792
1787
continue ;
1793
1788
}
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 );
1797
1790
i += _PyInstruction_GetLength (code , i );
1798
1791
}
1799
1792
}
1800
1793
#ifdef INSTRUMENT_DEBUG
1801
1794
sanity_check_instrumentation (code );
1802
1795
#endif
1796
+
1803
1797
uint8_t new_line_tools = new_events .tools [PY_MONITORING_EVENT_LINE ];
1804
1798
uint8_t new_per_instruction_tools = new_events .tools [PY_MONITORING_EVENT_INSTRUCTION ];
1805
1799
1806
1800
if (new_line_tools ) {
1807
1801
_PyCoLineInstrumentationData * line_data = code -> _co_monitoring -> lines ;
1808
1802
for (int i = code -> _co_firsttraceable ; i < code_len ;) {
1809
1803
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 );
1813
1805
}
1814
1806
i += _PyInstruction_GetLength (code , i );
1815
1807
}
@@ -1821,12 +1813,11 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
1821
1813
i += _PyInstruction_GetLength (code , i );
1822
1814
continue ;
1823
1815
}
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 );
1827
1817
i += _PyInstruction_GetLength (code , i );
1828
1818
}
1829
1819
}
1820
+
1830
1821
done :
1831
1822
FT_ATOMIC_STORE_UINTPTR_RELEASE (code -> _co_instrumentation_version ,
1832
1823
global_version (interp ));
@@ -1837,6 +1828,22 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
1837
1828
return 0 ;
1838
1829
}
1839
1830
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
+
1840
1847
int
1841
1848
_Py_Instrument (PyCodeObject * code , PyInterpreterState * interp )
1842
1849
{
@@ -1983,16 +1990,8 @@ _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEvent
1983
1990
goto done ;
1984
1991
}
1985
1992
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
1994
1993
1995
- res = instrument_lock_held (code , interp );
1994
+ res = force_instrument_lock_held (code , interp );
1996
1995
1997
1996
done :
1998
1997
UNLOCK_CODE ();
0 commit comments