mirror of
https://github.com/python/cpython.git
synced 2026-04-27 06:10:59 +00:00
gh-142186: Allow all PEP-669 events to be per-code object and disableable (GH-146182)
* Make the `PY_UNWIND` monitoring event available as a code-local event to allow trapping on function exit events when an exception bubbles up. This complements the PY_RETURN event by allowing to catch any function exit event. * Allow `PY_UNWIND` to be `DISABLE`d; disabling it disables the event for the whole code object. * Do the above for `PY_THROW`, `RAISE`, `EXCEPTION_HANDLED`, and `RERAISE` events.
This commit is contained in:
parent
09233bd198
commit
858e69eab0
11 changed files with 436 additions and 71 deletions
|
|
@ -2406,15 +2406,16 @@ void
|
|||
_PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame,
|
||||
_Py_CODEUNIT *instr)
|
||||
{
|
||||
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RAISE)) {
|
||||
if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_RAISE)) {
|
||||
return;
|
||||
}
|
||||
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RAISE);
|
||||
}
|
||||
|
||||
bool
|
||||
_PyEval_NoToolsForUnwind(PyThreadState *tstate) {
|
||||
return no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_UNWIND);
|
||||
_PyEval_NoToolsForUnwind(PyThreadState *tstate, _PyInterpreterFrame *frame)
|
||||
{
|
||||
return no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_PY_UNWIND);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ no_tools_for_global_event(PyThreadState *tstate, int event)
|
|||
static inline bool
|
||||
no_tools_for_local_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event)
|
||||
{
|
||||
assert(event < _PY_MONITORING_LOCAL_EVENTS);
|
||||
assert(event < _PY_MONITORING_UNGROUPED_EVENTS);
|
||||
_PyCoMonitoringData *data = _PyFrame_GetCode(frame)->_co_monitoring;
|
||||
if (data) {
|
||||
return data->active_monitors.tools[event] == 0;
|
||||
|
|
@ -382,7 +382,7 @@ monitor_handled(PyThreadState *tstate,
|
|||
_PyInterpreterFrame *frame,
|
||||
_Py_CODEUNIT *instr, PyObject *exc)
|
||||
{
|
||||
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) {
|
||||
if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) {
|
||||
return 0;
|
||||
}
|
||||
return _Py_call_instrumentation_arg(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED, frame, instr, exc);
|
||||
|
|
@ -393,7 +393,7 @@ monitor_throw(PyThreadState *tstate,
|
|||
_PyInterpreterFrame *frame,
|
||||
_Py_CODEUNIT *instr)
|
||||
{
|
||||
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_THROW)) {
|
||||
if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_PY_THROW)) {
|
||||
return;
|
||||
}
|
||||
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_THROW);
|
||||
|
|
@ -403,7 +403,7 @@ static void
|
|||
monitor_reraise(PyThreadState *tstate, _PyInterpreterFrame *frame,
|
||||
_Py_CODEUNIT *instr)
|
||||
{
|
||||
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RERAISE)) {
|
||||
if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_RERAISE)) {
|
||||
return;
|
||||
}
|
||||
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RERAISE);
|
||||
|
|
@ -431,7 +431,7 @@ monitor_unwind(PyThreadState *tstate,
|
|||
_PyInterpreterFrame *frame,
|
||||
_Py_CODEUNIT *instr)
|
||||
{
|
||||
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_UNWIND)) {
|
||||
if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_PY_UNWIND)) {
|
||||
return;
|
||||
}
|
||||
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_UNWIND);
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ is_instrumented(int opcode)
|
|||
static inline bool
|
||||
monitors_equals(_Py_LocalMonitors a, _Py_LocalMonitors b)
|
||||
{
|
||||
for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
|
||||
for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
|
||||
if (a.tools[i] != b.tools[i]) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -216,7 +216,7 @@ static inline _Py_LocalMonitors
|
|||
monitors_sub(_Py_LocalMonitors a, _Py_LocalMonitors b)
|
||||
{
|
||||
_Py_LocalMonitors res;
|
||||
for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
|
||||
for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
|
||||
res.tools[i] = a.tools[i] & ~b.tools[i];
|
||||
}
|
||||
return res;
|
||||
|
|
@ -227,7 +227,7 @@ static inline _Py_LocalMonitors
|
|||
monitors_and(_Py_LocalMonitors a, _Py_LocalMonitors b)
|
||||
{
|
||||
_Py_LocalMonitors res;
|
||||
for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
|
||||
for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
|
||||
res.tools[i] = a.tools[i] & b.tools[i];
|
||||
}
|
||||
return res;
|
||||
|
|
@ -243,7 +243,7 @@ static inline _Py_LocalMonitors
|
|||
local_union(_Py_GlobalMonitors a, _Py_LocalMonitors b)
|
||||
{
|
||||
_Py_LocalMonitors res;
|
||||
for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
|
||||
for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
|
||||
res.tools[i] = a.tools[i] | b.tools[i];
|
||||
}
|
||||
return res;
|
||||
|
|
@ -252,7 +252,7 @@ local_union(_Py_GlobalMonitors a, _Py_LocalMonitors b)
|
|||
static inline bool
|
||||
monitors_are_empty(_Py_LocalMonitors m)
|
||||
{
|
||||
for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
|
||||
for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
|
||||
if (m.tools[i]) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -263,7 +263,7 @@ monitors_are_empty(_Py_LocalMonitors m)
|
|||
static inline bool
|
||||
multiple_tools(_Py_LocalMonitors *m)
|
||||
{
|
||||
for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) {
|
||||
for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
|
||||
if (_Py_popcount32(m->tools[i]) > 1) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -275,7 +275,7 @@ static inline _PyMonitoringEventSet
|
|||
get_local_events(_Py_LocalMonitors *m, int tool_id)
|
||||
{
|
||||
_PyMonitoringEventSet result = 0;
|
||||
for (int e = 0; e < _PY_MONITORING_LOCAL_EVENTS; e++) {
|
||||
for (int e = 0; e < _PY_MONITORING_UNGROUPED_EVENTS; e++) {
|
||||
if ((m->tools[e] >> tool_id) & 1) {
|
||||
result |= (1 << e);
|
||||
}
|
||||
|
|
@ -453,7 +453,7 @@ static void
|
|||
dump_local_monitors(const char *prefix, _Py_LocalMonitors monitors, FILE*out)
|
||||
{
|
||||
fprintf(out, "%s monitors:\n", prefix);
|
||||
for (int event = 0; event < _PY_MONITORING_LOCAL_EVENTS; event++) {
|
||||
for (int event = 0; event < _PY_MONITORING_UNGROUPED_EVENTS; event++) {
|
||||
fprintf(out, " Event %d: Tools %x\n", event, monitors.tools[event]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1102,8 +1102,10 @@ get_tools_for_instruction(PyCodeObject *code, PyInterpreterState *interp, int i,
|
|||
event == PY_MONITORING_EVENT_C_RETURN);
|
||||
event = PY_MONITORING_EVENT_CALL;
|
||||
}
|
||||
assert(_PY_MONITORING_IS_UNGROUPED_EVENT(event));
|
||||
CHECK(debug_check_sanity(interp, code));
|
||||
if (PY_MONITORING_IS_INSTRUMENTED_EVENT(event)) {
|
||||
CHECK(debug_check_sanity(interp, code));
|
||||
/* Instrumented events use per-instruction tool bitmaps. */
|
||||
if (code->_co_monitoring->tools) {
|
||||
tools = code->_co_monitoring->tools[i];
|
||||
}
|
||||
|
|
@ -1112,7 +1114,9 @@ get_tools_for_instruction(PyCodeObject *code, PyInterpreterState *interp, int i,
|
|||
}
|
||||
}
|
||||
else {
|
||||
tools = interp->monitors.tools[event];
|
||||
/* Other (non-instrumented) events are not tied to specific instructions;
|
||||
* use the code-object-level active_monitors bitmap instead. */
|
||||
tools = code->_co_monitoring->active_monitors.tools[event];
|
||||
}
|
||||
return tools;
|
||||
}
|
||||
|
|
@ -1139,6 +1143,25 @@ static const char *const event_names [] = {
|
|||
[PY_MONITORING_EVENT_STOP_ITERATION] = "STOP_ITERATION",
|
||||
};
|
||||
|
||||
/* Disable an "other" (non-instrumented) event (e.g. PY_UNWIND) for a single
|
||||
* tool on this code object. Must be called with the world stopped or the
|
||||
* code lock held. */
|
||||
static void
|
||||
remove_local_tool(PyCodeObject *code, PyInterpreterState *interp,
|
||||
int event, int tool)
|
||||
{
|
||||
ASSERT_WORLD_STOPPED_OR_LOCKED(code);
|
||||
assert(_PY_MONITORING_IS_UNGROUPED_EVENT(event));
|
||||
assert(!PY_MONITORING_IS_INSTRUMENTED_EVENT(event));
|
||||
assert(code->_co_monitoring);
|
||||
code->_co_monitoring->local_monitors.tools[event] &= ~(1 << tool);
|
||||
/* Recompute active_monitors for this event as the union of global and
|
||||
* (now updated) local monitors. */
|
||||
code->_co_monitoring->active_monitors.tools[event] =
|
||||
interp->monitors.tools[event] |
|
||||
code->_co_monitoring->local_monitors.tools[event];
|
||||
}
|
||||
|
||||
static int
|
||||
call_instrumentation_vector(
|
||||
_Py_CODEUNIT *instr, PyThreadState *tstate, int event,
|
||||
|
|
@ -1183,7 +1206,18 @@ call_instrumentation_vector(
|
|||
}
|
||||
else {
|
||||
/* DISABLE */
|
||||
if (!PY_MONITORING_IS_INSTRUMENTED_EVENT(event)) {
|
||||
if (PY_MONITORING_IS_INSTRUMENTED_EVENT(event)) {
|
||||
_PyEval_StopTheWorld(interp);
|
||||
remove_tools(code, offset, event, 1 << tool);
|
||||
_PyEval_StartTheWorld(interp);
|
||||
}
|
||||
else if (_PY_MONITORING_IS_UNGROUPED_EVENT(event)) {
|
||||
/* Other (non-instrumented) event: disable for this code object. */
|
||||
_PyEval_StopTheWorld(interp);
|
||||
remove_local_tool(code, interp, event, tool);
|
||||
_PyEval_StartTheWorld(interp);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Cannot disable %s events. Callback removed.",
|
||||
event_names[event]);
|
||||
|
|
@ -1192,12 +1226,6 @@ call_instrumentation_vector(
|
|||
err = -1;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
PyInterpreterState *interp = tstate->interp;
|
||||
_PyEval_StopTheWorld(interp);
|
||||
remove_tools(code, offset, event, 1 << tool);
|
||||
_PyEval_StartTheWorld(interp);
|
||||
}
|
||||
}
|
||||
}
|
||||
Py_DECREF(arg2_obj);
|
||||
|
|
@ -1681,7 +1709,7 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp)
|
|||
_Py_LocalMonitors *local_monitors = &code->_co_monitoring->local_monitors;
|
||||
for (int i = 0; i < PY_MONITORING_TOOL_IDS; i++) {
|
||||
if (code->_co_monitoring->tool_versions[i] != interp->monitoring_tool_versions[i]) {
|
||||
for (int j = 0; j < _PY_MONITORING_LOCAL_EVENTS; j++) {
|
||||
for (int j = 0; j < _PY_MONITORING_UNGROUPED_EVENTS; j++) {
|
||||
local_monitors->tools[j] &= ~(1 << i);
|
||||
}
|
||||
}
|
||||
|
|
@ -1977,7 +2005,7 @@ 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++) {
|
||||
for (int e = 0; e < _PY_MONITORING_UNGROUPED_EVENTS; e++) {
|
||||
uint8_t *tools = &m->tools[e];
|
||||
int val = (events >> e) & 1;
|
||||
*tools &= ~(1 << tool_id);
|
||||
|
|
@ -2037,7 +2065,7 @@ _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_LOCAL_EVENTS));
|
||||
assert(events < (1 << _PY_MONITORING_UNGROUPED_EVENTS));
|
||||
if (code->_co_firsttraceable >= Py_SIZE(code)) {
|
||||
PyErr_Format(PyExc_SystemError, "cannot instrument shim code object '%U'", code->co_name);
|
||||
return -1;
|
||||
|
|
@ -2373,7 +2401,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_LOCAL_EVENTS; e++) {
|
||||
for (int e = 0; e < _PY_MONITORING_UNGROUPED_EVENTS; e++) {
|
||||
if ((data->local_monitors.tools[e] >> tool_id) & 1) {
|
||||
event_set |= (1 << e);
|
||||
}
|
||||
|
|
@ -2416,7 +2444,7 @@ monitoring_set_local_events_impl(PyObject *module, int tool_id,
|
|||
event_set &= ~(1 << PY_MONITORING_EVENT_BRANCH);
|
||||
event_set |= (1 << PY_MONITORING_EVENT_BRANCH_RIGHT) | (1 << PY_MONITORING_EVENT_BRANCH_LEFT);
|
||||
}
|
||||
if (event_set < 0 || event_set >= (1 << _PY_MONITORING_LOCAL_EVENTS)) {
|
||||
if (event_set < 0 || event_set >= (1 << _PY_MONITORING_UNGROUPED_EVENTS)) {
|
||||
PyErr_Format(PyExc_ValueError, "invalid local event set 0x%x", event_set);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue