mirror of
https://github.com/python/cpython.git
synced 2025-08-22 09:45:06 +00:00
GH-103082: Filter LINE events in VM, to simplify tool implementation. (GH-104387)
When monitoring LINE events, instrument all instructions that can have a predecessor on a different line. Then check that the a new line has been hit in the instrumentation code. This brings the behavior closer to that of 3.11, simplifying implementation and porting of tools.
This commit is contained in:
parent
19ee53d52e
commit
45f5aa8fc7
16 changed files with 255 additions and 161 deletions
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <stddef.h>
|
||||
#include "Python.h"
|
||||
#include "opcode.h"
|
||||
#include "pycore_ceval.h"
|
||||
#include "pycore_object.h"
|
||||
#include "pycore_sysmodule.h"
|
||||
|
@ -213,7 +214,6 @@ trace_line(
|
|||
if (line < 0) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
frame ->f_last_traced_line = line;
|
||||
Py_INCREF(frame);
|
||||
frame->f_lineno = line;
|
||||
int err = tstate->c_tracefunc(tstate->c_traceobj, frame, self->event, Py_None);
|
||||
|
@ -245,14 +245,12 @@ sys_trace_line_func(
|
|||
return NULL;
|
||||
}
|
||||
assert(args[0] == (PyObject *)frame->f_frame->f_code);
|
||||
if (frame ->f_last_traced_line == line) {
|
||||
/* Already traced this line */
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return trace_line(tstate, self, frame, line);
|
||||
}
|
||||
|
||||
|
||||
/* sys.settrace generates line events for all backward
|
||||
* edges, even if on the same line.
|
||||
* Handle that case here */
|
||||
static PyObject *
|
||||
sys_trace_jump_func(
|
||||
_PyLegacyEventHandler *self, PyObject *const *args,
|
||||
|
@ -268,61 +266,33 @@ sys_trace_jump_func(
|
|||
assert(from >= 0);
|
||||
int to = _PyLong_AsInt(args[2])/sizeof(_Py_CODEUNIT);
|
||||
assert(to >= 0);
|
||||
if (to > from) {
|
||||
/* Forward jump */
|
||||
return &_PyInstrumentation_DISABLE;
|
||||
}
|
||||
PyCodeObject *code = (PyCodeObject *)args[0];
|
||||
assert(PyCode_Check(code));
|
||||
/* We can call _Py_Instrumentation_GetLine because we always set
|
||||
* line events for tracing */
|
||||
int to_line = _Py_Instrumentation_GetLine(code, to);
|
||||
int from_line = _Py_Instrumentation_GetLine(code, from);
|
||||
if (to_line != from_line) {
|
||||
/* Will be handled by target INSTRUMENTED_LINE */
|
||||
return &_PyInstrumentation_DISABLE;
|
||||
}
|
||||
PyFrameObject *frame = PyEval_GetFrame();
|
||||
if (frame == NULL) {
|
||||
PyErr_SetString(PyExc_SystemError,
|
||||
"Missing frame when calling trace function.");
|
||||
return NULL;
|
||||
}
|
||||
if (!frame->f_trace_lines) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
PyCodeObject *code = (PyCodeObject *)args[0];
|
||||
assert(PyCode_Check(code));
|
||||
assert(code == frame->f_frame->f_code);
|
||||
/* We can call _Py_Instrumentation_GetLine because we always set
|
||||
* line events for tracing */
|
||||
int to_line = _Py_Instrumentation_GetLine(code, to);
|
||||
/* Backward jump: Always generate event
|
||||
* Forward jump: Only generate event if jumping to different line. */
|
||||
if (to > from && frame->f_last_traced_line == to_line) {
|
||||
/* Already traced this line */
|
||||
if (!frame->f_trace_lines) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return trace_line(tstate, self, frame, to_line);
|
||||
}
|
||||
|
||||
/* We don't care about the exception here,
|
||||
* we just treat it as a possible new line
|
||||
*/
|
||||
static PyObject *
|
||||
sys_trace_exception_handled(
|
||||
_PyLegacyEventHandler *self, PyObject *const *args,
|
||||
size_t nargsf, PyObject *kwnames
|
||||
) {
|
||||
assert(kwnames == NULL);
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
if (tstate->c_tracefunc == NULL) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
assert(PyVectorcall_NARGS(nargsf) == 3);
|
||||
PyFrameObject *frame = PyEval_GetFrame();
|
||||
PyCodeObject *code = (PyCodeObject *)args[0];
|
||||
assert(PyCode_Check(code));
|
||||
assert(code == frame->f_frame->f_code);
|
||||
assert(PyLong_Check(args[1]));
|
||||
int offset = _PyLong_AsInt(args[1])/sizeof(_Py_CODEUNIT);
|
||||
/* We can call _Py_Instrumentation_GetLine because we always set
|
||||
* line events for tracing */
|
||||
int line = _Py_Instrumentation_GetLine(code, offset);
|
||||
if (frame->f_last_traced_line == line) {
|
||||
/* Already traced this line */
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return trace_line(tstate, self, frame, line);
|
||||
}
|
||||
|
||||
|
||||
PyTypeObject _PyLegacyEventHandler_Type = {
|
||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||
"sys.legacy_event_handler",
|
||||
|
@ -487,7 +457,7 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
|||
}
|
||||
if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
|
||||
(vectorcallfunc)sys_trace_jump_func, PyTrace_LINE,
|
||||
PY_MONITORING_EVENT_JUMP, PY_MONITORING_EVENT_BRANCH)) {
|
||||
PY_MONITORING_EVENT_JUMP, -1)) {
|
||||
return -1;
|
||||
}
|
||||
if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
|
||||
|
@ -495,11 +465,6 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
|||
PY_MONITORING_EVENT_INSTRUCTION, -1)) {
|
||||
return -1;
|
||||
}
|
||||
if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
|
||||
(vectorcallfunc)sys_trace_exception_handled, PyTrace_LINE,
|
||||
PY_MONITORING_EVENT_EXCEPTION_HANDLED, -1)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int delta = (func != NULL) - (tstate->c_tracefunc != NULL);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue