Kaydet (Commit) cc1db4bf authored tarafından Victor Stinner's avatar Victor Stinner

python-gdb.py: enhance py-bt command

* Add py-bt-full command
* py-bt now gives an output similar to a regular Python traceback
* py-bt indicates:

  - if the garbage collector is running
  - if the thread is waiting for the GIL
  - detect PyCFunction_Call to get the name of the builtin function
üst 3c5ce404
...@@ -13,6 +13,12 @@ import sysconfig ...@@ -13,6 +13,12 @@ import sysconfig
from test import test_support from test import test_support
from test.test_support import run_unittest, findfile from test.test_support import run_unittest, findfile
# Is this Python configured to support threads?
try:
import thread
except ImportError:
thread = None
def get_gdb_version(): def get_gdb_version():
try: try:
proc = subprocess.Popen(["gdb", "-nx", "--version"], proc = subprocess.Popen(["gdb", "-nx", "--version"],
...@@ -728,20 +734,133 @@ $''') ...@@ -728,20 +734,133 @@ $''')
class PyBtTests(DebuggerTests): class PyBtTests(DebuggerTests):
@unittest.skipIf(python_is_optimized(), @unittest.skipIf(python_is_optimized(),
"Python was compiled with optimizations") "Python was compiled with optimizations")
def test_basic_command(self): def test_bt(self):
'Verify that the "py-bt" command works' 'Verify that the "py-bt" command works'
bt = self.get_stack_trace(script=self.get_sample_script(), bt = self.get_stack_trace(script=self.get_sample_script(),
cmds_after_breakpoint=['py-bt']) cmds_after_breakpoint=['py-bt'])
self.assertMultilineMatches(bt, self.assertMultilineMatches(bt,
r'''^.* r'''^.*
#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) Traceback \(most recent call first\):
File ".*gdb_sample.py", line 10, in baz
print\(42\)
File ".*gdb_sample.py", line 7, in bar
baz\(a, b, c\)
File ".*gdb_sample.py", line 4, in foo
bar\(a, b, 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=self.get_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\) 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\) #[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
bar\(a, b, c\) bar\(a, b, c\)
#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\) #[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
foo\(1, 2, 3\) foo\(1, 2, 3\)
''') ''')
@unittest.skipUnless(thread,
"Python was compiled without thread support")
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
print 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
@unittest.skipUnless(thread,
"Python was compiled without thread support")
def test_gc(self):
'Verify that "py-bt" indicates if a thread is garbage-collecting'
cmd = ('from gc import collect\n'
'print 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")
# 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
@unittest.skipUnless(thread,
"Python was compiled without thread support")
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 function gmtime', 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('#0 <built-in function gmtime', gdb_output)
class PyPrintTests(DebuggerTests): class PyPrintTests(DebuggerTests):
@unittest.skipIf(python_is_optimized(), @unittest.skipIf(python_is_optimized(),
"Python was compiled with optimizations") "Python was compiled with optimizations")
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment