brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.3 KiB · 4ffa864 Raw
205 lines · python
1import lldb2import os3import re4import socket5import time6 7from contextlib import closing8from lldbsuite.test.decorators import *9from lldbsuite.test.lldbtest import *10from lldbsuite.test.lldbpexpect import PExpectTest11from lldbgdbserverutils import get_lldb_server_exe12 13 14# PExpect uses many timeouts internally and doesn't play well15# under ASAN on a loaded machine..16@skipIfAsan17class TestStatusline(PExpectTest):18    # Change this value to something smaller to make debugging this test less19    # tedious.20    TIMEOUT = 6021 22    TERMINAL_HEIGHT = 1023    TERMINAL_WIDTH = 6024 25    def do_setup(self):26        # Create a target and run to a breakpoint.27        exe = self.getBuildArtifact("a.out")28        self.expect(29            "target create {}".format(exe), substrs=["Current executable set to"]30        )31        self.expect('breakpoint set -p "Break here"', substrs=["Breakpoint 1"])32        self.expect("run", substrs=["stop reason"])33        self.resize()34 35    def resize(self, height=None, width=None):36        height = self.TERMINAL_HEIGHT if not height else height37        width = self.TERMINAL_WIDTH if not width else width38        # Change the terminal dimensions. When we launch the tests, we reset39        # all the settings, leaving the terminal dimensions unset.40        self.child.setwinsize(height, width)41 42    def test(self):43        """Basic test for the statusline."""44        self.build()45        self.launch(timeout=self.TIMEOUT)46        self.do_setup()47 48        # Enable the statusline and check for the control character and that we49        # can see the target, the location and the stop reason.50        self.expect('set set separator "| "')51        self.expect(52            "set set show-statusline true",53            [54                "\x1b[1;{}r".format(self.TERMINAL_HEIGHT - 1),55                "a.out | main.c:2:11 | breakpoint 1.1                        ",56            ],57        )58 59        # Change the terminal dimensions and make sure it's reflected immediately.60        self.child.setwinsize(self.TERMINAL_HEIGHT, 25)61        self.child.expect(re.escape("a.out | main.c:2:11 | bre"))62        self.child.setwinsize(self.TERMINAL_HEIGHT, self.TERMINAL_WIDTH)63 64        # Change the separator.65        self.expect('set set separator "S "', ["a.out S main.c:2:11"])66 67        # Change the format.68        self.expect(69            'set set statusline-format "target = {${target.file.basename}} ${separator}"',70            ["target = a.out S"],71        )72        self.expect('set set separator "| "')73 74        # Hide the statusline and check for the control character.75        self.expect(76            "set set show-statusline false", ["\x1b[1;{}r".format(self.TERMINAL_HEIGHT)]77        )78 79    def test_no_color(self):80        """Basic test for the statusline with colors disabled."""81        self.build()82        self.launch(use_colors=False, timeout=self.TIMEOUT)83        self.do_setup()84 85        # Enable the statusline and check for the "reverse video" control character.86        self.expect(87            "set set show-statusline true",88            [89                "\x1b[7m",90            ],91        )92 93    def test_deadlock(self):94        """Regression test for lock inversion between the statusline mutex and95        the output mutex."""96        self.build()97        self.launch(98            extra_args=["-o", "settings set use-color false"], timeout=self.TIMEOUT99        )100        self.child.expect("(lldb)")101        self.resize()102 103        exe = self.getBuildArtifact("a.out")104 105        self.expect("file {}".format(exe), ["Current executable"])106        self.expect("help", ["Debugger commands"])107 108    def test_no_target(self):109        """Test that we print "no target" when launched without a target."""110        self.launch(timeout=self.TIMEOUT)111        self.resize()112 113        self.expect("set set show-statusline true", ["no target"])114 115    @skipIfEditlineSupportMissing116    def test_resize(self):117        """Test that move the cursor when resizing."""118        self.launch(timeout=self.TIMEOUT)119        self.resize()120        self.expect("set set show-statusline true", ["no target"])121        self.resize(20, 60)122        # Check for the escape code to resize the scroll window.123        self.child.expect(re.escape("\x1b[1;19r"))124        self.child.expect("(lldb)")125 126    @skipIfRemote127    @skipIfWindows128    @skipIfDarwin129    @skipIfLinux # https://github.com/llvm/llvm-project/issues/154763130    @add_test_categories(["lldb-server"])131    def test_modulelist_deadlock(self):132        """Regression test for a deadlock that occurs when the status line is enabled before connecting133        to a gdb-remote server.134        """135        if get_lldb_server_exe() is None:136            self.skipTest("lldb-server not found")137 138        MAX_RETRY_ATTEMPTS = 10139        DELAY = 0.25140 141        def _find_free_port(host):142            for attempt in range(MAX_RETRY_ATTEMPTS):143                try:144                    family, type, proto, _, _ = socket.getaddrinfo(145                        host, 0, proto=socket.IPPROTO_TCP146                    )[0]147                    with closing(socket.socket(family, type, proto)) as sock:148                        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)149                        sock.bind((host, 0))150                        return sock.getsockname()151                except OSError:152                    time.sleep(DELAY * 2**attempt)  # Exponential backoff153            raise RuntimeError(154                "Could not find a free port on {} after {} attempts.".format(155                    host, MAX_RETRY_ATTEMPTS156                )157            )158 159        def _wait_for_server_ready_in_log(log_file_path, ready_message):160            """Check log file for server ready message with retry logic"""161            for attempt in range(MAX_RETRY_ATTEMPTS):162                if os.path.exists(log_file_path):163                    with open(log_file_path, "r") as f:164                        if ready_message in f.read():165                            return166                time.sleep(pow(2, attempt) * DELAY)167            raise RuntimeError(168                "Server not ready after {} attempts.".format(MAX_RETRY_ATTEMPTS)169            )170 171        self.build()172        exe_path = self.getBuildArtifact("a.out")173        server_log_file = self.getLogBasenameForCurrentTest() + "-lldbserver.log"174        self.addTearDownHook(175            lambda: os.path.exists(server_log_file) and os.remove(server_log_file)176        )177 178        # Find a free port for the server179        addr = _find_free_port("localhost")180        connect_address = "[{}]:{}".format(*addr)181        commandline_args = [182            "gdbserver",183            connect_address,184            exe_path,185            "--log-file={}".format(server_log_file),186            "--log-channels=lldb conn",187        ]188 189        server_proc = self.spawnSubprocess(190            get_lldb_server_exe(), commandline_args, install_remote=False191        )192        self.assertIsNotNone(server_proc)193 194        # Wait for server to be ready by checking log file.195        server_ready_message = "Listen to {}".format(connect_address)196        _wait_for_server_ready_in_log(server_log_file, server_ready_message)197 198        # Launch LLDB client and connect to lldb-server with statusline enabled199        self.launch(timeout=self.TIMEOUT)200        self.resize()201        self.expect("settings set show-statusline true", ["no target"])202        self.expect(203            f"gdb-remote {connect_address}", [b"a.out \xe2\x94\x82 signal SIGSTOP"]204        )205