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
-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_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
+++ /dev/null
-python$target:::line
-/(copyinstr(arg1)=="test_line")/
-{
- printf("%d\t%s:%s:%s:%d\n", timestamp,
- probename, basename(copyinstr(arg0)),
- copyinstr(arg1), arg2);
-}
+++ /dev/null
-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
+++ /dev/null
-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()
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)
class DTraceBackend(TraceBackend):
EXTENSION = ".d"
COMMAND = ["dtrace", "-q", "-s"]
+ if sys.platform == "sunos5":
+ COMMAND.insert(2, "-Z")
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"}
+
+ @staticmethod
+ def _filter_probe_rows(output):
+ return "\n".join(
+ line for line in output.splitlines()
+ if line.partition("\t")[0].isdigit()
+ )
+
+ # Expected outputs for each test case
+ # Note: bpftrace captures <module> entry/return and may have slight timing
+ # differences compared to SystemTap due to probe firing order
+ EXPECTED = {
+ "call_stack": """function__entry:call_stack.py:<module>: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:<module>: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}"
+ )
+
+ stdout = self._filter_probe_rows(stdout)
+
+ # 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}"
+ )
+
+
+class BPFTraceOutputTests(unittest.TestCase):
+ def test_filter_probe_rows_ignores_warnings(self):
+ output = """stdin:1-19: WARNING: found external warnings
+HINT: include/vmlinux.h:1439:3: warning: declaration does not declare anything
+4623214882928\tgc__start:0
+4623214885575\tgc__done:0
+"""
+ self.assertEqual(
+ BPFTraceBackend._filter_probe_rows(output),
+ "4623214882928\tgc__start:0\n4623214885575\tgc__done:0",
+ )
+
+
@unittest.skipIf(MS_WINDOWS, "Tests not compliant with trace on Windows.")
class TraceTests:
# unittest.TestCase options
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()
def test_gc(self):
self.run_case("gc")
- def test_line(self):
- self.run_case("line")
-
class DTraceNormalTests(TraceTests, unittest.TestCase):
backend = DTraceBackend()
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):
"Name: audit",
"Name: gc__start",
"Name: gc__done",
+ "Name: function__entry",
+ "Name: function__return",
]
for probe_name in available_probe_names:
# Missing probes will be added in the future.
missing_probe_names = [
- "Name: function__entry",
- "Name: function__return",
"Name: line",
]
--- /dev/null
+Restore ``function__entry`` and ``function__return`` DTrace/SystemTap probes
+that were broken since Python 3.11.
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
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;
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);
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
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;
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
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);
}
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);
{
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());
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);
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;
_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);
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
}
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);
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());
error:
exception_unwind:
exit_unwind:
+ exit_unwind_notrace:
handle_eval_breaker:
resume_frame:
start_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)
{
#define CONSTS() _PyFrame_GetCode(frame)->co_consts
#define NAMES() _PyFrame_GetCode(frame)->co_names
+#if defined(WITH_DTRACE) && !defined(Py_BUILD_CORE_MODULE)
+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. */
_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;
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);
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());
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
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;
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);
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
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;
tstate->py_recursion_remaining--;
LOAD_SP();
LOAD_IP(0);
+ DTRACE_FUNCTION_ENTRY();
LLTRACE_RESUME_FRAME();
}
DISPATCH();
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);
}
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);
{
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());
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);