diff --git a/lldb/test/API/tools/lldb-server/commandline/TestGdbRemoteConnection.py b/lldb/test/API/tools/lldb-server/commandline/TestGdbRemoteConnection.py --- a/lldb/test/API/tools/lldb-server/commandline/TestGdbRemoteConnection.py +++ b/lldb/test/API/tools/lldb-server/commandline/TestGdbRemoteConnection.py @@ -5,6 +5,122 @@ import socket from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbplatformutil +import random + +if lldbplatformutil.getHostPlatform() == "windows": + import ctypes + import ctypes.wintypes + from ctypes.wintypes import (BOOL, DWORD, HANDLE, LPCWSTR, LPDWORD, LPVOID) + + kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) + + PIPE_ACCESS_INBOUND = 1 + FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000 + FILE_FLAG_OVERLAPPED = 0x40000000 + PIPE_TYPE_BYTE = 0 + PIPE_REJECT_REMOTE_CLIENTS = 8 + INVALID_HANDLE_VALUE = -1 + ERROR_ACCESS_DENIED = 5 + ERROR_IO_PENDING = 997 + + + class OVERLAPPED(ctypes.Structure): + _fields_ = [("Internal", LPVOID), ("InternalHigh", LPVOID), ("Offset", + DWORD), ("OffsetHigh", DWORD), ("hEvent", HANDLE)] + + def __init__(self): + super(OVERLAPPED, self).__init__(Internal=0, InternalHigh=0, + Offset=0, OffsetHigh=0, hEvent=None) + LPOVERLAPPED = ctypes.POINTER(OVERLAPPED) + + CreateNamedPipe = kernel32.CreateNamedPipeW + CreateNamedPipe.restype = HANDLE + CreateNamedPipe.argtypes = (LPCWSTR, DWORD, DWORD, DWORD, DWORD, DWORD, + DWORD, LPVOID) + + ConnectNamedPipe = kernel32.ConnectNamedPipe + ConnectNamedPipe.restype = BOOL + ConnectNamedPipe.argtypes = (HANDLE, LPOVERLAPPED) + + CreateEvent = kernel32.CreateEventW + CreateEvent.restype = HANDLE + CreateEvent.argtypes = (LPVOID, BOOL, BOOL, LPCWSTR) + + GetOverlappedResultEx = kernel32.GetOverlappedResultEx + GetOverlappedResultEx.restype = BOOL + GetOverlappedResultEx.argtypes = (HANDLE, LPOVERLAPPED, LPDWORD, DWORD, + BOOL) + + ReadFile = kernel32.ReadFile + ReadFile.restype = BOOL + ReadFile.argtypes = (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED) + + CloseHandle = kernel32.CloseHandle + CloseHandle.restype = BOOL + CloseHandle.argtypes = (HANDLE,) + + class Pipe(object): + def __init__(self, prefix): + while True: + self.name = "lldb-" + str(random.randrange(1e10)) + full_name = "\\\\.\\pipe\\" + self.name + self._handle = CreateNamedPipe(full_name, PIPE_ACCESS_INBOUND | + FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS, 1, 4096, + 4096, 0, None) + if self._handle != INVALID_HANDLE_VALUE: + break + if ctypes.get_last_error() != ERROR_ACCESS_DENIED: + raise ctypes.WinError(ctypes.get_last_error()) + + self._overlapped = OVERLAPPED() + self._overlapped.hEvent = CreateEvent(None, True, False, None) + result = ConnectNamedPipe(self._handle, self._overlapped) + assert result == 0 + if ctypes.get_last_error() != ERROR_IO_PENDING: + raise ctypes.WinError(ctypes.get_last_error()) + + def finish_connection(self, timeout): + if not GetOverlappedResultEx(self._handle, self._overlapped, + ctypes.byref(DWORD(0)), timeout*1000, True): + raise ctypes.WinError(ctypes.get_last_error()) + + def read(self, size, timeout): + buf = ctypes.create_string_buffer(size) + if not ReadFile(self._handle, ctypes.byref(buf), size, None, + self._overlapped): + if ctypes.get_last_error() != ERROR_IO_PENDING: + raise ctypes.WinError(ctypes.get_last_error()) + read = DWORD(0) + if not GetOverlappedResultEx(self._handle, self._overlapped, + ctypes.byref(read), timeout*1000, True): + raise ctypes.WinError(ctypes.get_last_error()) + return buf.raw[0:read.value] + + def close(self): + CloseHandle(self._overlapped.hEvent) + CloseHandle(self._handle) + + +else: + class Pipe(object): + def __init__(self, prefix): + self.name = os.path.join(prefix, "stub_port_number") + os.mkfifo(self.name) + self._fd = os.open(self.name, os.O_RDONLY | os.O_NONBLOCK) + + def finish_connection(self, timeout): + pass + + def read(self, size, timeout): + (readers, _, _) = select.select([self._fd], [], [], timeout) + if self._fd not in readers: + raise TimeoutError + return os.read(self._fd, size) + + def close(self): + os.close(self._fd) class TestGdbRemoteConnection(gdbremote_testcase.GdbRemoteTestCaseBase): @@ -19,7 +135,6 @@ self.do_handshake(self.sock) @skipIfRemote - @skipIfWindows def test_named_pipe_llgs(self): family, type, proto, _, addr = socket.getaddrinfo( self.stub_hostname, 0, proto=socket.IPPROTO_TCP)[0] @@ -28,16 +143,9 @@ self.addTearDownHook(lambda: self.sock.close()) - named_pipe_path = self.getBuildArtifact("stub_port_number") + pipe = Pipe(self.getBuildDir()) - # Create the named pipe. - os.mkfifo(named_pipe_path) - - # Open the read side of the pipe in non-blocking mode. This will - # return right away, ready or not. - named_pipe_fd = os.open(named_pipe_path, os.O_RDONLY | os.O_NONBLOCK) - - self.addTearDownHook(lambda: os.close(named_pipe_fd)) + self.addTearDownHook(lambda: pipe.close()) args = self.debug_monitor_extra_args if lldb.remote_platform: @@ -45,19 +153,15 @@ else: args += ["localhost:0"] - args += ["--named-pipe", named_pipe_path] + args += ["--named-pipe", pipe.name] server = self.spawnSubprocess( self.debug_monitor_exe, args, install_remote=False) - (ready_readers, _, _) = select.select( - [named_pipe_fd], [], [], self.DEFAULT_TIMEOUT) - self.assertIsNotNone( - ready_readers, - "write side of pipe has not written anything - stub isn't writing to pipe.") - port = os.read(named_pipe_fd, 10) + pipe.finish_connection(self.DEFAULT_TIMEOUT) + port = pipe.read(10, self.DEFAULT_TIMEOUT) # Trim null byte, convert to int addr = (addr[0], int(port[:-1])) self.sock.connect(addr)