8000 gh-103865: add monitoring support to LOAD_SUPER_ATTR · python/cpython@862c3dc · GitHub
[go: up one dir, main page]

Skip to content

Commit 862c3dc

Browse files
committed
gh-103865: add monitoring support to LOAD_SUPER_ATTR
1 parent ef25feb commit 862c3dc

10 files changed

+410
-234
lines changed

Include/internal/pycore_opcode.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/opcode.h

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/opcode.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,9 @@ def pseudo_op(name, op, real_ops):
227227
def_op('CALL_INTRINSIC_2', 174)
228228

229229
# Instrumented instructions
230-
MIN_INSTRUMENTED_OPCODE = 238
230+
MIN_INSTRUMENTED_OPCODE = 237
231231

232+
def_op('INSTRUMENTED_LOAD_SUPER_ATTR', 237)
232233
def_op('INSTRUMENTED_POP_JUMP_IF_NONE', 238)
233234
def_op('INSTRUMENTED_POP_JUMP_IF_NOT_NONE', 239)
234235
def_op('INSTRUMENTED_RESUME', 240)

Lib/test/test_monitoring.py

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import functools
55
import operator
66
import sys
7+
import textwrap
78
import types
89
import unittest
910

@@ -506,7 +507,7 @@ def test_lines_single(self):
506507
sys.monitoring.set_events(TEST_TOOL, 0)
507508
sys.monitoring.register_callback(TEST_TOOL, E.LINE, None)
508509
start = LineMonitoringTest.test_lines_single.__code__.co_firstlineno
509-
self.assertEqual(events, [start+7, 14, start+8])
510+
self.assertEqual(events, [start+7, 15, start+8])
510511
finally:
511512
sys.monitoring.set_events(TEST_TOOL, 0)
512513
sys.monitoring.register_callback(TEST_TOOL, E.LINE, None)
@@ -524,7 +525,7 @@ def test_lines_loop(self):
524525
sys.monitoring.set_events(TEST_TOOL, 0)
525526
sys.monitoring.register_callback(TEST_TOOL, E.LINE, None)
526527
start = LineMonitoringTest.test_lines_loop.__code__.co_firstlineno
527-
self.assertEqual(events, [start+7, 21, 22, 22, 21, start+8])
528+
self.assertEqual(events, [start+7, 22, 23, 23, 22, start+8])
528529
finally:
529530
sys.monitoring.set_events(TEST_TOOL, 0)
530531
sys.monitoring.register_callback(TEST_TOOL, E.LINE, None)
@@ -546,7 +547,7 @@ def test_lines_two(self):
546547
sys.monitoring.register_callback(TEST_TOOL, E.LINE, None)
547548
sys.monitoring.register_callback(TEST_TOOL2, E.LINE, None)
548549
start = LineMonitoringTest.test_lines_two.__code__.co_firstlineno
549-
expected = [start+10, 14, start+11]
550+
expected = [start+10, 15, start+11]
550551
self.assertEqual(events, expected)
551552
self.assertEqual(events2, expected)
552553
finally:
@@ -1082,6 +1083,99 @@ def func():
10821083
('line', 'check_events', 11)])
10831084

10841085

1086+
class TestLoadSuperAttr(CheckEvents):
1087+
def _super_method_call(self, optimized=False):
1088+
assignment = "x = 1" if optimized else "super = super"
1089+
codestr = textwrap.dedent(f"""
1090+
{assignment}
1091+
class A:
1092+
def method(self, x):
1093+
return x
1094+
1095+
class B(A):
1096+
def method(self, x):
1097+
return super(
1098+
).method(
1099+
x
1100+
)
1101+
1102+
b = B()
1103+
def f():
1104+
return b.method(1)
1105+
""")
1106+
d = {}
1107+
exec(codestr, d, d)
1108+
expected = [
1109+
('line', 'check_events', 10),
1110+
('call', 'f', sys.monitoring.MISSING),
1111+
('line', 'f', 1),
1112+
('call', 'method', d["b"]),
1113+
('line', 'method', 1),
1114+
('call', 'super', sys.monitoring.MISSING),
1115+
('C return', 'super', sys.monitoring.MISSING),
1116+
('line', 'method', 2),
1117+
('line', 'method', 3),
1118+
('line', 'method', 2),
1119+
('call', 'method', 1),
1120+
('line', 'method', 1),
1121+
('line', 'method', 1),
1122+
('line', 'check_events', 11),
1123+
('call', 'set_events', 2),
1124+
]
1125+
return d["f"], expected
1126+
1127+
def test_method_call(self):
1128+
nonopt_func, nonopt_expected = self._super_method_call(optimized=False)
1129+
opt_func, opt_expected = self._super_method_call(optimized=True)
1130+
1131+
recorders = CallRecorder, LineRecorder, CRaiseRecorder, CReturnRecorder
1132+
1133+
self.check_events(nonopt_func, recorders=recorders, expected=nonopt_expected)
1134+
self.check_events(opt_func, recorders=recorders, expected=opt_expected)
1135+
1136+
def _super_attr(self, optimized=False):
1137+
assignment = "x = 1" if optimized else "super = super"
1138+
codestr = textwrap.dedent(f"""
1139+
{assignment}
1140+
class A:
1141+
x = 1
1142+
1143+
class B(A):
1144+
def method(self):
1145+
return super(
1146+
).x
1147+
1148+
b = B()
1149+
def f():
1150+
return b.method()
1151+
""")
1152+
d = {}
1153+
exec(codestr, d, d)
1154+
expected = [
1155+
('line', 'check_events', 10),
1156+
('call', 'f', sys.monitoring.MISSING),
1157+
('line', 'f', 1),
1158+
('call', 'method', d["b"]),
1159+
('line', 'method', 1),
1160+
('call', 'super', sys.monitoring.MISSING),
1161+
('C return', 'super', sys.monitoring.MISSING),
1162+
('line', 'method', 2),
1163+
('line', 'method', 1),
1164+
('line', 'check_events', 11),
1165+
('call', 'set_events', 2)
1166+
]
1167+
return d["f"], expected
1168+
1169+
def test_attr(self):
1170+
nonopt_func, nonopt_expected = self._super_attr(optimized=False)
1171+
opt_func, opt_expected = self._super_attr(optimized=True)
1172+
1173+
recorders = CallRecorder, LineRecorder, CRaiseRecorder, CReturnRecorder
1174+
1175+
self.check_events(nonopt_func, recorders=recorders, expected=nonopt_expected)
1176+
self.check_events(opt_func, recorders=recorders, expected=opt_expected)
1177+
1178+
10851179
class TestSetGetEvents(MonitoringTestBase, unittest.TestCase):
10861180

