test_gdb.test_pycfunction: test more calling conventions (GH-13668)
As the code paths for various METH_* conventions are diverging due
to optimizations, we should check they continue to be covered by
GDB integration.
diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py
index 3127e69..f57e348 100644
--- a/Lib/test/test_gdb.py
+++ b/Lib/test/test_gdb.py
@@ -867,27 +867,40 @@
# unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
def test_pycfunction(self):
'Verify that "py-bt" displays invocations of PyCFunction instances'
- # Tested function must not be defined with METH_NOARGS or METH_O,
- # otherwise call_function() doesn't call PyCFunction_Call()
- cmd = ('from time import gmtime\n'
- 'def foo():\n'
- ' gmtime(1)\n'
- 'def bar():\n'
- ' foo()\n'
- 'bar()\n')
- # Verify with "py-bt":
- gdb_output = self.get_stack_trace(cmd,
- breakpoint='time_gmtime',
- cmds_after_breakpoint=['bt', 'py-bt'],
- )
- self.assertIn('<built-in method gmtime', gdb_output)
+ # Various optimizations multiply the code paths by which these are
+ # called, so test a variety of calling conventions.
+ for py_name, py_args, c_name, expected_frame_number in (
+ ('gmtime', '', 'time_gmtime', 1), # METH_VARARGS
+ ('len', '[]', 'builtin_len', 2), # METH_O
+ ('locals', '', 'builtin_locals', 2), # METH_NOARGS
+ ('iter', '[]', 'builtin_iter', 2), # METH_FASTCALL
+ ('sorted', '[]', 'builtin_sorted', 2), # METH_FASTCALL|METH_KEYWORDS
+ ):
+ with self.subTest(c_name):
+ cmd = ('from time import gmtime\n' # (not always needed)
+ 'def foo():\n'
+ f' {py_name}({py_args})\n'
+ 'def bar():\n'
+ ' foo()\n'
+ 'bar()\n')
+ # Verify with "py-bt":
+ gdb_output = self.get_stack_trace(
+ cmd,
+ breakpoint=c_name,
+ cmds_after_breakpoint=['bt', 'py-bt'],
+ )
+ self.assertIn(f'<built-in method {py_name}', gdb_output)
- # Verify with "py-bt-full":
- gdb_output = self.get_stack_trace(cmd,
- breakpoint='time_gmtime',
- cmds_after_breakpoint=['py-bt-full'],
- )
- self.assertIn('#1 <built-in method gmtime', gdb_output)
+ # Verify with "py-bt-full":
+ gdb_output = self.get_stack_trace(
+ cmd,
+ breakpoint=c_name,
+ cmds_after_breakpoint=['py-bt-full'],
+ )
+ self.assertIn(
+ f'#{expected_frame_number} <built-in method {py_name}',
+ gdb_output,
+ )
@unittest.skipIf(python_is_optimized(),
"Python was compiled with optimizations")