Skip to content

Commit aa8d3db

Browse files
[3.11] [3.12] gh-109972: Enhance test_gdb (GH-110026) (GH-110351) (#110354)
[3.12] gh-109972: Enhance test_gdb (GH-110026) (GH-110351) * gh-109972: Enhance test_gdb (GH-110026) * Split test_pycfunction.py: add test_cfunction_full.py. Split the function into the following 6 functions. In verbose mode, these "pycfunction" tests now log each tested call. * test_pycfunction_noargs() * test_pycfunction_o() * test_pycfunction_varargs() * test_pycfunction_varargs_keywords() * test_pycfunction_fastcall() * test_pycfunction_fastcall_keywords() * Move get_gdb_repr() to PrettyPrintTests. * Replace DebuggerTests.get_sample_script() with SAMPLE_SCRIPT. * Rename checkout_hook_path to CHECKOUT_HOOK_PATH. * Rename gdb_version to GDB_VERSION_TEXT. * Replace (gdb_major_version, gdb_minor_version) with GDB_VERSION. * run_gdb() uses "backslashreplace" error handler instead of "replace". * Add check_gdb() function to util.py. * Enhance support.check_cflags_pgo(): check also for sysconfig PGO_PROF_USE_FLAG (if available) in compiler flags. * Move some SkipTest checks to test_gdb/__init__.py. * Elaborate why gdb cannot be tested on Windows: gdb doesn't support PDB debug symbol files. (cherry picked from commit 757cbd4) * gh-104736: Fix test_gdb tests on ppc64le with clang (GH-109360) Fix test_gdb on Python built with LLVM clang 16 on Linux ppc64le (ex: Fedora 38). Search patterns in gdb "bt" command output to detect when gdb fails to retrieve the traceback. For example, skip a test if "Backtrace stopped: frame did not save the PC" is found. (cherry picked from commit 44d9a71) * gh-110166: Fix gdb CFunctionFullTests on ppc64le clang build (GH-110331) CFunctionFullTests now also runs "bt" command before "py-bt-full", similar to CFunctionTests which also runs "bt" command before "py-bt". So test_gdb can skip the test if patterns like "?? ()" are found in the gdb output. (cherry picked from commit bbce8bd) Co-authored-by: Victor Stinner <vstinner@python.org> (cherry picked from commit 1de9406) Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent d507493 commit aa8d3db

File tree

9 files changed

+303
-218
lines changed

9 files changed

+303
-218
lines changed

Lib/test/support/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -769,14 +769,17 @@ def check_cflags_pgo():
769769
# Check if Python was built with ./configure --enable-optimizations:
770770
# with Profile Guided Optimization (PGO).
771771
cflags_nodist = sysconfig.get_config_var('PY_CFLAGS_NODIST') or ''
772-
pgo_options = (
772+
pgo_options = [
773773
# GCC
774774
'-fprofile-use',
775775
# clang: -fprofile-instr-use=code.profclangd
776776
'-fprofile-instr-use',
777777
# ICC
778778
"-prof-use",
779-
)
779+
]
780+
PGO_PROF_USE_FLAG = sysconfig.get_config_var('PGO_PROF_USE_FLAG')
781+
if PGO_PROF_USE_FLAG:
782+
pgo_options.append(PGO_PROF_USE_FLAG)
780783
return any(option in cflags_nodist for option in pgo_options)
781784

782785

Lib/test/test_gdb/__init__.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,27 @@
44
# Lib/test/test_jit_gdb.py
55

66
import os
7-
from test.support import load_package_tests
7+
import sysconfig
8+
import unittest
9+
from test import support
10+
11+
12+
MS_WINDOWS = (os.name == 'nt')
13+
if MS_WINDOWS:
14+
# On Windows, Python is usually built by MSVC. Passing /p:DebugSymbols=true
15+
# option to MSBuild produces PDB debug symbols, but gdb doesn't support PDB
16+
# debug symbol files.
17+
raise unittest.SkipTest("test_gdb doesn't work on Windows")
18+
19+
if support.PGO:
20+
raise unittest.SkipTest("test_gdb is not useful for PGO")
21+
22+
if not sysconfig.is_python_build():
23+
raise unittest.SkipTest("test_gdb only works on source builds at the moment.")
24+
25+
if support.check_cflags_pgo():
26+
raise unittest.SkipTest("test_gdb is not reliable on PGO builds")
27+
828

929
def load_tests(*args):
10-
return load_package_tests(os.path.dirname(__file__), *args)
30+
return support.load_package_tests(os.path.dirname(__file__), *args)

Lib/test/test_gdb/test_backtrace.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from test import support
44
from test.support import python_is_optimized
55

6-
from .util import setup_module, DebuggerTests, CET_PROTECTION
6+
from .util import setup_module, DebuggerTests, CET_PROTECTION, SAMPLE_SCRIPT
77

88

99
def setUpModule():
@@ -15,7 +15,7 @@ class PyBtTests(DebuggerTests):
1515
"Python was compiled with optimizations")
1616
def test_bt(self):
1717
'Verify that the "py-bt" command works'
18-
bt = self.get_stack_trace(script=self.get_sample_script(),
18+
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
1919
cmds_after_breakpoint=['py-bt'])
2020
self.assertMultilineMatches(bt,
2121
r'''^.*
@@ -35,7 +35,7 @@ def test_bt(self):
3535
"Python was compiled with optimizations")
3636
def test_bt_full(self):
3737
'Verify that the "py-bt-full" command works'
38-
bt = self.get_stack_trace(script=self.get_sample_script(),
38+
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
3939
cmds_after_breakpoint=['py-bt-full'])
4040
self.assertMultilineMatches(bt,
4141
r'''^.*

Lib/test/test_gdb/test_cfunction.py

Lines changed: 58 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import re
21
import textwrap
32
import unittest
43
from test import support
5-
from test.support import python_is_optimized
64

75
from .util import setup_module, DebuggerTests
86

@@ -11,10 +9,22 @@ def setUpModule():
119
setup_module()
1210

1311

14-
@unittest.skipIf(python_is_optimized(),
12+
@unittest.skipIf(support.python_is_optimized(),
1513
"Python was compiled with optimizations")
1614
@support.requires_resource('cpu')
1715
class CFunctionTests(DebuggerTests):
16+
def check(self, func_name, cmd):
17+
# Verify with "py-bt":
18+
gdb_output = self.get_stack_trace(
19+
cmd,
20+
breakpoint=func_name,
21+
cmds_after_breakpoint=['bt', 'py-bt'],
22+
# bpo-45207: Ignore 'Function "meth_varargs" not
23+
# defined.' message in stderr.
24+
ignore_stderr=True,
25+
)
26+
self.assertIn(f'<built-in method {func_name}', gdb_output)
27+
1828
# Some older versions of gdb will fail with
1929
# "Cannot find new threads: generic error"
2030
# unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
@@ -24,60 +34,52 @@ class CFunctionTests(DebuggerTests):
2434
# This is because we are calling functions from an "external" module
2535
# (_testcapimodule) rather than compiled-in functions. It seems difficult
2636
# to suppress these. See also the comment in DebuggerTests.get_stack_trace
27-
def test_pycfunction(self):
37+
def check_pycfunction(self, func_name, args):
2838
'Verify that "py-bt" displays invocations of PyCFunction instances'
29-
# bpo-46600: If the compiler inlines _null_to_none() in meth_varargs()
30-
# (ex: clang -Og), _null_to_none() is the frame #1. Otherwise,
31-
# meth_varargs() is the frame #1.
32-
expected_frame = r'#(1|2)'
39+
40+
if support.verbose:
41+
print()
42+
3343
# Various optimizations multiply the code paths by which these are
3444
# called, so test a variety of calling conventions.
35-
for func_name, args in (
36-
('meth_varargs', ''),
37-
('meth_varargs_keywords', ''),
38-
('meth_o', '[]'),
39-
('meth_noargs', ''),
40-
('meth_fastcall', ''),
41-
('meth_fastcall_keywords', ''),
45+
for obj in (
46+
'_testcapi',
47+
'_testcapi.MethClass',
48+
'_testcapi.MethClass()',
49+
'_testcapi.MethStatic()',
50+
51+
# XXX: bound methods don't yet give nice tracebacks
52+
# '_testcapi.MethInstance()',
4253
):
43-
for obj in (
44-
'_testcapi',
45-
'_testcapi.MethClass',
46-
'_testcapi.MethClass()',
47-
'_testcapi.MethStatic()',
48-
49-
# XXX: bound methods don't yet give nice tracebacks
50-
# '_testcapi.MethInstance()',
51-
):
52-
with self.subTest(f'{obj}.{func_name}'):
53-
cmd = textwrap.dedent(f'''
54-
import _testcapi
55-
def foo():
56-
{obj}.{func_name}({args})
57-
def bar():
58-
foo()
59-
bar()
60-
''')
61-
# Verify with "py-bt":
62-
gdb_output = self.get_stack_trace(
63-
cmd,
64-
breakpoint=func_name,
65-
cmds_after_breakpoint=['bt', 'py-bt'],
66-
# bpo-45207: Ignore 'Function "meth_varargs" not
67-
# defined.' message in stderr.
68-
ignore_stderr=True,
69-
)
70-
self.assertIn(f'<built-in method {func_name}', gdb_output)
71-
72-
# Verify with "py-bt-full":
73-
gdb_output = self.get_stack_trace(
74-
cmd,
75-
breakpoint=func_name,
76-
cmds_after_breakpoint=['py-bt-full'],
77-
# bpo-45207: Ignore 'Function "meth_varargs" not
78-
# defined.' message in stderr.
79-
ignore_stderr=True,
80-
)
81-
regex = expected_frame
82-
regex += re.escape(f' <built-in method {func_name}')
83-
self.assertRegex(gdb_output, regex)
54+
with self.subTest(f'{obj}.{func_name}'):
55+
call = f'{obj}.{func_name}({args})'
56+
cmd = textwrap.dedent(f'''
57+
import _testcapi
58+
def foo():
59+
{call}
60+
def bar():
61+
foo()
62+
bar()
63+
''')
64+
if support.verbose:
65+
print(f' test call: {call}', flush=True)
66+
67+
self.check(func_name, cmd)
68+
69+
def test_pycfunction_noargs(self):
70+
self.check_pycfunction('meth_noargs', '')
71+
72+
def test_pycfunction_o(self):
73+
self.check_pycfunction('meth_o', '[]')
74+
75+
def test_pycfunction_varargs(self):
76+
self.check_pycfunction('meth_varargs', '')
77+
78+
def test_pycfunction_varargs_keywords(self):
79+
self.check_pycfunction('meth_varargs_keywords', '')
80+
81+
def test_pycfunction_fastcall(self):
82+
self.check_pycfunction('meth_fastcall', '')
83+
84+
def test_pycfunction_fastcall_keywords(self):
85+
self.check_pycfunction('meth_fastcall_keywords', '')
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
Similar to test_cfunction but test "py-bt-full" command.
3+
"""
4+
5+
import re
6+
7+
from .util import setup_module
8+
from .test_cfunction import CFunctionTests
9+
10+
11+
def setUpModule():
12+
setup_module()
13+
14+
15+
class CFunctionFullTests(CFunctionTests):
16+
def check(self, func_name, cmd):
17+
# Verify with "py-bt-full":
18+
gdb_output = self.get_stack_trace(
19+
cmd,
20+
breakpoint=func_name,
21+
cmds_after_breakpoint=['bt', 'py-bt-full'],
22+
# bpo-45207: Ignore 'Function "meth_varargs" not
23+
# defined.' message in stderr.
24+
ignore_stderr=True,
25+
)
26+
27+
# bpo-46600: If the compiler inlines _null_to_none() in
28+
# meth_varargs() (ex: clang -Og), _null_to_none() is the
29+
# frame #1. Otherwise, meth_varargs() is the frame #1.
30+
regex = r'#(1|2)'
31+
regex += re.escape(f' <built-in method {func_name}')
32+
self.assertRegex(gdb_output, regex)
33+
34+
35+
# Delete the test case, otherwise it's executed twice
36+
del CFunctionTests

Lib/test/test_gdb/test_misc.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import unittest
33
from test.support import python_is_optimized
44

5-
from .util import run_gdb, setup_module, DebuggerTests
5+
from .util import run_gdb, setup_module, DebuggerTests, SAMPLE_SCRIPT
66

77

88
def setUpModule():
@@ -32,7 +32,7 @@ def assertListing(self, expected, actual):
3232

3333
def test_basic_command(self):
3434
'Verify that the "py-list" command works'
35-
bt = self.get_stack_trace(script=self.get_sample_script(),
35+
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
3636
cmds_after_breakpoint=['py-list'])
3737

3838
self.assertListing(' 5 \n'
@@ -47,7 +47,7 @@ def test_basic_command(self):
4747

4848
def test_one_abs_arg(self):
4949
'Verify the "py-list" command with one absolute argument'
50-
bt = self.get_stack_trace(script=self.get_sample_script(),
50+
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
5151
cmds_after_breakpoint=['py-list 9'])
5252

5353
self.assertListing(' 9 def baz(*args):\n'
@@ -58,7 +58,7 @@ def test_one_abs_arg(self):
5858

5959
def test_two_abs_args(self):
6060
'Verify the "py-list" command with two absolute arguments'
61-
bt = self.get_stack_trace(script=self.get_sample_script(),
61+
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
6262
cmds_after_breakpoint=['py-list 1,3'])
6363

6464
self.assertListing(' 1 # Sample script for use by test_gdb\n'
@@ -101,15 +101,15 @@ def test_pyup_command(self):
101101
@unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
102102
def test_down_at_bottom(self):
103103
'Verify handling of "py-down" at the bottom of the stack'
104-
bt = self.get_stack_trace(script=self.get_sample_script(),
104+
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
105105
cmds_after_breakpoint=['py-down'])
106106
self.assertEndsWith(bt,
107107
'Unable to find a newer python frame\n')
108108

109109
@unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
110110
def test_up_at_top(self):
111111
'Verify handling of "py-up" at the top of the stack'
112-
bt = self.get_stack_trace(script=self.get_sample_script(),
112+
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
113113
cmds_after_breakpoint=['py-up'] * 5)
114114
self.assertEndsWith(bt,
115115
'Unable to find an older python frame\n')
@@ -150,15 +150,15 @@ def test_print_after_up(self):
150150
@unittest.skipIf(python_is_optimized(),
151151
"Python was compiled with optimizations")
152152
def test_printing_global(self):
153-
bt = self.get_stack_trace(script=self.get_sample_script(),
153+
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
154154
cmds_after_breakpoint=['py-up', 'py-print __name__'])
155155
self.assertMultilineMatches(bt,
156156
r".*\nglobal '__name__' = '__main__'\n.*")
157157

158158
@unittest.skipIf(python_is_optimized(),
159159
"Python was compiled with optimizations")
160160
def test_printing_builtin(self):
161-
bt = self.get_stack_trace(script=self.get_sample_script(),
161+
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
162162
cmds_after_breakpoint=['py-up', 'py-print len'])
163163
self.assertMultilineMatches(bt,
164164
r".*\nbuiltin 'len' = <built-in method len of module object at remote 0x-?[0-9a-f]+>\n.*")
@@ -167,7 +167,7 @@ class PyLocalsTests(DebuggerTests):
167167
@unittest.skipIf(python_is_optimized(),
168168
"Python was compiled with optimizations")
169169
def test_basic_command(self):
170-
bt = self.get_stack_trace(script=self.get_sample_script(),
170+
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
171171
cmds_after_breakpoint=['py-up', 'py-locals'])
172172
self.assertMultilineMatches(bt,
173173
r".*\nargs = \(1, 2, 3\)\n.*")
@@ -176,7 +176,7 @@ def test_basic_command(self):
176176
@unittest.skipIf(python_is_optimized(),
177177
"Python was compiled with optimizations")
178178
def test_locals_after_up(self):
179-
bt = self.get_stack_trace(script=self.get_sample_script(),
179+
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
180180
cmds_after_breakpoint=['py-up', 'py-up', 'py-locals'])
181181
self.assertMultilineMatches(bt,
182182
r'''^.*

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy