1a61c247cSAntonio Afonso"""Module for supporting unit testing of the lldb-server debug monitor exe. 2a61c247cSAntonio Afonso""" 3a61c247cSAntonio Afonso 4a2f4f7daSPavel Labathimport binascii 5a61c247cSAntonio Afonsoimport os 6a61c247cSAntonio Afonsoimport os.path 7a61c247cSAntonio Afonsoimport platform 8a61c247cSAntonio Afonsoimport re 9a2f4f7daSPavel Labathimport socket 10a61c247cSAntonio Afonsoimport subprocess 11a2f4f7daSPavel Labathfrom lldbsuite.support import seven 12a61c247cSAntonio Afonsofrom lldbsuite.test.lldbtest import * 1399298c7fSFred Rissfrom lldbsuite.test import configuration 14a2f4f7daSPavel Labathfrom textwrap import dedent 153ca7b2d0SPavel Labathimport shutil 16a52be0ccSSanthosh Kumar Ellendulaimport select 17781ba3c6SMuhammad Omair Javaidimport random 182238dcc3SJonas Devlieghere 193ca7b2d0SPavel Labathdef _get_support_exe(basename): 203ca7b2d0SPavel Labath support_dir = lldb.SBHostOS.GetLLDBPath(lldb.ePathTypeSupportExecutableDir) 21a61c247cSAntonio Afonso 223ca7b2d0SPavel Labath return shutil.which(basename, path=support_dir.GetDirectory()) 23a61c247cSAntonio Afonso 24a61c247cSAntonio Afonso 25a61c247cSAntonio Afonsodef get_lldb_server_exe(): 26a61c247cSAntonio Afonso """Return the lldb-server exe path. 27a61c247cSAntonio Afonso 28a61c247cSAntonio Afonso Returns: 29a61c247cSAntonio Afonso A path to the lldb-server exe if it is found to exist; otherwise, 30a61c247cSAntonio Afonso returns None. 31a61c247cSAntonio Afonso """ 32599fdfc5SPavel Labath 333ca7b2d0SPavel Labath return _get_support_exe("lldb-server") 34a61c247cSAntonio Afonso 35a61c247cSAntonio Afonso 36a61c247cSAntonio Afonsodef get_debugserver_exe(): 37a61c247cSAntonio Afonso """Return the debugserver exe path. 38a61c247cSAntonio Afonso 39a61c247cSAntonio Afonso Returns: 40a61c247cSAntonio Afonso A path to the debugserver exe if it is found to exist; otherwise, 41a61c247cSAntonio Afonso returns None. 42a61c247cSAntonio Afonso """ 432238dcc3SJonas Devlieghere if ( 442238dcc3SJonas Devlieghere configuration.arch 452238dcc3SJonas Devlieghere and configuration.arch == "x86_64" 462238dcc3SJonas Devlieghere and platform.machine().startswith("arm64") 472238dcc3SJonas Devlieghere ): 482238dcc3SJonas Devlieghere return "/Library/Apple/usr/libexec/oah/debugserver" 4999298c7fSFred Riss 503ca7b2d0SPavel Labath return _get_support_exe("debugserver") 51a61c247cSAntonio Afonso 522238dcc3SJonas Devlieghere 532238dcc3SJonas Devlieghere_LOG_LINE_REGEX = re.compile( 54ba66dfb1SDmitry Vasilyev r"^(lldb-server|debugserver)\s+<\s*(\d+)>\s+(read|send)\s+packet:\s+(.+)$" 552238dcc3SJonas Devlieghere) 56a61c247cSAntonio Afonso 57a61c247cSAntonio Afonso 58a61c247cSAntonio Afonsodef _is_packet_lldb_gdbserver_input(packet_type, llgs_input_is_read): 59a61c247cSAntonio Afonso """Return whether a given packet is input for lldb-gdbserver. 60a61c247cSAntonio Afonso 61a61c247cSAntonio Afonso Args: 62a61c247cSAntonio Afonso packet_type: a string indicating 'send' or 'receive', from a 63a61c247cSAntonio Afonso gdbremote packet protocol log. 64a61c247cSAntonio Afonso 65a61c247cSAntonio Afonso llgs_input_is_read: true if lldb-gdbserver input (content sent to 66a61c247cSAntonio Afonso lldb-gdbserver) is listed as 'read' or 'send' in the packet 67a61c247cSAntonio Afonso log entry. 68a61c247cSAntonio Afonso 69a61c247cSAntonio Afonso Returns: 70a61c247cSAntonio Afonso True if the packet should be considered input for lldb-gdbserver; False 71a61c247cSAntonio Afonso otherwise. 72a61c247cSAntonio Afonso """ 732238dcc3SJonas Devlieghere if packet_type == "read": 74a61c247cSAntonio Afonso # when llgs is the read side, then a read packet is meant for 75a61c247cSAntonio Afonso # input to llgs (when captured from the llgs/debugserver exe). 76a61c247cSAntonio Afonso return llgs_input_is_read 772238dcc3SJonas Devlieghere elif packet_type == "send": 78a61c247cSAntonio Afonso # when llgs is the send side, then a send packet is meant to 79a61c247cSAntonio Afonso # be input to llgs (when captured from the lldb exe). 80a61c247cSAntonio Afonso return not llgs_input_is_read 81a61c247cSAntonio Afonso else: 82a61c247cSAntonio Afonso # don't understand what type of packet this is 83a61c247cSAntonio Afonso raise "Unknown packet type: {}".format(packet_type) 84a61c247cSAntonio Afonso 85a61c247cSAntonio Afonso 862238dcc3SJonas Devlieghere_STRIP_CHECKSUM_REGEX = re.compile(r"#[0-9a-fA-F]{2}$") 87a61c247cSAntonio Afonso_STRIP_COMMAND_PREFIX_REGEX = re.compile(r"^\$") 88a61c247cSAntonio Afonso_STRIP_COMMAND_PREFIX_M_REGEX = re.compile(r"^\$m") 89a61c247cSAntonio Afonso 90a61c247cSAntonio Afonso 91a61c247cSAntonio Afonsodef assert_packets_equal(asserter, actual_packet, expected_packet): 92a61c247cSAntonio Afonso # strip off the checksum digits of the packet. When we're in 93a61c247cSAntonio Afonso # no-ack mode, the # checksum is ignored, and should not be cause 94a61c247cSAntonio Afonso # for a mismatched packet. 952238dcc3SJonas Devlieghere actual_stripped = _STRIP_CHECKSUM_REGEX.sub("", actual_packet) 962238dcc3SJonas Devlieghere expected_stripped = _STRIP_CHECKSUM_REGEX.sub("", expected_packet) 97a61c247cSAntonio Afonso asserter.assertEqual(actual_stripped, expected_stripped) 98a61c247cSAntonio Afonso 99a61c247cSAntonio Afonso 100a61c247cSAntonio Afonsodef expect_lldb_gdbserver_replay( 1012238dcc3SJonas Devlieghere asserter, server, test_sequence, timeout_seconds, logger=None 1022238dcc3SJonas Devlieghere): 103a61c247cSAntonio Afonso """Replay socket communication with lldb-gdbserver and verify responses. 104a61c247cSAntonio Afonso 105a61c247cSAntonio Afonso Args: 106a61c247cSAntonio Afonso asserter: the object providing assertEqual(first, second, msg=None), e.g. TestCase instance. 107a61c247cSAntonio Afonso 108a61c247cSAntonio Afonso test_sequence: a GdbRemoteTestSequence instance that describes 109a61c247cSAntonio Afonso the messages sent to the gdb remote and the responses 110a61c247cSAntonio Afonso expected from it. 111a61c247cSAntonio Afonso 112a61c247cSAntonio Afonso timeout_seconds: any response taking more than this number of 113a61c247cSAntonio Afonso seconds will cause an exception to be raised. 114a61c247cSAntonio Afonso 115a61c247cSAntonio Afonso logger: a Python logger instance. 116a61c247cSAntonio Afonso 117a61c247cSAntonio Afonso Returns: 118a61c247cSAntonio Afonso The context dictionary from running the given gdbremote 119a61c247cSAntonio Afonso protocol sequence. This will contain any of the capture 120a61c247cSAntonio Afonso elements specified to any GdbRemoteEntry instances in 121a61c247cSAntonio Afonso test_sequence. 122a61c247cSAntonio Afonso 123a61c247cSAntonio Afonso The context will also contain an entry, context["O_content"] 124a61c247cSAntonio Afonso which contains the text from the inferior received via $O 125a61c247cSAntonio Afonso packets. $O packets should not attempt to be matched 126a61c247cSAntonio Afonso directly since they are not entirely deterministic as to 127a61c247cSAntonio Afonso how many arrive and how much text is in each one. 128a61c247cSAntonio Afonso 129a61c247cSAntonio Afonso context["O_count"] will contain an integer of the number of 130a61c247cSAntonio Afonso O packets received. 131a61c247cSAntonio Afonso """ 132a61c247cSAntonio Afonso 133a61c247cSAntonio Afonso # Ensure we have some work to do. 134a61c247cSAntonio Afonso if len(test_sequence.entries) < 1: 135a61c247cSAntonio Afonso return {} 136a61c247cSAntonio Afonso 137a61c247cSAntonio Afonso context = {"O_count": 0, "O_content": ""} 138a2f4f7daSPavel Labath 139a61c247cSAntonio Afonso # Grab the first sequence entry. 140a61c247cSAntonio Afonso sequence_entry = test_sequence.entries.pop(0) 141a61c247cSAntonio Afonso 142a61c247cSAntonio Afonso # While we have an active sequence entry, send messages 143a61c247cSAntonio Afonso # destined for the stub and collect/match/process responses 144a61c247cSAntonio Afonso # expected from the stub. 145a61c247cSAntonio Afonso while sequence_entry: 146a61c247cSAntonio Afonso if sequence_entry.is_send_to_remote(): 147a61c247cSAntonio Afonso # This is an entry to send to the remote debug monitor. 148a61c247cSAntonio Afonso send_packet = sequence_entry.get_send_packet() 149a61c247cSAntonio Afonso if logger: 150a61c247cSAntonio Afonso if len(send_packet) == 1 and send_packet[0] == chr(3): 151a61c247cSAntonio Afonso packet_desc = "^C" 152a61c247cSAntonio Afonso else: 153a61c247cSAntonio Afonso packet_desc = send_packet 1542238dcc3SJonas Devlieghere logger.info("sending packet to remote: {}".format(packet_desc)) 155a2f4f7daSPavel Labath server.send_raw(send_packet.encode()) 156a61c247cSAntonio Afonso else: 157a61c247cSAntonio Afonso # This is an entry expecting to receive content from the remote 158a61c247cSAntonio Afonso # debug monitor. 159a61c247cSAntonio Afonso 160a61c247cSAntonio Afonso # We'll pull from (and wait on) the queue appropriate for the type of matcher. 161a61c247cSAntonio Afonso # We keep separate queues for process output (coming from non-deterministic 162a61c247cSAntonio Afonso # $O packet division) and for all other packets. 163a2f4f7daSPavel Labath try: 164a61c247cSAntonio Afonso if sequence_entry.is_output_matcher(): 165a61c247cSAntonio Afonso # Grab next entry from the output queue. 166a2f4f7daSPavel Labath content = server.get_raw_output_packet() 167a61c247cSAntonio Afonso else: 168a2f4f7daSPavel Labath content = server.get_raw_normal_packet() 169a2f4f7daSPavel Labath content = seven.bitcast_to_string(content) 170a2f4f7daSPavel Labath except socket.timeout: 171a2f4f7daSPavel Labath asserter.fail( 1722238dcc3SJonas Devlieghere "timed out while waiting for '{}':\n{}".format( 1732238dcc3SJonas Devlieghere sequence_entry, server 1742238dcc3SJonas Devlieghere ) 1752238dcc3SJonas Devlieghere ) 176a61c247cSAntonio Afonso 177a61c247cSAntonio Afonso # Give the sequence entry the opportunity to match the content. 178a61c247cSAntonio Afonso # Output matchers might match or pass after more output accumulates. 179a61c247cSAntonio Afonso # Other packet types generally must match. 180a61c247cSAntonio Afonso asserter.assertIsNotNone(content) 1812238dcc3SJonas Devlieghere context = sequence_entry.assert_match(asserter, content, context=context) 182a61c247cSAntonio Afonso 183a61c247cSAntonio Afonso # Move on to next sequence entry as needed. Some sequence entries support executing multiple 184a61c247cSAntonio Afonso # times in different states (for looping over query/response 185a61c247cSAntonio Afonso # packets). 186a61c247cSAntonio Afonso if sequence_entry.is_consumed(): 187a61c247cSAntonio Afonso if len(test_sequence.entries) > 0: 188a61c247cSAntonio Afonso sequence_entry = test_sequence.entries.pop(0) 189a61c247cSAntonio Afonso else: 190a61c247cSAntonio Afonso sequence_entry = None 191a61c247cSAntonio Afonso 192a61c247cSAntonio Afonso # Fill in the O_content entries. 193a61c247cSAntonio Afonso context["O_count"] = 1 194a2f4f7daSPavel Labath context["O_content"] = server.consume_accumulated_output() 195a61c247cSAntonio Afonso 196a61c247cSAntonio Afonso return context 197a61c247cSAntonio Afonso 198a61c247cSAntonio Afonso 199a61c247cSAntonio Afonsodef gdbremote_hex_encode_string(str): 2002238dcc3SJonas Devlieghere output = "" 201a61c247cSAntonio Afonso for c in str: 2022238dcc3SJonas Devlieghere output += "{0:02x}".format(ord(c)) 203a61c247cSAntonio Afonso return output 204a61c247cSAntonio Afonso 205a61c247cSAntonio Afonso 206a61c247cSAntonio Afonsodef gdbremote_hex_decode_string(str): 207a61c247cSAntonio Afonso return str.decode("hex") 208a61c247cSAntonio Afonso 209a61c247cSAntonio Afonso 210a61c247cSAntonio Afonsodef gdbremote_packet_encode_string(str): 211a61c247cSAntonio Afonso checksum = 0 212a61c247cSAntonio Afonso for c in str: 213a61c247cSAntonio Afonso checksum += ord(c) 2142238dcc3SJonas Devlieghere return "$" + str + "#{0:02x}".format(checksum % 256) 215a61c247cSAntonio Afonso 216a61c247cSAntonio Afonso 217a61c247cSAntonio Afonsodef build_gdbremote_A_packet(args_list): 2182238dcc3SJonas Devlieghere """Given a list of args, create a properly-formed $A packet containing each arg.""" 219a61c247cSAntonio Afonso payload = "A" 220a61c247cSAntonio Afonso 221a61c247cSAntonio Afonso # build the arg content 222a61c247cSAntonio Afonso arg_index = 0 223a61c247cSAntonio Afonso for arg in args_list: 224a61c247cSAntonio Afonso # Comma-separate the args. 225a61c247cSAntonio Afonso if arg_index > 0: 2262238dcc3SJonas Devlieghere payload += "," 227a61c247cSAntonio Afonso 228a61c247cSAntonio Afonso # Hex-encode the arg. 229a61c247cSAntonio Afonso hex_arg = gdbremote_hex_encode_string(arg) 230a61c247cSAntonio Afonso 231a61c247cSAntonio Afonso # Build the A entry. 232a61c247cSAntonio Afonso payload += "{},{},{}".format(len(hex_arg), arg_index, hex_arg) 233a61c247cSAntonio Afonso 234a61c247cSAntonio Afonso # Next arg index, please. 235a61c247cSAntonio Afonso arg_index += 1 236a61c247cSAntonio Afonso 237a61c247cSAntonio Afonso # return the packetized payload 238a61c247cSAntonio Afonso return gdbremote_packet_encode_string(payload) 239a61c247cSAntonio Afonso 240a61c247cSAntonio Afonso 241a61c247cSAntonio Afonsodef parse_reg_info_response(response_packet): 242a61c247cSAntonio Afonso if not response_packet: 243a61c247cSAntonio Afonso raise Exception("response_packet cannot be None") 244a61c247cSAntonio Afonso 245a61c247cSAntonio Afonso # Strip off prefix $ and suffix #xx if present. 246a61c247cSAntonio Afonso response_packet = _STRIP_COMMAND_PREFIX_REGEX.sub("", response_packet) 247a61c247cSAntonio Afonso response_packet = _STRIP_CHECKSUM_REGEX.sub("", response_packet) 248a61c247cSAntonio Afonso 249a61c247cSAntonio Afonso # Build keyval pairs 250a61c247cSAntonio Afonso values = {} 251a61c247cSAntonio Afonso for kv in response_packet.split(";"): 252a61c247cSAntonio Afonso if len(kv) < 1: 253a61c247cSAntonio Afonso continue 2542238dcc3SJonas Devlieghere (key, val) = kv.split(":") 255a61c247cSAntonio Afonso values[key] = val 256a61c247cSAntonio Afonso 257a61c247cSAntonio Afonso return values 258a61c247cSAntonio Afonso 259a61c247cSAntonio Afonso 260a61c247cSAntonio Afonsodef parse_threadinfo_response(response_packet): 261a61c247cSAntonio Afonso if not response_packet: 262a61c247cSAntonio Afonso raise Exception("response_packet cannot be None") 263a61c247cSAntonio Afonso 264a61c247cSAntonio Afonso # Strip off prefix $ and suffix #xx if present. 265a61c247cSAntonio Afonso response_packet = _STRIP_COMMAND_PREFIX_M_REGEX.sub("", response_packet) 266a61c247cSAntonio Afonso response_packet = _STRIP_CHECKSUM_REGEX.sub("", response_packet) 267a61c247cSAntonio Afonso 26875757c86SMichał Górny for tid in response_packet.split(","): 26975757c86SMichał Górny if not tid: 27075757c86SMichał Górny continue 27175757c86SMichał Górny if tid.startswith("p"): 27275757c86SMichał Górny pid, _, tid = tid.partition(".") 27375757c86SMichał Górny yield (int(pid[1:], 16), int(tid, 16)) 27475757c86SMichał Górny else: 27575757c86SMichał Górny yield int(tid, 16) 276a61c247cSAntonio Afonso 277a61c247cSAntonio Afonso 278a61c247cSAntonio Afonsodef unpack_endian_binary_string(endian, value_string): 279a61c247cSAntonio Afonso """Unpack a gdb-remote binary (post-unescaped, i.e. not escaped) response to an unsigned int given endianness of the inferior.""" 280a61c247cSAntonio Afonso if not endian: 281a61c247cSAntonio Afonso raise Exception("endian cannot be None") 282a61c247cSAntonio Afonso if not value_string or len(value_string) < 1: 283a61c247cSAntonio Afonso raise Exception("value_string cannot be None or empty") 284a61c247cSAntonio Afonso 2852238dcc3SJonas Devlieghere if endian == "little": 286a61c247cSAntonio Afonso value = 0 287a61c247cSAntonio Afonso i = 0 288a61c247cSAntonio Afonso while len(value_string) > 0: 2892238dcc3SJonas Devlieghere value += ord(value_string[0]) << i 290a61c247cSAntonio Afonso value_string = value_string[1:] 291a61c247cSAntonio Afonso i += 8 292a61c247cSAntonio Afonso return value 2932238dcc3SJonas Devlieghere elif endian == "big": 294a61c247cSAntonio Afonso value = 0 295a61c247cSAntonio Afonso while len(value_string) > 0: 296a61c247cSAntonio Afonso value = (value << 8) + ord(value_string[0]) 297a61c247cSAntonio Afonso value_string = value_string[1:] 298a61c247cSAntonio Afonso return value 299a61c247cSAntonio Afonso else: 300a61c247cSAntonio Afonso # pdp is valid but need to add parse code once needed. 301a61c247cSAntonio Afonso raise Exception("unsupported endian:{}".format(endian)) 302a61c247cSAntonio Afonso 303a61c247cSAntonio Afonso 304a61c247cSAntonio Afonsodef unpack_register_hex_unsigned(endian, value_string): 305a61c247cSAntonio Afonso """Unpack a gdb-remote $p-style response to an unsigned int given endianness of inferior.""" 306a61c247cSAntonio Afonso if not endian: 307a61c247cSAntonio Afonso raise Exception("endian cannot be None") 308a61c247cSAntonio Afonso if not value_string or len(value_string) < 1: 309a61c247cSAntonio Afonso raise Exception("value_string cannot be None or empty") 310a61c247cSAntonio Afonso 3112238dcc3SJonas Devlieghere if endian == "little": 312a61c247cSAntonio Afonso value = 0 313a61c247cSAntonio Afonso i = 0 314a61c247cSAntonio Afonso while len(value_string) > 0: 3152238dcc3SJonas Devlieghere value += int(value_string[0:2], 16) << i 316a61c247cSAntonio Afonso value_string = value_string[2:] 317a61c247cSAntonio Afonso i += 8 318a61c247cSAntonio Afonso return value 3192238dcc3SJonas Devlieghere elif endian == "big": 320a61c247cSAntonio Afonso return int(value_string, 16) 321a61c247cSAntonio Afonso else: 322a61c247cSAntonio Afonso # pdp is valid but need to add parse code once needed. 323a61c247cSAntonio Afonso raise Exception("unsupported endian:{}".format(endian)) 324a61c247cSAntonio Afonso 325a61c247cSAntonio Afonso 326a61c247cSAntonio Afonsodef pack_register_hex(endian, value, byte_size=None): 327a61c247cSAntonio Afonso """Unpack a gdb-remote $p-style response to an unsigned int given endianness of inferior.""" 328a61c247cSAntonio Afonso if not endian: 329a61c247cSAntonio Afonso raise Exception("endian cannot be None") 330a61c247cSAntonio Afonso 3312238dcc3SJonas Devlieghere if endian == "little": 332a61c247cSAntonio Afonso # Create the litt-endian return value. 333a61c247cSAntonio Afonso retval = "" 334a61c247cSAntonio Afonso while value != 0: 3352238dcc3SJonas Devlieghere retval = retval + "{:02x}".format(value & 0xFF) 336a61c247cSAntonio Afonso value = value >> 8 337a61c247cSAntonio Afonso if byte_size: 338a61c247cSAntonio Afonso # Add zero-fill to the right/end (MSB side) of the value. 339a61c247cSAntonio Afonso retval += "00" * (byte_size - len(retval) // 2) 340a61c247cSAntonio Afonso return retval 341a61c247cSAntonio Afonso 3422238dcc3SJonas Devlieghere elif endian == "big": 343a61c247cSAntonio Afonso retval = "" 344a61c247cSAntonio Afonso while value != 0: 3452238dcc3SJonas Devlieghere retval = "{:02x}".format(value & 0xFF) + retval 346a61c247cSAntonio Afonso value = value >> 8 347a61c247cSAntonio Afonso if byte_size: 348a61c247cSAntonio Afonso # Add zero-fill to the left/front (MSB side) of the value. 349a61c247cSAntonio Afonso retval = ("00" * (byte_size - len(retval) // 2)) + retval 350a61c247cSAntonio Afonso return retval 351a61c247cSAntonio Afonso 352a61c247cSAntonio Afonso else: 353a61c247cSAntonio Afonso # pdp is valid but need to add parse code once needed. 354a61c247cSAntonio Afonso raise Exception("unsupported endian:{}".format(endian)) 355a61c247cSAntonio Afonso 356a61c247cSAntonio Afonso 357a61c247cSAntonio Afonsoclass GdbRemoteEntryBase(object): 358a61c247cSAntonio Afonso def is_output_matcher(self): 359a61c247cSAntonio Afonso return False 360a61c247cSAntonio Afonso 361a61c247cSAntonio Afonso 362a61c247cSAntonio Afonsoclass GdbRemoteEntry(GdbRemoteEntryBase): 363a61c247cSAntonio Afonso def __init__( 3642238dcc3SJonas Devlieghere self, is_send_to_remote=True, exact_payload=None, regex=None, capture=None 3652238dcc3SJonas Devlieghere ): 366a61c247cSAntonio Afonso """Create an entry representing one piece of the I/O to/from a gdb remote debug monitor. 367a61c247cSAntonio Afonso 368a61c247cSAntonio Afonso Args: 369a61c247cSAntonio Afonso 370a61c247cSAntonio Afonso is_send_to_remote: True if this entry is a message to be 371a61c247cSAntonio Afonso sent to the gdbremote debug monitor; False if this 372a61c247cSAntonio Afonso entry represents text to be matched against the reply 373a61c247cSAntonio Afonso from the gdbremote debug monitor. 374a61c247cSAntonio Afonso 375a61c247cSAntonio Afonso exact_payload: if not None, then this packet is an exact 376a61c247cSAntonio Afonso send (when sending to the remote) or an exact match of 377a61c247cSAntonio Afonso the response from the gdbremote. The checksums are 378a61c247cSAntonio Afonso ignored on exact match requests since negotiation of 379a61c247cSAntonio Afonso no-ack makes the checksum content essentially 380a61c247cSAntonio Afonso undefined. 381a61c247cSAntonio Afonso 382ded66049SPavel Labath regex: currently only valid for receives from gdbremote. When 383ded66049SPavel Labath specified (and only if exact_payload is None), indicates the 384ded66049SPavel Labath gdbremote response must match the given regex. Match groups in 385ded66049SPavel Labath the regex can be used for the matching portion (see capture 386ded66049SPavel Labath arg). It is perfectly valid to have just a regex arg without a 387ded66049SPavel Labath capture arg. This arg only makes sense if exact_payload is not 388a61c247cSAntonio Afonso specified. 389a61c247cSAntonio Afonso 390a61c247cSAntonio Afonso capture: if specified, is a dictionary of regex match 391a61c247cSAntonio Afonso group indices (should start with 1) to variable names 392a61c247cSAntonio Afonso that will store the capture group indicated by the 393a61c247cSAntonio Afonso index. For example, {1:"thread_id"} will store capture 394a61c247cSAntonio Afonso group 1's content in the context dictionary where 395a61c247cSAntonio Afonso "thread_id" is the key and the match group value is 396ded66049SPavel Labath the value. This arg only makes sense when regex is specified. 397a61c247cSAntonio Afonso """ 398a61c247cSAntonio Afonso self._is_send_to_remote = is_send_to_remote 399a61c247cSAntonio Afonso self.exact_payload = exact_payload 400a61c247cSAntonio Afonso self.regex = regex 401a61c247cSAntonio Afonso self.capture = capture 402a61c247cSAntonio Afonso 403a61c247cSAntonio Afonso def is_send_to_remote(self): 404a61c247cSAntonio Afonso return self._is_send_to_remote 405a61c247cSAntonio Afonso 406a61c247cSAntonio Afonso def is_consumed(self): 407a61c247cSAntonio Afonso # For now, all packets are consumed after first use. 408a61c247cSAntonio Afonso return True 409a61c247cSAntonio Afonso 410a61c247cSAntonio Afonso def get_send_packet(self): 411a61c247cSAntonio Afonso if not self.is_send_to_remote(): 412a61c247cSAntonio Afonso raise Exception( 4132238dcc3SJonas Devlieghere "get_send_packet() called on GdbRemoteEntry that is not a send-to-remote packet" 4142238dcc3SJonas Devlieghere ) 415a61c247cSAntonio Afonso if not self.exact_payload: 416a61c247cSAntonio Afonso raise Exception( 4172238dcc3SJonas Devlieghere "get_send_packet() called on GdbRemoteEntry but it doesn't have an exact payload" 4182238dcc3SJonas Devlieghere ) 419a61c247cSAntonio Afonso return self.exact_payload 420a61c247cSAntonio Afonso 421a61c247cSAntonio Afonso def _assert_exact_payload_match(self, asserter, actual_packet): 422a61c247cSAntonio Afonso assert_packets_equal(asserter, actual_packet, self.exact_payload) 423a61c247cSAntonio Afonso return None 424a61c247cSAntonio Afonso 425a61c247cSAntonio Afonso def _assert_regex_match(self, asserter, actual_packet, context): 426a61c247cSAntonio Afonso # Ensure the actual packet matches from the start of the actual packet. 427a61c247cSAntonio Afonso match = self.regex.match(actual_packet) 428a61c247cSAntonio Afonso if not match: 429a61c247cSAntonio Afonso asserter.fail( 430a61c247cSAntonio Afonso "regex '{}' failed to match against content '{}'".format( 4312238dcc3SJonas Devlieghere self.regex.pattern, actual_packet 4322238dcc3SJonas Devlieghere ) 4332238dcc3SJonas Devlieghere ) 434a61c247cSAntonio Afonso 435a61c247cSAntonio Afonso if self.capture: 436a61c247cSAntonio Afonso # Handle captures. 437a61c247cSAntonio Afonso for group_index, var_name in list(self.capture.items()): 438a61c247cSAntonio Afonso capture_text = match.group(group_index) 439a61c247cSAntonio Afonso # It is okay for capture text to be None - which it will be if it is a group that can match nothing. 440a61c247cSAntonio Afonso # The user must be okay with it since the regex itself matched 441a61c247cSAntonio Afonso # above. 442a61c247cSAntonio Afonso context[var_name] = capture_text 443a61c247cSAntonio Afonso 444a61c247cSAntonio Afonso return context 445a61c247cSAntonio Afonso 446a61c247cSAntonio Afonso def assert_match(self, asserter, actual_packet, context=None): 447a61c247cSAntonio Afonso # This only makes sense for matching lines coming from the 448a61c247cSAntonio Afonso # remote debug monitor. 449a61c247cSAntonio Afonso if self.is_send_to_remote(): 450a61c247cSAntonio Afonso raise Exception( 4512238dcc3SJonas Devlieghere "Attempted to match a packet being sent to the remote debug monitor, doesn't make sense." 4522238dcc3SJonas Devlieghere ) 453a61c247cSAntonio Afonso 454a61c247cSAntonio Afonso # Create a new context if needed. 455a61c247cSAntonio Afonso if not context: 456a61c247cSAntonio Afonso context = {} 457a61c247cSAntonio Afonso 458a61c247cSAntonio Afonso # If this is an exact payload, ensure they match exactly, 459a61c247cSAntonio Afonso # ignoring the packet checksum which is optional for no-ack 460a61c247cSAntonio Afonso # mode. 461a61c247cSAntonio Afonso if self.exact_payload: 462a61c247cSAntonio Afonso self._assert_exact_payload_match(asserter, actual_packet) 463a61c247cSAntonio Afonso return context 464a61c247cSAntonio Afonso elif self.regex: 465a61c247cSAntonio Afonso return self._assert_regex_match(asserter, actual_packet, context) 466a61c247cSAntonio Afonso else: 467a61c247cSAntonio Afonso raise Exception( 4682238dcc3SJonas Devlieghere "Don't know how to match a remote-sent packet when exact_payload isn't specified." 4692238dcc3SJonas Devlieghere ) 470a61c247cSAntonio Afonso 471a61c247cSAntonio Afonso 472a61c247cSAntonio Afonsoclass MultiResponseGdbRemoteEntry(GdbRemoteEntryBase): 473a61c247cSAntonio Afonso """Represents a query/response style packet. 474a61c247cSAntonio Afonso 475a61c247cSAntonio Afonso Assumes the first item is sent to the gdb remote. 476a61c247cSAntonio Afonso An end sequence regex indicates the end of the query/response 477a61c247cSAntonio Afonso packet sequence. All responses up through (but not including) the 478a61c247cSAntonio Afonso end response are stored in a context variable. 479a61c247cSAntonio Afonso 480a61c247cSAntonio Afonso Settings accepted from params: 481a61c247cSAntonio Afonso 482a61c247cSAntonio Afonso next_query or query: required. The typical query packet without the $ prefix or #xx suffix. 483a61c247cSAntonio Afonso If there is a special first packet to start the iteration query, see the 484a61c247cSAntonio Afonso first_query key. 485a61c247cSAntonio Afonso 486a61c247cSAntonio Afonso first_query: optional. If the first query requires a special query command, specify 487a61c247cSAntonio Afonso it with this key. Do not specify the $ prefix or #xx suffix. 488a61c247cSAntonio Afonso 489a61c247cSAntonio Afonso append_iteration_suffix: defaults to False. Specify True if the 0-based iteration 490a61c247cSAntonio Afonso index should be appended as a suffix to the command. e.g. qRegisterInfo with 491a61c247cSAntonio Afonso this key set true will generate query packets of qRegisterInfo0, qRegisterInfo1, 492a61c247cSAntonio Afonso etc. 493a61c247cSAntonio Afonso 494a61c247cSAntonio Afonso end_regex: required. Specifies a compiled regex object that will match the full text 495a61c247cSAntonio Afonso of any response that signals an end to the iteration. It must include the 496a61c247cSAntonio Afonso initial $ and ending #xx and must match the whole packet. 497a61c247cSAntonio Afonso 498a61c247cSAntonio Afonso save_key: required. Specifies the key within the context where an array will be stored. 499a61c247cSAntonio Afonso Each packet received from the gdb remote that does not match the end_regex will get 500a61c247cSAntonio Afonso appended to the array stored within the context at that key. 501a61c247cSAntonio Afonso 502a61c247cSAntonio Afonso runaway_response_count: optional. Defaults to 10000. If this many responses are retrieved, 503a61c247cSAntonio Afonso assume there is something wrong with either the response collection or the ending 504a61c247cSAntonio Afonso detection regex and throw an exception. 505a61c247cSAntonio Afonso """ 506a61c247cSAntonio Afonso 507a61c247cSAntonio Afonso def __init__(self, params): 508a61c247cSAntonio Afonso self._next_query = params.get("next_query", params.get("query")) 509a61c247cSAntonio Afonso if not self._next_query: 510a61c247cSAntonio Afonso raise "either next_query or query key must be specified for MultiResponseGdbRemoteEntry" 511a61c247cSAntonio Afonso 512a61c247cSAntonio Afonso self._first_query = params.get("first_query", self._next_query) 5132238dcc3SJonas Devlieghere self._append_iteration_suffix = params.get("append_iteration_suffix", False) 514a61c247cSAntonio Afonso self._iteration = 0 515a61c247cSAntonio Afonso self._end_regex = params["end_regex"] 516a61c247cSAntonio Afonso self._save_key = params["save_key"] 5172238dcc3SJonas Devlieghere self._runaway_response_count = params.get("runaway_response_count", 10000) 518a61c247cSAntonio Afonso self._is_send_to_remote = True 519a61c247cSAntonio Afonso self._end_matched = False 520a61c247cSAntonio Afonso 521a61c247cSAntonio Afonso def is_send_to_remote(self): 522a61c247cSAntonio Afonso return self._is_send_to_remote 523a61c247cSAntonio Afonso 524a61c247cSAntonio Afonso def get_send_packet(self): 525a61c247cSAntonio Afonso if not self.is_send_to_remote(): 526a61c247cSAntonio Afonso raise Exception( 5272238dcc3SJonas Devlieghere "get_send_packet() called on MultiResponseGdbRemoteEntry that is not in the send state" 5282238dcc3SJonas Devlieghere ) 529a61c247cSAntonio Afonso if self._end_matched: 530a61c247cSAntonio Afonso raise Exception( 5312238dcc3SJonas Devlieghere "get_send_packet() called on MultiResponseGdbRemoteEntry but end of query/response sequence has already been seen." 5322238dcc3SJonas Devlieghere ) 533a61c247cSAntonio Afonso 534a61c247cSAntonio Afonso # Choose the first or next query for the base payload. 535a61c247cSAntonio Afonso if self._iteration == 0 and self._first_query: 536a61c247cSAntonio Afonso payload = self._first_query 537a61c247cSAntonio Afonso else: 538a61c247cSAntonio Afonso payload = self._next_query 539a61c247cSAntonio Afonso 540a61c247cSAntonio Afonso # Append the suffix as needed. 541a61c247cSAntonio Afonso if self._append_iteration_suffix: 542a61c247cSAntonio Afonso payload += "%x" % self._iteration 543a61c247cSAntonio Afonso 544a61c247cSAntonio Afonso # Keep track of the iteration. 545a61c247cSAntonio Afonso self._iteration += 1 546a61c247cSAntonio Afonso 547a61c247cSAntonio Afonso # Now that we've given the query packet, flip the mode to 548a61c247cSAntonio Afonso # receive/match. 549a61c247cSAntonio Afonso self._is_send_to_remote = False 550a61c247cSAntonio Afonso 551a61c247cSAntonio Afonso # Return the result, converted to packet form. 552a61c247cSAntonio Afonso return gdbremote_packet_encode_string(payload) 553a61c247cSAntonio Afonso 554a61c247cSAntonio Afonso def is_consumed(self): 555a61c247cSAntonio Afonso return self._end_matched 556a61c247cSAntonio Afonso 557a61c247cSAntonio Afonso def assert_match(self, asserter, actual_packet, context=None): 558a61c247cSAntonio Afonso # This only makes sense for matching lines coming from the remote debug 559a61c247cSAntonio Afonso # monitor. 560a61c247cSAntonio Afonso if self.is_send_to_remote(): 561a61c247cSAntonio Afonso raise Exception( 5622238dcc3SJonas Devlieghere "assert_match() called on MultiResponseGdbRemoteEntry but state is set to send a query packet." 5632238dcc3SJonas Devlieghere ) 564a61c247cSAntonio Afonso 565a61c247cSAntonio Afonso if self._end_matched: 566a61c247cSAntonio Afonso raise Exception( 5672238dcc3SJonas Devlieghere "assert_match() called on MultiResponseGdbRemoteEntry but end of query/response sequence has already been seen." 5682238dcc3SJonas Devlieghere ) 569a61c247cSAntonio Afonso 570a61c247cSAntonio Afonso # Set up a context as needed. 571a61c247cSAntonio Afonso if not context: 572a61c247cSAntonio Afonso context = {} 573a61c247cSAntonio Afonso 574a61c247cSAntonio Afonso # Check if the packet matches the end condition. 575a61c247cSAntonio Afonso match = self._end_regex.match(actual_packet) 576a61c247cSAntonio Afonso if match: 577a61c247cSAntonio Afonso # We're done iterating. 578a61c247cSAntonio Afonso self._end_matched = True 579a61c247cSAntonio Afonso return context 580a61c247cSAntonio Afonso 581a61c247cSAntonio Afonso # Not done iterating - save the packet. 582a61c247cSAntonio Afonso context[self._save_key] = context.get(self._save_key, []) 583a61c247cSAntonio Afonso context[self._save_key].append(actual_packet) 584a61c247cSAntonio Afonso 585a61c247cSAntonio Afonso # Check for a runaway response cycle. 586a61c247cSAntonio Afonso if len(context[self._save_key]) >= self._runaway_response_count: 587a61c247cSAntonio Afonso raise Exception( 5882238dcc3SJonas Devlieghere "runaway query/response cycle detected: %d responses captured so far. Last response: %s" 5892238dcc3SJonas Devlieghere % (len(context[self._save_key]), context[self._save_key][-1]) 5902238dcc3SJonas Devlieghere ) 591a61c247cSAntonio Afonso 592a61c247cSAntonio Afonso # Flip the mode to send for generating the query. 593a61c247cSAntonio Afonso self._is_send_to_remote = True 594a61c247cSAntonio Afonso return context 595a61c247cSAntonio Afonso 596a61c247cSAntonio Afonso 597a61c247cSAntonio Afonsoclass MatchRemoteOutputEntry(GdbRemoteEntryBase): 598a61c247cSAntonio Afonso """Waits for output from the debug monitor to match a regex or time out. 599a61c247cSAntonio Afonso 600a61c247cSAntonio Afonso This entry type tries to match each time new gdb remote output is accumulated 601a61c247cSAntonio Afonso using a provided regex. If the output does not match the regex within the 602a61c247cSAntonio Afonso given timeframe, the command fails the playback session. If the regex does 603a61c247cSAntonio Afonso match, any capture fields are recorded in the context. 604a61c247cSAntonio Afonso 605a61c247cSAntonio Afonso Settings accepted from params: 606a61c247cSAntonio Afonso 607a61c247cSAntonio Afonso regex: required. Specifies a compiled regex object that must either succeed 608a61c247cSAntonio Afonso with re.match or re.search (see regex_mode below) within the given timeout 609a61c247cSAntonio Afonso (see timeout_seconds below) or cause the playback to fail. 610a61c247cSAntonio Afonso 611a61c247cSAntonio Afonso regex_mode: optional. Available values: "match" or "search". If "match", the entire 612a61c247cSAntonio Afonso stub output as collected so far must match the regex. If search, then the regex 613a61c247cSAntonio Afonso must match starting somewhere within the output text accumulated thus far. 614a61c247cSAntonio Afonso Default: "match" (i.e. the regex must match the entirety of the accumulated output 615a61c247cSAntonio Afonso buffer, so unexpected text will generally fail the match). 616a61c247cSAntonio Afonso 617a61c247cSAntonio Afonso capture: optional. If specified, is a dictionary of regex match group indices (should start 618a61c247cSAntonio Afonso with 1) to variable names that will store the capture group indicated by the 619a61c247cSAntonio Afonso index. For example, {1:"thread_id"} will store capture group 1's content in the 620a61c247cSAntonio Afonso context dictionary where "thread_id" is the key and the match group value is 621ded66049SPavel Labath the value. This arg only makes sense when regex is specified. 622a61c247cSAntonio Afonso """ 623a61c247cSAntonio Afonso 624a61c247cSAntonio Afonso def __init__(self, regex=None, regex_mode="match", capture=None): 625a61c247cSAntonio Afonso self._regex = regex 626a61c247cSAntonio Afonso self._regex_mode = regex_mode 627a61c247cSAntonio Afonso self._capture = capture 628a61c247cSAntonio Afonso self._matched = False 629a61c247cSAntonio Afonso 630a61c247cSAntonio Afonso if not self._regex: 631a61c247cSAntonio Afonso raise Exception("regex cannot be None") 632a61c247cSAntonio Afonso 633a61c247cSAntonio Afonso if not self._regex_mode in ["match", "search"]: 634a61c247cSAntonio Afonso raise Exception( 6352238dcc3SJonas Devlieghere 'unsupported regex mode "{}": must be "match" or "search"'.format( 6362238dcc3SJonas Devlieghere self._regex_mode 6372238dcc3SJonas Devlieghere ) 6382238dcc3SJonas Devlieghere ) 639a61c247cSAntonio Afonso 640a61c247cSAntonio Afonso def is_output_matcher(self): 641a61c247cSAntonio Afonso return True 642a61c247cSAntonio Afonso 643a61c247cSAntonio Afonso def is_send_to_remote(self): 644a61c247cSAntonio Afonso # This is always a "wait for remote" command. 645a61c247cSAntonio Afonso return False 646a61c247cSAntonio Afonso 647a61c247cSAntonio Afonso def is_consumed(self): 648a61c247cSAntonio Afonso return self._matched 649a61c247cSAntonio Afonso 650a61c247cSAntonio Afonso def assert_match(self, asserter, accumulated_output, context): 651a61c247cSAntonio Afonso # Validate args. 652a61c247cSAntonio Afonso if not accumulated_output: 653a61c247cSAntonio Afonso raise Exception("accumulated_output cannot be none") 654a61c247cSAntonio Afonso if not context: 655a61c247cSAntonio Afonso raise Exception("context cannot be none") 656a61c247cSAntonio Afonso 657a61c247cSAntonio Afonso # Validate that we haven't already matched. 658a61c247cSAntonio Afonso if self._matched: 659a61c247cSAntonio Afonso raise Exception( 6602238dcc3SJonas Devlieghere "invalid state - already matched, attempting to match again" 6612238dcc3SJonas Devlieghere ) 662a61c247cSAntonio Afonso 663a61c247cSAntonio Afonso # If we don't have any content yet, we don't match. 664a61c247cSAntonio Afonso if len(accumulated_output) < 1: 665a61c247cSAntonio Afonso return context 666a61c247cSAntonio Afonso 667a61c247cSAntonio Afonso # Check if we match 668a61c247cSAntonio Afonso if self._regex_mode == "match": 669a61c247cSAntonio Afonso match = self._regex.match(accumulated_output) 670a61c247cSAntonio Afonso elif self._regex_mode == "search": 671a61c247cSAntonio Afonso match = self._regex.search(accumulated_output) 672a61c247cSAntonio Afonso else: 6732238dcc3SJonas Devlieghere raise Exception("Unexpected regex mode: {}".format(self._regex_mode)) 674a61c247cSAntonio Afonso 675a61c247cSAntonio Afonso # If we don't match, wait to try again after next $O content, or time 676a61c247cSAntonio Afonso # out. 677a61c247cSAntonio Afonso if not match: 678a61c247cSAntonio Afonso # print("re pattern \"{}\" did not match against \"{}\"".format(self._regex.pattern, accumulated_output)) 679a61c247cSAntonio Afonso return context 680a61c247cSAntonio Afonso 681a61c247cSAntonio Afonso # We do match. 682a61c247cSAntonio Afonso self._matched = True 683a61c247cSAntonio Afonso # print("re pattern \"{}\" matched against \"{}\"".format(self._regex.pattern, accumulated_output)) 684a61c247cSAntonio Afonso 685a61c247cSAntonio Afonso # Collect up any captures into the context. 686a61c247cSAntonio Afonso if self._capture: 687a61c247cSAntonio Afonso # Handle captures. 688a61c247cSAntonio Afonso for group_index, var_name in list(self._capture.items()): 689a61c247cSAntonio Afonso capture_text = match.group(group_index) 690a61c247cSAntonio Afonso if not capture_text: 6912238dcc3SJonas Devlieghere raise Exception("No content for group index {}".format(group_index)) 692a61c247cSAntonio Afonso context[var_name] = capture_text 693a61c247cSAntonio Afonso 694a61c247cSAntonio Afonso return context 695a61c247cSAntonio Afonso 696a61c247cSAntonio Afonso 697a61c247cSAntonio Afonsoclass GdbRemoteTestSequence(object): 6982238dcc3SJonas Devlieghere _LOG_LINE_REGEX = re.compile(r"^.*(read|send)\s+packet:\s+(.+)$") 699a61c247cSAntonio Afonso 700a61c247cSAntonio Afonso def __init__(self, logger): 701a61c247cSAntonio Afonso self.entries = [] 702a61c247cSAntonio Afonso self.logger = logger 703a61c247cSAntonio Afonso 704c888694aSMuhammad Omair Javaid def __len__(self): 705c888694aSMuhammad Omair Javaid return len(self.entries) 706c888694aSMuhammad Omair Javaid 707a61c247cSAntonio Afonso def add_log_lines(self, log_lines, remote_input_is_read): 708a61c247cSAntonio Afonso for line in log_lines: 709a61c247cSAntonio Afonso if isinstance(line, str): 710a61c247cSAntonio Afonso # Handle log line import 711a61c247cSAntonio Afonso # if self.logger: 712a61c247cSAntonio Afonso # self.logger.debug("processing log line: {}".format(line)) 713a61c247cSAntonio Afonso match = self._LOG_LINE_REGEX.match(line) 714a61c247cSAntonio Afonso if match: 715a61c247cSAntonio Afonso playback_packet = match.group(2) 716a61c247cSAntonio Afonso direction = match.group(1) 7172238dcc3SJonas Devlieghere if _is_packet_lldb_gdbserver_input(direction, remote_input_is_read): 718a61c247cSAntonio Afonso # Handle as something to send to the remote debug monitor. 719a61c247cSAntonio Afonso # if self.logger: 720a61c247cSAntonio Afonso # self.logger.info("processed packet to send to remote: {}".format(playback_packet)) 721a61c247cSAntonio Afonso self.entries.append( 722a61c247cSAntonio Afonso GdbRemoteEntry( 7232238dcc3SJonas Devlieghere is_send_to_remote=True, exact_payload=playback_packet 7242238dcc3SJonas Devlieghere ) 7252238dcc3SJonas Devlieghere ) 726a61c247cSAntonio Afonso else: 727a61c247cSAntonio Afonso # Log line represents content to be expected from the remote debug monitor. 728a61c247cSAntonio Afonso # if self.logger: 729a61c247cSAntonio Afonso # self.logger.info("receiving packet from llgs, should match: {}".format(playback_packet)) 730a61c247cSAntonio Afonso self.entries.append( 731a61c247cSAntonio Afonso GdbRemoteEntry( 7322238dcc3SJonas Devlieghere is_send_to_remote=False, exact_payload=playback_packet 7332238dcc3SJonas Devlieghere ) 7342238dcc3SJonas Devlieghere ) 735a61c247cSAntonio Afonso else: 7362238dcc3SJonas Devlieghere raise Exception("failed to interpret log line: {}".format(line)) 737a61c247cSAntonio Afonso elif isinstance(line, dict): 738a61c247cSAntonio Afonso entry_type = line.get("type", "regex_capture") 739a61c247cSAntonio Afonso if entry_type == "regex_capture": 740a61c247cSAntonio Afonso # Handle more explicit control over details via dictionary. 741a61c247cSAntonio Afonso direction = line.get("direction", None) 742a61c247cSAntonio Afonso regex = line.get("regex", None) 743a61c247cSAntonio Afonso capture = line.get("capture", None) 744a61c247cSAntonio Afonso 745a61c247cSAntonio Afonso # Compile the regex. 746a61c247cSAntonio Afonso if regex and (isinstance(regex, str)): 747ff94f602SJan Kratochvil regex = re.compile(regex, re.DOTALL) 748a61c247cSAntonio Afonso 7492238dcc3SJonas Devlieghere if _is_packet_lldb_gdbserver_input(direction, remote_input_is_read): 750a61c247cSAntonio Afonso # Handle as something to send to the remote debug monitor. 751a61c247cSAntonio Afonso # if self.logger: 752a61c247cSAntonio Afonso # self.logger.info("processed dict sequence to send to remote") 753a61c247cSAntonio Afonso self.entries.append( 754a61c247cSAntonio Afonso GdbRemoteEntry( 7552238dcc3SJonas Devlieghere is_send_to_remote=True, regex=regex, capture=capture 7562238dcc3SJonas Devlieghere ) 7572238dcc3SJonas Devlieghere ) 758a61c247cSAntonio Afonso else: 759a61c247cSAntonio Afonso # Log line represents content to be expected from the remote debug monitor. 760a61c247cSAntonio Afonso # if self.logger: 761a61c247cSAntonio Afonso # self.logger.info("processed dict sequence to match receiving from remote") 762a61c247cSAntonio Afonso self.entries.append( 763a61c247cSAntonio Afonso GdbRemoteEntry( 7642238dcc3SJonas Devlieghere is_send_to_remote=False, regex=regex, capture=capture 7652238dcc3SJonas Devlieghere ) 7662238dcc3SJonas Devlieghere ) 767a61c247cSAntonio Afonso elif entry_type == "multi_response": 768a61c247cSAntonio Afonso self.entries.append(MultiResponseGdbRemoteEntry(line)) 769a61c247cSAntonio Afonso elif entry_type == "output_match": 770a61c247cSAntonio Afonso regex = line.get("regex", None) 771a61c247cSAntonio Afonso # Compile the regex. 772a61c247cSAntonio Afonso if regex and (isinstance(regex, str)): 773a61c247cSAntonio Afonso regex = re.compile(regex, re.DOTALL) 774a61c247cSAntonio Afonso 775a61c247cSAntonio Afonso regex_mode = line.get("regex_mode", "match") 776a61c247cSAntonio Afonso capture = line.get("capture", None) 777a61c247cSAntonio Afonso self.entries.append( 778a61c247cSAntonio Afonso MatchRemoteOutputEntry( 7792238dcc3SJonas Devlieghere regex=regex, regex_mode=regex_mode, capture=capture 7802238dcc3SJonas Devlieghere ) 7812238dcc3SJonas Devlieghere ) 782a61c247cSAntonio Afonso else: 7832238dcc3SJonas Devlieghere raise Exception('unknown entry type "%s"' % entry_type) 784a61c247cSAntonio Afonso 785a61c247cSAntonio Afonso 786a61c247cSAntonio Afonsodef process_is_running(pid, unknown_value=True): 787a61c247cSAntonio Afonso """If possible, validate that the given pid represents a running process on the local system. 788a61c247cSAntonio Afonso 789a61c247cSAntonio Afonso Args: 790a61c247cSAntonio Afonso 791a61c247cSAntonio Afonso pid: an OS-specific representation of a process id. Should be an integral value. 792a61c247cSAntonio Afonso 793a61c247cSAntonio Afonso unknown_value: value used when we cannot determine how to check running local 794a61c247cSAntonio Afonso processes on the OS. 795a61c247cSAntonio Afonso 796a61c247cSAntonio Afonso Returns: 797a61c247cSAntonio Afonso 798a61c247cSAntonio Afonso If we can figure out how to check running process ids on the given OS: 799a61c247cSAntonio Afonso return True if the process is running, or False otherwise. 800a61c247cSAntonio Afonso 801a61c247cSAntonio Afonso If we don't know how to check running process ids on the given OS: 802a61c247cSAntonio Afonso return the value provided by the unknown_value arg. 803a61c247cSAntonio Afonso """ 80456f9cfe3SDave Lee if not isinstance(pid, int): 805a61c247cSAntonio Afonso raise Exception( 8062238dcc3SJonas Devlieghere "pid must be an integral type (actual type: %s)" % str(type(pid)) 8072238dcc3SJonas Devlieghere ) 808a61c247cSAntonio Afonso 809a61c247cSAntonio Afonso process_ids = [] 810a61c247cSAntonio Afonso 811a61c247cSAntonio Afonso if lldb.remote_platform: 812a61c247cSAntonio Afonso # Don't know how to get list of running process IDs on a remote 813a61c247cSAntonio Afonso # platform 814a61c247cSAntonio Afonso return unknown_value 8152238dcc3SJonas Devlieghere elif platform.system() in ["Darwin", "Linux", "FreeBSD", "NetBSD"]: 816a61c247cSAntonio Afonso # Build the list of running process ids 817a61c247cSAntonio Afonso output = subprocess.check_output( 8182238dcc3SJonas Devlieghere "ps ax | awk '{ print $1; }'", shell=True 8192238dcc3SJonas Devlieghere ).decode("utf-8") 8202238dcc3SJonas Devlieghere text_process_ids = output.split("\n")[1:] 821a61c247cSAntonio Afonso # Convert text pids to ints 8222238dcc3SJonas Devlieghere process_ids = [int(text_pid) for text_pid in text_process_ids if text_pid != ""] 8232238dcc3SJonas Devlieghere elif platform.system() == "Windows": 8242a39024aSAaron Smith output = subprocess.check_output( 8252238dcc3SJonas Devlieghere 'for /f "tokens=2 delims=," %F in (\'tasklist /nh /fi "PID ne 0" /fo csv\') do @echo %~F', 8262238dcc3SJonas Devlieghere shell=True, 8272238dcc3SJonas Devlieghere ).decode("utf-8") 8282238dcc3SJonas Devlieghere text_process_ids = output.split("\n")[1:] 8292238dcc3SJonas Devlieghere process_ids = [int(text_pid) for text_pid in text_process_ids if text_pid != ""] 830a61c247cSAntonio Afonso # elif {your_platform_here}: 831a61c247cSAntonio Afonso # fill in process_ids as a list of int type process IDs running on 832a61c247cSAntonio Afonso # the local system. 833a61c247cSAntonio Afonso else: 834a61c247cSAntonio Afonso # Don't know how to get list of running process IDs on this 835a61c247cSAntonio Afonso # OS, so return the "don't know" value. 836a61c247cSAntonio Afonso return unknown_value 837a61c247cSAntonio Afonso 838a61c247cSAntonio Afonso # Check if the pid is in the process_ids 839a61c247cSAntonio Afonso return pid in process_ids 840a61c247cSAntonio Afonso 8411903f358SMichał Górny 842a2f4f7daSPavel Labathdef _handle_output_packet_string(packet_contents): 8431903f358SMichał Górny # Warning: in non-stop mode, we currently handle only the first output 8441903f358SMichał Górny # packet since we'd need to inject vStdio packets 8451903f358SMichał Górny if not packet_contents.startswith((b"$O", b"%Stdio:O")): 846a2f4f7daSPavel Labath return None 8471903f358SMichał Górny elif packet_contents == b"$OK": 848a2f4f7daSPavel Labath return None 849a61c247cSAntonio Afonso else: 8501903f358SMichał Górny return binascii.unhexlify(packet_contents.partition(b"O")[2]) 8511903f358SMichał Górny 852a2f4f7daSPavel Labath 853a2f4f7daSPavel Labathclass Server(object): 8542238dcc3SJonas Devlieghere _GDB_REMOTE_PACKET_REGEX = re.compile(rb"^([\$%][^\#]*)#[0-9a-fA-F]{2}") 855a2f4f7daSPavel Labath 856a2f4f7daSPavel Labath class ChecksumMismatch(Exception): 857a2f4f7daSPavel Labath pass 858a2f4f7daSPavel Labath 859a2f4f7daSPavel Labath def __init__(self, sock, proc=None): 860a2f4f7daSPavel Labath self._accumulated_output = b"" 861a2f4f7daSPavel Labath self._receive_buffer = b"" 862a2f4f7daSPavel Labath self._normal_queue = [] 863a2f4f7daSPavel Labath self._output_queue = [] 864a2f4f7daSPavel Labath self._sock = sock 865a2f4f7daSPavel Labath self._proc = proc 866a2f4f7daSPavel Labath 867a2f4f7daSPavel Labath def send_raw(self, frame): 868a2f4f7daSPavel Labath self._sock.sendall(frame) 869a2f4f7daSPavel Labath 870872b1da6SPavel Labath def send_ack(self): 871872b1da6SPavel Labath self.send_raw(b"+") 872872b1da6SPavel Labath 873872b1da6SPavel Labath def send_packet(self, packet): 8742238dcc3SJonas Devlieghere self.send_raw(b"$%s#%02x" % (packet, self._checksum(packet))) 875872b1da6SPavel Labath 876872b1da6SPavel Labath @staticmethod 877872b1da6SPavel Labath def _checksum(packet): 878872b1da6SPavel Labath checksum = 0 87956f9cfe3SDave Lee for c in iter(packet): 880872b1da6SPavel Labath checksum += c 881872b1da6SPavel Labath return checksum % 256 882872b1da6SPavel Labath 883a2f4f7daSPavel Labath def _read(self, q): 884a2f4f7daSPavel Labath while not q: 885a2f4f7daSPavel Labath new_bytes = self._sock.recv(4096) 886a2f4f7daSPavel Labath self._process_new_bytes(new_bytes) 887a2f4f7daSPavel Labath return q.pop(0) 888a2f4f7daSPavel Labath 889a2f4f7daSPavel Labath def _process_new_bytes(self, new_bytes): 890a2f4f7daSPavel Labath # Add new bytes to our accumulated unprocessed packet bytes. 891a2f4f7daSPavel Labath self._receive_buffer += new_bytes 892a2f4f7daSPavel Labath 893a2f4f7daSPavel Labath # Parse fully-formed packets into individual packets. 894a2f4f7daSPavel Labath has_more = len(self._receive_buffer) > 0 895a2f4f7daSPavel Labath while has_more: 896a2f4f7daSPavel Labath if len(self._receive_buffer) <= 0: 897a2f4f7daSPavel Labath has_more = False 898a2f4f7daSPavel Labath # handle '+' ack 899a2f4f7daSPavel Labath elif self._receive_buffer[0:1] == b"+": 900a2f4f7daSPavel Labath self._normal_queue += [b"+"] 901a2f4f7daSPavel Labath self._receive_buffer = self._receive_buffer[1:] 902a2f4f7daSPavel Labath else: 9032238dcc3SJonas Devlieghere packet_match = self._GDB_REMOTE_PACKET_REGEX.match(self._receive_buffer) 904a2f4f7daSPavel Labath if packet_match: 905a2f4f7daSPavel Labath # Our receive buffer matches a packet at the 906a2f4f7daSPavel Labath # start of the receive buffer. 907a2f4f7daSPavel Labath new_output_content = _handle_output_packet_string( 9082238dcc3SJonas Devlieghere packet_match.group(1) 9092238dcc3SJonas Devlieghere ) 910a2f4f7daSPavel Labath if new_output_content: 911a2f4f7daSPavel Labath # This was an $O packet with new content. 912a2f4f7daSPavel Labath self._accumulated_output += new_output_content 913a2f4f7daSPavel Labath self._output_queue += [self._accumulated_output] 914a2f4f7daSPavel Labath else: 915a2f4f7daSPavel Labath # Any packet other than $O. 916a2f4f7daSPavel Labath self._normal_queue += [packet_match.group(0)] 917a2f4f7daSPavel Labath 918a2f4f7daSPavel Labath # Remove the parsed packet from the receive 919a2f4f7daSPavel Labath # buffer. 920a2f4f7daSPavel Labath self._receive_buffer = self._receive_buffer[ 9212238dcc3SJonas Devlieghere len(packet_match.group(0)) : 9222238dcc3SJonas Devlieghere ] 923a2f4f7daSPavel Labath else: 924a2f4f7daSPavel Labath # We don't have enough in the receive bufferto make a full 925a2f4f7daSPavel Labath # packet. Stop trying until we read more. 926a2f4f7daSPavel Labath has_more = False 927a2f4f7daSPavel Labath 928a2f4f7daSPavel Labath def get_raw_output_packet(self): 929a2f4f7daSPavel Labath return self._read(self._output_queue) 930a2f4f7daSPavel Labath 931a2f4f7daSPavel Labath def get_raw_normal_packet(self): 932a2f4f7daSPavel Labath return self._read(self._normal_queue) 933a2f4f7daSPavel Labath 934872b1da6SPavel Labath @staticmethod 935872b1da6SPavel Labath def _get_payload(frame): 936872b1da6SPavel Labath payload = frame[1:-3] 937872b1da6SPavel Labath checksum = int(frame[-2:], 16) 938872b1da6SPavel Labath if checksum != Server._checksum(payload): 939872b1da6SPavel Labath raise ChecksumMismatch 940872b1da6SPavel Labath return payload 941872b1da6SPavel Labath 942872b1da6SPavel Labath def get_normal_packet(self): 943872b1da6SPavel Labath frame = self.get_raw_normal_packet() 9442238dcc3SJonas Devlieghere if frame == b"+": 9452238dcc3SJonas Devlieghere return frame 946872b1da6SPavel Labath return self._get_payload(frame) 947872b1da6SPavel Labath 948a2f4f7daSPavel Labath def get_accumulated_output(self): 949a2f4f7daSPavel Labath return self._accumulated_output 950a2f4f7daSPavel Labath 951a2f4f7daSPavel Labath def consume_accumulated_output(self): 952a2f4f7daSPavel Labath output = self._accumulated_output 953a2f4f7daSPavel Labath self._accumulated_output = b"" 954a2f4f7daSPavel Labath return output 955a2f4f7daSPavel Labath 956a2f4f7daSPavel Labath def __str__(self): 9572238dcc3SJonas Devlieghere return dedent( 9582238dcc3SJonas Devlieghere """\ 959a2f4f7daSPavel Labath server '{}' on '{}' 960a2f4f7daSPavel Labath _receive_buffer: {} 961a2f4f7daSPavel Labath _normal_queue: {} 962a2f4f7daSPavel Labath _output_queue: {} 963a2f4f7daSPavel Labath _accumulated_output: {} 9642238dcc3SJonas Devlieghere """ 9652238dcc3SJonas Devlieghere ).format( 9662238dcc3SJonas Devlieghere self._proc, 9672238dcc3SJonas Devlieghere self._sock, 9682238dcc3SJonas Devlieghere self._receive_buffer, 9692238dcc3SJonas Devlieghere self._normal_queue, 9702238dcc3SJonas Devlieghere self._output_queue, 9712238dcc3SJonas Devlieghere self._accumulated_output, 9722238dcc3SJonas Devlieghere ) 973a52be0ccSSanthosh Kumar Ellendula 974a52be0ccSSanthosh Kumar Ellendula 975a52be0ccSSanthosh Kumar Ellendula# A class representing a pipe for communicating with debug server. 976a52be0ccSSanthosh Kumar Ellendula# This class includes menthods to open the pipe and read the port number from it. 977a52be0ccSSanthosh Kumar Ellendulaif lldbplatformutil.getHostPlatform() == "windows": 978a52be0ccSSanthosh Kumar Ellendula import ctypes 979a52be0ccSSanthosh Kumar Ellendula import ctypes.wintypes 980a52be0ccSSanthosh Kumar Ellendula from ctypes.wintypes import BOOL, DWORD, HANDLE, LPCWSTR, LPDWORD, LPVOID 981a52be0ccSSanthosh Kumar Ellendula 982a52be0ccSSanthosh Kumar Ellendula kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) 983a52be0ccSSanthosh Kumar Ellendula 984a52be0ccSSanthosh Kumar Ellendula PIPE_ACCESS_INBOUND = 1 985a52be0ccSSanthosh Kumar Ellendula FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000 986a52be0ccSSanthosh Kumar Ellendula FILE_FLAG_OVERLAPPED = 0x40000000 987a52be0ccSSanthosh Kumar Ellendula PIPE_TYPE_BYTE = 0 988a52be0ccSSanthosh Kumar Ellendula PIPE_REJECT_REMOTE_CLIENTS = 8 989a52be0ccSSanthosh Kumar Ellendula INVALID_HANDLE_VALUE = -1 990a52be0ccSSanthosh Kumar Ellendula ERROR_ACCESS_DENIED = 5 991a52be0ccSSanthosh Kumar Ellendula ERROR_IO_PENDING = 997 992a52be0ccSSanthosh Kumar Ellendula 993a52be0ccSSanthosh Kumar Ellendula class OVERLAPPED(ctypes.Structure): 994a52be0ccSSanthosh Kumar Ellendula _fields_ = [ 995a52be0ccSSanthosh Kumar Ellendula ("Internal", LPVOID), 996a52be0ccSSanthosh Kumar Ellendula ("InternalHigh", LPVOID), 997a52be0ccSSanthosh Kumar Ellendula ("Offset", DWORD), 998a52be0ccSSanthosh Kumar Ellendula ("OffsetHigh", DWORD), 999a52be0ccSSanthosh Kumar Ellendula ("hEvent", HANDLE), 1000a52be0ccSSanthosh Kumar Ellendula ] 1001a52be0ccSSanthosh Kumar Ellendula 1002a52be0ccSSanthosh Kumar Ellendula def __init__(self): 1003a52be0ccSSanthosh Kumar Ellendula super(OVERLAPPED, self).__init__( 1004a52be0ccSSanthosh Kumar Ellendula Internal=0, InternalHigh=0, Offset=0, OffsetHigh=0, hEvent=None 1005a52be0ccSSanthosh Kumar Ellendula ) 1006a52be0ccSSanthosh Kumar Ellendula 1007a52be0ccSSanthosh Kumar Ellendula LPOVERLAPPED = ctypes.POINTER(OVERLAPPED) 1008a52be0ccSSanthosh Kumar Ellendula 1009a52be0ccSSanthosh Kumar Ellendula CreateNamedPipe = kernel32.CreateNamedPipeW 1010a52be0ccSSanthosh Kumar Ellendula CreateNamedPipe.restype = HANDLE 1011a52be0ccSSanthosh Kumar Ellendula CreateNamedPipe.argtypes = ( 1012a52be0ccSSanthosh Kumar Ellendula LPCWSTR, 1013a52be0ccSSanthosh Kumar Ellendula DWORD, 1014a52be0ccSSanthosh Kumar Ellendula DWORD, 1015a52be0ccSSanthosh Kumar Ellendula DWORD, 1016a52be0ccSSanthosh Kumar Ellendula DWORD, 1017a52be0ccSSanthosh Kumar Ellendula DWORD, 1018a52be0ccSSanthosh Kumar Ellendula DWORD, 1019a52be0ccSSanthosh Kumar Ellendula LPVOID, 1020a52be0ccSSanthosh Kumar Ellendula ) 1021a52be0ccSSanthosh Kumar Ellendula 1022a52be0ccSSanthosh Kumar Ellendula ConnectNamedPipe = kernel32.ConnectNamedPipe 1023a52be0ccSSanthosh Kumar Ellendula ConnectNamedPipe.restype = BOOL 1024a52be0ccSSanthosh Kumar Ellendula ConnectNamedPipe.argtypes = (HANDLE, LPOVERLAPPED) 1025a52be0ccSSanthosh Kumar Ellendula 1026a52be0ccSSanthosh Kumar Ellendula CreateEvent = kernel32.CreateEventW 1027a52be0ccSSanthosh Kumar Ellendula CreateEvent.restype = HANDLE 1028a52be0ccSSanthosh Kumar Ellendula CreateEvent.argtypes = (LPVOID, BOOL, BOOL, LPCWSTR) 1029a52be0ccSSanthosh Kumar Ellendula 1030a52be0ccSSanthosh Kumar Ellendula GetOverlappedResultEx = kernel32.GetOverlappedResultEx 1031a52be0ccSSanthosh Kumar Ellendula GetOverlappedResultEx.restype = BOOL 1032a52be0ccSSanthosh Kumar Ellendula GetOverlappedResultEx.argtypes = (HANDLE, LPOVERLAPPED, LPDWORD, DWORD, BOOL) 1033a52be0ccSSanthosh Kumar Ellendula 1034a52be0ccSSanthosh Kumar Ellendula ReadFile = kernel32.ReadFile 1035a52be0ccSSanthosh Kumar Ellendula ReadFile.restype = BOOL 1036a52be0ccSSanthosh Kumar Ellendula ReadFile.argtypes = (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED) 1037a52be0ccSSanthosh Kumar Ellendula 1038a52be0ccSSanthosh Kumar Ellendula CloseHandle = kernel32.CloseHandle 1039a52be0ccSSanthosh Kumar Ellendula CloseHandle.restype = BOOL 1040a52be0ccSSanthosh Kumar Ellendula CloseHandle.argtypes = (HANDLE,) 1041a52be0ccSSanthosh Kumar Ellendula 1042a52be0ccSSanthosh Kumar Ellendula class Pipe(object): 1043a52be0ccSSanthosh Kumar Ellendula def __init__(self, prefix): 1044a52be0ccSSanthosh Kumar Ellendula while True: 1045*857700ffSKendal Harland self.name = "lldb-" + str(random.randrange(10**10)) 1046a52be0ccSSanthosh Kumar Ellendula full_name = "\\\\.\\pipe\\" + self.name 1047a52be0ccSSanthosh Kumar Ellendula self._handle = CreateNamedPipe( 1048a52be0ccSSanthosh Kumar Ellendula full_name, 1049a52be0ccSSanthosh Kumar Ellendula PIPE_ACCESS_INBOUND 1050a52be0ccSSanthosh Kumar Ellendula | FILE_FLAG_FIRST_PIPE_INSTANCE 1051a52be0ccSSanthosh Kumar Ellendula | FILE_FLAG_OVERLAPPED, 1052a52be0ccSSanthosh Kumar Ellendula PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS, 1053a52be0ccSSanthosh Kumar Ellendula 1, 1054a52be0ccSSanthosh Kumar Ellendula 4096, 1055a52be0ccSSanthosh Kumar Ellendula 4096, 1056a52be0ccSSanthosh Kumar Ellendula 0, 1057a52be0ccSSanthosh Kumar Ellendula None, 1058a52be0ccSSanthosh Kumar Ellendula ) 1059a52be0ccSSanthosh Kumar Ellendula if self._handle != INVALID_HANDLE_VALUE: 1060a52be0ccSSanthosh Kumar Ellendula break 1061a52be0ccSSanthosh Kumar Ellendula if ctypes.get_last_error() != ERROR_ACCESS_DENIED: 1062a52be0ccSSanthosh Kumar Ellendula raise ctypes.WinError(ctypes.get_last_error()) 1063a52be0ccSSanthosh Kumar Ellendula 1064a52be0ccSSanthosh Kumar Ellendula self._overlapped = OVERLAPPED() 1065a52be0ccSSanthosh Kumar Ellendula self._overlapped.hEvent = CreateEvent(None, True, False, None) 1066a52be0ccSSanthosh Kumar Ellendula result = ConnectNamedPipe(self._handle, self._overlapped) 1067a52be0ccSSanthosh Kumar Ellendula assert result == 0 1068a52be0ccSSanthosh Kumar Ellendula if ctypes.get_last_error() != ERROR_IO_PENDING: 1069a52be0ccSSanthosh Kumar Ellendula raise ctypes.WinError(ctypes.get_last_error()) 1070a52be0ccSSanthosh Kumar Ellendula 1071a52be0ccSSanthosh Kumar Ellendula def finish_connection(self, timeout): 1072a52be0ccSSanthosh Kumar Ellendula if not GetOverlappedResultEx( 1073a52be0ccSSanthosh Kumar Ellendula self._handle, 1074a52be0ccSSanthosh Kumar Ellendula self._overlapped, 1075a52be0ccSSanthosh Kumar Ellendula ctypes.byref(DWORD(0)), 1076a52be0ccSSanthosh Kumar Ellendula timeout * 1000, 1077a52be0ccSSanthosh Kumar Ellendula True, 1078a52be0ccSSanthosh Kumar Ellendula ): 1079a52be0ccSSanthosh Kumar Ellendula raise ctypes.WinError(ctypes.get_last_error()) 1080a52be0ccSSanthosh Kumar Ellendula 1081a52be0ccSSanthosh Kumar Ellendula def read(self, size, timeout): 1082a52be0ccSSanthosh Kumar Ellendula buf = ctypes.create_string_buffer(size) 1083a52be0ccSSanthosh Kumar Ellendula if not ReadFile( 1084a52be0ccSSanthosh Kumar Ellendula self._handle, ctypes.byref(buf), size, None, self._overlapped 1085a52be0ccSSanthosh Kumar Ellendula ): 1086a52be0ccSSanthosh Kumar Ellendula if ctypes.get_last_error() != ERROR_IO_PENDING: 1087a52be0ccSSanthosh Kumar Ellendula raise ctypes.WinError(ctypes.get_last_error()) 1088a52be0ccSSanthosh Kumar Ellendula read = DWORD(0) 1089a52be0ccSSanthosh Kumar Ellendula if not GetOverlappedResultEx( 1090a52be0ccSSanthosh Kumar Ellendula self._handle, self._overlapped, ctypes.byref(read), timeout * 1000, True 1091a52be0ccSSanthosh Kumar Ellendula ): 1092a52be0ccSSanthosh Kumar Ellendula raise ctypes.WinError(ctypes.get_last_error()) 1093a52be0ccSSanthosh Kumar Ellendula return buf.raw[0 : read.value] 1094a52be0ccSSanthosh Kumar Ellendula 1095a52be0ccSSanthosh Kumar Ellendula def close(self): 1096a52be0ccSSanthosh Kumar Ellendula CloseHandle(self._overlapped.hEvent) 1097a52be0ccSSanthosh Kumar Ellendula CloseHandle(self._handle) 1098a52be0ccSSanthosh Kumar Ellendula 1099a52be0ccSSanthosh Kumar Ellendulaelse: 1100a52be0ccSSanthosh Kumar Ellendula 1101a52be0ccSSanthosh Kumar Ellendula class Pipe(object): 1102a52be0ccSSanthosh Kumar Ellendula def __init__(self, prefix): 1103a52be0ccSSanthosh Kumar Ellendula self.name = os.path.join(prefix, "stub_port_number") 1104a52be0ccSSanthosh Kumar Ellendula os.mkfifo(self.name) 1105a52be0ccSSanthosh Kumar Ellendula self._fd = os.open(self.name, os.O_RDONLY | os.O_NONBLOCK) 1106a52be0ccSSanthosh Kumar Ellendula 1107a52be0ccSSanthosh Kumar Ellendula def finish_connection(self, timeout): 1108a52be0ccSSanthosh Kumar Ellendula pass 1109a52be0ccSSanthosh Kumar Ellendula 1110a52be0ccSSanthosh Kumar Ellendula def read(self, size, timeout): 1111a52be0ccSSanthosh Kumar Ellendula (readers, _, _) = select.select([self._fd], [], [], timeout) 1112a52be0ccSSanthosh Kumar Ellendula if self._fd not in readers: 1113a52be0ccSSanthosh Kumar Ellendula raise TimeoutError 1114a52be0ccSSanthosh Kumar Ellendula return os.read(self._fd, size) 1115a52be0ccSSanthosh Kumar Ellendula 1116a52be0ccSSanthosh Kumar Ellendula def close(self): 1117a52be0ccSSanthosh Kumar Ellendula os.close(self._fd) 1118