brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.6 KiB · e886a6f Raw
180 lines · python
1import logging2import os3import os.path4import random5 6import lldb7from lldbsuite.test.lldbtest import *8from lldbsuite.test.gdbclientutils import *9import lldbgdbserverutils10from lldbsuite.support import seven11 12 13class GDBProxyTestBase(TestBase):14    """15    Base class for gdbserver proxy tests.16 17    This class will setup and start a mock GDB server for the test to use.18    It pases through requests to a regular lldb-server/debugserver and19    forwards replies back to the LLDB under test.20    """21 22    """The gdbserver that we implement."""23    server = None24    """The inner lldb-server/debugserver process that we proxy requests into."""25    monitor_server = None26    monitor_sock = None27 28    server_socket_class = TCPServerSocket29 30    DEFAULT_TIMEOUT = 20 * (10 if ("ASAN_OPTIONS" in os.environ) else 1)31 32    _verbose_log_handler = None33    _log_formatter = logging.Formatter(fmt="%(asctime)-15s %(levelname)-8s %(message)s")34 35    def setUpBaseLogging(self):36        self.logger = logging.getLogger(__name__)37 38        self.logger.propagate = False39        self.logger.setLevel(logging.DEBUG)40 41        # log all warnings to stderr42        self._stderr_log_handler = logging.StreamHandler()43        self._stderr_log_handler.setLevel(44            logging.DEBUG if self.TraceOn() else logging.WARNING45        )46        self._stderr_log_handler.setFormatter(self._log_formatter)47        self.logger.addHandler(self._stderr_log_handler)48 49    def setUp(self):50        TestBase.setUp(self)51 52        self.setUpBaseLogging()53 54        if self.isVerboseLoggingRequested():55            # If requested, full logs go to a log file56            log_file_name = self.getLogBasenameForCurrentTest() + "-proxy.log"57            self._verbose_log_handler = logging.FileHandler(log_file_name)58            self._verbose_log_handler.setFormatter(self._log_formatter)59            self._verbose_log_handler.setLevel(logging.DEBUG)60            self.logger.addHandler(self._verbose_log_handler)61 62        if lldbplatformutil.getPlatform() == "macosx":63            self.debug_monitor_exe = lldbgdbserverutils.get_debugserver_exe()64            self.debug_monitor_extra_args = []65        else:66            self.debug_monitor_exe = lldbgdbserverutils.get_lldb_server_exe()67            self.debug_monitor_extra_args = ["gdbserver"]68        self.assertIsNotNone(self.debug_monitor_exe)69 70        self.server = MockGDBServer(self.server_socket_class())71        self.server.responder = self72 73    def tearDown(self):74        # TestBase.tearDown will kill the process, but we need to kill it early75        # so its client connection closes and we can stop the server before76        # finally calling the base tearDown.77        if self.process() is not None:78            self.process().Kill()79        self.server.stop()80 81        self.logger.removeHandler(self._verbose_log_handler)82        self._verbose_log_handler = None83        self.logger.removeHandler(self._stderr_log_handler)84        self._stderr_log_handler = None85 86        TestBase.tearDown(self)87 88    def isVerboseLoggingRequested(self):89        # We will report our detailed logs if the user requested that the "gdb-remote" channel is90        # logged.91        return any(("gdb-remote" in channel) for channel in lldbtest_config.channels)92 93    def connect(self, target):94        """95        Create a process by connecting to the mock GDB server.96        """97        self.prep_debug_monitor_and_inferior()98        self.server.start()99 100        listener = self.dbg.GetListener()101        error = lldb.SBError()102        process = target.ConnectRemote(103            listener, self.server.get_connect_url(), "gdb-remote", error104        )105        self.assertTrue(error.Success(), error.description)106        self.assertTrue(process, PROCESS_IS_VALID)107        return process108 109    def prep_debug_monitor_and_inferior(self):110        inferior_exe_path = self.getBuildArtifact("a.out")111        self.connect_to_debug_monitor([inferior_exe_path])112        self.assertIsNotNone(self.monitor_server)113        self.initial_handshake()114 115    def initial_handshake(self):116        self.monitor_server.send_packet(seven.bitcast_to_bytes("+"))117        reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())118        self.assertEqual(reply, "+")119        self.monitor_server.send_packet(seven.bitcast_to_bytes("QStartNoAckMode"))120        reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())121        self.assertEqual(reply, "+")122        reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())123        self.assertEqual(reply, "OK")124        self.monitor_server.set_validate_checksums(False)125        self.monitor_server.send_packet(seven.bitcast_to_bytes("+"))126        reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())127        self.assertEqual(reply, "+")128 129    def get_debug_monitor_command_line_args(self, connect_address, launch_args):130        return (131            self.debug_monitor_extra_args132            + ["--reverse-connect", connect_address]133            + launch_args134        )135 136    def launch_debug_monitor(self, launch_args):137        family, type, proto, _, addr = socket.getaddrinfo(138            "localhost", 0, proto=socket.IPPROTO_TCP139        )[0]140        sock = socket.socket(family, type, proto)141        sock.settimeout(self.DEFAULT_TIMEOUT)142        sock.bind(addr)143        sock.listen(1)144        addr = sock.getsockname()145        connect_address = "[{}]:{}".format(*addr)146 147        commandline_args = self.get_debug_monitor_command_line_args(148            connect_address, launch_args149        )150 151        # Start the server.152        self.logger.info(f"Spawning monitor {commandline_args}")153        monitor_process = self.spawnSubprocess(154            self.debug_monitor_exe, commandline_args, install_remote=False155        )156        self.assertIsNotNone(monitor_process)157 158        self.monitor_sock = sock.accept()[0]159        self.monitor_sock.settimeout(self.DEFAULT_TIMEOUT)160        return monitor_process161 162    def connect_to_debug_monitor(self, launch_args):163        monitor_process = self.launch_debug_monitor(launch_args)164        # Turn off checksum validation because debugserver does not produce165        # correct checksums.166        self.monitor_server = lldbgdbserverutils.Server(167            self.monitor_sock, monitor_process168        )169 170    def respond(self, packet):171        """Subclasses can override this to change how packets are handled."""172        return self.pass_through(packet)173 174    def pass_through(self, packet):175        self.logger.info(f"Sending packet {packet}")176        self.monitor_server.send_packet(seven.bitcast_to_bytes(packet))177        reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())178        self.logger.info(f"Received reply {reply}")179        return reply180