17db96d56Sopenharmony_ci# Verify that gdb can pretty-print the various PyObject* types
27db96d56Sopenharmony_ci#
37db96d56Sopenharmony_ci# The code for testing gdb was adapted from similar work in Unladen Swallow's
47db96d56Sopenharmony_ci# Lib/test/test_jit_gdb.py
57db96d56Sopenharmony_ci
67db96d56Sopenharmony_ciimport os
77db96d56Sopenharmony_ciimport platform
87db96d56Sopenharmony_ciimport re
97db96d56Sopenharmony_ciimport subprocess
107db96d56Sopenharmony_ciimport sys
117db96d56Sopenharmony_ciimport sysconfig
127db96d56Sopenharmony_ciimport textwrap
137db96d56Sopenharmony_ciimport unittest
147db96d56Sopenharmony_ci
157db96d56Sopenharmony_cifrom test import support
167db96d56Sopenharmony_cifrom test.support import findfile, python_is_optimized
177db96d56Sopenharmony_ci
187db96d56Sopenharmony_cidef get_gdb_version():
197db96d56Sopenharmony_ci    try:
207db96d56Sopenharmony_ci        cmd = ["gdb", "-nx", "--version"]
217db96d56Sopenharmony_ci        proc = subprocess.Popen(cmd,
227db96d56Sopenharmony_ci                                stdout=subprocess.PIPE,
237db96d56Sopenharmony_ci                                stderr=subprocess.PIPE,
247db96d56Sopenharmony_ci                                universal_newlines=True)
257db96d56Sopenharmony_ci        with proc:
267db96d56Sopenharmony_ci            version, stderr = proc.communicate()
277db96d56Sopenharmony_ci
287db96d56Sopenharmony_ci        if proc.returncode:
297db96d56Sopenharmony_ci            raise Exception(f"Command {' '.join(cmd)!r} failed "
307db96d56Sopenharmony_ci                            f"with exit code {proc.returncode}: "
317db96d56Sopenharmony_ci                            f"stdout={version!r} stderr={stderr!r}")
327db96d56Sopenharmony_ci    except OSError:
337db96d56Sopenharmony_ci        # This is what "no gdb" looks like.  There may, however, be other
347db96d56Sopenharmony_ci        # errors that manifest this way too.
357db96d56Sopenharmony_ci        raise unittest.SkipTest("Couldn't find gdb on the path")
367db96d56Sopenharmony_ci
377db96d56Sopenharmony_ci    # Regex to parse:
387db96d56Sopenharmony_ci    # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7
397db96d56Sopenharmony_ci    # 'GNU gdb (GDB) Fedora 7.9.1-17.fc22\n' -> 7.9
407db96d56Sopenharmony_ci    # 'GNU gdb 6.1.1 [FreeBSD]\n' -> 6.1
417db96d56Sopenharmony_ci    # 'GNU gdb (GDB) Fedora (7.5.1-37.fc18)\n' -> 7.5
427db96d56Sopenharmony_ci    # 'HP gdb 6.7 for HP Itanium (32 or 64 bit) and target HP-UX 11iv2 and 11iv3.\n' -> 6.7
437db96d56Sopenharmony_ci    match = re.search(r"^(?:GNU|HP) gdb.*?\b(\d+)\.(\d+)", version)
447db96d56Sopenharmony_ci    if match is None:
457db96d56Sopenharmony_ci        raise Exception("unable to parse GDB version: %r" % version)
467db96d56Sopenharmony_ci    return (version, int(match.group(1)), int(match.group(2)))
477db96d56Sopenharmony_ci
487db96d56Sopenharmony_cigdb_version, gdb_major_version, gdb_minor_version = get_gdb_version()
497db96d56Sopenharmony_ciif gdb_major_version < 7:
507db96d56Sopenharmony_ci    raise unittest.SkipTest("gdb versions before 7.0 didn't support python "
517db96d56Sopenharmony_ci                            "embedding. Saw %s.%s:\n%s"
527db96d56Sopenharmony_ci                            % (gdb_major_version, gdb_minor_version,
537db96d56Sopenharmony_ci                               gdb_version))
547db96d56Sopenharmony_ci
557db96d56Sopenharmony_ciif not sysconfig.is_python_build():
567db96d56Sopenharmony_ci    raise unittest.SkipTest("test_gdb only works on source builds at the moment.")
577db96d56Sopenharmony_ci
587db96d56Sopenharmony_ciif 'Clang' in platform.python_compiler() and sys.platform == 'darwin':
597db96d56Sopenharmony_ci    raise unittest.SkipTest("test_gdb doesn't work correctly when python is"
607db96d56Sopenharmony_ci                            " built with LLVM clang")
617db96d56Sopenharmony_ci
627db96d56Sopenharmony_ciif ((sysconfig.get_config_var('PGO_PROF_USE_FLAG') or 'xxx') in
637db96d56Sopenharmony_ci    (sysconfig.get_config_var('PY_CORE_CFLAGS') or '')):
647db96d56Sopenharmony_ci    raise unittest.SkipTest("test_gdb is not reliable on PGO builds")
657db96d56Sopenharmony_ci
667db96d56Sopenharmony_ci# Location of custom hooks file in a repository checkout.
677db96d56Sopenharmony_cicheckout_hook_path = os.path.join(os.path.dirname(sys.executable),
687db96d56Sopenharmony_ci                                  'python-gdb.py')
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_ciPYTHONHASHSEED = '123'
717db96d56Sopenharmony_ci
727db96d56Sopenharmony_ci
737db96d56Sopenharmony_cidef cet_protection():
747db96d56Sopenharmony_ci    cflags = sysconfig.get_config_var('CFLAGS')
757db96d56Sopenharmony_ci    if not cflags:
767db96d56Sopenharmony_ci        return False
777db96d56Sopenharmony_ci    flags = cflags.split()
787db96d56Sopenharmony_ci    # True if "-mcet -fcf-protection" options are found, but false
797db96d56Sopenharmony_ci    # if "-fcf-protection=none" or "-fcf-protection=return" is found.
807db96d56Sopenharmony_ci    return (('-mcet' in flags)
817db96d56Sopenharmony_ci            and any((flag.startswith('-fcf-protection')
827db96d56Sopenharmony_ci                     and not flag.endswith(("=none", "=return")))
837db96d56Sopenharmony_ci                    for flag in flags))
847db96d56Sopenharmony_ci
857db96d56Sopenharmony_ci# Control-flow enforcement technology
867db96d56Sopenharmony_ciCET_PROTECTION = cet_protection()
877db96d56Sopenharmony_ci
887db96d56Sopenharmony_ci
897db96d56Sopenharmony_cidef run_gdb(*args, **env_vars):
907db96d56Sopenharmony_ci    """Runs gdb in --batch mode with the additional arguments given by *args.
917db96d56Sopenharmony_ci
927db96d56Sopenharmony_ci    Returns its (stdout, stderr) decoded from utf-8 using the replace handler.
937db96d56Sopenharmony_ci    """
947db96d56Sopenharmony_ci    if env_vars:
957db96d56Sopenharmony_ci        env = os.environ.copy()
967db96d56Sopenharmony_ci        env.update(env_vars)
977db96d56Sopenharmony_ci    else:
987db96d56Sopenharmony_ci        env = None
997db96d56Sopenharmony_ci    # -nx: Do not execute commands from any .gdbinit initialization files
1007db96d56Sopenharmony_ci    #      (issue #22188)
1017db96d56Sopenharmony_ci    base_cmd = ('gdb', '--batch', '-nx')
1027db96d56Sopenharmony_ci    if (gdb_major_version, gdb_minor_version) >= (7, 4):
1037db96d56Sopenharmony_ci        base_cmd += ('-iex', 'add-auto-load-safe-path ' + checkout_hook_path)
1047db96d56Sopenharmony_ci    proc = subprocess.Popen(base_cmd + args,
1057db96d56Sopenharmony_ci                            # Redirect stdin to prevent GDB from messing with
1067db96d56Sopenharmony_ci                            # the terminal settings
1077db96d56Sopenharmony_ci                            stdin=subprocess.PIPE,
1087db96d56Sopenharmony_ci                            stdout=subprocess.PIPE,
1097db96d56Sopenharmony_ci                            stderr=subprocess.PIPE,
1107db96d56Sopenharmony_ci                            env=env)
1117db96d56Sopenharmony_ci    with proc:
1127db96d56Sopenharmony_ci        out, err = proc.communicate()
1137db96d56Sopenharmony_ci    return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace')
1147db96d56Sopenharmony_ci
1157db96d56Sopenharmony_ci# Verify that "gdb" was built with the embedded python support enabled:
1167db96d56Sopenharmony_cigdbpy_version, _ = run_gdb("--eval-command=python import sys; print(sys.version_info)")
1177db96d56Sopenharmony_ciif not gdbpy_version:
1187db96d56Sopenharmony_ci    raise unittest.SkipTest("gdb not built with embedded python support")
1197db96d56Sopenharmony_ci
1207db96d56Sopenharmony_ciif "major=2" in gdbpy_version:
1217db96d56Sopenharmony_ci    raise unittest.SkipTest("gdb built with Python 2")
1227db96d56Sopenharmony_ci
1237db96d56Sopenharmony_ci# Verify that "gdb" can load our custom hooks, as OS security settings may
1247db96d56Sopenharmony_ci# disallow this without a customized .gdbinit.
1257db96d56Sopenharmony_ci_, gdbpy_errors = run_gdb('--args', sys.executable)
1267db96d56Sopenharmony_ciif "auto-loading has been declined" in gdbpy_errors:
1277db96d56Sopenharmony_ci    msg = "gdb security settings prevent use of custom hooks: "
1287db96d56Sopenharmony_ci    raise unittest.SkipTest(msg + gdbpy_errors.rstrip())
1297db96d56Sopenharmony_ci
1307db96d56Sopenharmony_cidef gdb_has_frame_select():
1317db96d56Sopenharmony_ci    # Does this build of gdb have gdb.Frame.select ?
1327db96d56Sopenharmony_ci    stdout, _ = run_gdb("--eval-command=python print(dir(gdb.Frame))")
1337db96d56Sopenharmony_ci    m = re.match(r'.*\[(.*)\].*', stdout)
1347db96d56Sopenharmony_ci    if not m:
1357db96d56Sopenharmony_ci        raise unittest.SkipTest("Unable to parse output from gdb.Frame.select test")
1367db96d56Sopenharmony_ci    gdb_frame_dir = m.group(1).split(', ')
1377db96d56Sopenharmony_ci    return "'select'" in gdb_frame_dir
1387db96d56Sopenharmony_ci
1397db96d56Sopenharmony_ciHAS_PYUP_PYDOWN = gdb_has_frame_select()
1407db96d56Sopenharmony_ci
1417db96d56Sopenharmony_ciBREAKPOINT_FN='builtin_id'
1427db96d56Sopenharmony_ci
1437db96d56Sopenharmony_ci@unittest.skipIf(support.PGO, "not useful for PGO")
1447db96d56Sopenharmony_ciclass DebuggerTests(unittest.TestCase):
1457db96d56Sopenharmony_ci
1467db96d56Sopenharmony_ci    """Test that the debugger can debug Python."""
1477db96d56Sopenharmony_ci
1487db96d56Sopenharmony_ci    def get_stack_trace(self, source=None, script=None,
1497db96d56Sopenharmony_ci                        breakpoint=BREAKPOINT_FN,
1507db96d56Sopenharmony_ci                        cmds_after_breakpoint=None,
1517db96d56Sopenharmony_ci                        import_site=False,
1527db96d56Sopenharmony_ci                        ignore_stderr=False):
1537db96d56Sopenharmony_ci        '''
1547db96d56Sopenharmony_ci        Run 'python -c SOURCE' under gdb with a breakpoint.
1557db96d56Sopenharmony_ci
1567db96d56Sopenharmony_ci        Support injecting commands after the breakpoint is reached
1577db96d56Sopenharmony_ci
1587db96d56Sopenharmony_ci        Returns the stdout from gdb
1597db96d56Sopenharmony_ci
1607db96d56Sopenharmony_ci        cmds_after_breakpoint: if provided, a list of strings: gdb commands
1617db96d56Sopenharmony_ci        '''
1627db96d56Sopenharmony_ci        # We use "set breakpoint pending yes" to avoid blocking with a:
1637db96d56Sopenharmony_ci        #   Function "foo" not defined.
1647db96d56Sopenharmony_ci        #   Make breakpoint pending on future shared library load? (y or [n])
1657db96d56Sopenharmony_ci        # error, which typically happens python is dynamically linked (the
1667db96d56Sopenharmony_ci        # breakpoints of interest are to be found in the shared library)
1677db96d56Sopenharmony_ci        # When this happens, we still get:
1687db96d56Sopenharmony_ci        #   Function "textiowrapper_write" not defined.
1697db96d56Sopenharmony_ci        # emitted to stderr each time, alas.
1707db96d56Sopenharmony_ci
1717db96d56Sopenharmony_ci        # Initially I had "--eval-command=continue" here, but removed it to
1727db96d56Sopenharmony_ci        # avoid repeated print breakpoints when traversing hierarchical data
1737db96d56Sopenharmony_ci        # structures
1747db96d56Sopenharmony_ci
1757db96d56Sopenharmony_ci        # Generate a list of commands in gdb's language:
1767db96d56Sopenharmony_ci        commands = ['set breakpoint pending yes',
1777db96d56Sopenharmony_ci                    'break %s' % breakpoint,
1787db96d56Sopenharmony_ci
1797db96d56Sopenharmony_ci                    # The tests assume that the first frame of printed
1807db96d56Sopenharmony_ci                    #  backtrace will not contain program counter,
1817db96d56Sopenharmony_ci                    #  that is however not guaranteed by gdb
1827db96d56Sopenharmony_ci                    #  therefore we need to use 'set print address off' to
1837db96d56Sopenharmony_ci                    #  make sure the counter is not there. For example:
1847db96d56Sopenharmony_ci                    # #0 in PyObject_Print ...
1857db96d56Sopenharmony_ci                    #  is assumed, but sometimes this can be e.g.
1867db96d56Sopenharmony_ci                    # #0 0x00003fffb7dd1798 in PyObject_Print ...
1877db96d56Sopenharmony_ci                    'set print address off',
1887db96d56Sopenharmony_ci
1897db96d56Sopenharmony_ci                    'run']
1907db96d56Sopenharmony_ci
1917db96d56Sopenharmony_ci        # GDB as of 7.4 onwards can distinguish between the
1927db96d56Sopenharmony_ci        # value of a variable at entry vs current value:
1937db96d56Sopenharmony_ci        #   http://sourceware.org/gdb/onlinedocs/gdb/Variables.html
1947db96d56Sopenharmony_ci        # which leads to the selftests failing with errors like this:
1957db96d56Sopenharmony_ci        #   AssertionError: 'v@entry=()' != '()'
1967db96d56Sopenharmony_ci        # Disable this:
1977db96d56Sopenharmony_ci        if (gdb_major_version, gdb_minor_version) >= (7, 4):
1987db96d56Sopenharmony_ci            commands += ['set print entry-values no']
1997db96d56Sopenharmony_ci
2007db96d56Sopenharmony_ci        if cmds_after_breakpoint:
2017db96d56Sopenharmony_ci            if CET_PROTECTION:
2027db96d56Sopenharmony_ci                # bpo-32962: When Python is compiled with -mcet
2037db96d56Sopenharmony_ci                # -fcf-protection, function arguments are unusable before
2047db96d56Sopenharmony_ci                # running the first instruction of the function entry point.
2057db96d56Sopenharmony_ci                # The 'next' command makes the required first step.
2067db96d56Sopenharmony_ci                commands += ['next']
2077db96d56Sopenharmony_ci            commands += cmds_after_breakpoint
2087db96d56Sopenharmony_ci        else:
2097db96d56Sopenharmony_ci            commands += ['backtrace']
2107db96d56Sopenharmony_ci
2117db96d56Sopenharmony_ci        # print commands
2127db96d56Sopenharmony_ci
2137db96d56Sopenharmony_ci        # Use "commands" to generate the arguments with which to invoke "gdb":
2147db96d56Sopenharmony_ci        args = ['--eval-command=%s' % cmd for cmd in commands]
2157db96d56Sopenharmony_ci        args += ["--args",
2167db96d56Sopenharmony_ci                 sys.executable]
2177db96d56Sopenharmony_ci        args.extend(subprocess._args_from_interpreter_flags())
2187db96d56Sopenharmony_ci
2197db96d56Sopenharmony_ci        if not import_site:
2207db96d56Sopenharmony_ci            # -S suppresses the default 'import site'
2217db96d56Sopenharmony_ci            args += ["-S"]
2227db96d56Sopenharmony_ci
2237db96d56Sopenharmony_ci        if source:
2247db96d56Sopenharmony_ci            args += ["-c", source]
2257db96d56Sopenharmony_ci        elif script:
2267db96d56Sopenharmony_ci            args += [script]
2277db96d56Sopenharmony_ci
2287db96d56Sopenharmony_ci        # Use "args" to invoke gdb, capturing stdout, stderr:
2297db96d56Sopenharmony_ci        out, err = run_gdb(*args, PYTHONHASHSEED=PYTHONHASHSEED)
2307db96d56Sopenharmony_ci
2317db96d56Sopenharmony_ci        if not ignore_stderr:
2327db96d56Sopenharmony_ci            for line in err.splitlines():
2337db96d56Sopenharmony_ci                print(line, file=sys.stderr)
2347db96d56Sopenharmony_ci
2357db96d56Sopenharmony_ci        # bpo-34007: Sometimes some versions of the shared libraries that
2367db96d56Sopenharmony_ci        # are part of the traceback are compiled in optimised mode and the
2377db96d56Sopenharmony_ci        # Program Counter (PC) is not present, not allowing gdb to walk the
2387db96d56Sopenharmony_ci        # frames back. When this happens, the Python bindings of gdb raise
2397db96d56Sopenharmony_ci        # an exception, making the test impossible to succeed.
2407db96d56Sopenharmony_ci        if "PC not saved" in err:
2417db96d56Sopenharmony_ci            raise unittest.SkipTest("gdb cannot walk the frame object"
2427db96d56Sopenharmony_ci                                    " because the Program Counter is"
2437db96d56Sopenharmony_ci                                    " not present")
2447db96d56Sopenharmony_ci
2457db96d56Sopenharmony_ci        # bpo-40019: Skip the test if gdb failed to read debug information
2467db96d56Sopenharmony_ci        # because the Python binary is optimized.
2477db96d56Sopenharmony_ci        for pattern in (
2487db96d56Sopenharmony_ci            '(frame information optimized out)',
2497db96d56Sopenharmony_ci            'Unable to read information on python frame',
2507db96d56Sopenharmony_ci        ):
2517db96d56Sopenharmony_ci            if pattern in out:
2527db96d56Sopenharmony_ci                raise unittest.SkipTest(f"{pattern!r} found in gdb output")
2537db96d56Sopenharmony_ci
2547db96d56Sopenharmony_ci        return out
2557db96d56Sopenharmony_ci
2567db96d56Sopenharmony_ci    def get_gdb_repr(self, source,
2577db96d56Sopenharmony_ci                     cmds_after_breakpoint=None,
2587db96d56Sopenharmony_ci                     import_site=False):
2597db96d56Sopenharmony_ci        # Given an input python source representation of data,
2607db96d56Sopenharmony_ci        # run "python -c'id(DATA)'" under gdb with a breakpoint on
2617db96d56Sopenharmony_ci        # builtin_id and scrape out gdb's representation of the "op"
2627db96d56Sopenharmony_ci        # parameter, and verify that the gdb displays the same string
2637db96d56Sopenharmony_ci        #
2647db96d56Sopenharmony_ci        # Verify that the gdb displays the expected string
2657db96d56Sopenharmony_ci        #
2667db96d56Sopenharmony_ci        # For a nested structure, the first time we hit the breakpoint will
2677db96d56Sopenharmony_ci        # give us the top-level structure
2687db96d56Sopenharmony_ci
2697db96d56Sopenharmony_ci        # NOTE: avoid decoding too much of the traceback as some
2707db96d56Sopenharmony_ci        # undecodable characters may lurk there in optimized mode
2717db96d56Sopenharmony_ci        # (issue #19743).
2727db96d56Sopenharmony_ci        cmds_after_breakpoint = cmds_after_breakpoint or ["backtrace 1"]
2737db96d56Sopenharmony_ci        gdb_output = self.get_stack_trace(source, breakpoint=BREAKPOINT_FN,
2747db96d56Sopenharmony_ci                                          cmds_after_breakpoint=cmds_after_breakpoint,
2757db96d56Sopenharmony_ci                                          import_site=import_site)
2767db96d56Sopenharmony_ci        # gdb can insert additional '\n' and space characters in various places
2777db96d56Sopenharmony_ci        # in its output, depending on the width of the terminal it's connected
2787db96d56Sopenharmony_ci        # to (using its "wrap_here" function)
2797db96d56Sopenharmony_ci        m = re.search(
2807db96d56Sopenharmony_ci            # Match '#0 builtin_id(self=..., v=...)'
2817db96d56Sopenharmony_ci            r'#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)?\)'
2827db96d56Sopenharmony_ci            # Match ' at Python/bltinmodule.c'.
2837db96d56Sopenharmony_ci            # bpo-38239: builtin_id() is defined in Python/bltinmodule.c,
2847db96d56Sopenharmony_ci            # but accept any "Directory\file.c" to support Link Time
2857db96d56Sopenharmony_ci            # Optimization (LTO).
2867db96d56Sopenharmony_ci            r'\s+at\s+\S*[A-Za-z]+/[A-Za-z0-9_-]+\.c',
2877db96d56Sopenharmony_ci            gdb_output, re.DOTALL)
2887db96d56Sopenharmony_ci        if not m:
2897db96d56Sopenharmony_ci            self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output))
2907db96d56Sopenharmony_ci        return m.group(1), gdb_output
2917db96d56Sopenharmony_ci
2927db96d56Sopenharmony_ci    def assertEndsWith(self, actual, exp_end):
2937db96d56Sopenharmony_ci        '''Ensure that the given "actual" string ends with "exp_end"'''
2947db96d56Sopenharmony_ci        self.assertTrue(actual.endswith(exp_end),
2957db96d56Sopenharmony_ci                        msg='%r did not end with %r' % (actual, exp_end))
2967db96d56Sopenharmony_ci
2977db96d56Sopenharmony_ci    def assertMultilineMatches(self, actual, pattern):
2987db96d56Sopenharmony_ci        m = re.match(pattern, actual, re.DOTALL)
2997db96d56Sopenharmony_ci        if not m:
3007db96d56Sopenharmony_ci            self.fail(msg='%r did not match %r' % (actual, pattern))
3017db96d56Sopenharmony_ci
3027db96d56Sopenharmony_ci    def get_sample_script(self):
3037db96d56Sopenharmony_ci        return findfile('gdb_sample.py')
3047db96d56Sopenharmony_ci
3057db96d56Sopenharmony_ciclass PrettyPrintTests(DebuggerTests):
3067db96d56Sopenharmony_ci    def test_getting_backtrace(self):
3077db96d56Sopenharmony_ci        gdb_output = self.get_stack_trace('id(42)')
3087db96d56Sopenharmony_ci        self.assertTrue(BREAKPOINT_FN in gdb_output)
3097db96d56Sopenharmony_ci
3107db96d56Sopenharmony_ci    def assertGdbRepr(self, val, exp_repr=None):
3117db96d56Sopenharmony_ci        # Ensure that gdb's rendering of the value in a debugged process
3127db96d56Sopenharmony_ci        # matches repr(value) in this process:
3137db96d56Sopenharmony_ci        gdb_repr, gdb_output = self.get_gdb_repr('id(' + ascii(val) + ')')
3147db96d56Sopenharmony_ci        if not exp_repr:
3157db96d56Sopenharmony_ci            exp_repr = repr(val)
3167db96d56Sopenharmony_ci        self.assertEqual(gdb_repr, exp_repr,
3177db96d56Sopenharmony_ci                         ('%r did not equal expected %r; full output was:\n%s'
3187db96d56Sopenharmony_ci                          % (gdb_repr, exp_repr, gdb_output)))
3197db96d56Sopenharmony_ci
3207db96d56Sopenharmony_ci    def test_int(self):
3217db96d56Sopenharmony_ci        'Verify the pretty-printing of various int values'
3227db96d56Sopenharmony_ci        self.assertGdbRepr(42)
3237db96d56Sopenharmony_ci        self.assertGdbRepr(0)
3247db96d56Sopenharmony_ci        self.assertGdbRepr(-7)
3257db96d56Sopenharmony_ci        self.assertGdbRepr(1000000000000)
3267db96d56Sopenharmony_ci        self.assertGdbRepr(-1000000000000000)
3277db96d56Sopenharmony_ci
3287db96d56Sopenharmony_ci    def test_singletons(self):
3297db96d56Sopenharmony_ci        'Verify the pretty-printing of True, False and None'
3307db96d56Sopenharmony_ci        self.assertGdbRepr(True)
3317db96d56Sopenharmony_ci        self.assertGdbRepr(False)
3327db96d56Sopenharmony_ci        self.assertGdbRepr(None)
3337db96d56Sopenharmony_ci
3347db96d56Sopenharmony_ci    def test_dicts(self):
3357db96d56Sopenharmony_ci        'Verify the pretty-printing of dictionaries'
3367db96d56Sopenharmony_ci        self.assertGdbRepr({})
3377db96d56Sopenharmony_ci        self.assertGdbRepr({'foo': 'bar'}, "{'foo': 'bar'}")
3387db96d56Sopenharmony_ci        # Python preserves insertion order since 3.6
3397db96d56Sopenharmony_ci        self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'foo': 'bar', 'douglas': 42}")
3407db96d56Sopenharmony_ci
3417db96d56Sopenharmony_ci    def test_lists(self):
3427db96d56Sopenharmony_ci        'Verify the pretty-printing of lists'
3437db96d56Sopenharmony_ci        self.assertGdbRepr([])
3447db96d56Sopenharmony_ci        self.assertGdbRepr(list(range(5)))
3457db96d56Sopenharmony_ci
3467db96d56Sopenharmony_ci    def test_bytes(self):
3477db96d56Sopenharmony_ci        'Verify the pretty-printing of bytes'
3487db96d56Sopenharmony_ci        self.assertGdbRepr(b'')
3497db96d56Sopenharmony_ci        self.assertGdbRepr(b'And now for something hopefully the same')
3507db96d56Sopenharmony_ci        self.assertGdbRepr(b'string with embedded NUL here \0 and then some more text')
3517db96d56Sopenharmony_ci        self.assertGdbRepr(b'this is a tab:\t'
3527db96d56Sopenharmony_ci                           b' this is a slash-N:\n'
3537db96d56Sopenharmony_ci                           b' this is a slash-R:\r'
3547db96d56Sopenharmony_ci                           )
3557db96d56Sopenharmony_ci
3567db96d56Sopenharmony_ci        self.assertGdbRepr(b'this is byte 255:\xff and byte 128:\x80')
3577db96d56Sopenharmony_ci
3587db96d56Sopenharmony_ci        self.assertGdbRepr(bytes([b for b in range(255)]))
3597db96d56Sopenharmony_ci
3607db96d56Sopenharmony_ci    def test_strings(self):
3617db96d56Sopenharmony_ci        'Verify the pretty-printing of unicode strings'
3627db96d56Sopenharmony_ci        # We cannot simply call locale.getpreferredencoding() here,
3637db96d56Sopenharmony_ci        # as GDB might have been linked against a different version
3647db96d56Sopenharmony_ci        # of Python with a different encoding and coercion policy
3657db96d56Sopenharmony_ci        # with respect to PEP 538 and PEP 540.
3667db96d56Sopenharmony_ci        out, err = run_gdb(
3677db96d56Sopenharmony_ci            '--eval-command',
3687db96d56Sopenharmony_ci            'python import locale; print(locale.getpreferredencoding())')
3697db96d56Sopenharmony_ci
3707db96d56Sopenharmony_ci        encoding = out.rstrip()
3717db96d56Sopenharmony_ci        if err or not encoding:
3727db96d56Sopenharmony_ci            raise RuntimeError(
3737db96d56Sopenharmony_ci                f'unable to determine the preferred encoding '
3747db96d56Sopenharmony_ci                f'of embedded Python in GDB: {err}')
3757db96d56Sopenharmony_ci
3767db96d56Sopenharmony_ci        def check_repr(text):
3777db96d56Sopenharmony_ci            try:
3787db96d56Sopenharmony_ci                text.encode(encoding)
3797db96d56Sopenharmony_ci            except UnicodeEncodeError:
3807db96d56Sopenharmony_ci                self.assertGdbRepr(text, ascii(text))
3817db96d56Sopenharmony_ci            else:
3827db96d56Sopenharmony_ci                self.assertGdbRepr(text)
3837db96d56Sopenharmony_ci
3847db96d56Sopenharmony_ci        self.assertGdbRepr('')
3857db96d56Sopenharmony_ci        self.assertGdbRepr('And now for something hopefully the same')
3867db96d56Sopenharmony_ci        self.assertGdbRepr('string with embedded NUL here \0 and then some more text')
3877db96d56Sopenharmony_ci
3887db96d56Sopenharmony_ci        # Test printing a single character:
3897db96d56Sopenharmony_ci        #    U+2620 SKULL AND CROSSBONES
3907db96d56Sopenharmony_ci        check_repr('\u2620')
3917db96d56Sopenharmony_ci
3927db96d56Sopenharmony_ci        # Test printing a Japanese unicode string
3937db96d56Sopenharmony_ci        # (I believe this reads "mojibake", using 3 characters from the CJK
3947db96d56Sopenharmony_ci        # Unified Ideographs area, followed by U+3051 HIRAGANA LETTER KE)
3957db96d56Sopenharmony_ci        check_repr('\u6587\u5b57\u5316\u3051')
3967db96d56Sopenharmony_ci
3977db96d56Sopenharmony_ci        # Test a character outside the BMP:
3987db96d56Sopenharmony_ci        #    U+1D121 MUSICAL SYMBOL C CLEF
3997db96d56Sopenharmony_ci        # This is:
4007db96d56Sopenharmony_ci        # UTF-8: 0xF0 0x9D 0x84 0xA1
4017db96d56Sopenharmony_ci        # UTF-16: 0xD834 0xDD21
4027db96d56Sopenharmony_ci        check_repr(chr(0x1D121))
4037db96d56Sopenharmony_ci
4047db96d56Sopenharmony_ci    def test_tuples(self):
4057db96d56Sopenharmony_ci        'Verify the pretty-printing of tuples'
4067db96d56Sopenharmony_ci        self.assertGdbRepr(tuple(), '()')
4077db96d56Sopenharmony_ci        self.assertGdbRepr((1,), '(1,)')
4087db96d56Sopenharmony_ci        self.assertGdbRepr(('foo', 'bar', 'baz'))
4097db96d56Sopenharmony_ci
4107db96d56Sopenharmony_ci    def test_sets(self):
4117db96d56Sopenharmony_ci        'Verify the pretty-printing of sets'
4127db96d56Sopenharmony_ci        if (gdb_major_version, gdb_minor_version) < (7, 3):
4137db96d56Sopenharmony_ci            self.skipTest("pretty-printing of sets needs gdb 7.3 or later")
4147db96d56Sopenharmony_ci        self.assertGdbRepr(set(), "set()")
4157db96d56Sopenharmony_ci        self.assertGdbRepr(set(['a']), "{'a'}")
4167db96d56Sopenharmony_ci        # PYTHONHASHSEED is need to get the exact frozenset item order
4177db96d56Sopenharmony_ci        if not sys.flags.ignore_environment:
4187db96d56Sopenharmony_ci            self.assertGdbRepr(set(['a', 'b']), "{'a', 'b'}")
4197db96d56Sopenharmony_ci            self.assertGdbRepr(set([4, 5, 6]), "{4, 5, 6}")
4207db96d56Sopenharmony_ci
4217db96d56Sopenharmony_ci        # Ensure that we handle sets containing the "dummy" key value,
4227db96d56Sopenharmony_ci        # which happens on deletion:
4237db96d56Sopenharmony_ci        gdb_repr, gdb_output = self.get_gdb_repr('''s = set(['a','b'])
4247db96d56Sopenharmony_cis.remove('a')
4257db96d56Sopenharmony_ciid(s)''')
4267db96d56Sopenharmony_ci        self.assertEqual(gdb_repr, "{'b'}")
4277db96d56Sopenharmony_ci
4287db96d56Sopenharmony_ci    def test_frozensets(self):
4297db96d56Sopenharmony_ci        'Verify the pretty-printing of frozensets'
4307db96d56Sopenharmony_ci        if (gdb_major_version, gdb_minor_version) < (7, 3):
4317db96d56Sopenharmony_ci            self.skipTest("pretty-printing of frozensets needs gdb 7.3 or later")
4327db96d56Sopenharmony_ci        self.assertGdbRepr(frozenset(), "frozenset()")
4337db96d56Sopenharmony_ci        self.assertGdbRepr(frozenset(['a']), "frozenset({'a'})")
4347db96d56Sopenharmony_ci        # PYTHONHASHSEED is need to get the exact frozenset item order
4357db96d56Sopenharmony_ci        if not sys.flags.ignore_environment:
4367db96d56Sopenharmony_ci            self.assertGdbRepr(frozenset(['a', 'b']), "frozenset({'a', 'b'})")
4377db96d56Sopenharmony_ci            self.assertGdbRepr(frozenset([4, 5, 6]), "frozenset({4, 5, 6})")
4387db96d56Sopenharmony_ci
4397db96d56Sopenharmony_ci    def test_exceptions(self):
4407db96d56Sopenharmony_ci        # Test a RuntimeError
4417db96d56Sopenharmony_ci        gdb_repr, gdb_output = self.get_gdb_repr('''
4427db96d56Sopenharmony_citry:
4437db96d56Sopenharmony_ci    raise RuntimeError("I am an error")
4447db96d56Sopenharmony_ciexcept RuntimeError as e:
4457db96d56Sopenharmony_ci    id(e)
4467db96d56Sopenharmony_ci''')
4477db96d56Sopenharmony_ci        self.assertEqual(gdb_repr,
4487db96d56Sopenharmony_ci                         "RuntimeError('I am an error',)")
4497db96d56Sopenharmony_ci
4507db96d56Sopenharmony_ci
4517db96d56Sopenharmony_ci        # Test division by zero:
4527db96d56Sopenharmony_ci        gdb_repr, gdb_output = self.get_gdb_repr('''
4537db96d56Sopenharmony_citry:
4547db96d56Sopenharmony_ci    a = 1 / 0
4557db96d56Sopenharmony_ciexcept ZeroDivisionError as e:
4567db96d56Sopenharmony_ci    id(e)
4577db96d56Sopenharmony_ci''')
4587db96d56Sopenharmony_ci        self.assertEqual(gdb_repr,
4597db96d56Sopenharmony_ci                         "ZeroDivisionError('division by zero',)")
4607db96d56Sopenharmony_ci
4617db96d56Sopenharmony_ci    def test_modern_class(self):
4627db96d56Sopenharmony_ci        'Verify the pretty-printing of new-style class instances'
4637db96d56Sopenharmony_ci        gdb_repr, gdb_output = self.get_gdb_repr('''
4647db96d56Sopenharmony_ciclass Foo:
4657db96d56Sopenharmony_ci    pass
4667db96d56Sopenharmony_cifoo = Foo()
4677db96d56Sopenharmony_cifoo.an_int = 42
4687db96d56Sopenharmony_ciid(foo)''')
4697db96d56Sopenharmony_ci        m = re.match(r'<Foo\(an_int=42\) at remote 0x-?[0-9a-f]+>', gdb_repr)
4707db96d56Sopenharmony_ci        self.assertTrue(m,
4717db96d56Sopenharmony_ci                        msg='Unexpected new-style class rendering %r' % gdb_repr)
4727db96d56Sopenharmony_ci
4737db96d56Sopenharmony_ci    def test_subclassing_list(self):
4747db96d56Sopenharmony_ci        'Verify the pretty-printing of an instance of a list subclass'
4757db96d56Sopenharmony_ci        gdb_repr, gdb_output = self.get_gdb_repr('''
4767db96d56Sopenharmony_ciclass Foo(list):
4777db96d56Sopenharmony_ci    pass
4787db96d56Sopenharmony_cifoo = Foo()
4797db96d56Sopenharmony_cifoo += [1, 2, 3]
4807db96d56Sopenharmony_cifoo.an_int = 42
4817db96d56Sopenharmony_ciid(foo)''')
4827db96d56Sopenharmony_ci        m = re.match(r'<Foo\(an_int=42\) at remote 0x-?[0-9a-f]+>', gdb_repr)
4837db96d56Sopenharmony_ci
4847db96d56Sopenharmony_ci        self.assertTrue(m,
4857db96d56Sopenharmony_ci                        msg='Unexpected new-style class rendering %r' % gdb_repr)
4867db96d56Sopenharmony_ci
4877db96d56Sopenharmony_ci    def test_subclassing_tuple(self):
4887db96d56Sopenharmony_ci        'Verify the pretty-printing of an instance of a tuple subclass'
4897db96d56Sopenharmony_ci        # This should exercise the negative tp_dictoffset code in the
4907db96d56Sopenharmony_ci        # new-style class support
4917db96d56Sopenharmony_ci        gdb_repr, gdb_output = self.get_gdb_repr('''
4927db96d56Sopenharmony_ciclass Foo(tuple):
4937db96d56Sopenharmony_ci    pass
4947db96d56Sopenharmony_cifoo = Foo((1, 2, 3))
4957db96d56Sopenharmony_cifoo.an_int = 42
4967db96d56Sopenharmony_ciid(foo)''')
4977db96d56Sopenharmony_ci        m = re.match(r'<Foo\(an_int=42\) at remote 0x-?[0-9a-f]+>', gdb_repr)
4987db96d56Sopenharmony_ci
4997db96d56Sopenharmony_ci        self.assertTrue(m,
5007db96d56Sopenharmony_ci                        msg='Unexpected new-style class rendering %r' % gdb_repr)
5017db96d56Sopenharmony_ci
5027db96d56Sopenharmony_ci    def assertSane(self, source, corruption, exprepr=None):
5037db96d56Sopenharmony_ci        '''Run Python under gdb, corrupting variables in the inferior process
5047db96d56Sopenharmony_ci        immediately before taking a backtrace.
5057db96d56Sopenharmony_ci
5067db96d56Sopenharmony_ci        Verify that the variable's representation is the expected failsafe
5077db96d56Sopenharmony_ci        representation'''
5087db96d56Sopenharmony_ci        if corruption:
5097db96d56Sopenharmony_ci            cmds_after_breakpoint=[corruption, 'backtrace']
5107db96d56Sopenharmony_ci        else:
5117db96d56Sopenharmony_ci            cmds_after_breakpoint=['backtrace']
5127db96d56Sopenharmony_ci
5137db96d56Sopenharmony_ci        gdb_repr, gdb_output = \
5147db96d56Sopenharmony_ci            self.get_gdb_repr(source,
5157db96d56Sopenharmony_ci                              cmds_after_breakpoint=cmds_after_breakpoint)
5167db96d56Sopenharmony_ci        if exprepr:
5177db96d56Sopenharmony_ci            if gdb_repr == exprepr:
5187db96d56Sopenharmony_ci                # gdb managed to print the value in spite of the corruption;
5197db96d56Sopenharmony_ci                # this is good (see http://bugs.python.org/issue8330)
5207db96d56Sopenharmony_ci                return
5217db96d56Sopenharmony_ci
5227db96d56Sopenharmony_ci        # Match anything for the type name; 0xDEADBEEF could point to
5237db96d56Sopenharmony_ci        # something arbitrary (see  http://bugs.python.org/issue8330)
5247db96d56Sopenharmony_ci        pattern = '<.* at remote 0x-?[0-9a-f]+>'
5257db96d56Sopenharmony_ci
5267db96d56Sopenharmony_ci        m = re.match(pattern, gdb_repr)
5277db96d56Sopenharmony_ci        if not m:
5287db96d56Sopenharmony_ci            self.fail('Unexpected gdb representation: %r\n%s' % \
5297db96d56Sopenharmony_ci                          (gdb_repr, gdb_output))
5307db96d56Sopenharmony_ci
5317db96d56Sopenharmony_ci    def test_NULL_ptr(self):
5327db96d56Sopenharmony_ci        'Ensure that a NULL PyObject* is handled gracefully'
5337db96d56Sopenharmony_ci        gdb_repr, gdb_output = (
5347db96d56Sopenharmony_ci            self.get_gdb_repr('id(42)',
5357db96d56Sopenharmony_ci                              cmds_after_breakpoint=['set variable v=0',
5367db96d56Sopenharmony_ci                                                     'backtrace'])
5377db96d56Sopenharmony_ci            )
5387db96d56Sopenharmony_ci
5397db96d56Sopenharmony_ci        self.assertEqual(gdb_repr, '0x0')
5407db96d56Sopenharmony_ci
5417db96d56Sopenharmony_ci    def test_NULL_ob_type(self):
5427db96d56Sopenharmony_ci        'Ensure that a PyObject* with NULL ob_type is handled gracefully'
5437db96d56Sopenharmony_ci        self.assertSane('id(42)',
5447db96d56Sopenharmony_ci                        'set v->ob_type=0')
5457db96d56Sopenharmony_ci
5467db96d56Sopenharmony_ci    def test_corrupt_ob_type(self):
5477db96d56Sopenharmony_ci        'Ensure that a PyObject* with a corrupt ob_type is handled gracefully'
5487db96d56Sopenharmony_ci        self.assertSane('id(42)',
5497db96d56Sopenharmony_ci                        'set v->ob_type=0xDEADBEEF',
5507db96d56Sopenharmony_ci                        exprepr='42')
5517db96d56Sopenharmony_ci
5527db96d56Sopenharmony_ci    def test_corrupt_tp_flags(self):
5537db96d56Sopenharmony_ci        'Ensure that a PyObject* with a type with corrupt tp_flags is handled'
5547db96d56Sopenharmony_ci        self.assertSane('id(42)',
5557db96d56Sopenharmony_ci                        'set v->ob_type->tp_flags=0x0',
5567db96d56Sopenharmony_ci                        exprepr='42')
5577db96d56Sopenharmony_ci
5587db96d56Sopenharmony_ci    def test_corrupt_tp_name(self):
5597db96d56Sopenharmony_ci        'Ensure that a PyObject* with a type with corrupt tp_name is handled'
5607db96d56Sopenharmony_ci        self.assertSane('id(42)',
5617db96d56Sopenharmony_ci                        'set v->ob_type->tp_name=0xDEADBEEF',
5627db96d56Sopenharmony_ci                        exprepr='42')
5637db96d56Sopenharmony_ci
5647db96d56Sopenharmony_ci    def test_builtins_help(self):
5657db96d56Sopenharmony_ci        'Ensure that the new-style class _Helper in site.py can be handled'
5667db96d56Sopenharmony_ci
5677db96d56Sopenharmony_ci        if sys.flags.no_site:
5687db96d56Sopenharmony_ci            self.skipTest("need site module, but -S option was used")
5697db96d56Sopenharmony_ci
5707db96d56Sopenharmony_ci        # (this was the issue causing tracebacks in
5717db96d56Sopenharmony_ci        #  http://bugs.python.org/issue8032#msg100537 )
5727db96d56Sopenharmony_ci        gdb_repr, gdb_output = self.get_gdb_repr('id(__builtins__.help)', import_site=True)
5737db96d56Sopenharmony_ci
5747db96d56Sopenharmony_ci        m = re.match(r'<_Helper\(\) at remote 0x-?[0-9a-f]+>', gdb_repr)
5757db96d56Sopenharmony_ci        self.assertTrue(m,
5767db96d56Sopenharmony_ci                        msg='Unexpected rendering %r' % gdb_repr)
5777db96d56Sopenharmony_ci
5787db96d56Sopenharmony_ci    def test_selfreferential_list(self):
5797db96d56Sopenharmony_ci        '''Ensure that a reference loop involving a list doesn't lead proxyval
5807db96d56Sopenharmony_ci        into an infinite loop:'''
5817db96d56Sopenharmony_ci        gdb_repr, gdb_output = \
5827db96d56Sopenharmony_ci            self.get_gdb_repr("a = [3, 4, 5] ; a.append(a) ; id(a)")
5837db96d56Sopenharmony_ci        self.assertEqual(gdb_repr, '[3, 4, 5, [...]]')
5847db96d56Sopenharmony_ci
5857db96d56Sopenharmony_ci        gdb_repr, gdb_output = \
5867db96d56Sopenharmony_ci            self.get_gdb_repr("a = [3, 4, 5] ; b = [a] ; a.append(b) ; id(a)")
5877db96d56Sopenharmony_ci        self.assertEqual(gdb_repr, '[3, 4, 5, [[...]]]')
5887db96d56Sopenharmony_ci
5897db96d56Sopenharmony_ci    def test_selfreferential_dict(self):
5907db96d56Sopenharmony_ci        '''Ensure that a reference loop involving a dict doesn't lead proxyval
5917db96d56Sopenharmony_ci        into an infinite loop:'''
5927db96d56Sopenharmony_ci        gdb_repr, gdb_output = \
5937db96d56Sopenharmony_ci            self.get_gdb_repr("a = {} ; b = {'bar':a} ; a['foo'] = b ; id(a)")
5947db96d56Sopenharmony_ci
5957db96d56Sopenharmony_ci        self.assertEqual(gdb_repr, "{'foo': {'bar': {...}}}")
5967db96d56Sopenharmony_ci
5977db96d56Sopenharmony_ci    def test_selfreferential_old_style_instance(self):
5987db96d56Sopenharmony_ci        gdb_repr, gdb_output = \
5997db96d56Sopenharmony_ci            self.get_gdb_repr('''
6007db96d56Sopenharmony_ciclass Foo:
6017db96d56Sopenharmony_ci    pass
6027db96d56Sopenharmony_cifoo = Foo()
6037db96d56Sopenharmony_cifoo.an_attr = foo
6047db96d56Sopenharmony_ciid(foo)''')
6057db96d56Sopenharmony_ci        self.assertTrue(re.match(r'<Foo\(an_attr=<\.\.\.>\) at remote 0x-?[0-9a-f]+>',
6067db96d56Sopenharmony_ci                                 gdb_repr),
6077db96d56Sopenharmony_ci                        'Unexpected gdb representation: %r\n%s' % \
6087db96d56Sopenharmony_ci                            (gdb_repr, gdb_output))
6097db96d56Sopenharmony_ci
6107db96d56Sopenharmony_ci    def test_selfreferential_new_style_instance(self):
6117db96d56Sopenharmony_ci        gdb_repr, gdb_output = \
6127db96d56Sopenharmony_ci            self.get_gdb_repr('''
6137db96d56Sopenharmony_ciclass Foo(object):
6147db96d56Sopenharmony_ci    pass
6157db96d56Sopenharmony_cifoo = Foo()
6167db96d56Sopenharmony_cifoo.an_attr = foo
6177db96d56Sopenharmony_ciid(foo)''')
6187db96d56Sopenharmony_ci        self.assertTrue(re.match(r'<Foo\(an_attr=<\.\.\.>\) at remote 0x-?[0-9a-f]+>',
6197db96d56Sopenharmony_ci                                 gdb_repr),
6207db96d56Sopenharmony_ci                        'Unexpected gdb representation: %r\n%s' % \
6217db96d56Sopenharmony_ci                            (gdb_repr, gdb_output))
6227db96d56Sopenharmony_ci
6237db96d56Sopenharmony_ci        gdb_repr, gdb_output = \
6247db96d56Sopenharmony_ci            self.get_gdb_repr('''
6257db96d56Sopenharmony_ciclass Foo(object):
6267db96d56Sopenharmony_ci    pass
6277db96d56Sopenharmony_cia = Foo()
6287db96d56Sopenharmony_cib = Foo()
6297db96d56Sopenharmony_cia.an_attr = b
6307db96d56Sopenharmony_cib.an_attr = a
6317db96d56Sopenharmony_ciid(a)''')
6327db96d56Sopenharmony_ci        self.assertTrue(re.match(r'<Foo\(an_attr=<Foo\(an_attr=<\.\.\.>\) at remote 0x-?[0-9a-f]+>\) at remote 0x-?[0-9a-f]+>',
6337db96d56Sopenharmony_ci                                 gdb_repr),
6347db96d56Sopenharmony_ci                        'Unexpected gdb representation: %r\n%s' % \
6357db96d56Sopenharmony_ci                            (gdb_repr, gdb_output))
6367db96d56Sopenharmony_ci
6377db96d56Sopenharmony_ci    def test_truncation(self):
6387db96d56Sopenharmony_ci        'Verify that very long output is truncated'
6397db96d56Sopenharmony_ci        gdb_repr, gdb_output = self.get_gdb_repr('id(list(range(1000)))')
6407db96d56Sopenharmony_ci        self.assertEqual(gdb_repr,
6417db96d56Sopenharmony_ci                         "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, "
6427db96d56Sopenharmony_ci                         "14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, "
6437db96d56Sopenharmony_ci                         "27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, "
6447db96d56Sopenharmony_ci                         "40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, "
6457db96d56Sopenharmony_ci                         "53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, "
6467db96d56Sopenharmony_ci                         "66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, "
6477db96d56Sopenharmony_ci                         "79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, "
6487db96d56Sopenharmony_ci                         "92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, "
6497db96d56Sopenharmony_ci                         "104, 105, 106, 107, 108, 109, 110, 111, 112, 113, "
6507db96d56Sopenharmony_ci                         "114, 115, 116, 117, 118, 119, 120, 121, 122, 123, "
6517db96d56Sopenharmony_ci                         "124, 125, 126, 127, 128, 129, 130, 131, 132, 133, "
6527db96d56Sopenharmony_ci                         "134, 135, 136, 137, 138, 139, 140, 141, 142, 143, "
6537db96d56Sopenharmony_ci                         "144, 145, 146, 147, 148, 149, 150, 151, 152, 153, "
6547db96d56Sopenharmony_ci                         "154, 155, 156, 157, 158, 159, 160, 161, 162, 163, "
6557db96d56Sopenharmony_ci                         "164, 165, 166, 167, 168, 169, 170, 171, 172, 173, "
6567db96d56Sopenharmony_ci                         "174, 175, 176, 177, 178, 179, 180, 181, 182, 183, "
6577db96d56Sopenharmony_ci                         "184, 185, 186, 187, 188, 189, 190, 191, 192, 193, "
6587db96d56Sopenharmony_ci                         "194, 195, 196, 197, 198, 199, 200, 201, 202, 203, "
6597db96d56Sopenharmony_ci                         "204, 205, 206, 207, 208, 209, 210, 211, 212, 213, "
6607db96d56Sopenharmony_ci                         "214, 215, 216, 217, 218, 219, 220, 221, 222, 223, "
6617db96d56Sopenharmony_ci                         "224, 225, 226...(truncated)")
6627db96d56Sopenharmony_ci        self.assertEqual(len(gdb_repr),
6637db96d56Sopenharmony_ci                         1024 + len('...(truncated)'))
6647db96d56Sopenharmony_ci
6657db96d56Sopenharmony_ci    def test_builtin_method(self):
6667db96d56Sopenharmony_ci        gdb_repr, gdb_output = self.get_gdb_repr('import sys; id(sys.stdout.readlines)')
6677db96d56Sopenharmony_ci        self.assertTrue(re.match(r'<built-in method readlines of _io.TextIOWrapper object at remote 0x-?[0-9a-f]+>',
6687db96d56Sopenharmony_ci                                 gdb_repr),
6697db96d56Sopenharmony_ci                        'Unexpected gdb representation: %r\n%s' % \
6707db96d56Sopenharmony_ci                            (gdb_repr, gdb_output))
6717db96d56Sopenharmony_ci
6727db96d56Sopenharmony_ci    def test_frames(self):
6737db96d56Sopenharmony_ci        gdb_output = self.get_stack_trace('''
6747db96d56Sopenharmony_ciimport sys
6757db96d56Sopenharmony_cidef foo(a, b, c):
6767db96d56Sopenharmony_ci    return sys._getframe(0)
6777db96d56Sopenharmony_ci
6787db96d56Sopenharmony_cif = foo(3, 4, 5)
6797db96d56Sopenharmony_ciid(f)''',
6807db96d56Sopenharmony_ci                                          breakpoint='builtin_id',
6817db96d56Sopenharmony_ci                                          cmds_after_breakpoint=['print (PyFrameObject*)v']
6827db96d56Sopenharmony_ci                                          )
6837db96d56Sopenharmony_ci        self.assertTrue(re.match(r'.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file <string>, line 4, in foo \(a=3.*',
6847db96d56Sopenharmony_ci                                 gdb_output,
6857db96d56Sopenharmony_ci                                 re.DOTALL),
6867db96d56Sopenharmony_ci                        'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output))
6877db96d56Sopenharmony_ci
6887db96d56Sopenharmony_ci@unittest.skipIf(python_is_optimized(),
6897db96d56Sopenharmony_ci                 "Python was compiled with optimizations")
6907db96d56Sopenharmony_ciclass PyListTests(DebuggerTests):
6917db96d56Sopenharmony_ci    def assertListing(self, expected, actual):
6927db96d56Sopenharmony_ci        self.assertEndsWith(actual, expected)
6937db96d56Sopenharmony_ci
6947db96d56Sopenharmony_ci    def test_basic_command(self):
6957db96d56Sopenharmony_ci        'Verify that the "py-list" command works'
6967db96d56Sopenharmony_ci        bt = self.get_stack_trace(script=self.get_sample_script(),
6977db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-list'])
6987db96d56Sopenharmony_ci
6997db96d56Sopenharmony_ci        self.assertListing('   5    \n'
7007db96d56Sopenharmony_ci                           '   6    def bar(a, b, c):\n'
7017db96d56Sopenharmony_ci                           '   7        baz(a, b, c)\n'
7027db96d56Sopenharmony_ci                           '   8    \n'
7037db96d56Sopenharmony_ci                           '   9    def baz(*args):\n'
7047db96d56Sopenharmony_ci                           ' >10        id(42)\n'
7057db96d56Sopenharmony_ci                           '  11    \n'
7067db96d56Sopenharmony_ci                           '  12    foo(1, 2, 3)\n',
7077db96d56Sopenharmony_ci                           bt)
7087db96d56Sopenharmony_ci
7097db96d56Sopenharmony_ci    def test_one_abs_arg(self):
7107db96d56Sopenharmony_ci        'Verify the "py-list" command with one absolute argument'
7117db96d56Sopenharmony_ci        bt = self.get_stack_trace(script=self.get_sample_script(),
7127db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-list 9'])
7137db96d56Sopenharmony_ci
7147db96d56Sopenharmony_ci        self.assertListing('   9    def baz(*args):\n'
7157db96d56Sopenharmony_ci                           ' >10        id(42)\n'
7167db96d56Sopenharmony_ci                           '  11    \n'
7177db96d56Sopenharmony_ci                           '  12    foo(1, 2, 3)\n',
7187db96d56Sopenharmony_ci                           bt)
7197db96d56Sopenharmony_ci
7207db96d56Sopenharmony_ci    def test_two_abs_args(self):
7217db96d56Sopenharmony_ci        'Verify the "py-list" command with two absolute arguments'
7227db96d56Sopenharmony_ci        bt = self.get_stack_trace(script=self.get_sample_script(),
7237db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-list 1,3'])
7247db96d56Sopenharmony_ci
7257db96d56Sopenharmony_ci        self.assertListing('   1    # Sample script for use by test_gdb.py\n'
7267db96d56Sopenharmony_ci                           '   2    \n'
7277db96d56Sopenharmony_ci                           '   3    def foo(a, b, c):\n',
7287db96d56Sopenharmony_ci                           bt)
7297db96d56Sopenharmony_ci
7307db96d56Sopenharmony_ciSAMPLE_WITH_C_CALL = """
7317db96d56Sopenharmony_ci
7327db96d56Sopenharmony_cifrom _testcapi import pyobject_fastcall
7337db96d56Sopenharmony_ci
7347db96d56Sopenharmony_cidef foo(a, b, c):
7357db96d56Sopenharmony_ci    bar(a, b, c)
7367db96d56Sopenharmony_ci
7377db96d56Sopenharmony_cidef bar(a, b, c):
7387db96d56Sopenharmony_ci    pyobject_fastcall(baz, (a, b, c))
7397db96d56Sopenharmony_ci
7407db96d56Sopenharmony_cidef baz(*args):
7417db96d56Sopenharmony_ci    id(42)
7427db96d56Sopenharmony_ci
7437db96d56Sopenharmony_cifoo(1, 2, 3)
7447db96d56Sopenharmony_ci
7457db96d56Sopenharmony_ci"""
7467db96d56Sopenharmony_ci
7477db96d56Sopenharmony_ci
7487db96d56Sopenharmony_ciclass StackNavigationTests(DebuggerTests):
7497db96d56Sopenharmony_ci    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
7507db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
7517db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
7527db96d56Sopenharmony_ci    def test_pyup_command(self):
7537db96d56Sopenharmony_ci        'Verify that the "py-up" command works'
7547db96d56Sopenharmony_ci        bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL,
7557db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-up', 'py-up'])
7567db96d56Sopenharmony_ci        self.assertMultilineMatches(bt,
7577db96d56Sopenharmony_ci                                    r'''^.*
7587db96d56Sopenharmony_ci#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\)
7597db96d56Sopenharmony_ci#[0-9]+ <built-in method pyobject_fastcall of module object at remote 0x[0-9a-f]+>
7607db96d56Sopenharmony_ci$''')
7617db96d56Sopenharmony_ci
7627db96d56Sopenharmony_ci    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
7637db96d56Sopenharmony_ci    def test_down_at_bottom(self):
7647db96d56Sopenharmony_ci        'Verify handling of "py-down" at the bottom of the stack'
7657db96d56Sopenharmony_ci        bt = self.get_stack_trace(script=self.get_sample_script(),
7667db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-down'])
7677db96d56Sopenharmony_ci        self.assertEndsWith(bt,
7687db96d56Sopenharmony_ci                            'Unable to find a newer python frame\n')
7697db96d56Sopenharmony_ci
7707db96d56Sopenharmony_ci    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
7717db96d56Sopenharmony_ci    def test_up_at_top(self):
7727db96d56Sopenharmony_ci        'Verify handling of "py-up" at the top of the stack'
7737db96d56Sopenharmony_ci        bt = self.get_stack_trace(script=self.get_sample_script(),
7747db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-up'] * 5)
7757db96d56Sopenharmony_ci        self.assertEndsWith(bt,
7767db96d56Sopenharmony_ci                            'Unable to find an older python frame\n')
7777db96d56Sopenharmony_ci
7787db96d56Sopenharmony_ci    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
7797db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
7807db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
7817db96d56Sopenharmony_ci    def test_up_then_down(self):
7827db96d56Sopenharmony_ci        'Verify "py-up" followed by "py-down"'
7837db96d56Sopenharmony_ci        bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL,
7847db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-up', 'py-up', 'py-down'])
7857db96d56Sopenharmony_ci        self.assertMultilineMatches(bt,
7867db96d56Sopenharmony_ci                                    r'''^.*
7877db96d56Sopenharmony_ci#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\)
7887db96d56Sopenharmony_ci#[0-9]+ <built-in method pyobject_fastcall of module object at remote 0x[0-9a-f]+>
7897db96d56Sopenharmony_ci#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\)
7907db96d56Sopenharmony_ci$''')
7917db96d56Sopenharmony_ci
7927db96d56Sopenharmony_ciclass PyBtTests(DebuggerTests):
7937db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
7947db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
7957db96d56Sopenharmony_ci    def test_bt(self):
7967db96d56Sopenharmony_ci        'Verify that the "py-bt" command works'
7977db96d56Sopenharmony_ci        bt = self.get_stack_trace(script=self.get_sample_script(),
7987db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-bt'])
7997db96d56Sopenharmony_ci        self.assertMultilineMatches(bt,
8007db96d56Sopenharmony_ci                                    r'''^.*
8017db96d56Sopenharmony_ciTraceback \(most recent call first\):
8027db96d56Sopenharmony_ci  <built-in method id of module object .*>
8037db96d56Sopenharmony_ci  File ".*gdb_sample.py", line 10, in baz
8047db96d56Sopenharmony_ci    id\(42\)
8057db96d56Sopenharmony_ci  File ".*gdb_sample.py", line 7, in bar
8067db96d56Sopenharmony_ci    baz\(a, b, c\)
8077db96d56Sopenharmony_ci  File ".*gdb_sample.py", line 4, in foo
8087db96d56Sopenharmony_ci    bar\(a=a, b=b, c=c\)
8097db96d56Sopenharmony_ci  File ".*gdb_sample.py", line 12, in <module>
8107db96d56Sopenharmony_ci    foo\(1, 2, 3\)
8117db96d56Sopenharmony_ci''')
8127db96d56Sopenharmony_ci
8137db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
8147db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
8157db96d56Sopenharmony_ci    def test_bt_full(self):
8167db96d56Sopenharmony_ci        'Verify that the "py-bt-full" command works'
8177db96d56Sopenharmony_ci        bt = self.get_stack_trace(script=self.get_sample_script(),
8187db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-bt-full'])
8197db96d56Sopenharmony_ci        self.assertMultilineMatches(bt,
8207db96d56Sopenharmony_ci                                    r'''^.*
8217db96d56Sopenharmony_ci#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
8227db96d56Sopenharmony_ci    baz\(a, b, c\)
8237db96d56Sopenharmony_ci#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
8247db96d56Sopenharmony_ci    bar\(a=a, b=b, c=c\)
8257db96d56Sopenharmony_ci#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
8267db96d56Sopenharmony_ci    foo\(1, 2, 3\)
8277db96d56Sopenharmony_ci''')
8287db96d56Sopenharmony_ci
8297db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
8307db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
8317db96d56Sopenharmony_ci    def test_threads(self):
8327db96d56Sopenharmony_ci        'Verify that "py-bt" indicates threads that are waiting for the GIL'
8337db96d56Sopenharmony_ci        cmd = '''
8347db96d56Sopenharmony_cifrom threading import Thread
8357db96d56Sopenharmony_ci
8367db96d56Sopenharmony_ciclass TestThread(Thread):
8377db96d56Sopenharmony_ci    # These threads would run forever, but we'll interrupt things with the
8387db96d56Sopenharmony_ci    # debugger
8397db96d56Sopenharmony_ci    def run(self):
8407db96d56Sopenharmony_ci        i = 0
8417db96d56Sopenharmony_ci        while 1:
8427db96d56Sopenharmony_ci             i += 1
8437db96d56Sopenharmony_ci
8447db96d56Sopenharmony_cit = {}
8457db96d56Sopenharmony_cifor i in range(4):
8467db96d56Sopenharmony_ci   t[i] = TestThread()
8477db96d56Sopenharmony_ci   t[i].start()
8487db96d56Sopenharmony_ci
8497db96d56Sopenharmony_ci# Trigger a breakpoint on the main thread
8507db96d56Sopenharmony_ciid(42)
8517db96d56Sopenharmony_ci
8527db96d56Sopenharmony_ci'''
8537db96d56Sopenharmony_ci        # Verify with "py-bt":
8547db96d56Sopenharmony_ci        gdb_output = self.get_stack_trace(cmd,
8557db96d56Sopenharmony_ci                                          cmds_after_breakpoint=['thread apply all py-bt'])
8567db96d56Sopenharmony_ci        self.assertIn('Waiting for the GIL', gdb_output)
8577db96d56Sopenharmony_ci
8587db96d56Sopenharmony_ci        # Verify with "py-bt-full":
8597db96d56Sopenharmony_ci        gdb_output = self.get_stack_trace(cmd,
8607db96d56Sopenharmony_ci                                          cmds_after_breakpoint=['thread apply all py-bt-full'])
8617db96d56Sopenharmony_ci        self.assertIn('Waiting for the GIL', gdb_output)
8627db96d56Sopenharmony_ci
8637db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
8647db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
8657db96d56Sopenharmony_ci    # Some older versions of gdb will fail with
8667db96d56Sopenharmony_ci    #  "Cannot find new threads: generic error"
8677db96d56Sopenharmony_ci    # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
8687db96d56Sopenharmony_ci    def test_gc(self):
8697db96d56Sopenharmony_ci        'Verify that "py-bt" indicates if a thread is garbage-collecting'
8707db96d56Sopenharmony_ci        cmd = ('from gc import collect\n'
8717db96d56Sopenharmony_ci               'id(42)\n'
8727db96d56Sopenharmony_ci               'def foo():\n'
8737db96d56Sopenharmony_ci               '    collect()\n'
8747db96d56Sopenharmony_ci               'def bar():\n'
8757db96d56Sopenharmony_ci               '    foo()\n'
8767db96d56Sopenharmony_ci               'bar()\n')
8777db96d56Sopenharmony_ci        # Verify with "py-bt":
8787db96d56Sopenharmony_ci        gdb_output = self.get_stack_trace(cmd,
8797db96d56Sopenharmony_ci                                          cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt'],
8807db96d56Sopenharmony_ci                                          )
8817db96d56Sopenharmony_ci        self.assertIn('Garbage-collecting', gdb_output)
8827db96d56Sopenharmony_ci
8837db96d56Sopenharmony_ci        # Verify with "py-bt-full":
8847db96d56Sopenharmony_ci        gdb_output = self.get_stack_trace(cmd,
8857db96d56Sopenharmony_ci                                          cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt-full'],
8867db96d56Sopenharmony_ci                                          )
8877db96d56Sopenharmony_ci        self.assertIn('Garbage-collecting', gdb_output)
8887db96d56Sopenharmony_ci
8897db96d56Sopenharmony_ci
8907db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
8917db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
8927db96d56Sopenharmony_ci    # Some older versions of gdb will fail with
8937db96d56Sopenharmony_ci    #  "Cannot find new threads: generic error"
8947db96d56Sopenharmony_ci    # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
8957db96d56Sopenharmony_ci    #
8967db96d56Sopenharmony_ci    # gdb will also generate many erroneous errors such as:
8977db96d56Sopenharmony_ci    #     Function "meth_varargs" not defined.
8987db96d56Sopenharmony_ci    # This is because we are calling functions from an "external" module
8997db96d56Sopenharmony_ci    # (_testcapimodule) rather than compiled-in functions. It seems difficult
9007db96d56Sopenharmony_ci    # to suppress these. See also the comment in DebuggerTests.get_stack_trace
9017db96d56Sopenharmony_ci    def test_pycfunction(self):
9027db96d56Sopenharmony_ci        'Verify that "py-bt" displays invocations of PyCFunction instances'
9037db96d56Sopenharmony_ci        # bpo-46600: If the compiler inlines _null_to_none() in meth_varargs()
9047db96d56Sopenharmony_ci        # (ex: clang -Og), _null_to_none() is the frame #1. Otherwise,
9057db96d56Sopenharmony_ci        # meth_varargs() is the frame #1.
9067db96d56Sopenharmony_ci        expected_frame = r'#(1|2)'
9077db96d56Sopenharmony_ci        # Various optimizations multiply the code paths by which these are
9087db96d56Sopenharmony_ci        # called, so test a variety of calling conventions.
9097db96d56Sopenharmony_ci        for func_name, args in (
9107db96d56Sopenharmony_ci            ('meth_varargs', ''),
9117db96d56Sopenharmony_ci            ('meth_varargs_keywords', ''),
9127db96d56Sopenharmony_ci            ('meth_o', '[]'),
9137db96d56Sopenharmony_ci            ('meth_noargs', ''),
9147db96d56Sopenharmony_ci            ('meth_fastcall', ''),
9157db96d56Sopenharmony_ci            ('meth_fastcall_keywords', ''),
9167db96d56Sopenharmony_ci        ):
9177db96d56Sopenharmony_ci            for obj in (
9187db96d56Sopenharmony_ci                '_testcapi',
9197db96d56Sopenharmony_ci                '_testcapi.MethClass',
9207db96d56Sopenharmony_ci                '_testcapi.MethClass()',
9217db96d56Sopenharmony_ci                '_testcapi.MethStatic()',
9227db96d56Sopenharmony_ci
9237db96d56Sopenharmony_ci                # XXX: bound methods don't yet give nice tracebacks
9247db96d56Sopenharmony_ci                # '_testcapi.MethInstance()',
9257db96d56Sopenharmony_ci            ):
9267db96d56Sopenharmony_ci                with self.subTest(f'{obj}.{func_name}'):
9277db96d56Sopenharmony_ci                    cmd = textwrap.dedent(f'''
9287db96d56Sopenharmony_ci                        import _testcapi
9297db96d56Sopenharmony_ci                        def foo():
9307db96d56Sopenharmony_ci                            {obj}.{func_name}({args})
9317db96d56Sopenharmony_ci                        def bar():
9327db96d56Sopenharmony_ci                            foo()
9337db96d56Sopenharmony_ci                        bar()
9347db96d56Sopenharmony_ci                    ''')
9357db96d56Sopenharmony_ci                    # Verify with "py-bt":
9367db96d56Sopenharmony_ci                    gdb_output = self.get_stack_trace(
9377db96d56Sopenharmony_ci                        cmd,
9387db96d56Sopenharmony_ci                        breakpoint=func_name,
9397db96d56Sopenharmony_ci                        cmds_after_breakpoint=['bt', 'py-bt'],
9407db96d56Sopenharmony_ci                        # bpo-45207: Ignore 'Function "meth_varargs" not
9417db96d56Sopenharmony_ci                        # defined.' message in stderr.
9427db96d56Sopenharmony_ci                        ignore_stderr=True,
9437db96d56Sopenharmony_ci                    )
9447db96d56Sopenharmony_ci                    self.assertIn(f'<built-in method {func_name}', gdb_output)
9457db96d56Sopenharmony_ci
9467db96d56Sopenharmony_ci                    # Verify with "py-bt-full":
9477db96d56Sopenharmony_ci                    gdb_output = self.get_stack_trace(
9487db96d56Sopenharmony_ci                        cmd,
9497db96d56Sopenharmony_ci                        breakpoint=func_name,
9507db96d56Sopenharmony_ci                        cmds_after_breakpoint=['py-bt-full'],
9517db96d56Sopenharmony_ci                        # bpo-45207: Ignore 'Function "meth_varargs" not
9527db96d56Sopenharmony_ci                        # defined.' message in stderr.
9537db96d56Sopenharmony_ci                        ignore_stderr=True,
9547db96d56Sopenharmony_ci                    )
9557db96d56Sopenharmony_ci                    regex = expected_frame
9567db96d56Sopenharmony_ci                    regex += re.escape(f' <built-in method {func_name}')
9577db96d56Sopenharmony_ci                    self.assertRegex(gdb_output, regex)
9587db96d56Sopenharmony_ci
9597db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
9607db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
9617db96d56Sopenharmony_ci    def test_wrapper_call(self):
9627db96d56Sopenharmony_ci        cmd = textwrap.dedent('''
9637db96d56Sopenharmony_ci            class MyList(list):
9647db96d56Sopenharmony_ci                def __init__(self):
9657db96d56Sopenharmony_ci                    super().__init__()   # wrapper_call()
9667db96d56Sopenharmony_ci
9677db96d56Sopenharmony_ci            id("first break point")
9687db96d56Sopenharmony_ci            l = MyList()
9697db96d56Sopenharmony_ci        ''')
9707db96d56Sopenharmony_ci        cmds_after_breakpoint = ['break wrapper_call', 'continue']
9717db96d56Sopenharmony_ci        if CET_PROTECTION:
9727db96d56Sopenharmony_ci            # bpo-32962: same case as in get_stack_trace():
9737db96d56Sopenharmony_ci            # we need an additional 'next' command in order to read
9747db96d56Sopenharmony_ci            # arguments of the innermost function of the call stack.
9757db96d56Sopenharmony_ci            cmds_after_breakpoint.append('next')
9767db96d56Sopenharmony_ci        cmds_after_breakpoint.append('py-bt')
9777db96d56Sopenharmony_ci
9787db96d56Sopenharmony_ci        # Verify with "py-bt":
9797db96d56Sopenharmony_ci        gdb_output = self.get_stack_trace(cmd,
9807db96d56Sopenharmony_ci                                          cmds_after_breakpoint=cmds_after_breakpoint)
9817db96d56Sopenharmony_ci        self.assertRegex(gdb_output,
9827db96d56Sopenharmony_ci                         r"<method-wrapper u?'__init__' of MyList object at ")
9837db96d56Sopenharmony_ci
9847db96d56Sopenharmony_ciclass PyPrintTests(DebuggerTests):
9857db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
9867db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
9877db96d56Sopenharmony_ci    def test_basic_command(self):
9887db96d56Sopenharmony_ci        'Verify that the "py-print" command works'
9897db96d56Sopenharmony_ci        bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL,
9907db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-up', 'py-print args'])
9917db96d56Sopenharmony_ci        self.assertMultilineMatches(bt,
9927db96d56Sopenharmony_ci                                    r".*\nlocal 'args' = \(1, 2, 3\)\n.*")
9937db96d56Sopenharmony_ci
9947db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
9957db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
9967db96d56Sopenharmony_ci    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
9977db96d56Sopenharmony_ci    def test_print_after_up(self):
9987db96d56Sopenharmony_ci        bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL,
9997db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-up', 'py-up', 'py-print c', 'py-print b', 'py-print a'])
10007db96d56Sopenharmony_ci        self.assertMultilineMatches(bt,
10017db96d56Sopenharmony_ci                                    r".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*")
10027db96d56Sopenharmony_ci
10037db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
10047db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
10057db96d56Sopenharmony_ci    def test_printing_global(self):
10067db96d56Sopenharmony_ci        bt = self.get_stack_trace(script=self.get_sample_script(),
10077db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-up', 'py-print __name__'])
10087db96d56Sopenharmony_ci        self.assertMultilineMatches(bt,
10097db96d56Sopenharmony_ci                                    r".*\nglobal '__name__' = '__main__'\n.*")
10107db96d56Sopenharmony_ci
10117db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
10127db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
10137db96d56Sopenharmony_ci    def test_printing_builtin(self):
10147db96d56Sopenharmony_ci        bt = self.get_stack_trace(script=self.get_sample_script(),
10157db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-up', 'py-print len'])
10167db96d56Sopenharmony_ci        self.assertMultilineMatches(bt,
10177db96d56Sopenharmony_ci                                    r".*\nbuiltin 'len' = <built-in method len of module object at remote 0x-?[0-9a-f]+>\n.*")
10187db96d56Sopenharmony_ci
10197db96d56Sopenharmony_ciclass PyLocalsTests(DebuggerTests):
10207db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
10217db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
10227db96d56Sopenharmony_ci    def test_basic_command(self):
10237db96d56Sopenharmony_ci        bt = self.get_stack_trace(script=self.get_sample_script(),
10247db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-up', 'py-locals'])
10257db96d56Sopenharmony_ci        self.assertMultilineMatches(bt,
10267db96d56Sopenharmony_ci                                    r".*\nargs = \(1, 2, 3\)\n.*")
10277db96d56Sopenharmony_ci
10287db96d56Sopenharmony_ci    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
10297db96d56Sopenharmony_ci    @unittest.skipIf(python_is_optimized(),
10307db96d56Sopenharmony_ci                     "Python was compiled with optimizations")
10317db96d56Sopenharmony_ci    def test_locals_after_up(self):
10327db96d56Sopenharmony_ci        bt = self.get_stack_trace(script=self.get_sample_script(),
10337db96d56Sopenharmony_ci                                  cmds_after_breakpoint=['py-up', 'py-up', 'py-locals'])
10347db96d56Sopenharmony_ci        self.assertMultilineMatches(bt,
10357db96d56Sopenharmony_ci                                    r'''^.*
10367db96d56Sopenharmony_ciLocals for foo
10377db96d56Sopenharmony_cia = 1
10387db96d56Sopenharmony_cib = 2
10397db96d56Sopenharmony_cic = 3
10407db96d56Sopenharmony_ciLocals for <module>
10417db96d56Sopenharmony_ci.*$''')
10427db96d56Sopenharmony_ci
10437db96d56Sopenharmony_ci
10447db96d56Sopenharmony_cidef setUpModule():
10457db96d56Sopenharmony_ci    if support.verbose:
10467db96d56Sopenharmony_ci        print("GDB version %s.%s:" % (gdb_major_version, gdb_minor_version))
10477db96d56Sopenharmony_ci        for line in gdb_version.splitlines():
10487db96d56Sopenharmony_ci            print(" " * 4 + line)
10497db96d56Sopenharmony_ci
10507db96d56Sopenharmony_ci
10517db96d56Sopenharmony_ciif __name__ == "__main__":
10527db96d56Sopenharmony_ci    unittest.main()
1053