1from __future__ import print_function 2 3import gdbremote_testcase 4import random 5import select 6import socket 7from lldbsuite.test.decorators import * 8from lldbsuite.test.lldbtest import * 9from lldbgdbserverutils import Server 10import lldbsuite.test.lldbplatformutil 11 12if lldbplatformutil.getHostPlatform() == "windows": 13 import ctypes 14 import ctypes.wintypes 15 from ctypes.wintypes import (BOOL, DWORD, HANDLE, LPCWSTR, LPDWORD, LPVOID) 16 17 kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) 18 19 PIPE_ACCESS_INBOUND = 1 20 FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000 21 FILE_FLAG_OVERLAPPED = 0x40000000 22 PIPE_TYPE_BYTE = 0 23 PIPE_REJECT_REMOTE_CLIENTS = 8 24 INVALID_HANDLE_VALUE = -1 25 ERROR_ACCESS_DENIED = 5 26 ERROR_IO_PENDING = 997 27 28 29 class OVERLAPPED(ctypes.Structure): 30 _fields_ = [("Internal", LPVOID), ("InternalHigh", LPVOID), ("Offset", 31 DWORD), ("OffsetHigh", DWORD), ("hEvent", HANDLE)] 32 33 def __init__(self): 34 super(OVERLAPPED, self).__init__(Internal=0, InternalHigh=0, 35 Offset=0, OffsetHigh=0, hEvent=None) 36 LPOVERLAPPED = ctypes.POINTER(OVERLAPPED) 37 38 CreateNamedPipe = kernel32.CreateNamedPipeW 39 CreateNamedPipe.restype = HANDLE 40 CreateNamedPipe.argtypes = (LPCWSTR, DWORD, DWORD, DWORD, DWORD, DWORD, 41 DWORD, LPVOID) 42 43 ConnectNamedPipe = kernel32.ConnectNamedPipe 44 ConnectNamedPipe.restype = BOOL 45 ConnectNamedPipe.argtypes = (HANDLE, LPOVERLAPPED) 46 47 CreateEvent = kernel32.CreateEventW 48 CreateEvent.restype = HANDLE 49 CreateEvent.argtypes = (LPVOID, BOOL, BOOL, LPCWSTR) 50 51 GetOverlappedResultEx = kernel32.GetOverlappedResultEx 52 GetOverlappedResultEx.restype = BOOL 53 GetOverlappedResultEx.argtypes = (HANDLE, LPOVERLAPPED, LPDWORD, DWORD, 54 BOOL) 55 56 ReadFile = kernel32.ReadFile 57 ReadFile.restype = BOOL 58 ReadFile.argtypes = (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED) 59 60 CloseHandle = kernel32.CloseHandle 61 CloseHandle.restype = BOOL 62 CloseHandle.argtypes = (HANDLE,) 63 64 class Pipe(object): 65 def __init__(self, prefix): 66 while True: 67 self.name = "lldb-" + str(random.randrange(1e10)) 68 full_name = "\\\\.\\pipe\\" + self.name 69 self._handle = CreateNamedPipe(full_name, PIPE_ACCESS_INBOUND | 70 FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED, 71 PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS, 1, 4096, 72 4096, 0, None) 73 if self._handle != INVALID_HANDLE_VALUE: 74 break 75 if ctypes.get_last_error() != ERROR_ACCESS_DENIED: 76 raise ctypes.WinError(ctypes.get_last_error()) 77 78 self._overlapped = OVERLAPPED() 79 self._overlapped.hEvent = CreateEvent(None, True, False, None) 80 result = ConnectNamedPipe(self._handle, self._overlapped) 81 assert result == 0 82 if ctypes.get_last_error() != ERROR_IO_PENDING: 83 raise ctypes.WinError(ctypes.get_last_error()) 84 85 def finish_connection(self, timeout): 86 if not GetOverlappedResultEx(self._handle, self._overlapped, 87 ctypes.byref(DWORD(0)), timeout*1000, True): 88 raise ctypes.WinError(ctypes.get_last_error()) 89 90 def read(self, size, timeout): 91 buf = ctypes.create_string_buffer(size) 92 if not ReadFile(self._handle, ctypes.byref(buf), size, None, 93 self._overlapped): 94 if ctypes.get_last_error() != ERROR_IO_PENDING: 95 raise ctypes.WinError(ctypes.get_last_error()) 96 read = DWORD(0) 97 if not GetOverlappedResultEx(self._handle, self._overlapped, 98 ctypes.byref(read), timeout*1000, True): 99 raise ctypes.WinError(ctypes.get_last_error()) 100 return buf.raw[0:read.value] 101 102 def close(self): 103 CloseHandle(self._overlapped.hEvent) 104 CloseHandle(self._handle) 105 106 107else: 108 class Pipe(object): 109 def __init__(self, prefix): 110 self.name = os.path.join(prefix, "stub_port_number") 111 os.mkfifo(self.name) 112 self._fd = os.open(self.name, os.O_RDONLY | os.O_NONBLOCK) 113 114 def finish_connection(self, timeout): 115 pass 116 117 def read(self, size, timeout): 118 (readers, _, _) = select.select([self._fd], [], [], timeout) 119 if self._fd not in readers: 120 raise TimeoutError 121 return os.read(self._fd, size) 122 123 def close(self): 124 os.close(self._fd) 125 126 127class TestGdbRemoteConnection(gdbremote_testcase.GdbRemoteTestCaseBase): 128 129 @skipIfRemote # reverse connect is not a supported use case for now 130 def test_reverse_connect(self): 131 # Reverse connect is the default connection method. 132 self.connect_to_debug_monitor() 133 # Verify we can do the handshake. If that works, we'll call it good. 134 self.do_handshake() 135 136 @skipIfRemote 137 def test_named_pipe(self): 138 family, type, proto, _, addr = socket.getaddrinfo( 139 self.stub_hostname, 0, proto=socket.IPPROTO_TCP)[0] 140 self.sock = socket.socket(family, type, proto) 141 self.sock.settimeout(self.DEFAULT_TIMEOUT) 142 143 self.addTearDownHook(lambda: self.sock.close()) 144 145 pipe = Pipe(self.getBuildDir()) 146 147 self.addTearDownHook(lambda: pipe.close()) 148 149 args = self.debug_monitor_extra_args 150 if lldb.remote_platform: 151 args += ["*:0"] 152 else: 153 args += ["localhost:0"] 154 155 args += ["--named-pipe", pipe.name] 156 157 server = self.spawnSubprocess( 158 self.debug_monitor_exe, 159 args, 160 install_remote=False) 161 162 pipe.finish_connection(self.DEFAULT_TIMEOUT) 163 port = pipe.read(10, self.DEFAULT_TIMEOUT) 164 # Trim null byte, convert to int 165 addr = (addr[0], int(port[:-1])) 166 self.sock.connect(addr) 167 self._server = Server(self.sock, server) 168 169 # Verify we can do the handshake. If that works, we'll call it good. 170 self.do_handshake() 171