diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py index 845185be737eb2..99b9f6d937c13c 100644 --- a/Lib/test/test_monitoring.py +++ b/Lib/test/test_monitoring.py @@ -1152,6 +1152,15 @@ def func1(): ('instruction', 'func1', 14), ('line', 'get_events', 11)]) + def test_gh108976(self): + sys.monitoring.use_tool_id(0, "test") + sys.monitoring.set_events(0, 0) + sys.monitoring.register_callback(0, E.LINE, lambda *args: sys.monitoring.set_events(0, 0)) + sys.monitoring.register_callback(0, E.INSTRUCTION, lambda *args: 0) + sys.monitoring.set_events(0, E.LINE | E.INSTRUCTION) + sys.monitoring.set_events(0, 0) + + class TestInstallIncrementallly(MonitoringTestBase, unittest.TestCase): def check_events(self, func, must_include, tool=TEST_TOOL, recorders=(ExceptionRecorder,)): diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 734b5c83cdff7d..a0c6b330a3a520 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -2198,6 +2198,27 @@ def test_pdb_issue_gh_101517(): (Pdb) continue """ +def test_pdb_issue_gh_108976(): + """See GH-108976 + + Make sure setting f_trace_opcodes = True won't crash pdb + + >>> def test_function(): + ... import sys + ... sys._getframe().f_trace_opcodes = True + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... a = 1 + + >>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE + ... 'continue' + ... ]): + ... test_function() + bdb.Bdb.dispatch: unknown debugging event: 'opcode' + > (5)test_function() + -> a = 1 + (Pdb) continue + """ + def test_pdb_ambiguous_statements(): """See GH-104301 diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-07-00-22-55.gh-issue-108976.RivVn6.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-07-00-22-55.gh-issue-108976.RivVn6.rst new file mode 100644 index 00000000000000..7155949965f13a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-09-07-00-22-55.gh-issue-108976.RivVn6.rst @@ -0,0 +1 @@ +Make sure instrumentation line returns the correct opcode when instruction instrumentation is stripped diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 36459687be7936..b15845a356c9b8 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -1223,8 +1223,19 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, remove_line_tools(code, i, 1 << tool); } } while (tools); + Py_DECREF(line_obj); done: + // original_opcode is acquired before trace function and the callbacks. It's + // possible that the trace function or the callbacks turn off the instruction + // monitoring. If the instruction instrumentation is stripped, using the + // opcode could crash the interpreter. We should use the original opcode + // instead. + if (monitoring->active_monitors.tools[PY_MONITORING_EVENT_INSTRUCTION] == 0 && + original_opcode == INSTRUMENTED_INSTRUCTION) { + original_opcode = instr->op.code; + } + assert(original_opcode != 0); assert(original_opcode != INSTRUMENTED_LINE); assert(_PyOpcode_Deopt[original_opcode] == original_opcode);