Skip to content

Commit 965a543

Browse files
committed
Strengthen JIT GDB backtrace tests
1 parent d890add commit 965a543

File tree

2 files changed

+44
-5
lines changed

2 files changed

+44
-5
lines changed

Lib/test/test_gdb/test_jit.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@
1515
# This bound is just a generous upper limit so the test fails clearly if the
1616
# expected stack shape changes.
1717
MAX_FINISH_STEPS = 20
18+
# Break directly on the lazy shim entry in the binary, then single-step just
19+
# enough to let it install the compiled shim and set a temporary breakpoint on
20+
# the resulting JIT entry address.
21+
MAX_SHIM_SETUP_STEPS = 20
1822
# After landing on the executor frame, single-step a little further into the
1923
# blob so the backtrace is taken from executor code itself rather than the
2024
# immediate helper-return site.
2125
EXECUTOR_SINGLE_STEPS = 2
26+
EVAL_FRAME_RE = r"(_PyEval_EvalFrameDefault|_PyEval_Vector)"
2227

2328
FINISH_TO_JIT_EXECUTOR = (
2429
"python exec(\"import gdb\\n"
@@ -31,6 +36,18 @@
3136
"else:\\n"
3237
" raise RuntimeError('did not reach %s' % target)\\n\")"
3338
)
39+
BREAK_IN_COMPILED_SHIM = (
40+
"python exec(\"import gdb\\n"
41+
"lazy = int(gdb.parse_and_eval('(void*)_Py_LazyJitShim'))\\n"
42+
f"for _ in range({MAX_SHIM_SETUP_STEPS}):\\n"
43+
" entry = int(gdb.parse_and_eval('(void*)_Py_jit_entry'))\\n"
44+
" if entry != lazy:\\n"
45+
" gdb.execute('tbreak *0x%x' % entry)\\n"
46+
" break\\n"
47+
" gdb.execute('next')\\n"
48+
"else:\\n"
49+
" raise RuntimeError('compiled shim was not installed')\\n\")"
50+
)
3451

3552

3653
def setUpModule():
@@ -42,17 +59,38 @@ def setUpModule():
4259
"requires a JIT-enabled build",
4360
)
4461
class JitBacktraceTests(DebuggerTests):
62+
def test_bt_shows_compiled_jit_shim(self):
63+
gdb_output = self.get_stack_trace(
64+
script=JIT_SAMPLE_SCRIPT,
65+
breakpoint="_Py_LazyJitShim",
66+
cmds_after_breakpoint=[
67+
BREAK_IN_COMPILED_SHIM,
68+
"continue",
69+
"bt",
70+
],
71+
PYTHON_JIT="1",
72+
)
73+
self.assertRegex(
74+
gdb_output,
75+
re.compile(
76+
rf"#0\s+py::jit_shim:<jit>.*{EVAL_FRAME_RE}",
77+
re.DOTALL,
78+
),
79+
)
80+
4581
def test_bt_unwinds_through_jit_frames(self):
4682
gdb_output = self.get_stack_trace(
4783
script=JIT_SAMPLE_SCRIPT,
4884
cmds_after_breakpoint=["bt"],
4985
PYTHON_JIT="1",
5086
)
87+
# The executor should appear as a named JIT frame and unwind back into
88+
# the eval loop. Whether GDB also materializes a separate shim frame is
89+
# an implementation detail of the synthetic executor CFI.
5190
self.assertRegex(
5291
gdb_output,
5392
re.compile(
54-
r"py::jit_executor:<jit>.*py::jit_shim:<jit>.*"
55-
r"(_PyEval_EvalFrameDefault|_PyEval_Vector)",
93+
rf"py::jit_executor:<jit>.*{EVAL_FRAME_RE}",
5694
re.DOTALL,
5795
),
5896
)
@@ -67,11 +105,12 @@ def test_bt_unwinds_from_inside_jit_executor(self):
67105
],
68106
PYTHON_JIT="1",
69107
)
108+
# Once the selected PC is inside the executor, we only require that
109+
# GDB can identify the JIT frame and keep unwinding into _PyEval_*.
70110
self.assertRegex(
71111
gdb_output,
72112
re.compile(
73-
r"#0\s+py::jit_executor:<jit>.*#1\s+py::jit_shim:<jit>.*"
74-
r"(_PyEval_EvalFrameDefault|_PyEval_Vector)",
113+
rf"#0\s+py::jit_executor:<jit>.*{EVAL_FRAME_RE}",
75114
re.DOTALL,
76115
),
77116
)

Modules/_testinternalcapi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1220,7 +1220,7 @@ write_perf_map_entry(PyObject *self, PyObject *args)
12201220
return NULL;
12211221
}
12221222

1223-
int ret = PyUnstable_WritePerfMapEntry(code_addr, code_size, entry_name);
1223+
int ret = PyUnstable_WritePerfMapEntry(code_addr, (unsigned int)code_size, entry_name);
12241224
if (ret < 0) {
12251225
PyErr_SetFromErrno(PyExc_OSError);
12261226
return NULL;

0 commit comments

Comments
 (0)