@@ -70,7 +70,7 @@ def _frame_pointers_expected(machine):
7070 return None
7171
7272
73- def _build_stack_and_unwind ():
73+ def _build_stack_and_unwind (unwinder ):
7474 import operator
7575
7676 def build_stack (n , unwinder , warming_up_caller = False ):
@@ -89,7 +89,7 @@ def build_stack(n, unwinder, warming_up_caller=False):
8989 result = operator .call (build_stack , n - 1 , unwinder , warming_up )
9090 return result
9191
92- stack = build_stack (10 , _testinternalcapi . manual_frame_pointer_unwind )
92+ stack = build_stack (10 , unwinder )
9393 return stack
9494
9595
@@ -112,8 +112,9 @@ def _classify_stack(stack, jit_enabled):
112112 return annotated , python_frames , jit_frames , other_frames
113113
114114
115- def _annotate_unwind ():
116- stack = _build_stack_and_unwind ()
115+ def _annotate_unwind (unwinder_name = "manual_frame_pointer_unwind" ):
116+ unwinder = getattr (_testinternalcapi , unwinder_name )
117+ stack = _build_stack_and_unwind (unwinder )
117118 jit_enabled = hasattr (sys , "_jit" ) and sys ._jit .is_enabled ()
118119 jit_backend = _testinternalcapi .get_jit_backend ()
119120 ranges = _testinternalcapi .get_jit_code_ranges () if jit_enabled else []
@@ -132,13 +133,14 @@ def _annotate_unwind():
132133 "jit_frames" : jit_frames ,
133134 "other_frames" : other_frames ,
134135 "jit_backend" : jit_backend ,
136+ "unwinder" : unwinder_name ,
135137 })
136138
137139
138- def _manual_unwind_length ( ** env ):
140+ def _unwind_result ( unwinder_name , ** env ):
139141 code = (
140142 "from test.test_frame_pointer_unwind import _annotate_unwind; "
141- "print(_annotate_unwind());"
143+ f "print(_annotate_unwind({ unwinder_name !r } ));"
142144 )
143145 run_env = os .environ .copy ()
144146 run_env .update (env )
@@ -197,7 +199,7 @@ def test_manual_unwind_respects_frame_pointers(self):
197199
198200 for env , using_jit in envs :
199201 with self .subTest (env = env ):
200- result = _manual_unwind_length ( ** env )
202+ result = _unwind_result ( "manual_frame_pointer_unwind" , ** env )
201203 jit_frames = result ["jit_frames" ]
202204 python_frames = result .get ("python_frames" , 0 )
203205 jit_backend = result .get ("jit_backend" )
@@ -240,5 +242,51 @@ def test_manual_unwind_respects_frame_pointers(self):
240242 )
241243
242244
245+ @support .requires_gil_enabled ("test requires the GIL enabled" )
246+ @unittest .skipIf (support .is_wasi , "test not supported on WASI" )
247+ @unittest .skipUnless (sys .platform == "linux" , "GNU backtrace unwinding test requires Linux" )
248+ class GnuBacktraceUnwindTests (unittest .TestCase ):
249+
250+ def setUp (self ):
251+ super ().setUp ()
252+ try :
253+ _testinternalcapi .gnu_backtrace_unwind ()
254+ except RuntimeError as exc :
255+ if "not supported" in str (exc ):
256+ self .skipTest ("gnu backtrace unwinding not supported on this platform" )
257+ raise
258+
259+ def test_gnu_backtrace_unwinds_through_jit_frames (self ):
260+ jit_available = hasattr (sys , "_jit" ) and sys ._jit .is_available ()
261+ envs = [({"PYTHON_JIT" : "0" }, False )]
262+ if jit_available :
263+ envs .append (({"PYTHON_JIT" : "1" }, True ))
264+
265+ for env , using_jit in envs :
266+ with self .subTest (env = env ):
267+ result = _unwind_result ("gnu_backtrace_unwind" , ** env )
268+ python_frames = result .get ("python_frames" , 0 )
269+ jit_frames = result .get ("jit_frames" , 0 )
270+ jit_backend = result .get ("jit_backend" )
271+
272+ self .assertGreater (
273+ python_frames ,
274+ 0 ,
275+ f"expected to find Python frames in GNU backtrace with env { env } " ,
276+ )
277+ if using_jit and jit_backend == "jit" :
278+ self .assertGreater (
279+ jit_frames ,
280+ 0 ,
281+ f"expected GNU backtrace to include JIT frames with env { env } " ,
282+ )
283+ else :
284+ self .assertEqual (
285+ jit_frames ,
286+ 0 ,
287+ f"unexpected JIT frames counted in GNU backtrace with env { env } " ,
288+ )
289+
290+
243291if __name__ == "__main__" :
244292 unittest .main ()
0 commit comments