10871181
def test_global(self):

Python/bytecodes.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,14 @@ dummy_func(
15541554
PREDICT(JUMP_BACKWARD);
15551555
}
15561556

1557+
inst(INSTRUMENTED_LOAD_SUPER_ATTR, (unused/9, unused, unused, unused -- unused if (oparg & 1), unused)) {
1558+
_PySuperAttrCache *cache = (_PySuperAttrCache *)next_instr;
1559+
// cancel out the decrement that will happen in LOAD_SUPER_ATTR; we
1560+
// don't want to specialize instrumented instructions
1561+
INCREMENT_ADAPTIVE_COUNTER(cache->counter);
1562+
GO_TO_INSTRUCTION(LOAD_SUPER_ATTR);
1563+
}
1564+
15571565
family(load_super_attr, INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR) = {
15581566
LOAD_SUPER_ATTR,
15591567
LOAD_SUPER_ATTR_METHOD,
@@ -1573,6 +1581,14 @@ dummy_func(
15731581
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
15741582
#endif /* ENABLE_SPECIALIZATION */
15751583

1584+
if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) {
1585+
PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING;
1586+
int err = _Py_call_instrumentation_2args(
1587+
tstate, PY_MONITORING_EVENT_CALL,
1588+
frame, next_instr-1, global_super, arg);
1589+
ERROR_IF(err, error);
1590+
}
1591+
15761592
// we make no attempt to optimize here; specializations should
15771593
// handle any case whose performance we care about
15781594
PyObject *stack[] = {class, self};
@@ -1581,6 +1597,22 @@ dummy_func(
15811597
ERROR_IF(super == NULL, error);
15821598
res = PyObject_GetAttr(super, name);
15831599
Py_DECREF(super);
1600+
if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) {
1601+
PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING;
1602+
if (res == NULL) {
1603+
_Py_call_instrumentation_exc2(
1604+
tstate, PY_MONITORING_EVENT_C_RAISE,
1605+
frame, next_instr-1, global_super, arg);
1606+
}
1607+
else {
1608+
int err = _Py_call_instrumentation_2args(
1609+
tstate, PY_MONITORING_EVENT_C_RETURN,
1610+
frame, next_instr-1, global_super, arg);
1611+
if (err < 0) {
1612+
Py_CLEAR(res);
1613+
}
1614+
}
1615+
}
15841616
ERROR_IF(res == NULL, error);
15851617
}
15861618

Python/compile.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4411,6 +4411,8 @@ maybe_optimize_method_call(struct compiler *c, expr_ty e)
44114411
int opcode = asdl_seq_LEN(meth->v.Attribute.value->v.Call.args) ?
44124412
LOAD_SUPER_METHOD : LOAD_ZERO_SUPER_METHOD;
44134413
ADDOP_NAME(c, loc, opcode, meth->v.Attribute.attr, names);
4414+
loc = update_start_location_to_match_attr(c, loc, meth);
4415+
ADDOP(c, loc, NOP);
44144416
} else {
44154417
VISIT(c, expr, meth->v.Attribute.value);
44164418
loc = update_start_location_to_match_attr(c, loc, meth);
@@ -5429,6 +5431,8 @@ compiler_visit_expr1(struct compiler *c, expr_ty e)
54295431
int opcode = asdl_seq_LEN(e->v.Attribute.value->v.Call.args) ?
54305432
LOAD_SUPER_ATTR : LOAD_ZERO_SUPER_ATTR;
54315433
ADDOP_NAME(c, loc, opcode, e->v.Attribute.attr, names);
5434+
loc = update_start_location_to_match_attr(c, loc, e);
5435+
ADDOP(c, loc, NOP);
54325436
return SUCCESS;
54335437
}
54345438
VISIT(c, expr, e->v.Attribute.value);

0 commit comments

Comments
 (0)
0