Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e0afed7

Browse filesBrowse files
gh-103615: Use local events for opcode tracing (GH-109472)
* Use local monitoring for opcode trace * Remove f_opcode_trace_set * Add test for setting f_trace_opcodes after settrace
1 parent 2bc01cc commit e0afed7
Copy full SHA for e0afed7

File tree

Expand file treeCollapse file tree

9 files changed

+114
-8
lines changed
Filter options
Expand file treeCollapse file tree

9 files changed

+114
-8
lines changed

‎Include/internal/pycore_ceval.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_ceval.h
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ PyAPI_FUNC(int) _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyO
2222

2323
extern int _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg);
2424

25+
extern int _PyEval_SetOpcodeTrace(PyFrameObject *f, bool enable);
26+
2527
// Helper to look up a builtin object
2628
// Export for 'array' shared extension
2729
PyAPI_FUNC(PyObject*) _PyEval_GetBuiltin(PyObject *);

‎Include/internal/pycore_instruments.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_instruments.h
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ typedef uint32_t _PyMonitoringEventSet;
6363
PyObject *_PyMonitoring_RegisterCallback(int tool_id, int event_id, PyObject *obj);
6464

6565
int _PyMonitoring_SetEvents(int tool_id, _PyMonitoringEventSet events);
66+
int _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEventSet events);
67+
int _PyMonitoring_GetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEventSet *events);
6668

6769
extern int
6870
_Py_call_instrumentation(PyThreadState *tstate, int event,

‎Include/internal/pycore_interp.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_interp.h
-1Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,6 @@ struct _is {
200200
uint32_t next_func_version;
201201

202202
_Py_GlobalMonitors monitors;
203-
bool f_opcode_trace_set;
204203
bool sys_profile_initialized;
205204
bool sys_trace_initialized;
206205
Py_ssize_t sys_profiling_threads; /* Count of threads with c_profilefunc set */

‎Lib/test/test_sys_settrace.py

Copy file name to clipboardExpand all lines: Lib/test/test_sys_settrace.py
+37Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
import asyncio
1010
from test.support import import_helper
1111
import contextlib
12+
import os
13+
import tempfile
14+
import textwrap
15+
import subprocess
1216
import warnings
1317

1418
support.requires_working_socket(module=True)
@@ -1802,6 +1806,39 @@ def compare_events(self, line_offset, events, expected_events):
18021806
def make_tracer():
18031807
return Tracer(trace_opcode_events=True)
18041808

1809+
def test_trace_opcodes_after_settrace(self):
1810+
"""Make sure setting f_trace_opcodes after starting trace works even
1811+
if it's the first time f_trace_opcodes is being set. GH-103615"""
1812+
1813+
code = textwrap.dedent("""
1814+
import sys
1815+
1816+
def opcode_trace_func(frame, event, arg):
1817+
if event == "opcode":
1818+
print("opcode trace triggered")
1819+
return opcode_trace_func
1820+
1821+
sys.settrace(opcode_trace_func)
1822+
sys._getframe().f_trace = opcode_trace_func
1823+
sys._getframe().f_trace_opcodes = True
1824+
a = 1
1825+
""")
1826+
1827+
# We can't use context manager because Windows can't execute a file while
1828+
# it's being written
1829+
tmp = tempfile.NamedTemporaryFile(delete=False, suffix='.py')
1830+
tmp.write(code.encode('utf-8'))
1831+
tmp.close()
1832+
try:
1833+
p = subprocess.Popen([sys.executable, tmp.name], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1834+
p.wait()
1835+
out = p.stdout.read()
1836+
finally:
1837+
os.remove(tmp.name)
1838+
p.stdout.close()
1839+
p.stderr.close()
1840+
self.assertIn(b"opcode trace triggered", out)
1841+
18051842

18061843
class RaisingTraceFuncTestCase(unittest.TestCase):
18071844
def setUp(self):
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Use local events for opcode tracing

‎Objects/frameobject.c

Copy file name to clipboardExpand all lines: Objects/frameobject.c
+7-1Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,13 @@ frame_settrace_opcodes(PyFrameObject *f, PyObject* value, void *Py_UNUSED(ignore
127127
}
128128
if (value == Py_True) {
129129
f->f_trace_opcodes = 1;
130-
_PyInterpreterState_GET()->f_opcode_trace_set = true;
130+
if (f->f_trace) {
131+
return _PyEval_SetOpcodeTrace(f, true);
132+
}
131133
}
132134
else {
133135
f->f_trace_opcodes = 0;
136+
return _PyEval_SetOpcodeTrace(f, false);
134137
}
135138
return 0;
136139
}
@@ -842,6 +845,9 @@ frame_settrace(PyFrameObject *f, PyObject* v, void *closure)
842845
}
843846
if (v != f->f_trace) {
844847
Py_XSETREF(f->f_trace, Py_XNewRef(v));
848+
if (v != NULL && f->f_trace_opcodes) {
849+
return _PyEval_SetOpcodeTrace(f, true);
850+
}
845851
}
846852
return 0;
847853
}

‎Python/instrumentation.c

Copy file name to clipboardExpand all lines: Python/instrumentation.c
+17Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1833,6 +1833,23 @@ _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEvent
18331833
return 0;
18341834
}
18351835

