|
13 | 13 | from test import test_support |
14 | 14 | from test.test_support import run_unittest, findfile |
15 | 15 |
|
| 16 | +# Is this Python configured to support threads? |
| 17 | +try: |
| 18 | + import thread |
| 19 | +except ImportError: |
| 20 | + thread = None |
| 21 | + |
16 | 22 | def get_gdb_version(): |
17 | 23 | try: |
18 | 24 | proc = subprocess.Popen(["gdb", "-nx", "--version"], |
@@ -728,20 +734,133 @@ def test_up_then_down(self): |
728 | 734 | class PyBtTests(DebuggerTests): |
729 | 735 | @unittest.skipIf(python_is_optimized(), |
730 | 736 | "Python was compiled with optimizations") |
731 | | - def test_basic_command(self): |
| 737 | + def test_bt(self): |
732 | 738 | 'Verify that the "py-bt" command works' |
733 | 739 | bt = self.get_stack_trace(script=self.get_sample_script(), |
734 | 740 | cmds_after_breakpoint=['py-bt']) |
735 | 741 | self.assertMultilineMatches(bt, |
736 | 742 | r'''^.* |
737 | | -#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) |
| 743 | +Traceback \(most recent call first\): |
| 744 | + File ".*gdb_sample.py", line 10, in baz |
| 745 | + print\(42\) |
| 746 | + File ".*gdb_sample.py", line 7, in bar |
| 747 | + baz\(a, b, c\) |
| 748 | + File ".*gdb_sample.py", line 4, in foo |
| 749 | + bar\(a, b, c\) |
| 750 | + File ".*gdb_sample.py", line 12, in <module> |
| 751 | + foo\(1, 2, 3\) |
| 752 | +''') |
| 753 | + |
| 754 | + @unittest.skipIf(python_is_optimized(), |
| 755 | + "Python was compiled with optimizations") |
| 756 | + def test_bt_full(self): |
| 757 | + 'Verify that the "py-bt-full" command works' |
| 758 | + bt = self.get_stack_trace(script=self.get_sample_script(), |
| 759 | + cmds_after_breakpoint=['py-bt-full']) |
| 760 | + self.assertMultilineMatches(bt, |
| 761 | + r'''^.* |
| 762 | +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) |
738 | 763 | baz\(a, b, c\) |
739 | | -#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\) |
| 764 | +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\) |
740 | 765 | bar\(a, b, c\) |
741 | | -#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\) |
| 766 | +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\) |
742 | 767 | foo\(1, 2, 3\) |
743 | 768 | ''') |
744 | 769 |
|
| 770 | + @unittest.skipUnless(thread, |
| 771 | + "Python was compiled without thread support") |
| 772 | + def test_threads(self): |
| 773 | + 'Verify that "py-bt" indicates threads that are waiting for the GIL' |
| 774 | + cmd = ''' |
| 775 | +from threading import Thread |
| 776 | +
|
| 777 | +class TestThread(Thread): |
| 778 | + # These threads would run forever, but we'll interrupt things with the |
| 779 | + # debugger |
| 780 | + def run(self): |
| 781 | + i = 0 |
| 782 | + while 1: |
| 783 | + i += 1 |
| 784 | +
|
| 785 | +t = {} |
| 786 | +for i in range(4): |
| 787 | + t[i] = TestThread() |
| 788 | + t[i].start() |
| 789 | +
|
| 790 | +# Trigger a breakpoint on the main thread |
| 791 | +print 42 |
| 792 | +
|
| 793 | +''' |
| 794 | + # Verify with "py-bt": |
| 795 | + gdb_output = self.get_stack_trace(cmd, |
| 796 | + cmds_after_breakpoint=['thread apply all py-bt']) |
| 797 | + self.assertIn('Waiting for the GIL', gdb_output) |
| 798 | + |
| 799 | + # Verify with "py-bt-full": |
| 800 | + gdb_output = self.get_stack_trace(cmd, |
| 801 | + cmds_after_breakpoint=['thread apply all py-bt-full']) |
| 802 | + self.assertIn('Waiting for the GIL', gdb_output) |
| 803 | + |
| 804 | + @unittest.skipIf(python_is_optimized(), |
| 805 | + "Python was compiled with optimizations") |
| 806 | + # Some older versions of gdb will fail with |
| 807 | + # "Cannot find new threads: generic error" |
| 808 | + # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround |
| 809 | + @unittest.skipUnless(thread, |
| 810 | + "Python was compiled without thread support") |
| 811 | + def test_gc(self): |
| 812 | + 'Verify that "py-bt" indicates if a thread is garbage-collecting' |
| 813 | + cmd = ('from gc import collect\n' |
| 814 | + 'print 42\n' |
| 815 | + 'def foo():\n' |
| 816 | + ' collect()\n' |
| 817 | + 'def bar():\n' |
| 818 | + ' foo()\n' |
| 819 | + 'bar()\n') |
| 820 | + # Verify with "py-bt": |
| 821 | + gdb_output = self.get_stack_trace(cmd, |
| 822 | + cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt'], |
| 823 | + ) |
| 824 | + self.assertIn('Garbage-collecting', gdb_output) |
| 825 | + |
| 826 | + # Verify with "py-bt-full": |
| 827 | + gdb_output = self.get_stack_trace(cmd, |
| 828 | + cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt-full'], |
| 829 | + ) |
| 830 | + self.assertIn('Garbage-collecting', gdb_output) |
| 831 | + |
| 832 | + @unittest.skipIf(python_is_optimized(), |
| 833 | + "Python was compiled with optimizations") |
| 834 | + # Some older versions of gdb will fail with |
| 835 | + # "Cannot find new threads: generic error" |
| 836 | + # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround |
| 837 | + @unittest.skipUnless(thread, |
| 838 | + "Python was compiled without thread support") |
| 839 | + def test_pycfunction(self): |
| 840 | + 'Verify that "py-bt" displays invocations of PyCFunction instances' |
| 841 | + # Tested function must not be defined with METH_NOARGS or METH_O, |
| 842 | + # otherwise call_function() doesn't call PyCFunction_Call() |
| 843 | + cmd = ('from time import gmtime\n' |
| 844 | + 'def foo():\n' |
| 845 | + ' gmtime(1)\n' |
| 846 | + 'def bar():\n' |
| 847 | + ' foo()\n' |
| 848 | + 'bar()\n') |
| 849 | + # Verify with "py-bt": |
| 850 | + gdb_output = self.get_stack_trace(cmd, |
| 851 | + breakpoint='time_gmtime', |
| 852 | + cmds_after_breakpoint=['bt', 'py-bt'], |
| 853 | + ) |
| 854 | + self.assertIn('<built-in function gmtime', gdb_output) |
| 855 | + |
| 856 | + # Verify with "py-bt-full": |
| 857 | + gdb_output = self.get_stack_trace(cmd, |
| 858 | + breakpoint='time_gmtime', |
| 859 | + cmds_after_breakpoint=['py-bt-full'], |
| 860 | + ) |
| 861 | + self.assertIn('#0 <built-in function gmtime', gdb_output) |
| 862 | + |
| 863 | + |
745 | 864 | class PyPrintTests(DebuggerTests): |
746 | 865 | @unittest.skipIf(python_is_optimized(), |
747 | 866 | "Python was compiled with optimizations") |
|
0 commit comments