mirror of
https://github.com/python/cpython.git
synced 2024-12-05 07:43:50 +08:00
ea2c001650
This is the implementation of PEP683 Motivation: The PR introduces the ability to immortalize instances in CPython which bypasses reference counting. Tagging objects as immortal allows up to skip certain operations when we know that the object will be around for the entire execution of the runtime. Note that this by itself will bring a performance regression to the runtime due to the extra reference count checks. However, this brings the ability of having truly immutable objects that are useful in other contexts such as immutable data sharing between sub-interpreters.
529 lines
17 KiB
C
529 lines
17 KiB
C
/* Support for legacy tracing on top of PEP 669 instrumentation
|
|
* Provides callables to forward PEP 669 events to legacy events.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include "Python.h"
|
|
#include "pycore_ceval.h"
|
|
#include "pycore_object.h"
|
|
#include "pycore_sysmodule.h"
|
|
|
|
typedef struct _PyLegacyEventHandler {
|
|
PyObject_HEAD
|
|
vectorcallfunc vectorcall;
|
|
int event;
|
|
} _PyLegacyEventHandler;
|
|
|
|
/* The Py_tracefunc function expects the following arguments:
|
|
* obj: the trace object (PyObject *)
|
|
* frame: the current frame (PyFrameObject *)
|
|
* kind: the kind of event, see PyTrace_XXX #defines (int)
|
|
* arg: The arg (a PyObject *)
|
|
*/
|
|
|
|
static PyObject *
|
|
call_profile_func(_PyLegacyEventHandler *self, PyObject *arg)
|
|
{
|
|
PyThreadState *tstate = _PyThreadState_GET();
|
|
if (tstate->c_profilefunc == NULL) {
|
|
Py_RETURN_NONE;
|
|
}
|
|
PyFrameObject *frame = PyEval_GetFrame();
|
|
if (frame == NULL) {
|
|
PyErr_SetString(PyExc_SystemError,
|
|
"Missing frame when calling profile function.");
|
|
return NULL;
|
|
}
|
|
Py_INCREF(frame);
|
|
int err = tstate->c_profilefunc(tstate->c_profileobj, frame, self->event, arg);
|
|
Py_DECREF(frame);
|
|
if (err) {
|
|
return NULL;
|
|
}
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *
|
|
sys_profile_func2(
|
|
_PyLegacyEventHandler *self, PyObject *const *args,
|
|
size_t nargsf, PyObject *kwnames
|
|
) {
|
|
assert(kwnames == NULL);
|
|
assert(PyVectorcall_NARGS(nargsf) == 2);
|
|
return call_profile_func(self, Py_None);
|
|
}
|
|
|
|
static PyObject *
|
|
sys_profile_func3(
|
|
_PyLegacyEventHandler *self, PyObject *const *args,
|
|
size_t nargsf, PyObject *kwnames
|
|
) {
|
|
assert(kwnames == NULL);
|
|
assert(PyVectorcall_NARGS(nargsf) == 3);
|
|
return call_profile_func(self, args[2]);
|
|
}
|
|
|
|
static PyObject *
|
|
sys_profile_call_or_return(
|
|
_PyLegacyEventHandler *self, PyObject *const *args,
|
|
size_t nargsf, PyObject *kwnames
|
|
) {
|
|
assert(kwnames == NULL);
|
|
assert(PyVectorcall_NARGS(nargsf) == 4);
|
|
PyObject *callable = args[2];
|
|
if (PyCFunction_Check(callable)) {
|
|
return call_profile_func(self, callable);
|
|
}
|
|
if (Py_TYPE(callable) == &PyMethodDescr_Type) {
|
|
PyObject *self_arg = args[3];
|
|
/* For backwards compatibility need to
|
|
* convert to builtin method */
|
|
|
|
/* If no arg, skip */
|
|
if (self_arg == &_PyInstrumentation_MISSING) {
|
|
Py_RETURN_NONE;
|
|
}
|
|
PyObject *meth = Py_TYPE(callable)->tp_descr_get(
|
|
callable, self_arg, (PyObject*)Py_TYPE(self_arg));
|
|
if (meth == NULL) {
|
|
return NULL;
|
|
}
|
|
PyObject *res = call_profile_func(self, meth);
|
|
Py_DECREF(meth);
|
|
return res;
|
|
}
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *
|
|
call_trace_func(_PyLegacyEventHandler *self, PyObject *arg)
|
|
{
|
|
PyThreadState *tstate = _PyThreadState_GET();
|
|
if (tstate->c_tracefunc == NULL) {
|
|
Py_RETURN_NONE;
|
|
}
|
|
PyFrameObject *frame = PyEval_GetFrame();
|
|
if (frame == NULL) {
|
|
PyErr_SetString(PyExc_SystemError,
|
|
"Missing frame when calling trace function.");
|
|
return NULL;
|
|
}
|
|
Py_INCREF(frame);
|
|
int err = tstate->c_tracefunc(tstate->c_traceobj, frame, self->event, arg);
|
|
Py_DECREF(frame);
|
|
if (err) {
|
|
return NULL;
|
|
}
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *
|
|
sys_trace_exception_func(
|
|
_PyLegacyEventHandler *self, PyObject *const *args,
|
|
size_t nargsf, PyObject *kwnames
|
|
) {
|
|
assert(kwnames == NULL);
|
|
assert(PyVectorcall_NARGS(nargsf) == 3);
|
|
PyObject *exc = args[2];
|
|
assert(PyExceptionInstance_Check(exc));
|
|
PyObject *type = (PyObject *)Py_TYPE(exc);
|
|
PyObject *tb = PyException_GetTraceback(exc);
|
|
if (tb == NULL) {
|
|
tb = Py_NewRef(Py_None);
|
|
}
|
|
PyObject *tuple = PyTuple_Pack(3, type, exc, tb);
|
|
Py_DECREF(tb);
|
|
if (tuple == NULL) {
|
|
return NULL;
|
|
}
|
|
PyObject *res = call_trace_func(self, tuple);
|
|
Py_DECREF(tuple);
|
|
return res;
|
|
}
|
|
|
|
static PyObject *
|
|
sys_trace_func2(
|
|
_PyLegacyEventHandler *self, PyObject *const *args,
|
|
size_t nargsf, PyObject *kwnames
|
|
) {
|
|
assert(kwnames == NULL);
|
|
assert(PyVectorcall_NARGS(nargsf) == 2);
|
|
return call_trace_func(self, Py_None);
|
|
}
|
|
|
|
static PyObject *
|
|
sys_trace_return(
|
|
_PyLegacyEventHandler *self, PyObject *const *args,
|
|
size_t nargsf, PyObject *kwnames
|
|
) {
|
|
assert(!PyErr_Occurred());
|
|
assert(kwnames == NULL);
|
|
assert(PyVectorcall_NARGS(nargsf) == 3);
|
|
assert(PyCode_Check(args[0]));
|
|
PyObject *val = args[2];
|
|
PyObject *res = call_trace_func(self, val);
|
|
return res;
|
|
}
|
|
|
|
static PyObject *
|
|
sys_trace_yield(
|
|
_PyLegacyEventHandler *self, PyObject *const *args,
|
|
size_t nargsf, PyObject *kwnames
|
|
) {
|
|
assert(kwnames == NULL);
|
|
assert(PyVectorcall_NARGS(nargsf) == 3);
|
|
return call_trace_func(self, args[2]);
|
|
}
|
|
|
|
static PyObject *
|
|
sys_trace_instruction_func(
|
|
_PyLegacyEventHandler *self, PyObject *const *args,
|
|
size_t nargsf, PyObject *kwnames
|
|
) {
|
|
assert(kwnames == NULL);
|
|
assert(PyVectorcall_NARGS(nargsf) == 2);
|
|
PyFrameObject *frame = PyEval_GetFrame();
|
|
if (frame == NULL) {
|
|
PyErr_SetString(PyExc_SystemError,
|
|
"Missing frame when calling trace function.");
|
|
return NULL;
|
|
}
|
|
if (!frame->f_trace_opcodes) {
|
|
Py_RETURN_NONE;
|
|
}
|
|
Py_INCREF(frame);
|
|
PyThreadState *tstate = _PyThreadState_GET();
|
|
int err = tstate->c_tracefunc(tstate->c_traceobj, frame, self->event, Py_None);
|
|
frame->f_lineno = 0;
|
|
Py_DECREF(frame);
|
|
if (err) {
|
|
return NULL;
|
|
}
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *
|
|
trace_line(
|
|
PyThreadState *tstate, _PyLegacyEventHandler *self,
|
|
PyFrameObject *frame, int line
|
|
) {
|
|
if (!frame->f_trace_lines) {
|
|
Py_RETURN_NONE;
|
|
}
|
|
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);
|
|
frame->f_lineno = 0;
|
|
Py_DECREF(frame);
|
|
if (err) {
|
|
return NULL;
|
|
}
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *
|
|
sys_trace_line_func(
|
|
_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) == 2);
|
|
int line = _PyLong_AsInt(args[1]);
|
|
assert(line >= 0);
|
|
PyFrameObject *frame = PyEval_GetFrame();
|
|
if (frame == NULL) {
|
|
PyErr_SetString(PyExc_SystemError,
|
|
"Missing frame when calling trace function.");
|
|
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);
|
|
}
|
|
|
|
|
|
static PyObject *
|
|
sys_trace_jump_func(
|
|
_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);
|
|
int from = _PyLong_AsInt(args[1])/sizeof(_Py_CODEUNIT);
|
|
assert(from >= 0);
|
|
int to = _PyLong_AsInt(args[2])/sizeof(_Py_CODEUNIT);
|
|
assert(to >= 0);
|
|
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 */
|
|
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",
|
|
sizeof(_PyLegacyEventHandler),
|
|
.tp_dealloc = (destructor)PyObject_Free,
|
|
.tp_vectorcall_offset = offsetof(_PyLegacyEventHandler, vectorcall),
|
|
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
|
|
Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_DISALLOW_INSTANTIATION,
|
|
.tp_call = PyVectorcall_Call,
|
|
};
|
|
|
|
static int
|
|
set_callbacks(int tool, vectorcallfunc vectorcall, int legacy_event, int event1, int event2)
|
|
{
|
|
_PyLegacyEventHandler *callback =
|
|
PyObject_NEW(_PyLegacyEventHandler, &_PyLegacyEventHandler_Type);
|
|
if (callback == NULL) {
|
|
return -1;
|
|
}
|
|
callback->vectorcall = vectorcall;
|
|
callback->event = legacy_event;
|
|
Py_XDECREF(_PyMonitoring_RegisterCallback(tool, event1, (PyObject *)callback));
|
|
if (event2 >= 0) {
|
|
Py_XDECREF(_PyMonitoring_RegisterCallback(tool, event2, (PyObject *)callback));
|
|
}
|
|
Py_DECREF(callback);
|
|
return 0;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
/* Ensure that tstate is valid: sanity check for PyEval_AcquireThread() and
|
|
PyEval_RestoreThread(). Detect if tstate memory was freed. It can happen
|
|
when a thread continues to run after Python finalization, especially
|
|
daemon threads. */
|
|
static int
|
|
is_tstate_valid(PyThreadState *tstate)
|
|
{
|
|
assert(!_PyMem_IsPtrFreed(tstate));
|
|
assert(!_PyMem_IsPtrFreed(tstate->interp));
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
_PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
|
{
|
|
assert(is_tstate_valid(tstate));
|
|
/* The caller must hold the GIL */
|
|
assert(PyGILState_Check());
|
|
|
|
/* Call _PySys_Audit() in the context of the current thread state,
|
|
even if tstate is not the current thread state. */
|
|
PyThreadState *current_tstate = _PyThreadState_GET();
|
|
if (_PySys_Audit(current_tstate, "sys.setprofile", NULL) < 0) {
|
|
return -1;
|
|
}
|
|
/* Setup PEP 669 monitoring callbacks and events. */
|
|
if (!tstate->interp->sys_profile_initialized) {
|
|
tstate->interp->sys_profile_initialized = true;
|
|
if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID,
|
|
(vectorcallfunc)sys_profile_func2, PyTrace_CALL,
|
|
PY_MONITORING_EVENT_PY_START, PY_MONITORING_EVENT_PY_RESUME)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID,
|
|
(vectorcallfunc)sys_profile_func3, PyTrace_RETURN,
|
|
PY_MONITORING_EVENT_PY_RETURN, PY_MONITORING_EVENT_PY_YIELD)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID,
|
|
(vectorcallfunc)sys_profile_func2, PyTrace_RETURN,
|
|
PY_MONITORING_EVENT_PY_UNWIND, -1)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID,
|
|
(vectorcallfunc)sys_profile_call_or_return, PyTrace_C_CALL,
|
|
PY_MONITORING_EVENT_CALL, -1)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID,
|
|
(vectorcallfunc)sys_profile_call_or_return, PyTrace_C_RETURN,
|
|
PY_MONITORING_EVENT_C_RETURN, -1)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID,
|
|
(vectorcallfunc)sys_profile_call_or_return, PyTrace_C_EXCEPTION,
|
|
PY_MONITORING_EVENT_C_RAISE, -1)) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int delta = (func != NULL) - (tstate->c_profilefunc != NULL);
|
|
tstate->c_profilefunc = func;
|
|
PyObject *old_profileobj = tstate->c_profileobj;
|
|
tstate->c_profileobj = Py_XNewRef(arg);
|
|
Py_XDECREF(old_profileobj);
|
|
tstate->interp->sys_profiling_threads += delta;
|
|
assert(tstate->interp->sys_profiling_threads >= 0);
|
|
|
|
uint32_t events = 0;
|
|
if (tstate->interp->sys_profiling_threads) {
|
|
events =
|
|
(1 << PY_MONITORING_EVENT_PY_START) | (1 << PY_MONITORING_EVENT_PY_RESUME) |
|
|
(1 << PY_MONITORING_EVENT_PY_RETURN) | (1 << PY_MONITORING_EVENT_PY_YIELD) |
|
|
(1 << PY_MONITORING_EVENT_CALL) | (1 << PY_MONITORING_EVENT_PY_UNWIND);
|
|
}
|
|
return _PyMonitoring_SetEvents(PY_MONITORING_SYS_PROFILE_ID, events);
|
|
}
|
|
|
|
int
|
|
_PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
|
{
|
|
assert(is_tstate_valid(tstate));
|
|
/* The caller must hold the GIL */
|
|
assert(PyGILState_Check());
|
|
|
|
/* Call _PySys_Audit() in the context of the current thread state,
|
|
even if tstate is not the current thread state. */
|
|
PyThreadState *current_tstate = _PyThreadState_GET();
|
|
if (_PySys_Audit(current_tstate, "sys.settrace", NULL) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
assert(tstate->interp->sys_tracing_threads >= 0);
|
|
/* Setup PEP 669 monitoring callbacks and events. */
|
|
if (!tstate->interp->sys_trace_initialized) {
|
|
tstate->interp->sys_trace_initialized = true;
|
|
if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
|
|
(vectorcallfunc)sys_trace_func2, PyTrace_CALL,
|
|
PY_MONITORING_EVENT_PY_START, PY_MONITORING_EVENT_PY_RESUME)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
|
|
(vectorcallfunc)sys_trace_func2, PyTrace_CALL,
|
|
PY_MONITORING_EVENT_PY_THROW, -1)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
|
|
(vectorcallfunc)sys_trace_return, PyTrace_RETURN,
|
|
PY_MONITORING_EVENT_PY_RETURN, -1)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
|
|
(vectorcallfunc)sys_trace_yield, PyTrace_RETURN,
|
|
PY_MONITORING_EVENT_PY_YIELD, -1)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
|
|
(vectorcallfunc)sys_trace_exception_func, PyTrace_EXCEPTION,
|
|
PY_MONITORING_EVENT_RAISE, PY_MONITORING_EVENT_STOP_ITERATION)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
|
|
(vectorcallfunc)sys_trace_line_func, PyTrace_LINE,
|
|
PY_MONITORING_EVENT_LINE, -1)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
|
|
(vectorcallfunc)sys_trace_func2, PyTrace_RETURN,
|
|
PY_MONITORING_EVENT_PY_UNWIND, -1)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
|
|
(vectorcallfunc)sys_trace_jump_func, PyTrace_LINE,
|
|
PY_MONITORING_EVENT_JUMP, PY_MONITORING_EVENT_BRANCH)) {
|
|
return -1;
|
|
}
|
|
if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
|
|
(vectorcallfunc)sys_trace_instruction_func, PyTrace_OPCODE,
|
|
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);
|
|
tstate->c_tracefunc = func;
|
|
PyObject *old_traceobj = tstate->c_traceobj;
|
|
tstate->c_traceobj = Py_XNewRef(arg);
|
|
Py_XDECREF(old_traceobj);
|
|
tstate->interp->sys_tracing_threads += delta;
|
|
assert(tstate->interp->sys_tracing_threads >= 0);
|
|
|
|
uint32_t events = 0;
|
|
if (tstate->interp->sys_tracing_threads) {
|
|
events =
|
|
(1 << PY_MONITORING_EVENT_PY_START) | (1 << PY_MONITORING_EVENT_PY_RESUME) |
|
|
(1 << PY_MONITORING_EVENT_PY_RETURN) | (1 << PY_MONITORING_EVENT_PY_YIELD) |
|
|
(1 << PY_MONITORING_EVENT_RAISE) | (1 << PY_MONITORING_EVENT_LINE) |
|
|
(1 << PY_MONITORING_EVENT_JUMP) | (1 << PY_MONITORING_EVENT_BRANCH) |
|
|
(1 << PY_MONITORING_EVENT_PY_UNWIND) | (1 << PY_MONITORING_EVENT_PY_THROW) |
|
|
(1 << PY_MONITORING_EVENT_STOP_ITERATION) |
|
|
(1 << PY_MONITORING_EVENT_EXCEPTION_HANDLED);
|
|
if (tstate->interp->f_opcode_trace_set) {
|
|
events |= (1 << PY_MONITORING_EVENT_INSTRUCTION);
|
|
}
|
|
}
|
|
return _PyMonitoring_SetEvents(PY_MONITORING_SYS_TRACE_ID, events);
|
|
}
|