summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2023-09-05 07:03:53 (GMT)
committerGitHub <noreply@github.com>2023-09-05 07:03:53 (GMT)
commit5a2a04615171899885b977d77dc379bd78bac87f (patch)
tree07b3b359d819e917cca725ca787734bad8735387
parent04a0830b00879efe057e3dfe75e9aa9c0caf1a26 (diff)
downloadcpython-5a2a04615171899885b977d77dc379bd78bac87f.zip
cpython-5a2a04615171899885b977d77dc379bd78bac87f.tar.gz
cpython-5a2a04615171899885b977d77dc379bd78bac87f.tar.bz2
GH-108390: Prevent non-local events being set with `sys.monitoring.set_local_events()` (GH-108420)
-rw-r--r--Include/cpython/code.h17
-rw-r--r--Include/internal/pycore_instruments.h2
-rw-r--r--Include/internal/pycore_interp.h2
-rw-r--r--Lib/test/test_monitoring.py30
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-08-13-17-18-22.gh-issue-108390.TkBccC.rst4
-rw-r--r--Python/ceval.c32
-rw-r--r--Python/instrumentation.c130
7 files changed, 143 insertions, 74 deletions
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 24c5ec2..45b09a1 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -8,16 +8,21 @@
extern "C" {
#endif
-
+/* Count of all local monitoring events */
+#define _PY_MONITORING_LOCAL_EVENTS 10
/* Count of all "real" monitoring events (not derived from other events) */
#define _PY_MONITORING_UNGROUPED_EVENTS 15
/* Count of all monitoring events */
#define _PY_MONITORING_EVENTS 17
-/* Table of which tools are active for each monitored event. */
-typedef struct _Py_Monitors {
+/* Tables of which tools are active for each monitored event. */
+typedef struct _Py_LocalMonitors {
+ uint8_t tools[_PY_MONITORING_LOCAL_EVENTS];
+} _Py_LocalMonitors;
+
+typedef struct _Py_GlobalMonitors {
uint8_t tools[_PY_MONITORING_UNGROUPED_EVENTS];
-} _Py_Monitors;
+} _Py_GlobalMonitors;
/* Each instruction in a code object is a fixed-width value,
* currently 2 bytes: 1-byte opcode + 1-byte oparg. The EXTENDED_ARG
@@ -88,9 +93,9 @@ typedef struct {
*/
typedef struct {
/* Monitoring specific to this code object */
- _Py_Monitors local_monitors;
+ _Py_LocalMonitors local_monitors;
/* Monitoring that is active on this code object */
- _Py_Monitors active_monitors;
+ _Py_LocalMonitors active_monitors;
/* The tools that are to be notified for events for the matching code unit */
uint8_t *tools;
/* Information to support line events */
diff --git a/Include/internal/pycore_instruments.h b/Include/internal/pycore_instruments.h
index 43214ae..fad475c 100644
--- a/Include/internal/pycore_instruments.h
+++ b/Include/internal/pycore_instruments.h
@@ -29,7 +29,7 @@ extern "C" {
#define PY_MONITORING_EVENT_STOP_ITERATION 9
#define PY_MONITORING_IS_INSTRUMENTED_EVENT(ev) \
- ((ev) <= PY_MONITORING_EVENT_STOP_ITERATION)
+ ((ev) < _PY_MONITORING_LOCAL_EVENTS)
/* Other events, mainly exceptions */
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index f171c54..e0b7a32 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -187,7 +187,7 @@ struct _is {
uint16_t optimizer_resume_threshold;
uint16_t optimizer_backedge_threshold;
- _Py_Monitors monitors;
+ _Py_GlobalMonitors monitors;
bool f_opcode_trace_set;
bool sys_profile_initialized;
bool sys_trace_initialized;
diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py
index 845185b..95fe8d0 100644
--- a/Lib/test/test_monitoring.py
+++ b/Lib/test/test_monitoring.py
@@ -1218,9 +1218,11 @@ class TestInstallIncrementallly(MonitoringTestBase, unittest.TestCase):
self.check_events(self.func2,
recorders = recorders, must_include = self.MUST_INCLUDE_CI)
+LOCAL_RECORDERS = CallRecorder, LineRecorder, CReturnRecorder, CRaiseRecorder
+
class TestLocalEvents(MonitoringTestBase, unittest.TestCase):
- def check_events(self, func, expected, tool=TEST_TOOL, recorders=(ExceptionRecorder,)):
+ def check_events(self, func, expected, tool=TEST_TOOL, recorders=()):
try:
self.assertEqual(sys.monitoring._all_events(), {})
event_list = []
@@ -1248,7 +1250,7 @@ class TestLocalEvents(MonitoringTestBase, unittest.TestCase):
line2 = 2
line3 = 3
- self.check_events(func1, recorders = MANY_RECORDERS, expected = [
+ self.check_events(func1, recorders = LOCAL_RECORDERS, expected = [
('line', 'func1', 1),
('line', 'func1', 2),
('line', 'func1', 3)])
@@ -1260,7 +1262,7 @@ class TestLocalEvents(MonitoringTestBase, unittest.TestCase):
[].append(2)
line3 = 3
- self.check_events(func2, recorders = MANY_RECORDERS, expected = [
+ self.check_events(func2, recorders = LOCAL_RECORDERS, expected = [
('line', 'func2', 1),
('line', 'func2', 2),
('call', 'append', [2]),
@@ -1277,15 +1279,17 @@ class TestLocalEvents(MonitoringTestBase, unittest.TestCase):
line = 5
line = 6
- self.check_events(func3, recorders = MANY_RECORDERS, expected = [
+ self.check_events(func3, recorders = LOCAL_RECORDERS, expected = [
('line', 'func3', 1),
('line', 'func3', 2),
('line', 'func3', 3),
- ('raise', KeyError),
('line', 'func3', 4),
('line', 'func3', 5),
('line', 'func3', 6)])
+ def test_set_non_local_event(self):
+ with self.assertRaises(ValueError):
+ sys.monitoring.set_local_events(TEST_TOOL, just_call.__code__, E.RAISE)
def line_from_offset(code, offset):
for start, end, line in code.co_lines():
@@ -1698,3 +1702,19 @@ class TestRegressions(MonitoringTestBase, unittest.TestCase):
self.assertEqual(caught, "inner")
finally:
sys.monitoring.set_events(TEST_TOOL, 0)
+
+ def test_108390(self):
+
+ class Foo:
+ def __init__(self, set_event):
+ if set_event:
+ sys.monitoring.set_events(TEST_TOOL, E.PY_RESUME)
+
+ def make_foo_optimized_then_set_event():
+ for i in range(100):
+ Foo(i == 99)
+
+ try:
+ make_foo_optimized_then_set_event()
+ finally:
+ sys.monitoring.set_events(TEST_TOOL, 0)
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-08-13-17-18-22.gh-issue-108390.TkBccC.rst b/Misc/NEWS.d/next/Core and Builtins/2023-08-13-17-18-22.gh-issue-108390.TkBccC.rst
new file mode 100644
index 0000000..3ed5960
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-08-13-17-18-22.gh-issue-108390.TkBccC.rst
@@ -0,0 +1,4 @@
+Raise an exception when setting a non-local event (``RAISE``, ``EXCEPTION_HANDLED``,
+etc.) in ``sys.monitoring.set_local_events``.
+
+Fixes crash when tracing in recursive calls to Python classes.
diff --git a/Python/ceval.c b/Python/ceval.c
index 6f90d8e..b02bf60 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1978,28 +1978,30 @@ do_monitor_exc(PyThreadState *tstate, _PyInterpreterFrame *frame,
return err;
}
-static inline int
-no_tools_for_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event)
+static inline bool
+no_tools_for_global_event(PyThreadState *tstate, int event)
{
+ return tstate->interp->monitors.tools[event] == 0;
+}
+
+static inline bool
+no_tools_for_local_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event)
+{
+ assert(event < _PY_MONITORING_LOCAL_EVENTS);
_PyCoMonitoringData *data = _PyFrame_GetCode(frame)->_co_monitoring;
if (data) {
- if (data->active_monitors.tools[event] == 0) {
- return 1;
- }
+ return data->active_monitors.tools[event] == 0;
}
else {
- if (tstate->interp->monitors.tools[event] == 0) {
- return 1;
- }
+ return no_tools_for_global_event(tstate, event);
}
- return 0;
}
static void
monitor_raise(PyThreadState *tstate, _PyInterpreterFrame *frame,
_Py_CODEUNIT *instr)
{
- if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_RAISE)) {
+ if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RAISE)) {
return;
}
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RAISE);
@@ -2009,7 +2011,7 @@ static void
monitor_reraise(PyThreadState *tstate, _PyInterpreterFrame *frame,
_Py_CODEUNIT *instr)
{
- if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_RERAISE)) {
+ if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RERAISE)) {
return;
}
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RERAISE);
@@ -2019,7 +2021,7 @@ static int
monitor_stop_iteration(PyThreadState *tstate, _PyInterpreterFrame *frame,
_Py_CODEUNIT *instr)
{
- if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_STOP_ITERATION)) {
+ if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_STOP_ITERATION)) {
return 0;
}
return do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_STOP_ITERATION);
@@ -2030,7 +2032,7 @@ monitor_unwind(PyThreadState *tstate,
_PyInterpreterFrame *frame,
_Py_CODEUNIT *instr)
{
- if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_PY_UNWIND)) {
+ if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_UNWIND)) {
return;
}
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_UNWIND);
@@ -2042,7 +2044,7 @@ monitor_handled(PyThreadState *tstate,
_PyInterpreterFrame *frame,
_Py_CODEUNIT *instr, PyObject *exc)
{
- if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) {
+ if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) {
return 0;
}
return _Py_call_instrumentation_arg(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED, frame, instr, exc);
@@ -2053,7 +2055,7 @@ monitor_throw(PyThreadState *tstate,
_PyInterpreterFrame *frame,
_Py_CODEUNIT *instr)
{
- if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_PY_THROW)) {
+ if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_THROW)) {
return;
}
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_THROW);
diff --git a/Python/instrumentation.c b/Python/instrumentation.c
index 3645968..9065043 100644
--- a/Python/instrumentation.c
+++ b/Python/instrumentation.c
@@ -138,9 +138,9 @@ is_instrumented(int opcode)
#ifndef NDEBUG
static inline bool
-monitors_equals(_Py_Monitors a, _Py_Monitors b)
+monitors_equals(_Py_LocalMonitors a, _Py_LocalMonitors b)
{
- for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
+ for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
if (a.tools[i] != b.tools[i]) {
return false;
}
@@ -149,42 +149,47 @@ monitors_equals(_Py_Monitors a, _Py_Monitors b)
}
#endif
-static inline _Py_Monitors
-monitors_sub(_Py_Monitors a, _Py_Monitors b)
+static inline _Py_LocalMonitors
+monitors_sub(_Py_LocalMonitors a, _Py_LocalMonitors b)
{
- _Py_Monitors res;
- for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
+ _Py_LocalMonitors res;
+ for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
res.tools[i] = a.tools[i] & ~b.tools[i];
}
return res;
}
#ifndef NDEBUG
-static inline _Py_Monitors
-monitors_and(_Py_Monitors a, _Py_Monitors b)
+static inline _Py_LocalMonitors
+monitors_and(_Py_LocalMonitors a, _Py_LocalMonitors b)
{
- _Py_Monitors res;
- for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
+ _Py_LocalMonitors res;
+ for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
res.tools[i] = a.tools[i] & b.tools[i];
}
return res;
}
#endif
-static inline _Py_Monitors
-monitors_or(_Py_Monitors a, _Py_Monitors b)
+/* The union of the *local* events in a and b.
+ * Global events like RAISE are ignored.
+ * Used for instrumentation, as only local
+ * events get instrumented.
+ */
+static inline _Py_LocalMonitors
+local_union(_Py_GlobalMonitors a, _Py_LocalMonitors b)
{
- _Py_Monitors res;
- for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
+ _Py_LocalMonitors res;
+ for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
res.tools[i] = a.tools[i] | b.tools[i];
}
return res;
}
static inline bool
-monitors_are_empty(_Py_Monitors m)
+monitors_are_empty(_Py_LocalMonitors m)
{
- for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
+ for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
if (m.tools[i]) {
return false;
}
@@ -193,9 +198,9 @@ monitors_are_empty(_Py_Monitors m)
}
static inline bool
-multiple_tools(_Py_Monitors *m)
+multiple_tools(_Py_LocalMonitors *m)
{
- for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
+ for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
if (_Py_popcount32(m->tools[i]) > 1) {
return true;
}
@@ -204,7 +209,19 @@ multiple_tools(_Py_Monitors *m)
}
static inline _PyMonitoringEventSet
-get_events(_Py_Monitors *m, int tool_id)
+get_local_events(_Py_LocalMonitors *m, int tool_id)
+{
+ _PyMonitoringEventSet result = 0;
+ for (int e = 0; e < _PY_MONITORING_LOCAL_EVENTS; e++) {
+ if ((m->tools[e] >> tool_id) & 1) {
+ result |= (1 << e);
+ }
+ }
+ return result;
+}
+
+static inline _PyMonitoringEventSet
+get_events(_Py_GlobalMonitors *m, int tool_id)
{
_PyMonitoringEventSet result = 0;
for (int e = 0; e < _PY_MONITORING_UNGROUPED_EVENTS; e++) {
@@ -457,10 +474,10 @@ sanity_check_instrumentation(PyCodeObject *code)
if (data == NULL) {
return;
}
- _Py_Monitors active_monitors = _PyInterpreterState_GET()->monitors;
+ _Py_GlobalMonitors active_monitors = _PyInterpreterState_GET()->monitors;
if (code->_co_monitoring) {
_Py_Monitors local_monitors = code->_co_monitoring->local_monitors;
- active_monitors = monitors_or(active_monitors, local_monitors);
+ active_monitors = local_union(active_monitors, local_monitors);
}
assert(monitors_equals(
code->_co_monitoring->active_monitors,
@@ -909,7 +926,7 @@ is_version_up_to_date(PyCodeObject *code, PyInterpreterState *interp)
static bool
instrumentation_cross_checks(PyInterpreterState *interp, PyCodeObject *code)
{
- _Py_Monitors expected = monitors_or(
+ _Py_LocalMonitors expected = local_union(
interp->monitors,
code->_co_monitoring->local_monitors);
return monitors_equals(code->_co_monitoring->active_monitors, expected);
@@ -938,12 +955,7 @@ get_tools_for_instruction(PyCodeObject *code, PyInterpreterState *interp, int i,
}
}
else {
- if (code->_co_monitoring) {
- tools = code->_co_monitoring->active_monitors.tools[event];
- }
- else {
- tools = interp->monitors.tools[event];
- }
+ tools = interp->monitors.tools[event];
}
return tools;
}
@@ -1471,7 +1483,7 @@ initialize_lines(PyCodeObject *code)
}
static void
-initialize_line_tools(PyCodeObject *code, _Py_Monitors *all_events)
+initialize_line_tools(PyCodeObject *code, _Py_LocalMonitors *all_events)
{
uint8_t *line_tools = code->_co_monitoring->line_tools;
assert(line_tools != NULL);
@@ -1491,8 +1503,8 @@ allocate_instrumentation_data(PyCodeObject *code)
PyErr_NoMemory();
return -1;
}
- code->_co_monitoring->local_monitors = (_Py_Monitors){ 0 };
- code->_co_monitoring->active_monitors = (_Py_Monitors){ 0 };
+ code->_co_monitoring->local_monitors = (_Py_LocalMonitors){ 0 };
+ code->_co_monitoring->active_monitors = (_Py_LocalMonitors){ 0 };
code->_co_monitoring->tools = NULL;
code->_co_monitoring->lines = NULL;
code->_co_monitoring->line_tools = NULL;
@@ -1509,7 +1521,7 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp)
if (allocate_instrumentation_data(code)) {
return -1;
}
- _Py_Monitors all_events = monitors_or(
+ _Py_LocalMonitors all_events = local_union(
interp->monitors,
code->_co_monitoring->local_monitors);
bool multitools = multiple_tools(&all_events);
@@ -1577,14 +1589,23 @@ _Py_Instrument(PyCodeObject *code, PyInterpreterState *interp)
return 0;
}
int code_len = (int)Py_SIZE(code);
+ /* code->_co_firsttraceable >= code_len indicates
+ * that no instrumentation can be inserted.
+ * Exit early to avoid creating instrumentation
+ * data for potential statically allocated code
+ * objects.
+ * See https://github.com/python/cpython/issues/108390 */
+ if (code->_co_firsttraceable >= code_len) {
+ return 0;
+ }
if (update_instrumentation_data(code, interp)) {
return -1;
}
- _Py_Monitors active_events = monitors_or(
+ _Py_LocalMonitors active_events = local_union(
interp->monitors,
code->_co_monitoring->local_monitors);
- _Py_Monitors new_events;
- _Py_Monitors removed_events;
+ _Py_LocalMonitors new_events;
+ _Py_LocalMonitors removed_events;
bool restarted = interp->last_restart_version > code->_co_instrumentation_version;
if (restarted) {
@@ -1725,11 +1746,23 @@ instrument_all_executing_code_objects(PyInterpreterState *interp) {
}
static void
-set_events(_Py_Monitors *m, int tool_id, _PyMonitoringEventSet events)
+set_events(_Py_GlobalMonitors *m, int tool_id, _PyMonitoringEventSet events)
{
assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS);
for (int e = 0; e < _PY_MONITORING_UNGROUPED_EVENTS; e++) {
uint8_t *tools = &m->tools[e];
+ int active = (events >> e) & 1;
+ *tools &= ~(1 << tool_id);
+ *tools |= (active << tool_id);
+ }
+}
+
+static void
+set_local_events(_Py_LocalMonitors *m, int tool_id, _PyMonitoringEventSet events)
+{
+ assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS);
+ for (int e = 0; e < _PY_MONITORING_LOCAL_EVENTS; e++) {
+ uint8_t *tools = &m->tools[e];
int val = (events >> e) & 1;
*tools &= ~(1 << tool_id);
*tools |= (val << tool_id);
@@ -1771,19 +1804,23 @@ _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEvent
{
assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS);
PyInterpreterState *interp = _PyInterpreterState_GET();
- assert(events < (1 << _PY_MONITORING_UNGROUPED_EVENTS));
+ assert(events < (1 << _PY_MONITORING_LOCAL_EVENTS));
+ if (code->_co_firsttraceable >= Py_SIZE(code)) {
+ PyErr_Format(PyExc_SystemError, "cannot instrument shim code object '%U'", code->co_name);
+ return -1;
+ }
if (check_tool(interp, tool_id)) {
return -1;
}
if (allocate_instrumentation_data(code)) {
return -1;
}
- _Py_Monitors *local = &code->_co_monitoring->local_monitors;
- uint32_t existing_events = get_events(local, tool_id);
+ _Py_LocalMonitors *local = &code->_co_monitoring->local_monitors;
+ uint32_t existing_events = get_local_events(local, tool_id);
if (existing_events == events) {
return 0;
}
- set_events(local, tool_id, events);
+ set_local_events(local, tool_id, events);
if (is_version_up_to_date(code, interp)) {
/* Force instrumentation update */
code->_co_instrumentation_version = UINT64_MAX;
@@ -1942,7 +1979,7 @@ monitoring_get_events_impl(PyObject *module, int tool_id)
if (check_valid_tool(tool_id)) {
return -1;
}
- _Py_Monitors *m = &_PyInterpreterState_GET()->monitors;
+ _Py_GlobalMonitors *m = &_PyInterpreterState_GET()->monitors;
_PyMonitoringEventSet event_set = get_events(m, tool_id);
return event_set;
}
@@ -2005,7 +2042,7 @@ monitoring_get_local_events_impl(PyObject *module, int tool_id,
_PyMonitoringEventSet event_set = 0;
_PyCoMonitoringData *data = ((PyCodeObject *)code)->_co_monitoring;
if (data != NULL) {
- for (int e = 0; e < _PY_MONITORING_UNGROUPED_EVENTS; e++) {
+ for (int e = 0; e < _PY_MONITORING_LOCAL_EVENTS; e++) {
if ((data->local_monitors.tools[e] >> tool_id) & 1) {
event_set |= (1 << e);
}
@@ -2039,15 +2076,16 @@ monitoring_set_local_events_impl(PyObject *module, int tool_id,
if (check_valid_tool(tool_id)) {
return NULL;
}
- if (event_set < 0 || event_set >= (1 << _PY_MONITORING_EVENTS)) {
- PyErr_Format(PyExc_ValueError, "invalid event set 0x%x", event_set);
- return NULL;
- }
if ((event_set & C_RETURN_EVENTS) && (event_set & C_CALL_EVENTS) != C_CALL_EVENTS) {
PyErr_Format(PyExc_ValueError, "cannot set C_RETURN or C_RAISE events independently");
return NULL;
}
event_set &= ~C_RETURN_EVENTS;
+ if (event_set < 0 || event_set >= (1 << _PY_MONITORING_LOCAL_EVENTS)) {
+ PyErr_Format(PyExc_ValueError, "invalid local event set 0x%x", event_set);
+ return NULL;
+ }
+
if (_PyMonitoring_SetLocalEvents((PyCodeObject*)code, tool_id, event_set)) {
return NULL;
}