1836+
int
1837+
_PyMonitoring_GetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEventSet *events)
1838+
{
1839+
assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS);
1840+
PyInterpreterState *interp = _PyInterpreterState_GET();
1841+
if (check_tool(interp, tool_id)) {
1842+
return -1;
1843+
}
1844+
if (code->_co_monitoring == NULL) {
1845+
*events = 0;
1846+
return 0;
1847+
}
1848+
_Py_LocalMonitors *local = &code->_co_monitoring->local_monitors;
1849+
*events = get_local_events(local, tool_id);
1850+
return 0;
1851+
}
1852+
18361853
/*[clinic input]
18371854
module monitoring
18381855
[clinic start generated code]*/

‎Python/legacy_tracing.c

Copy file name to clipboardExpand all lines: Python/legacy_tracing.c
+48-4Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,35 @@ sys_profile_call_or_return(
117117
Py_RETURN_NONE;
118118
}
119119

120+
int
121+
_PyEval_SetOpcodeTrace(
122+
PyFrameObject *frame,
123+
bool enable
124+
) {
125+
assert(frame != NULL);
126+
assert(PyCode_Check(frame->f_frame->f_executable));
127+
128+
PyCodeObject *code = (PyCodeObject *)frame->f_frame->f_executable;
129+
_PyMonitoringEventSet events = 0;
130+
131+
if (_PyMonitoring_GetLocalEvents(code, PY_MONITORING_SYS_TRACE_ID, &events) < 0) {
132+
return -1;
133+
}
134+
135+
if (enable) {
136+
if (events & (1 << PY_MONITORING_EVENT_INSTRUCTION)) {
137+
return 0;
138+
}
139+
events |= (1 << PY_MONITORING_EVENT_INSTRUCTION);
140+
} else {
141+
if (!(events & (1 << PY_MONITORING_EVENT_INSTRUCTION))) {
142+
return 0;
143+
}
144+
events &= (~(1 << PY_MONITORING_EVENT_INSTRUCTION));
145+
}
146+
return _PyMonitoring_SetLocalEvents(code, PY_MONITORING_SYS_TRACE_ID, events);
147+
}
148+
120149
static PyObject *
121150
call_trace_func(_PyLegacyEventHandler *self, PyObject *arg)
122151
{
@@ -130,6 +159,12 @@ call_trace_func(_PyLegacyEventHandler *self, PyObject *arg)
130159
"Missing frame when calling trace function.");
131160
return NULL;
132161
}
162+
if (frame->f_trace_opcodes) {
163+
if (_PyEval_SetOpcodeTrace(frame, true) != 0) {
164+
return NULL;
165+
}
166+
}
167+
133168
Py_INCREF(frame);
134169
int err = tstate->c_tracefunc(tstate->c_traceobj, frame, self->event, arg);
135170
Py_DECREF(frame);
@@ -230,11 +265,14 @@ sys_trace_instruction_func(
230265
"Missing frame when calling trace function.");
231266
return NULL;
232267
}
233-
if (!frame->f_trace_opcodes) {
268+
PyThreadState *tstate = _PyThreadState_GET();
269+
if (!tstate->c_tracefunc || !frame->f_trace_opcodes) {
270+
if (_PyEval_SetOpcodeTrace(frame, false) != 0) {
271+
return NULL;
272+
}
234273
Py_RETURN_NONE;
235274
}
236275
Py_INCREF(frame);
237-
PyThreadState *tstate = _PyThreadState_GET();
238276
int err = tstate->c_tracefunc(tstate->c_traceobj, frame, self->event, Py_None);
239277
frame->f_lineno = 0;
240278
Py_DECREF(frame);
@@ -531,9 +569,15 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
531569
(1 << PY_MONITORING_EVENT_PY_UNWIND) | (1 << PY_MONITORING_EVENT_PY_THROW) |
532570
(1 << PY_MONITORING_EVENT_STOP_ITERATION) |
533571
(1 << PY_MONITORING_EVENT_EXCEPTION_HANDLED);
534-
if (tstate->interp->f_opcode_trace_set) {
535-
events |= (1 << PY_MONITORING_EVENT_INSTRUCTION);
572+
573+
PyFrameObject* frame = PyEval_GetFrame();
574+
if (frame->f_trace_opcodes) {
575+
int ret = _PyEval_SetOpcodeTrace(frame, true);
576+
if (ret != 0) {
577+
return ret;
578+
}
536579
}
537580
}
581+
538582
return _PyMonitoring_SetEvents(PY_MONITORING_SYS_TRACE_ID, events);
539583
}

‎Python/pystate.c

Copy file name to clipboardExpand all lines: Python/pystate.c
-2Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,6 @@ init_interpreter(PyInterpreterState *interp,
708708
/* Fix the self-referential, statically initialized fields. */
709709
interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp);
710710
}
711-
interp->f_opcode_trace_set = false;
712711

713712
interp->_initialized = 1;
714713
return _PyStatus_OK();
@@ -958,7 +957,6 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
958957
interp->code_watchers[i] = NULL;
959958
}
960959
interp->active_code_watchers = 0;
961-
interp->f_opcode_trace_set = false;
962960
// XXX Once we have one allocator per interpreter (i.e.
963961
// per-interpreter GC) we must ensure that all of the interpreter's
964962
// objects have been cleaned up at the point.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.