Example #1
0
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=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-bt'])
        self.assertMultilineMatches(
            bt, r'''^.*
Traceback \(most recent call first\):
  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, 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\)
#[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\)
#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
    foo\(1, 2, 3\)
''')
Example #2
0
class PyPrintTests(DebuggerTests):
    @unittest.skipIf(python_is_optimized(),
                     "Python was compiled with optimizations")
    def test_basic_command(self):
        'Verify that the "py-print" command works'
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-print args'])
        self.assertMultilineMatches(bt,
                                    r".*\nlocal 'args' = \(1, 2, 3\)\n.*")

    @unittest.skipIf(python_is_optimized(),
                     "Python was compiled with optimizations")
    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
    def test_print_after_up(self):
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-up', 'py-print c', 'py-print b', 'py-print a'])
        self.assertMultilineMatches(bt,
                                    r".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*")

    @unittest.skipIf(python_is_optimized(),
                     "Python was compiled with optimizations")
    def test_printing_global(self):
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-print __name__'])
        self.assertMultilineMatches(bt,
                                    r".*\nglobal '__name__' = '__main__'\n.*")

    @unittest.skipIf(python_is_optimized(),
                     "Python was compiled with optimizations")
    def test_printing_builtin(self):
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-print len'])
        self.assertMultilineMatches(bt,
                                    r".*\nbuiltin 'len' = <built-in method len of module object at remote 0x-?[0-9a-f]+>\n.*")
Example #3
0
class PyLocalsTests(DebuggerTests):
    @unittest.skipIf(python_is_optimized(),
                     "Python was compiled with optimizations")
    def test_basic_command(self):
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-locals'])
        self.assertMultilineMatches(bt, r".*\nargs = \(1, 2, 3\)\n.*")

    @unittest.skipUnless(HAS_PYUP_PYDOWN,
                         "test requires py-up/py-down commands")
    @unittest.skipIf(python_is_optimized(),
                     "Python was compiled with optimizations")
    def test_locals_after_up(self):
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-up', 'py-locals'])
        self.assertMultilineMatches(bt, r".*\na = 1\nb = 2\nc = 3\n.*")
Example #4
0
class StackNavigationTests(DebuggerTests):
    @unittest.skipUnless(HAS_PYUP_PYDOWN,
                         "test requires py-up/py-down commands")
    @unittest.skipIf(python_is_optimized(),
                     "Python was compiled with optimizations")
    def test_pyup_command(self):
        'Verify that the "py-up" command works'
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-up', 'py-up'])
        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\)
$''')

    @unittest.skipUnless(HAS_PYUP_PYDOWN,
                         "test requires py-up/py-down commands")
    def test_down_at_bottom(self):
        'Verify handling of "py-down" at the bottom of the stack'
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-down'])
        self.assertEndsWith(bt, 'Unable to find a newer python frame\n')

    @unittest.skipUnless(HAS_PYUP_PYDOWN,
                         "test requires py-up/py-down commands")
    def test_up_at_top(self):
        'Verify handling of "py-up" at the top of the stack'
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-up'] * 5)
        self.assertEndsWith(bt, 'Unable to find an older python frame\n')

    @unittest.skipUnless(HAS_PYUP_PYDOWN,
                         "test requires py-up/py-down commands")
    @unittest.skipIf(python_is_optimized(),
                     "Python was compiled with optimizations")
    def test_up_then_down(self):
        'Verify "py-up" followed by "py-down"'
        bt = self.get_stack_trace(
            script=self.get_sample_script(),
            cmds_after_breakpoint=['py-up', 'py-up', 'py-down'])
        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 10, in baz \(args=\(1, 2, 3\)\)
    id\(42\)
$''')
Example #5
0
class StackNavigationTests(DebuggerTests):
    @unittest.skipUnless(HAS_PYUP_PYDOWN,
                         "test requires py-up/py-down commands")
    @unittest.skipIf(python_is_optimized(),
                     "Python was compiled with optimizations")
    def test_pyup_command(self):
        'Verify that the "py-up" command works'
        bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL,
                                  cmds_after_breakpoint=['py-up', 'py-up'])
        self.assertMultilineMatches(
            bt, r'''^.*
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\)
#[0-9]+ <built-in method pyobject_fastcall of module object at remote 0x[0-9a-f]+>
$''')

    @unittest.skipUnless(HAS_PYUP_PYDOWN,
                         "test requires py-up/py-down commands")
    def test_down_at_bottom(self):
        'Verify handling of "py-down" at the bottom of the stack'
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-down'])
        self.assertEndsWith(bt, 'Unable to find a newer python frame\n')

    @unittest.skipUnless(HAS_PYUP_PYDOWN,
                         "test requires py-up/py-down commands")
    def test_up_at_top(self):
        'Verify handling of "py-up" at the top of the stack'
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-up'] * 5)
        self.assertEndsWith(bt, 'Unable to find an older python frame\n')

    @unittest.skipUnless(HAS_PYUP_PYDOWN,
                         "test requires py-up/py-down commands")
    @unittest.skipIf(python_is_optimized(),
                     "Python was compiled with optimizations")
    def test_up_then_down(self):
        'Verify "py-up" followed by "py-down"'
        bt = self.get_stack_trace(
            source=SAMPLE_WITH_C_CALL,
            cmds_after_breakpoint=['py-up', 'py-up', 'py-down'])
        self.assertMultilineMatches(
            bt, r'''^.*
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\)
#[0-9]+ <built-in method pyobject_fastcall of module object at remote 0x[0-9a-f]+>
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\)
$''')
Example #6
0
def test_main():
    if python_is_optimized():
        raise unittest.SkipTest("Python was compiled with optimizations")
    run_unittest(PrettyPrintTests,
                 PyListTests,
                 StackNavigationTests,
                 PyBtTests,
                 PyPrintTests,
                 PyLocalsTests
                 )
