forked from python/cpython
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pythongh-109972: Split test_gdb into a package of 3 tests
Split test_gdb.py file into a test_gdb package made 3 tests, so tests can now be run in parallel. * Create Lib/test/test_gdb/ directory. * Split test_gdb.py into multiple files in Lib/test/test_gdb/ directory. * Move Lib/test/gdb_sample.py to Lib/test/test_gdb/ directory. * Split PyBtTests.test_pycfunction() into 2 files (test_cfunction and test_cfunction_full) and 6 functions: * test_pycfunction_noargs() * test_pycfunction_o() * test_pycfunction_varargs() * test_pycfunction_varargs_keywords() * test_pycfunction_fastcall() * test_pycfunction_fastcall_keywords() * In verbose mode, these "pycfunction" tests now log each tested call. * 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. * Add check_gdb() function to util.py. * Explicitly skip test_gdb on Windows.
- Loading branch information
Showing
11 changed files
with
1,195 additions
and
1,066 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Verify that gdb can pretty-print the various PyObject* types | ||
# | ||
# The code for testing gdb was adapted from similar work in Unladen Swallow's | ||
# Lib/test/test_jit_gdb.py | ||
|
||
import os | ||
from test.support import load_package_tests | ||
|
||
def load_tests(*args): | ||
return load_package_tests(os.path.dirname(__file__), *args) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
# Sample script for use by test_gdb.py | ||
# Sample script for use by test_gdb | ||
|
||
def foo(a, b, c): | ||
bar(a=a, b=b, c=c) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import textwrap | ||
import unittest | ||
from test import support | ||
from test.support import python_is_optimized | ||
|
||
from .util import setup_module, DebuggerTests, CET_PROTECTION, SAMPLE_SCRIPT | ||
|
||
|
||
def setUpModule(): | ||
setup_module() | ||
|
||
|
||
class PyBtTests(DebuggerTests): | ||
@unittest.skipIf(python_is_optimized(), | ||
"Python was compiled with optimizations") | ||
def test_bt(self): | ||
'Verify that the "py-bt" command works' | ||
bt = self.get_stack_trace(script=SAMPLE_SCRIPT, | ||
cmds_after_breakpoint=['py-bt']) | ||
self.assertMultilineMatches(bt, | ||
r'''^.* | ||
Traceback \(most recent call first\): | ||
<built-in method id of module object .*> | ||
File ".*gdb_sample.py", line 10, in baz | ||
id\(42\) | ||
File ".*gdb_sample.py", line 7, in bar | ||
baz\(a, b, c\) | ||
File ".*gdb_sample.py", line 4, in foo | ||
bar\(a=a, b=b, c=c\) | ||
File ".*gdb_sample.py", line 12, in <module> | ||
foo\(1, 2, 3\) | ||
''') | ||
|
||
@unittest.skipIf(python_is_optimized(), | ||
"Python was compiled with optimizations") | ||
def test_bt_full(self): | ||
'Verify that the "py-bt-full" command works' | ||
bt = self.get_stack_trace(script=SAMPLE_SCRIPT, | ||
cmds_after_breakpoint=['py-bt-full']) | ||
self.assertMultilineMatches(bt, | ||
r'''^.* | ||
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) | ||
baz\(a, b, c\) | ||
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\) | ||
bar\(a=a, b=b, c=c\) | ||
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\) | ||
foo\(1, 2, 3\) | ||
''') | ||
|
||
@unittest.skipIf(python_is_optimized(), | ||
"Python was compiled with optimizations") | ||
@support.requires_resource('cpu') | ||
def test_threads(self): | ||
'Verify that "py-bt" indicates threads that are waiting for the GIL' | ||
cmd = ''' | ||
from threading import Thread | ||
class TestThread(Thread): | ||
# These threads would run forever, but we'll interrupt things with the | ||
# debugger | ||
def run(self): | ||
i = 0 | ||
while 1: | ||
i += 1 | ||
t = {} | ||
for i in range(4): | ||
t[i] = TestThread() | ||
t[i].start() | ||
# Trigger a breakpoint on the main thread | ||
id(42) | ||
''' | ||
# Verify with "py-bt": | ||
gdb_output = self.get_stack_trace(cmd, | ||
cmds_after_breakpoint=['thread apply all py-bt']) | ||
self.assertIn('Waiting for the GIL', gdb_output) | ||
|
||
# Verify with "py-bt-full": | ||
gdb_output = self.get_stack_trace(cmd, | ||
cmds_after_breakpoint=['thread apply all py-bt-full']) | ||
self.assertIn('Waiting for the GIL', gdb_output) | ||
|
||
@unittest.skipIf(python_is_optimized(), | ||
"Python was compiled with optimizations") | ||
# Some older versions of gdb will fail with | ||
# "Cannot find new threads: generic error" | ||
# unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround | ||
def test_gc(self): | ||
'Verify that "py-bt" indicates if a thread is garbage-collecting' | ||
cmd = ('from gc import collect\n' | ||
'id(42)\n' | ||
'def foo():\n' | ||
' collect()\n' | ||
'def bar():\n' | ||
' foo()\n' | ||
'bar()\n') | ||
# Verify with "py-bt": | ||
gdb_output = self.get_stack_trace(cmd, | ||
cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt'], | ||
) | ||
self.assertIn('Garbage-collecting', gdb_output) | ||
|
||
# Verify with "py-bt-full": | ||
gdb_output = self.get_stack_trace(cmd, | ||
cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt-full'], | ||
) | ||
self.assertIn('Garbage-collecting', gdb_output) | ||
|
||
@unittest.skipIf(python_is_optimized(), | ||
"Python was compiled with optimizations") | ||
def test_wrapper_call(self): | ||
cmd = textwrap.dedent(''' | ||
class MyList(list): | ||
def __init__(self): | ||
super(*[]).__init__() # wrapper_call() | ||
id("first break point") | ||
l = MyList() | ||
''') | ||
cmds_after_breakpoint = ['break wrapper_call', 'continue'] | ||
if CET_PROTECTION: | ||
# bpo-32962: same case as in get_stack_trace(): | ||
# we need an additional 'next' command in order to read | ||
# arguments of the innermost function of the call stack. | ||
cmds_after_breakpoint.append('next') | ||
cmds_after_breakpoint.append('py-bt') | ||
|
||
# Verify with "py-bt": | ||
gdb_output = self.get_stack_trace(cmd, | ||
cmds_after_breakpoint=cmds_after_breakpoint) | ||
self.assertRegex(gdb_output, | ||
r"<method-wrapper u?'__init__' of MyList object at ") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import textwrap | ||
import unittest | ||
from test import support | ||
|
||
from .util import setup_module, DebuggerTests | ||
|
||
|
||
def setUpModule(): | ||
setup_module() | ||
|
||
|
||
@unittest.skipIf(support.python_is_optimized(), | ||
"Python was compiled with optimizations") | ||
@support.requires_resource('cpu') | ||
class CFunctionTests(DebuggerTests): | ||
def check(self, func_name, cmd): | ||
# Verify with "py-bt": | ||
gdb_output = self.get_stack_trace( | ||
cmd, | ||
breakpoint=func_name, | ||
cmds_after_breakpoint=['bt', 'py-bt'], | ||
# bpo-45207: Ignore 'Function "meth_varargs" not | ||
# defined.' message in stderr. | ||
ignore_stderr=True, | ||
) | ||
self.assertIn(f'<built-in method {func_name}', gdb_output) | ||
|
||
# Some older versions of gdb will fail with | ||
# "Cannot find new threads: generic error" | ||
# unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround | ||
# | ||
# gdb will also generate many erroneous errors such as: | ||
# Function "meth_varargs" not defined. | ||
# This is because we are calling functions from an "external" module | ||
# (_testcapimodule) rather than compiled-in functions. It seems difficult | ||
# to suppress these. See also the comment in DebuggerTests.get_stack_trace | ||
def check_pycfunction(self, func_name, args): | ||
'Verify that "py-bt" displays invocations of PyCFunction instances' | ||
|
||
if support.verbose: | ||
print() | ||
|
||
# Various optimizations multiply the code paths by which these are | ||
# called, so test a variety of calling conventions. | ||
for obj in ( | ||
'_testcapi', | ||
'_testcapi.MethClass', | ||
'_testcapi.MethClass()', | ||
'_testcapi.MethStatic()', | ||
|
||
# XXX: bound methods don't yet give nice tracebacks | ||
# '_testcapi.MethInstance()', | ||
): | ||
with self.subTest(f'{obj}.{func_name}'): | ||
call = f'{obj}.{func_name}({args})' | ||
cmd = textwrap.dedent(f''' | ||
import _testcapi | ||
def foo(): | ||
{call} | ||
def bar(): | ||
foo() | ||
bar() | ||
''') | ||
if support.verbose: | ||
print(f' test call: {call}', flush=True) | ||
|
||
self.check(func_name, cmd) | ||
|
||
def test_pycfunction_noargs(self): | ||
self.check_pycfunction('meth_noargs', '') | ||
|
||
def test_pycfunction_o(self): | ||
self.check_pycfunction('meth_o', '[]') | ||
|
||
def test_pycfunction_varargs(self): | ||
self.check_pycfunction('meth_varargs', '') | ||
|
||
def test_pycfunction_varargs_keywords(self): | ||
self.check_pycfunction('meth_varargs_keywords', '') | ||
|
||
def test_pycfunction_fastcall(self): | ||
self.check_pycfunction('meth_fastcall', '') | ||
|
||
def test_pycfunction_fastcall_keywords(self): | ||
self.check_pycfunction('meth_fastcall_keywords', '') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
""" | ||
Similar to test_cfunction but test "py-bt-full" command. | ||
""" | ||
|
||
import re | ||
|
||
from .util import setup_module | ||
from .test_cfunction import CFunctionTests | ||
|
||
|
||
def setUpModule(): | ||
setup_module() | ||
|
||
|
||
class CFunctionFullTests(CFunctionTests): | ||
def check(self, func_name, cmd): | ||
# Verify with "py-bt-full": | ||
gdb_output = self.get_stack_trace( | ||
cmd, | ||
breakpoint=func_name, | ||
cmds_after_breakpoint=['py-bt-full'], | ||
# bpo-45207: Ignore 'Function "meth_varargs" not | ||
# defined.' message in stderr. | ||
ignore_stderr=True, | ||
) | ||
|
||
# bpo-46600: If the compiler inlines _null_to_none() in | ||
# meth_varargs() (ex: clang -Og), _null_to_none() is the | ||
# frame #1. Otherwise, meth_varargs() is the frame #1. | ||
regex = r'#(1|2)' | ||
regex += re.escape(f' <built-in method {func_name}') | ||
self.assertRegex(gdb_output, regex) | ||
|
||
|
||
# Delete the test case, otherwise it's executed twice | ||
del CFunctionTests |
Oops, something went wrong.