18cc49becSPavel Labathfrom __future__ import print_function
28cc49becSPavel Labath
38cc49becSPavel Labathimport gdbremote_testcase
4*872b1da6SPavel Labathimport random
58cc49becSPavel Labathimport select
68cc49becSPavel Labathimport socket
78cc49becSPavel Labathfrom lldbsuite.test.decorators import *
88cc49becSPavel Labathfrom lldbsuite.test.lldbtest import *
9*872b1da6SPavel Labathfrom lldbgdbserverutils import Server
1085f025e5SPavel Labathimport lldbsuite.test.lldbplatformutil
1185f025e5SPavel Labath
1285f025e5SPavel Labathif lldbplatformutil.getHostPlatform() == "windows":
1385f025e5SPavel Labath    import ctypes
1485f025e5SPavel Labath    import ctypes.wintypes
1585f025e5SPavel Labath    from ctypes.wintypes import (BOOL, DWORD, HANDLE, LPCWSTR, LPDWORD, LPVOID)
1685f025e5SPavel Labath
1785f025e5SPavel Labath    kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
1885f025e5SPavel Labath
1985f025e5SPavel Labath    PIPE_ACCESS_INBOUND = 1
2085f025e5SPavel Labath    FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000
2185f025e5SPavel Labath    FILE_FLAG_OVERLAPPED = 0x40000000
2285f025e5SPavel Labath    PIPE_TYPE_BYTE = 0
2385f025e5SPavel Labath    PIPE_REJECT_REMOTE_CLIENTS = 8
2485f025e5SPavel Labath    INVALID_HANDLE_VALUE = -1
2585f025e5SPavel Labath    ERROR_ACCESS_DENIED = 5
2685f025e5SPavel Labath    ERROR_IO_PENDING = 997
2785f025e5SPavel Labath
2885f025e5SPavel Labath
2985f025e5SPavel Labath    class OVERLAPPED(ctypes.Structure):
3085f025e5SPavel Labath        _fields_ = [("Internal", LPVOID), ("InternalHigh", LPVOID), ("Offset",
3185f025e5SPavel Labath            DWORD), ("OffsetHigh", DWORD), ("hEvent", HANDLE)]
3285f025e5SPavel Labath
3385f025e5SPavel Labath        def __init__(self):
3485f025e5SPavel Labath            super(OVERLAPPED, self).__init__(Internal=0, InternalHigh=0,
3585f025e5SPavel Labath                Offset=0, OffsetHigh=0, hEvent=None)
3685f025e5SPavel Labath    LPOVERLAPPED = ctypes.POINTER(OVERLAPPED)
3785f025e5SPavel Labath
3885f025e5SPavel Labath    CreateNamedPipe = kernel32.CreateNamedPipeW
3985f025e5SPavel Labath    CreateNamedPipe.restype = HANDLE
4085f025e5SPavel Labath    CreateNamedPipe.argtypes = (LPCWSTR, DWORD, DWORD, DWORD, DWORD, DWORD,
4185f025e5SPavel Labath            DWORD, LPVOID)
4285f025e5SPavel Labath
4385f025e5SPavel Labath    ConnectNamedPipe = kernel32.ConnectNamedPipe
4485f025e5SPavel Labath    ConnectNamedPipe.restype = BOOL
4585f025e5SPavel Labath    ConnectNamedPipe.argtypes = (HANDLE, LPOVERLAPPED)
4685f025e5SPavel Labath
4785f025e5SPavel Labath    CreateEvent = kernel32.CreateEventW
4885f025e5SPavel Labath    CreateEvent.restype = HANDLE
4985f025e5SPavel Labath    CreateEvent.argtypes = (LPVOID, BOOL, BOOL, LPCWSTR)
5085f025e5SPavel Labath
5185f025e5SPavel Labath    GetOverlappedResultEx = kernel32.GetOverlappedResultEx
5285f025e5SPavel Labath    GetOverlappedResultEx.restype = BOOL
5385f025e5SPavel Labath    GetOverlappedResultEx.argtypes = (HANDLE, LPOVERLAPPED, LPDWORD, DWORD,
5485f025e5SPavel Labath        BOOL)
5585f025e5SPavel Labath
5685f025e5SPavel Labath    ReadFile = kernel32.ReadFile
5785f025e5SPavel Labath    ReadFile.restype = BOOL
5885f025e5SPavel Labath    ReadFile.argtypes = (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED)
5985f025e5SPavel Labath
6085f025e5SPavel Labath    CloseHandle = kernel32.CloseHandle
6185f025e5SPavel Labath    CloseHandle.restype = BOOL
6285f025e5SPavel Labath    CloseHandle.argtypes = (HANDLE,)
6385f025e5SPavel Labath
6485f025e5SPavel Labath    class Pipe(object):
6585f025e5SPavel Labath        def __init__(self, prefix):
6685f025e5SPavel Labath            while True:
6785f025e5SPavel Labath                self.name = "lldb-" + str(random.randrange(1e10))
6885f025e5SPavel Labath                full_name = "\\\\.\\pipe\\" + self.name
6985f025e5SPavel Labath                self._handle = CreateNamedPipe(full_name, PIPE_ACCESS_INBOUND |
7085f025e5SPavel Labath                        FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
7185f025e5SPavel Labath                        PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS, 1, 4096,
7285f025e5SPavel Labath                        4096, 0, None)
7385f025e5SPavel Labath                if self._handle != INVALID_HANDLE_VALUE:
7485f025e5SPavel Labath                    break
7585f025e5SPavel Labath                if ctypes.get_last_error() != ERROR_ACCESS_DENIED:
7685f025e5SPavel Labath                    raise ctypes.WinError(ctypes.get_last_error())
7785f025e5SPavel Labath
7885f025e5SPavel Labath            self._overlapped = OVERLAPPED()
7985f025e5SPavel Labath            self._overlapped.hEvent = CreateEvent(None, True, False, None)
8085f025e5SPavel Labath            result = ConnectNamedPipe(self._handle, self._overlapped)
8185f025e5SPavel Labath            assert result == 0
8285f025e5SPavel Labath            if ctypes.get_last_error() != ERROR_IO_PENDING:
8385f025e5SPavel Labath                raise ctypes.WinError(ctypes.get_last_error())
8485f025e5SPavel Labath
8585f025e5SPavel Labath        def finish_connection(self, timeout):
8685f025e5SPavel Labath            if not GetOverlappedResultEx(self._handle, self._overlapped,
8785f025e5SPavel Labath                    ctypes.byref(DWORD(0)), timeout*1000, True):
8885f025e5SPavel Labath                raise ctypes.WinError(ctypes.get_last_error())
8985f025e5SPavel Labath
9085f025e5SPavel Labath        def read(self, size, timeout):
9185f025e5SPavel Labath            buf = ctypes.create_string_buffer(size)
9285f025e5SPavel Labath            if not ReadFile(self._handle, ctypes.byref(buf), size, None,
9385f025e5SPavel Labath                    self._overlapped):
9485f025e5SPavel Labath                if ctypes.get_last_error() != ERROR_IO_PENDING:
9585f025e5SPavel Labath                    raise ctypes.WinError(ctypes.get_last_error())
9685f025e5SPavel Labath            read = DWORD(0)
9785f025e5SPavel Labath            if not GetOverlappedResultEx(self._handle, self._overlapped,
9885f025e5SPavel Labath                    ctypes.byref(read), timeout*1000, True):
9985f025e5SPavel Labath                raise ctypes.WinError(ctypes.get_last_error())
10085f025e5SPavel Labath            return buf.raw[0:read.value]
10185f025e5SPavel Labath
10285f025e5SPavel Labath        def close(self):
10385f025e5SPavel Labath            CloseHandle(self._overlapped.hEvent)
10485f025e5SPavel Labath            CloseHandle(self._handle)
10585f025e5SPavel Labath
10685f025e5SPavel Labath
10785f025e5SPavel Labathelse:
10885f025e5SPavel Labath    class Pipe(object):
10985f025e5SPavel Labath        def __init__(self, prefix):
11085f025e5SPavel Labath            self.name = os.path.join(prefix, "stub_port_number")
11185f025e5SPavel Labath            os.mkfifo(self.name)
11285f025e5SPavel Labath            self._fd = os.open(self.name, os.O_RDONLY | os.O_NONBLOCK)
11385f025e5SPavel Labath
11485f025e5SPavel Labath        def finish_connection(self, timeout):
11585f025e5SPavel Labath            pass
11685f025e5SPavel Labath
11785f025e5SPavel Labath        def read(self, size, timeout):
11885f025e5SPavel Labath            (readers, _, _) = select.select([self._fd], [], [], timeout)
11985f025e5SPavel Labath            if self._fd not in readers:
12085f025e5SPavel Labath                raise TimeoutError
12185f025e5SPavel Labath            return os.read(self._fd, size)
12285f025e5SPavel Labath
12385f025e5SPavel Labath        def close(self):
12485f025e5SPavel Labath            os.close(self._fd)
1258cc49becSPavel Labath
1268cc49becSPavel Labath
1278cc49becSPavel Labathclass TestGdbRemoteConnection(gdbremote_testcase.GdbRemoteTestCaseBase):
1288cc49becSPavel Labath
1298cc49becSPavel Labath    @skipIfRemote  # reverse connect is not a supported use case for now
130a63daf69SPavel Labath    def test_reverse_connect(self):
1318cc49becSPavel Labath        # Reverse connect is the default connection method.
1328cc49becSPavel Labath        self.connect_to_debug_monitor()
1338cc49becSPavel Labath        # Verify we can do the handshake.  If that works, we'll call it good.
134*872b1da6SPavel Labath        self.do_handshake()
1358cc49becSPavel Labath
1368cc49becSPavel Labath    @skipIfRemote
137a63daf69SPavel Labath    def test_named_pipe(self):
1388cc49becSPavel Labath        family, type, proto, _, addr = socket.getaddrinfo(
1398cc49becSPavel Labath            self.stub_hostname, 0, proto=socket.IPPROTO_TCP)[0]
1408cc49becSPavel Labath        self.sock = socket.socket(family, type, proto)
1418cc49becSPavel Labath        self.sock.settimeout(self.DEFAULT_TIMEOUT)
1428cc49becSPavel Labath
1438cc49becSPavel Labath        self.addTearDownHook(lambda: self.sock.close())
1448cc49becSPavel Labath
14585f025e5SPavel Labath        pipe = Pipe(self.getBuildDir())
1468cc49becSPavel Labath
14785f025e5SPavel Labath        self.addTearDownHook(lambda: pipe.close())
1488cc49becSPavel Labath
1498cc49becSPavel Labath        args = self.debug_monitor_extra_args
1508cc49becSPavel Labath        if lldb.remote_platform:
1518cc49becSPavel Labath            args += ["*:0"]
1528cc49becSPavel Labath        else:
1538cc49becSPavel Labath            args += ["localhost:0"]
1548cc49becSPavel Labath
15585f025e5SPavel Labath        args += ["--named-pipe", pipe.name]
1568cc49becSPavel Labath
1578cc49becSPavel Labath        server = self.spawnSubprocess(
1588cc49becSPavel Labath            self.debug_monitor_exe,
1598cc49becSPavel Labath            args,
1608cc49becSPavel Labath            install_remote=False)
1618cc49becSPavel Labath
16285f025e5SPavel Labath        pipe.finish_connection(self.DEFAULT_TIMEOUT)
16385f025e5SPavel Labath        port = pipe.read(10, self.DEFAULT_TIMEOUT)
1648cc49becSPavel Labath        # Trim null byte, convert to int
1658cc49becSPavel Labath        addr = (addr[0], int(port[:-1]))
1668cc49becSPavel Labath        self.sock.connect(addr)
167*872b1da6SPavel Labath        self._server = Server(self.sock, server)
1688cc49becSPavel Labath
1698cc49becSPavel Labath        # Verify we can do the handshake.  If that works, we'll call it good.
170*872b1da6SPavel Labath        self.do_handshake()
171