def test_main():
    if python_is_optimized():
        raise unittest.SkipTest("Python was compiled with optimizations")
    run_unittest(PrettyPrintTests,
                 PyListTests,
                 StackNavigationTests,
                 PyBtTests,
                 PyPrintTests,
                 PyLocalsTests
                 )
Example #8
0
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=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-bt'])
        self.assertMultilineMatches(
            bt, r'''^.*
Traceback \(most recent call first\):
  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, 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\)
#[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\)
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
    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
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
    @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'
               '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")
    # 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'
        cmd = ('from time import sleep\n'
               'def foo():\n'
               '    sleep(1)\n'
               'def bar():\n'
               '    foo()\n'
               'bar()\n')
        # Verify with "py-bt":
        gdb_output = self.get_stack_trace(
            cmd,
            breakpoint='time_sleep',
            cmds_after_breakpoint=['bt', 'py-bt'],
        )
        self.assertIn('<built-in method sleep', gdb_output)

        # Verify with "py-bt-full":
        gdb_output = self.get_stack_trace(
            cmd,
            breakpoint='time_sleep',
            cmds_after_breakpoint=['py-bt-full'],
        )
        self.assertIn('#0 <built-in method sleep', gdb_output)
Example #9
0
    pass

foo(3, 4, 5)
id(foo.__code__)''',
            breakpoint='builtin_id',
            cmds_after_breakpoint=[
                'print (PyFrameObject*)(((PyCodeObject*)v)->co_zombieframe)'
            ])
        self.assertTrue(
            re.match(
                '.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file <string>, line 3, in foo \(\)\s+.*',
                gdb_output, re.DOTALL),
            'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output))


@unittest.skipIf(python_is_optimized(),
                 "Python was compiled with optimizations")
class PyListTests(DebuggerTests):
    def assertListing(self, expected, actual):
        self.assertEndsWith(actual, expected)

    def test_basic_command(self):
        'Verify that the "py-list" command works'
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-list'])

        self.assertListing(
            '   5    \n'
            '   6    def bar(a, b, c):\n'
            '   7        baz(a, b, c)\n'
            '   8    \n'
    def test_frames(self):
        gdb_output = self.get_stack_trace('''
def foo(a, b, c):
    pass

foo(3, 4, 5)
id(foo.__code__)''',
                                          breakpoint='builtin_id',
                                          cmds_after_breakpoint=['print (PyFrameObject*)(((PyCodeObject*)v)->co_zombieframe)']
                                          )
        self.assertTrue(re.match('.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file <string>, line 3, in foo \(\)\s+.*',
                                 gdb_output,
                                 re.DOTALL),
                        'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output))

@unittest.skipIf(python_is_optimized(),
                 "Python was compiled with optimizations")
class PyListTests(DebuggerTests):
    def assertListing(self, expected, actual):
        self.assertEndsWith(actual, expected)

    def test_basic_command(self):
        'Verify that the "py-list" command works'
        bt = self.get_stack_trace(script=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-list'])

        self.assertListing('   5    \n'
                           '   6    def bar(a, b, c):\n'
                           '   7        baz(a, b, c)\n'
                           '   8    \n'
                           '   9    def baz(*args):\n'
Example #11
0
 def test_python_is_optimized(self):
     self.assertIsInstance(support.python_is_optimized(), bool)
Example #12
0
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=self.get_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, 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\)
#[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\)
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
    foo\(1, 2, 3\)
''')

    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")
    # 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_pycfunction(self):
        'Verify that "py-bt" displays invocations of PyCFunction instances'
        # 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', 1),  # METH_O
            ('locals', '', 'builtin_locals', 1),  # METH_NOARGS
            ('iter', '[]', 'builtin_iter', 1),  # METH_FASTCALL
            ('sorted', '[]', 'builtin_sorted', 1),  # 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=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")
    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 ")
Example #13
0
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=self.get_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, 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\)
#[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\)
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
    foo\(1, 2, 3\)
''')

    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")
    # 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_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)

        # Verify with "py-bt-full":
        gdb_output = self.get_stack_trace(
            cmd,
            breakpoint='time_gmtime',
            cmds_after_breakpoint=['py-bt-full'],
        )
        self.assertIn('#2 <built-in method gmtime', 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()
        ''')
        # Verify with "py-bt":
        gdb_output = self.get_stack_trace(
            cmd,
            cmds_after_breakpoint=['break wrapper_call', 'continue', 'py-bt'])
        self.assertRegex(gdb_output,
                         r"<method-wrapper u?'__init__' of MyList object at ")
Example #14
0
    pass

foo(3, 4, 5)
id(foo.__code__)""",
            breakpoint="builtin_id",
            cmds_after_breakpoint=["print (PyFrameObject*)(((PyCodeObject*)v)->co_zombieframe)"],
        )
        self.assertTrue(
            re.match(
                ".*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file <string>, line 3, in foo \(\)\s+.*", gdb_output, re.DOTALL
            ),
            "Unexpected gdb representation: %r\n%s" % (gdb_output, gdb_output),
        )


@unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations")
class PyListTests(DebuggerTests):
    def assertListing(self, expected, actual):
        self.assertEndsWith(actual, expected)

    def test_basic_command(self):
        'Verify that the "py-list" command works'
        bt = self.get_stack_trace(script=self.get_sample_script(), cmds_after_breakpoint=["py-list"])

        self.assertListing(
            "   5    \n"
            "   6    def bar(a, b, c):\n"
            "   7        baz(a, b, c)\n"
            "   8    \n"
            "   9    def baz(*args):\n"
            " >10        id(42)\n"
Example #15
0
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=self.get_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, 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\)
#[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\)
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
    foo\(1, 2, 3\)
''')

    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")
    # 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 test_pycfunction(self):
        'Verify that "py-bt" displays invocations of PyCFunction instances'
        # Various optimizations multiply the code paths by which these are
        # called, so test a variety of calling conventions.
        for func_name, args, expected_frame in (
            ('meth_varargs', '', 1),
            ('meth_varargs_keywords', '', 1),
            ('meth_o', '[]', 1),
            ('meth_noargs', '', 1),
            ('meth_fastcall', '', 1),
            ('meth_fastcall_keywords', '', 1),
        ):
            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}'):
                    cmd = textwrap.dedent(f'''
                        import _testcapi
                        def foo():
                            {obj}.{func_name}({args})
                        def bar():
                            foo()
                        bar()
                    ''')
                    # Verify with "py-bt":
                    gdb_output = self.get_stack_trace(
                        cmd,
                        breakpoint=func_name,
                        cmds_after_breakpoint=['bt', 'py-bt'],
                    )
                    self.assertIn(f'<built-in method {func_name}', gdb_output)

                    # Verify with "py-bt-full":
                    gdb_output = self.get_stack_trace(
                        cmd,
                        breakpoint=func_name,
                        cmds_after_breakpoint=['py-bt-full'],
                    )
                    self.assertIn(
                        f'#{expected_frame} <built-in method {func_name}',
                        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 ")
Example #16
0
 def test_python_is_optimized(self):
     self.assertIsInstance(support.python_is_optimized(), bool)
Example #17
0
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=self.get_sample_script(),
                                  cmds_after_breakpoint=['py-bt'])
        self.assertMultilineMatches(
            bt, """^.*
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, 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, """^.*
#[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, b, c\\)
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \\(\\)
    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
id(42)

"""
        gdb_output = self.get_stack_trace(
            cmd, cmds_after_breakpoint=['thread apply all py-bt'])
        self.assertIn('Waiting for the GIL', gdb_output)
        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')
    @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
id(42)
def foo():
    collect()
def bar():
    foo()
bar()
"""
        gdb_output = self.get_stack_trace(
            cmd,
            cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt'])
        self.assertIn('Garbage-collecting', gdb_output)
        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')
    @unittest.skipUnless(_thread, 'Python was compiled without thread support')
    def test_pycfunction(self):
        """Verify that "py-bt" displays invocations of PyCFunction instances"""
        cmd = (
            'from time import gmtime\ndef foo():\n    gmtime(1)\ndef bar():\n    foo()\nbar()\n'
        )
        gdb_output = self.get_stack_trace(
            cmd,
            breakpoint='time_gmtime',
            cmds_after_breakpoint=['bt', 'py-bt'])
        self.assertIn('<built-in method gmtime', gdb_output)
        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)

    @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()
        """)
        gdb_output = self.get_stack_trace(
            cmd,
            cmds_after_breakpoint=['break wrapper_call', 'continue', 'py-bt'])
        self.assertRegex(gdb_output,
                         "<method-wrapper u?'__init__' of MyList object at ")