Index: lldb/test/API/tools/lldb-server/TestPtyServer.py =================================================================== --- /dev/null +++ lldb/test/API/tools/lldb-server/TestPtyServer.py @@ -0,0 +1,73 @@ +import gdbremote_testcase +import lldbgdbserverutils +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbgdbserverutils import * + +import xml.etree.ElementTree as ET + + +@skipIfWindows +class PtyServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase): + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + super().setUp() + import pty + import tty + master, slave = pty.openpty() + tty.setraw(master) + self._master = io.FileIO(master, 'r+b') + self._slave = io.FileIO(slave, 'r+b') + + def get_debug_monitor_command_line_args(self, attach_pid=None): + commandline_args = self.debug_monitor_extra_args + if attach_pid: + commandline_args += ["--attach=%d" % attach_pid] + + libc = ctypes.CDLL(None) + libc.ptsname.argtypes = (ctypes.c_int,) + libc.ptsname.restype = ctypes.c_char_p + pty_path = libc.ptsname(self._master.fileno()).decode() + commandline_args += ["serial://%s" % (pty_path,)] + return commandline_args + + def connect_to_debug_monitor(self, attach_pid=None): + self.reverse_connect = False + server = self.launch_debug_monitor(attach_pid=attach_pid) + self.assertIsNotNone(server) + + # TODO: make it into proper abstraction + class FakeSocket: + def __init__(self, fd): + self.fd = fd + + def sendall(self, frame): + self.fd.write(frame) + + def recv(self, count): + return self.fd.read(count) + + self.sock = FakeSocket(self._master) + self._server = Server(self.sock, server) + return server + + @add_test_categories(["llgs"]) + def test_pty_server(self): + self.build() + self.set_inferior_startup_launch() + self.prep_debug_monitor_and_inferior() + + # target.xml transfer should trigger a large enough packet to check + # for partial write regression + self.test_sequence.add_log_lines([ + "read packet: $qXfer:features:read:target.xml:0,200000#00", + { + "direction": "send", + "regex": re.compile("^\$l(.+)#[0-9a-fA-F]{2}$"), + "capture": {1: "target_xml"}, + }], + True) + context = self.expect_gdbremote_sequence() + # verify that we have received a complete, non-malformed XML + self.assertIsNotNone(ET.fromstring(context.get("target_xml"))) Index: lldb/tools/lldb-server/lldb-gdbserver.cpp =================================================================== --- lldb/tools/lldb-server/lldb-gdbserver.cpp +++ lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -230,24 +230,27 @@ final_host_and_port.append("localhost"); final_host_and_port.append(host_and_port.str()); - if (reverse_connect) { + bool is_url = host_and_port.contains("://"); + if (reverse_connect || is_url) { // llgs will connect to the gdb-remote client. - // Ensure we have a port number for the connection. - // Note: use rfind, because the host/port may look like "[::1]:12345". - uint32_t connection_portno = 0; - const std::string::size_type colon_pos = final_host_and_port.rfind(':'); - if (colon_pos != std::string::npos) - llvm::to_integer(final_host_and_port.substr(colon_pos + 1), - connection_portno); - if (connection_portno == 0) { - llvm::errs() << "error: port number must be specified on when using " - "reverse connect\n"; - exit(1); - } + if (!is_url) { + // Ensure we have a port number for the connection. + // Note: use rfind, because the host/port may look like "[::1]:12345". + uint32_t connection_portno = 0; + const std::string::size_type colon_pos = final_host_and_port.rfind(':'); + if (colon_pos != std::string::npos) + llvm::to_integer(final_host_and_port.substr(colon_pos + 1), + connection_portno); + if (connection_portno == 0) { + llvm::errs() << "error: port number must be specified on when using " + "reverse connect\n"; + exit(1); + } - // Build the connection string. - final_host_and_port.insert(0, "connect://"); + // Build the connection string. + final_host_and_port.insert(0, "connect://"); + } // Create the connection. connection_up.reset(new ConnectionFileDescriptor);