diff --git a/Lib/test/dtracedata/call_stack.py b/Lib/test/dtracedata/call_stack.py index ee9f3ae8d6c9f4..11c0369d4baa04 100644 --- a/Lib/test/dtracedata/call_stack.py +++ b/Lib/test/dtracedata/call_stack.py @@ -5,16 +5,16 @@ def function_1(): def function_2(): function_1() -# CALL_FUNCTION_VAR +# CALL with positional args def function_3(dummy, dummy2): pass -# CALL_FUNCTION_KW +# CALL_KW (keyword arguments) def function_4(**dummy): return 1 return 2 # unreachable -# CALL_FUNCTION_VAR_KW +# CALL_FUNCTION_EX (unpacking) def function_5(dummy, dummy2, **dummy3): if False: return 7 diff --git a/Lib/test/dtracedata/call_stack.stp.expected b/Lib/test/dtracedata/call_stack.stp.expected index 32cf396f820629..044eae62018f57 100644 --- a/Lib/test/dtracedata/call_stack.stp.expected +++ b/Lib/test/dtracedata/call_stack.stp.expected @@ -1,8 +1,11 @@ -function__entry:call_stack.py:start:23 function__entry:call_stack.py:function_1:1 +function__entry:call_stack.py:function_3:9 +function__return:call_stack.py:function_3:10 function__return:call_stack.py:function_1:2 function__entry:call_stack.py:function_2:5 function__entry:call_stack.py:function_1:1 +function__entry:call_stack.py:function_3:9 +function__return:call_stack.py:function_3:10 function__return:call_stack.py:function_1:2 function__return:call_stack.py:function_2:6 function__entry:call_stack.py:function_3:9 @@ -11,4 +14,3 @@ function__entry:call_stack.py:function_4:13 function__return:call_stack.py:function_4:14 function__entry:call_stack.py:function_5:18 function__return:call_stack.py:function_5:21 -function__return:call_stack.py:start:28 diff --git a/Lib/test/dtracedata/line.d b/Lib/test/dtracedata/line.d deleted file mode 100644 index 03f22db6fcc1a0..00000000000000 --- a/Lib/test/dtracedata/line.d +++ /dev/null @@ -1,7 +0,0 @@ -python$target:::line -/(copyinstr(arg1)=="test_line")/ -{ - printf("%d\t%s:%s:%s:%d\n", timestamp, - probename, basename(copyinstr(arg0)), - copyinstr(arg1), arg2); -} diff --git a/Lib/test/dtracedata/line.d.expected b/Lib/test/dtracedata/line.d.expected deleted file mode 100644 index 9b16ce76ee60a4..00000000000000 --- a/Lib/test/dtracedata/line.d.expected +++ /dev/null @@ -1,20 +0,0 @@ -line:line.py:test_line:2 -line:line.py:test_line:3 -line:line.py:test_line:4 -line:line.py:test_line:5 -line:line.py:test_line:6 -line:line.py:test_line:7 -line:line.py:test_line:8 -line:line.py:test_line:9 -line:line.py:test_line:10 -line:line.py:test_line:11 -line:line.py:test_line:4 -line:line.py:test_line:5 -line:line.py:test_line:6 -line:line.py:test_line:7 -line:line.py:test_line:8 -line:line.py:test_line:10 -line:line.py:test_line:11 -line:line.py:test_line:4 -line:line.py:test_line:12 -line:line.py:test_line:13 diff --git a/Lib/test/dtracedata/line.py b/Lib/test/dtracedata/line.py deleted file mode 100644 index 0930ff391f7a05..00000000000000 --- a/Lib/test/dtracedata/line.py +++ /dev/null @@ -1,17 +0,0 @@ -def test_line(): - a = 1 - print('# Preamble', a) - for i in range(2): - a = i - b = i+2 - c = i+3 - if c < 4: - a = c - d = a + b +c - print('#', a, b, c, d) - a = 1 - print('# Epilogue', a) - - -if __name__ == '__main__': - test_line() diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py index ba2fa99707cd46..4ee22b71207e33 100644 --- a/Lib/test/test_dtrace.py +++ b/Lib/test/test_dtrace.py @@ -33,11 +33,17 @@ def normalize_trace_output(output): result = [ row.split("\t") for row in output.splitlines() - if row and not row.startswith('#') + if row and not row.startswith('#') and not row.startswith('@') ] result.sort(key=lambda row: int(row[0])) result = [row[1] for row in result] - return "\n".join(result) + # Normalize paths to basenames (bpftrace outputs full paths) + normalized = [] + for line in result: + # Replace full paths with just the filename + line = re.sub(r'/[^:]+/([^/:]+\.py)', r'\1', line) + normalized.append(line) + return "\n".join(normalized) except (IndexError, ValueError): raise AssertionError( "tracer produced unparsable output:\n{}".format(output) @@ -96,6 +102,8 @@ def assert_usable(self): class DTraceBackend(TraceBackend): EXTENSION = ".d" COMMAND = ["dtrace", "-q", "-s"] + if sys.platform == "sunos5": + COMMAND.insert(2, "-Z") class SystemTapBackend(TraceBackend): @@ -103,6 +111,155 @@ class SystemTapBackend(TraceBackend): COMMAND = ["stap", "-g"] +class BPFTraceBackend(TraceBackend): + EXTENSION = ".bt" + COMMAND = ["bpftrace"] + + # Inline bpftrace programs for each test case + PROGRAMS = { + "call_stack": """ + usdt:{python}:python:function__entry {{ + printf("%lld\\tfunction__entry:%s:%s:%d\\n", + nsecs, str(arg0), str(arg1), arg2); + }} + usdt:{python}:python:function__return {{ + printf("%lld\\tfunction__return:%s:%s:%d\\n", + nsecs, str(arg0), str(arg1), arg2); + }} + """, + "gc": """ + usdt:{python}:python:function__entry {{ + if (str(arg1) == "start") {{ @tracing = 1; }} + }} + usdt:{python}:python:function__return {{ + if (str(arg1) == "start") {{ @tracing = 0; }} + }} + usdt:{python}:python:gc__start {{ + if (@tracing) {{ + printf("%lld\\tgc__start:%d\\n", nsecs, arg0); + }} + }} + usdt:{python}:python:gc__done {{ + if (@tracing) {{ + printf("%lld\\tgc__done:%lld\\n", nsecs, arg0); + }} + }} + END {{ clear(@tracing); }} + """, + } + + # Which test scripts to filter by filename (None = use @tracing flag) + FILTER_BY_FILENAME = {"call_stack": "call_stack.py"} + + # Expected outputs for each test case + # Note: bpftrace captures entry/return and may have slight timing + # differences compared to SystemTap due to probe firing order + EXPECTED = { + "call_stack": """function__entry:call_stack.py::0 +function__entry:call_stack.py:start:23 +function__entry:call_stack.py:function_1:1 +function__entry:call_stack.py:function_3:9 +function__return:call_stack.py:function_3:10 +function__return:call_stack.py:function_1:2 +function__entry:call_stack.py:function_2:5 +function__entry:call_stack.py:function_1:1 +function__entry:call_stack.py:function_3:9 +function__return:call_stack.py:function_3:10 +function__return:call_stack.py:function_1:2 +function__return:call_stack.py:function_2:6 +function__entry:call_stack.py:function_3:9 +function__return:call_stack.py:function_3:10 +function__entry:call_stack.py:function_4:13 +function__return:call_stack.py:function_4:14 +function__entry:call_stack.py:function_5:18 +function__return:call_stack.py:function_5:21 +function__return:call_stack.py:start:28 +function__return:call_stack.py::30""", + "gc": """gc__start:0 +gc__done:0 +gc__start:1 +gc__done:0 +gc__start:2 +gc__done:0 +gc__start:2 +gc__done:1""", + } + + def run_case(self, name, optimize_python=None): + if name not in self.PROGRAMS: + raise unittest.SkipTest(f"No bpftrace program for {name}") + + python_file = abspath(name + ".py") + python_flags = [] + if optimize_python: + python_flags.extend(["-O"] * optimize_python) + + subcommand = [sys.executable] + python_flags + [python_file] + program = self.PROGRAMS[name].format(python=sys.executable) + + try: + proc = subprocess.Popen( + ["bpftrace", "-e", program, "-c", " ".join(subcommand)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + stdout, stderr = proc.communicate(timeout=60) + except subprocess.TimeoutExpired: + proc.kill() + raise AssertionError("bpftrace timed out") + except (FileNotFoundError, PermissionError) as e: + raise unittest.SkipTest(f"bpftrace not available: {e}") + + if proc.returncode != 0: + raise AssertionError( + f"bpftrace failed with code {proc.returncode}:\n{stderr}" + ) + + # Filter output by filename if specified (bpftrace captures everything) + if name in self.FILTER_BY_FILENAME: + filter_filename = self.FILTER_BY_FILENAME[name] + filtered_lines = [ + line for line in stdout.splitlines() + if filter_filename in line + ] + stdout = "\n".join(filtered_lines) + + actual_output = normalize_trace_output(stdout) + expected_output = self.EXPECTED[name].strip() + + return (expected_output, actual_output) + + def assert_usable(self): + # Check if bpftrace is available and can attach to USDT probes + program = f'usdt:{sys.executable}:python:function__entry {{ printf("probe: success\\n"); exit(); }}' + try: + proc = subprocess.Popen( + ["bpftrace", "-e", program, "-c", f"{sys.executable} -c pass"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + stdout, stderr = proc.communicate(timeout=10) + except subprocess.TimeoutExpired: + proc.kill() + proc.communicate() # Clean up + raise unittest.SkipTest("bpftrace timed out during usability check") + except OSError as e: + raise unittest.SkipTest(f"bpftrace not available: {e}") + + # Check for permission errors (bpftrace usually requires root) + if proc.returncode != 0: + raise unittest.SkipTest( + f"bpftrace(1) failed with code {proc.returncode}: {stderr}" + ) + + if "probe: success" not in stdout: + raise unittest.SkipTest( + f"bpftrace(1) failed: stdout={stdout!r} stderr={stderr!r}" + ) + + @unittest.skipIf(MS_WINDOWS, "Tests not compliant with trace on Windows.") class TraceTests: # unittest.TestCase options @@ -127,7 +284,8 @@ def test_function_entry_return(self): def test_verify_call_opcodes(self): """Ensure our call stack test hits all function call opcodes""" - opcodes = set(["CALL_FUNCTION", "CALL_FUNCTION_EX", "CALL_FUNCTION_KW"]) + # Modern Python uses CALL, CALL_KW, and CALL_FUNCTION_EX + opcodes = set(["CALL", "CALL_FUNCTION_EX", "CALL_KW"]) with open(abspath("call_stack.py")) as f: code_string = f.read() @@ -152,9 +310,6 @@ def get_function_instructions(funcname): def test_gc(self): self.run_case("gc") - def test_line(self): - self.run_case("line") - class DTraceNormalTests(TraceTests, unittest.TestCase): backend = DTraceBackend() @@ -175,6 +330,17 @@ class SystemTapOptimizedTests(TraceTests, unittest.TestCase): backend = SystemTapBackend() optimize_python = 2 + +class BPFTraceNormalTests(TraceTests, unittest.TestCase): + backend = BPFTraceBackend() + optimize_python = 0 + + +class BPFTraceOptimizedTests(TraceTests, unittest.TestCase): + backend = BPFTraceBackend() + optimize_python = 2 + + class CheckDtraceProbes(unittest.TestCase): @classmethod def setUpClass(cls): @@ -235,6 +401,8 @@ def test_check_probes(self): "Name: audit", "Name: gc__start", "Name: gc__done", + "Name: function__entry", + "Name: function__return", ] for probe_name in available_probe_names: @@ -247,8 +415,6 @@ def test_missing_probes(self): # Missing probes will be added in the future. missing_probe_names = [ - "Name: function__entry", - "Name: function__return", "Name: line", ] diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-08-00-25-35.gh-issue-98894.hKWyfqNx.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-08-00-25-35.gh-issue-98894.hKWyfqNx.rst new file mode 100644 index 00000000000000..09ccf198a90583 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-08-00-25-35.gh-issue-98894.hKWyfqNx.rst @@ -0,0 +1,2 @@ +Restore ``function__entry`` and ``function__return`` DTrace/SystemTap probes +that were broken since Python 3.11. diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 6f2d4c89893547..6dde49873d81c7 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -775,6 +775,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -1986,6 +1987,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -2129,6 +2131,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -2257,6 +2260,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -2786,6 +2790,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -3367,6 +3372,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -3554,6 +3560,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -4306,6 +4313,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -4406,6 +4414,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -5955,6 +5964,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -7563,6 +7573,7 @@ ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); assert(STACK_LEVEL() == 0); + DTRACE_FUNCTION_RETURN(); _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *dying = frame; frame = tstate->current_frame = dying->previous; @@ -7624,6 +7635,7 @@ stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); + DTRACE_FUNCTION_RETURN(); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); @@ -8741,6 +8753,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -10709,6 +10722,7 @@ ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); assert(STACK_LEVEL() == 0); + DTRACE_FUNCTION_RETURN(); _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *dying = frame; frame = tstate->current_frame = dying->previous; @@ -10891,6 +10905,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -12564,6 +12579,7 @@ stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); + DTRACE_FUNCTION_RETURN(); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); @@ -12745,6 +12761,7 @@ JUMP_TO_LABEL(error); if (too_deep) { JUMP_TO_LABEL(exit_unwind); } + DTRACE_FUNCTION_ENTRY(); next_instr = frame->instr_ptr; #ifdef Py_DEBUG int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index edccb1ea5a0144..1bcb1afb51f195 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1328,6 +1328,7 @@ dummy_func( DEAD(retval); SAVE_STACK(); assert(STACK_LEVEL() == 0); + DTRACE_FUNCTION_RETURN(); _Py_LeaveRecursiveCallPy(tstate); // GH-99729: We need to unlink the frame *before* clearing it: _PyInterpreterFrame *dying = frame; @@ -1507,6 +1508,7 @@ dummy_func( _PyStackRef temp = retval; DEAD(retval); SAVE_STACK(); + DTRACE_FUNCTION_RETURN(); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); @@ -4164,6 +4166,7 @@ dummy_func( tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } @@ -5975,6 +5978,12 @@ dummy_func( } spilled label(exit_unwind) { + assert(_PyErr_Occurred(tstate)); + DTRACE_FUNCTION_RETURN(); + goto exit_unwind_notrace; + } + + spilled label(exit_unwind_notrace) { assert(_PyErr_Occurred(tstate)); _Py_LeaveRecursiveCallPy(tstate); assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); @@ -6007,8 +6016,9 @@ dummy_func( spilled label(start_frame) { int too_deep = _Py_EnterRecursivePy(tstate); if (too_deep) { - goto exit_unwind; + goto exit_unwind_notrace; } + DTRACE_FUNCTION_ENTRY(); next_instr = frame->instr_ptr; #ifdef Py_DEBUG int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); @@ -6100,6 +6110,7 @@ dummy_func( error: exception_unwind: exit_unwind: + exit_unwind_notrace: handle_eval_breaker: resume_frame: start_frame: diff --git a/Python/ceval.c b/Python/ceval.c index 8a6895834cbb7e..10d0ffd987d2f0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1180,6 +1180,38 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame) #define DONT_SLP_VECTORIZE #endif +#ifdef WITH_DTRACE +static void +dtrace_function_entry(_PyInterpreterFrame *frame) +{ + const char *filename; + const char *funcname; + int lineno; + + PyCodeObject *code = _PyFrame_GetCode(frame); + filename = PyUnicode_AsUTF8(code->co_filename); + funcname = PyUnicode_AsUTF8(code->co_name); + lineno = PyUnstable_InterpreterFrame_GetLine(frame); + + PyDTrace_FUNCTION_ENTRY(filename, funcname, lineno); +} + +static void +dtrace_function_return(_PyInterpreterFrame *frame) +{ + const char *filename; + const char *funcname; + int lineno; + + PyCodeObject *code = _PyFrame_GetCode(frame); + filename = PyUnicode_AsUTF8(code->co_filename); + funcname = PyUnicode_AsUTF8(code->co_name); + lineno = PyUnstable_InterpreterFrame_GetLine(frame); + + PyDTrace_FUNCTION_RETURN(filename, funcname, lineno); +} +#endif + PyObject* _Py_HOT_FUNCTION DONT_SLP_VECTORIZE _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) { diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index b127812b4bf703..76ab72498451d2 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -329,11 +329,24 @@ GETITEM(PyObject *v, Py_ssize_t i) { #define CONSTS() _PyFrame_GetCode(frame)->co_consts #define NAMES() _PyFrame_GetCode(frame)->co_names +#ifdef WITH_DTRACE +static void dtrace_function_entry(_PyInterpreterFrame *); +static void dtrace_function_return(_PyInterpreterFrame *); + #define DTRACE_FUNCTION_ENTRY() \ if (PyDTrace_FUNCTION_ENTRY_ENABLED()) { \ dtrace_function_entry(frame); \ } +#define DTRACE_FUNCTION_RETURN() \ + if (PyDTrace_FUNCTION_RETURN_ENABLED()) { \ + dtrace_function_return(frame); \ + } +#else +#define DTRACE_FUNCTION_ENTRY() ((void)0) +#define DTRACE_FUNCTION_RETURN() ((void)0) +#endif + /* This takes a uint16_t instead of a _Py_BackoffCounter, * because it is used directly on the cache entry in generated code, * which is always an integral type. */ diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index fbbfd521e0d74c..99b0752b4f6d37 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -6986,6 +6986,7 @@ _PyStackRef temp = retval; _PyFrame_SetStackPointer(frame, stack_pointer); assert(STACK_LEVEL() == 0); + DTRACE_FUNCTION_RETURN(); _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *dying = frame; frame = tstate->current_frame = dying->previous; @@ -7190,6 +7191,7 @@ assert(oparg == 0 || oparg == 1); _PyStackRef temp = retval; _PyFrame_SetStackPointer(frame, stack_pointer); + DTRACE_FUNCTION_RETURN(); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); @@ -14118,6 +14120,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); SET_CURRENT_CACHED_VALUES(0); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 0d8686c1b5856a..d3e9436e06a820 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -775,6 +775,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -1986,6 +1987,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -2129,6 +2131,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -2257,6 +2260,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -2786,6 +2790,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -3367,6 +3372,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -3554,6 +3560,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -4306,6 +4313,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -4406,6 +4414,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -5955,6 +5964,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -7562,6 +7572,7 @@ ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); assert(STACK_LEVEL() == 0); + DTRACE_FUNCTION_RETURN(); _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *dying = frame; frame = tstate->current_frame = dying->previous; @@ -7623,6 +7634,7 @@ stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); + DTRACE_FUNCTION_RETURN(); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); @@ -8740,6 +8752,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -10706,6 +10719,7 @@ ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); assert(STACK_LEVEL() == 0); + DTRACE_FUNCTION_RETURN(); _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *dying = frame; frame = tstate->current_frame = dying->previous; @@ -10888,6 +10902,7 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); @@ -12561,6 +12576,7 @@ stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); + DTRACE_FUNCTION_RETURN(); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); @@ -12708,6 +12724,13 @@ JUMP_TO_LABEL(error); } LABEL(exit_unwind) + { + assert(_PyErr_Occurred(tstate)); + DTRACE_FUNCTION_RETURN(); + JUMP_TO_LABEL(exit_unwind_notrace); + } + + LABEL(exit_unwind_notrace) { assert(_PyErr_Occurred(tstate)); _Py_LeaveRecursiveCallPy(tstate); @@ -12740,8 +12763,9 @@ JUMP_TO_LABEL(error); { int too_deep = _Py_EnterRecursivePy(tstate); if (too_deep) { - JUMP_TO_LABEL(exit_unwind); + JUMP_TO_LABEL(exit_unwind_notrace); } + DTRACE_FUNCTION_ENTRY(); next_instr = frame->instr_ptr; #ifdef Py_DEBUG int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index def462bacec176..49806269aa5203 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -527,6 +527,7 @@ static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_pop_1_error(TAIL_CALL_PARAMS); static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_error(TAIL_CALL_PARAMS); static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_exception_unwind(TAIL_CALL_PARAMS); static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_exit_unwind(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_exit_unwind_notrace(TAIL_CALL_PARAMS); static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_start_frame(TAIL_CALL_PARAMS); static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_stop_tracing(TAIL_CALL_PARAMS);