@@ -71,7 +71,7 @@ def _frame_pointers_expected(machine):
7171 return None
7272
7373
74- def _build_stack_and_unwind ():
74+ def _build_stack_and_unwind (unwinder ):
7575 import operator
7676
7777 def build_stack (n , unwinder , warming_up_caller = False ):
@@ -90,7 +90,7 @@ def build_stack(n, unwinder, warming_up_caller=False):
9090 result = operator .call (build_stack , n - 1 , unwinder , warming_up )
9191 return result
9292
93- stack = build_stack (10 , _testinternalcapi . manual_frame_pointer_unwind )
93+ stack = build_stack (10 , unwinder )
9494 return stack
9595
9696
@@ -113,8 +113,9 @@ def _classify_stack(stack, jit_enabled):
113113 return annotated , python_frames , jit_frames , other_frames
114114
115115
116- def _annotate_unwind ():
117- stack = _build_stack_and_unwind ()
116+ def _annotate_unwind (unwinder_name = "manual_frame_pointer_unwind" ):
117+ unwinder = getattr (_testinternalcapi , unwinder_name )
118+ stack = _build_stack_and_unwind (unwinder )
118119 jit_enabled = hasattr (sys , "_jit" ) and sys ._jit .is_enabled ()
119120 jit_backend = _testinternalcapi .get_jit_backend ()
120121 ranges = _testinternalcapi .get_jit_code_ranges () if jit_enabled else []
@@ -133,13 +134,14 @@ def _annotate_unwind():
133134 "jit_frames" : jit_frames ,
134135 "other_frames" : other_frames ,
135136 "jit_backend" : jit_backend ,
137+ "unwinder" : unwinder_name ,
136138 })
137139
138140
139- def _manual_unwind_length ( ** env ):
141+ def _unwind_result ( unwinder_name , ** env ):
140142 code = (
141143 "from test.test_frame_pointer_unwind import _annotate_unwind; "
142- "print(_annotate_unwind());"
144+ f "print(_annotate_unwind({ unwinder_name !r } ));"
143145 )
144146 run_env = os .environ .copy ()
145147 run_env .update (env )
@@ -198,7 +200,7 @@ def test_manual_unwind_respects_frame_pointers(self):
198200
199201 for env , using_jit in envs :
200202 with self .subTest (env = env ):
201- result = _manual_unwind_length ( ** env )
203+ result = _unwind_result ( "manual_frame_pointer_unwind" , ** env )
202204 jit_frames = result ["jit_frames" ]
203205 python_frames = result .get ("python_frames" , 0 )
204206 jit_backend = result .get ("jit_backend" )
@@ -241,5 +243,51 @@ def test_manual_unwind_respects_frame_pointers(self):
241243 )
242244
243245
246+ @support .requires_gil_enabled ("test requires the GIL enabled" )
247+ @unittest .skipIf (support .is_wasi , "test not supported on WASI" )
248+ @unittest .skipUnless (sys .platform == "linux" , "GNU backtrace unwinding test requires Linux" )
249+ class GnuBacktraceUnwindTests (unittest .TestCase ):
250+
251+ def setUp (self ):
252+ super ().setUp ()
253+ try :
254+ _testinternalcapi .gnu_backtrace_unwind ()
255+ except RuntimeError as exc :
256+ if "not supported" in str (exc ):
257+ self .skipTest ("gnu backtrace unwinding not supported on this platform" )
258+ raise
259+
260+ def test_gnu_backtrace_unwinds_through_jit_frames (self ):
261+ jit_available = hasattr (sys , "_jit" ) and sys ._jit .is_available ()
262+ envs = [({"PYTHON_JIT" : "0" }, False )]
263+ if jit_available :
264+ envs .append (({"PYTHON_JIT" : "1" }, True ))
265+
266+ for env , using_jit in envs :
267+ with self .subTest (env = env ):
268+ result = _unwind_result ("gnu_backtrace_unwind" , ** env )
269+ python_frames = result .get ("python_frames" , 0 )
270+ jit_frames = result .get ("jit_frames" , 0 )
271+ jit_backend = result .get ("jit_backend" )
272+
273+ self .assertGreater (
274+ python_frames ,
275+ 0 ,
276+ f"expected to find Python frames in GNU backtrace with env { env } " ,
277+ )
278+ if using_jit and jit_backend == "jit" :
279+ self .assertGreater (
280+ jit_frames ,
281+ 0 ,
282+ f"expected GNU backtrace to include JIT frames with env { env } " ,
283+ )
284+ else :
285+ self .assertEqual (
286+ jit_frames ,
287+ 0 ,
288+ f"unexpected JIT frames counted in GNU backtrace with env { env } " ,
289+ )
290+
291+
244292if __name__ == "__main__" :
245293 unittest .main ()
0 commit comments