Skip to content

Commit bcea36f

Browse files
gh-103845: Remove line & instruction instrumentations before adding them back (GH-103851)
1 parent 0a5cd98 commit bcea36f

File tree

3 files changed

+70
-8
lines changed

3 files changed

+70
-8
lines changed

Lib/test/test_monitoring.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,42 @@ def func3():
876876
('instruction', 'func3', 34),
877877
('line', 'check_events', 11)])
878878

879+
def test_with_restart(self):
880+
def func1():
881+
line1 = 1
882+
line2 = 2
883+
line3 = 3
884+
885+
self.check_events(func1, recorders = LINE_AND_INSTRUCTION_RECORDERS, expected = [
886+
('line', 'check_events', 10),
887+
('line', 'func1', 1),
888+
('instruction', 'func1', 2),
889+
('instruction', 'func1', 4),
890+
('line', 'func1', 2),
891+
('instruction', 'func1', 6),
892+
('instruction', 'func1', 8),
893+
('line', 'func1', 3),
894+
('instruction', 'func1', 10),
895+
('instruction', 'func1', 12),
896+
('instruction', 'func1', 14),
897+
('line', 'check_events', 11)])
898+
899+
sys.monitoring.restart_events()
900+
901+
self.check_events(func1, recorders = LINE_AND_INSTRUCTION_RECORDERS, expected = [
902+
('line', 'check_events', 10),
903+
('line', 'func1', 1),
904+
('instruction', 'func1', 2),
905+
('instruction', 'func1', 4),
906+
('line', 'func1', 2),
907+
('instruction', 'func1', 6),
908+
('instruction', 'func1', 8),
909+
('line', 'func1', 3),
910+
('instruction', 'func1', 10),
911+
('instruction', 'func1', 12),
912+
('instruction', 'func1', 14),
913+
('line', 'check_events', 11)])
914+
879915
class TestInstallIncrementallly(MonitoringTestBase, unittest.TestCase):
880916

881917
def check_events(self, func, must_include, tool=TEST_TOOL, recorders=(ExceptionRecorder,)):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Remove both line and instruction instrumentation before adding new ones for monitoring, to avoid newly added instrumentation being removed immediately.

Python/instrumentation.c

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,25 +1477,25 @@ _Py_Instrument(PyCodeObject *code, PyInterpreterState *interp)
14771477
}
14781478
}
14791479
}
1480-
uint8_t new_line_tools = new_events.tools[PY_MONITORING_EVENT_LINE];
1480+
1481+
// GH-103845: We need to remove both the line and instruction instrumentation before
1482+
// adding new ones, otherwise we may remove the newly added instrumentation.
1483+
14811484
uint8_t removed_line_tools = removed_events.tools[PY_MONITORING_EVENT_LINE];
1482-
if (new_line_tools | removed_line_tools) {
1485+
uint8_t removed_per_instruction_tools = removed_events.tools[PY_MONITORING_EVENT_INSTRUCTION];
1486+
1487+
if (removed_line_tools) {
14831488
_PyCoLineInstrumentationData *line_data = code->_co_monitoring->lines;
14841489
for (int i = code->_co_firsttraceable; i < code_len;) {
14851490
if (line_data[i].original_opcode) {
14861491
if (removed_line_tools) {
14871492
remove_line_tools(code, i, removed_line_tools);
14881493
}
1489-
if (new_line_tools) {
1490-
add_line_tools(code, i, new_line_tools);
1491-
}
14921494
}
14931495
i += instruction_length(code, i);
14941496
}
14951497
}
1496-
uint8_t new_per_instruction_tools = new_events.tools[PY_MONITORING_EVENT_INSTRUCTION];
1497-
uint8_t removed_per_instruction_tools = removed_events.tools[PY_MONITORING_EVENT_INSTRUCTION];
1498-
if (new_per_instruction_tools | removed_per_instruction_tools) {
1498+
if (removed_per_instruction_tools) {
14991499
for (int i = code->_co_firsttraceable; i < code_len;) {
15001500
int opcode = _Py_GetBaseOpcode(code, i);
15011501
if (opcode == RESUME || opcode == END_FOR) {
@@ -1505,6 +1505,31 @@ _Py_Instrument(PyCodeObject *code, PyInterpreterState *interp)
15051505
if (removed_per_instruction_tools) {
15061506
remove_per_instruction_tools(code, i, removed_per_instruction_tools);
15071507
}
1508+
i += instruction_length(code, i);
1509+
}
1510+
}
1511+
1512+
uint8_t new_line_tools = new_events.tools[PY_MONITORING_EVENT_LINE];
1513+
uint8_t new_per_instruction_tools = new_events.tools[PY_MONITORING_EVENT_INSTRUCTION];
1514+
1515+
if (new_line_tools) {
1516+
_PyCoLineInstrumentationData *line_data = code->_co_monitoring->lines;
1517+
for (int i = code->_co_firsttraceable; i < code_len;) {
1518+
if (line_data[i].original_opcode) {
1519+
if (new_line_tools) {
1520+
add_line_tools(code, i, new_line_tools);
1521+
}
1522+
}
1523+
i += instruction_length(code, i);
1524+
}
1525+
}
1526+
if (new_per_instruction_tools) {
1527+
for (int i = code->_co_firsttraceable; i < code_len;) {
1528+
int opcode = _Py_GetBaseOpcode(code, i);
1529+
if (opcode == RESUME || opcode == END_FOR) {
1530+
i += instruction_length(code, i);
1531+
continue;
1532+
}
15081533
if (new_per_instruction_tools) {
15091534
add_per_instruction_tools(code, i, new_per_instruction_tools);
15101535
}

0 commit comments

Comments
 (0)