1061da546Spatrick"""Module for supporting unit testing of the lldb-server debug monitor exe. 2061da546Spatrick""" 3061da546Spatrick 4061da546Spatrickfrom __future__ import division, print_function 5061da546Spatrick 6be691f3bSpatrickimport binascii 7061da546Spatrickimport os 8061da546Spatrickimport os.path 9061da546Spatrickimport platform 10061da546Spatrickimport re 11be691f3bSpatrickimport socket 12061da546Spatrickimport subprocess 13be691f3bSpatrickfrom lldbsuite.support import seven 14061da546Spatrickfrom lldbsuite.test.lldbtest import * 15be691f3bSpatrickfrom lldbsuite.test import configuration 16be691f3bSpatrickfrom textwrap import dedent 17be691f3bSpatrickimport shutil 18061da546Spatrick 19be691f3bSpatrickdef _get_support_exe(basename): 20be691f3bSpatrick support_dir = lldb.SBHostOS.GetLLDBPath(lldb.ePathTypeSupportExecutableDir) 21061da546Spatrick 22be691f3bSpatrick return shutil.which(basename, path=support_dir.GetDirectory()) 23061da546Spatrick 24061da546Spatrick 25061da546Spatrickdef get_lldb_server_exe(): 26061da546Spatrick """Return the lldb-server exe path. 27061da546Spatrick 28061da546Spatrick Returns: 29061da546Spatrick A path to the lldb-server exe if it is found to exist; otherwise, 30061da546Spatrick returns None. 31061da546Spatrick """ 32061da546Spatrick 33be691f3bSpatrick return _get_support_exe("lldb-server") 34061da546Spatrick 35061da546Spatrick 36061da546Spatrickdef get_debugserver_exe(): 37061da546Spatrick """Return the debugserver exe path. 38061da546Spatrick 39061da546Spatrick Returns: 40061da546Spatrick A path to the debugserver exe if it is found to exist; otherwise, 41061da546Spatrick returns None. 42061da546Spatrick """ 43be691f3bSpatrick if configuration.arch and configuration.arch == "x86_64" and \ 44be691f3bSpatrick platform.machine().startswith("arm64"): 45be691f3bSpatrick return '/Library/Apple/usr/libexec/oah/debugserver' 46061da546Spatrick 47be691f3bSpatrick return _get_support_exe("debugserver") 48061da546Spatrick 49061da546Spatrick_LOG_LINE_REGEX = re.compile(r'^(lldb-server|debugserver)\s+<\s*(\d+)>' + 50061da546Spatrick '\s+(read|send)\s+packet:\s+(.+)$') 51061da546Spatrick 52061da546Spatrick 53061da546Spatrickdef _is_packet_lldb_gdbserver_input(packet_type, llgs_input_is_read): 54061da546Spatrick """Return whether a given packet is input for lldb-gdbserver. 55061da546Spatrick 56061da546Spatrick Args: 57061da546Spatrick packet_type: a string indicating 'send' or 'receive', from a 58061da546Spatrick gdbremote packet protocol log. 59061da546Spatrick 60061da546Spatrick llgs_input_is_read: true if lldb-gdbserver input (content sent to 61061da546Spatrick lldb-gdbserver) is listed as 'read' or 'send' in the packet 62061da546Spatrick log entry. 63061da546Spatrick 64061da546Spatrick Returns: 65061da546Spatrick True if the packet should be considered input for lldb-gdbserver; False 66061da546Spatrick otherwise. 67061da546Spatrick """ 68061da546Spatrick if packet_type == 'read': 69061da546Spatrick # when llgs is the read side, then a read packet is meant for 70061da546Spatrick # input to llgs (when captured from the llgs/debugserver exe). 71061da546Spatrick return llgs_input_is_read 72061da546Spatrick elif packet_type == 'send': 73061da546Spatrick # when llgs is the send side, then a send packet is meant to 74061da546Spatrick # be input to llgs (when captured from the lldb exe). 75061da546Spatrick return not llgs_input_is_read 76061da546Spatrick else: 77061da546Spatrick # don't understand what type of packet this is 78061da546Spatrick raise "Unknown packet type: {}".format(packet_type) 79061da546Spatrick 80061da546Spatrick 81061da546Spatrick_STRIP_CHECKSUM_REGEX = re.compile(r'#[0-9a-fA-F]{2}$') 82061da546Spatrick_STRIP_COMMAND_PREFIX_REGEX = re.compile(r"^\$") 83061da546Spatrick_STRIP_COMMAND_PREFIX_M_REGEX = re.compile(r"^\$m") 84061da546Spatrick 85061da546Spatrick 86061da546Spatrickdef assert_packets_equal(asserter, actual_packet, expected_packet): 87061da546Spatrick # strip off the checksum digits of the packet. When we're in 88061da546Spatrick # no-ack mode, the # checksum is ignored, and should not be cause 89061da546Spatrick # for a mismatched packet. 90061da546Spatrick actual_stripped = _STRIP_CHECKSUM_REGEX.sub('', actual_packet) 91061da546Spatrick expected_stripped = _STRIP_CHECKSUM_REGEX.sub('', expected_packet) 92061da546Spatrick asserter.assertEqual(actual_stripped, expected_stripped) 93061da546Spatrick 94061da546Spatrick 95061da546Spatrickdef expect_lldb_gdbserver_replay( 96061da546Spatrick asserter, 97be691f3bSpatrick server, 98061da546Spatrick test_sequence, 99061da546Spatrick timeout_seconds, 100061da546Spatrick logger=None): 101061da546Spatrick """Replay socket communication with lldb-gdbserver and verify responses. 102061da546Spatrick 103061da546Spatrick Args: 104061da546Spatrick asserter: the object providing assertEqual(first, second, msg=None), e.g. TestCase instance. 105061da546Spatrick 106061da546Spatrick test_sequence: a GdbRemoteTestSequence instance that describes 107061da546Spatrick the messages sent to the gdb remote and the responses 108061da546Spatrick expected from it. 109061da546Spatrick 110061da546Spatrick timeout_seconds: any response taking more than this number of 111061da546Spatrick seconds will cause an exception to be raised. 112061da546Spatrick 113061da546Spatrick logger: a Python logger instance. 114061da546Spatrick 115061da546Spatrick Returns: 116061da546Spatrick The context dictionary from running the given gdbremote 117061da546Spatrick protocol sequence. This will contain any of the capture 118061da546Spatrick elements specified to any GdbRemoteEntry instances in 119061da546Spatrick test_sequence. 120061da546Spatrick 121061da546Spatrick The context will also contain an entry, context["O_content"] 122061da546Spatrick which contains the text from the inferior received via $O 123061da546Spatrick packets. $O packets should not attempt to be matched 124061da546Spatrick directly since they are not entirely deterministic as to 125061da546Spatrick how many arrive and how much text is in each one. 126061da546Spatrick 127061da546Spatrick context["O_count"] will contain an integer of the number of 128061da546Spatrick O packets received. 129061da546Spatrick """ 130061da546Spatrick 131061da546Spatrick # Ensure we have some work to do. 132061da546Spatrick if len(test_sequence.entries) < 1: 133061da546Spatrick return {} 134061da546Spatrick 135061da546Spatrick context = {"O_count": 0, "O_content": ""} 136be691f3bSpatrick 137061da546Spatrick # Grab the first sequence entry. 138061da546Spatrick sequence_entry = test_sequence.entries.pop(0) 139061da546Spatrick 140061da546Spatrick # While we have an active sequence entry, send messages 141061da546Spatrick # destined for the stub and collect/match/process responses 142061da546Spatrick # expected from the stub. 143061da546Spatrick while sequence_entry: 144061da546Spatrick if sequence_entry.is_send_to_remote(): 145061da546Spatrick # This is an entry to send to the remote debug monitor. 146061da546Spatrick send_packet = sequence_entry.get_send_packet() 147061da546Spatrick if logger: 148061da546Spatrick if len(send_packet) == 1 and send_packet[0] == chr(3): 149061da546Spatrick packet_desc = "^C" 150061da546Spatrick else: 151061da546Spatrick packet_desc = send_packet 152061da546Spatrick logger.info( 153061da546Spatrick "sending packet to remote: {}".format(packet_desc)) 154be691f3bSpatrick server.send_raw(send_packet.encode()) 155061da546Spatrick else: 156061da546Spatrick # This is an entry expecting to receive content from the remote 157061da546Spatrick # debug monitor. 158061da546Spatrick 159061da546Spatrick # We'll pull from (and wait on) the queue appropriate for the type of matcher. 160061da546Spatrick # We keep separate queues for process output (coming from non-deterministic 161061da546Spatrick # $O packet division) and for all other packets. 162be691f3bSpatrick try: 163061da546Spatrick if sequence_entry.is_output_matcher(): 164061da546Spatrick # Grab next entry from the output queue. 165be691f3bSpatrick content = server.get_raw_output_packet() 166061da546Spatrick else: 167be691f3bSpatrick content = server.get_raw_normal_packet() 168be691f3bSpatrick content = seven.bitcast_to_string(content) 169be691f3bSpatrick except socket.timeout: 170be691f3bSpatrick asserter.fail( 171be691f3bSpatrick "timed out while waiting for '{}':\n{}".format(sequence_entry, server)) 172061da546Spatrick 173061da546Spatrick # Give the sequence entry the opportunity to match the content. 174061da546Spatrick # Output matchers might match or pass after more output accumulates. 175061da546Spatrick # Other packet types generally must match. 176061da546Spatrick asserter.assertIsNotNone(content) 177061da546Spatrick context = sequence_entry.assert_match( 178061da546Spatrick asserter, content, context=context) 179061da546Spatrick 180061da546Spatrick # Move on to next sequence entry as needed. Some sequence entries support executing multiple 181061da546Spatrick # times in different states (for looping over query/response 182061da546Spatrick # packets). 183061da546Spatrick if sequence_entry.is_consumed(): 184061da546Spatrick if len(test_sequence.entries) > 0: 185061da546Spatrick sequence_entry = test_sequence.entries.pop(0) 186061da546Spatrick else: 187061da546Spatrick sequence_entry = None 188061da546Spatrick 189061da546Spatrick # Fill in the O_content entries. 190061da546Spatrick context["O_count"] = 1 191be691f3bSpatrick context["O_content"] = server.consume_accumulated_output() 192061da546Spatrick 193061da546Spatrick return context 194061da546Spatrick 195061da546Spatrick 196061da546Spatrickdef gdbremote_hex_encode_string(str): 197061da546Spatrick output = '' 198061da546Spatrick for c in str: 199061da546Spatrick output += '{0:02x}'.format(ord(c)) 200061da546Spatrick return output 201061da546Spatrick 202061da546Spatrick 203061da546Spatrickdef gdbremote_hex_decode_string(str): 204061da546Spatrick return str.decode("hex") 205061da546Spatrick 206061da546Spatrick 207061da546Spatrickdef gdbremote_packet_encode_string(str): 208061da546Spatrick checksum = 0 209061da546Spatrick for c in str: 210061da546Spatrick checksum += ord(c) 211061da546Spatrick return '$' + str + '#{0:02x}'.format(checksum % 256) 212061da546Spatrick 213061da546Spatrick 214061da546Spatrickdef build_gdbremote_A_packet(args_list): 215061da546Spatrick """Given a list of args, create a properly-formed $A packet containing each arg. 216061da546Spatrick """ 217061da546Spatrick payload = "A" 218061da546Spatrick 219061da546Spatrick # build the arg content 220061da546Spatrick arg_index = 0 221061da546Spatrick for arg in args_list: 222061da546Spatrick # Comma-separate the args. 223061da546Spatrick if arg_index > 0: 224061da546Spatrick payload += ',' 225061da546Spatrick 226061da546Spatrick # Hex-encode the arg. 227061da546Spatrick hex_arg = gdbremote_hex_encode_string(arg) 228061da546Spatrick 229061da546Spatrick # Build the A entry. 230061da546Spatrick payload += "{},{},{}".format(len(hex_arg), arg_index, hex_arg) 231061da546Spatrick 232061da546Spatrick # Next arg index, please. 233061da546Spatrick arg_index += 1 234061da546Spatrick 235061da546Spatrick # return the packetized payload 236061da546Spatrick return gdbremote_packet_encode_string(payload) 237061da546Spatrick 238061da546Spatrick 239061da546Spatrickdef parse_reg_info_response(response_packet): 240061da546Spatrick if not response_packet: 241061da546Spatrick raise Exception("response_packet cannot be None") 242061da546Spatrick 243061da546Spatrick # Strip off prefix $ and suffix #xx if present. 244061da546Spatrick response_packet = _STRIP_COMMAND_PREFIX_REGEX.sub("", response_packet) 245061da546Spatrick response_packet = _STRIP_CHECKSUM_REGEX.sub("", response_packet) 246061da546Spatrick 247061da546Spatrick # Build keyval pairs 248061da546Spatrick values = {} 249061da546Spatrick for kv in response_packet.split(";"): 250061da546Spatrick if len(kv) < 1: 251061da546Spatrick continue 252061da546Spatrick (key, val) = kv.split(':') 253061da546Spatrick values[key] = val 254061da546Spatrick 255061da546Spatrick return values 256061da546Spatrick 257061da546Spatrick 258061da546Spatrickdef parse_threadinfo_response(response_packet): 259061da546Spatrick if not response_packet: 260061da546Spatrick raise Exception("response_packet cannot be None") 261061da546Spatrick 262061da546Spatrick # Strip off prefix $ and suffix #xx if present. 263061da546Spatrick response_packet = _STRIP_COMMAND_PREFIX_M_REGEX.sub("", response_packet) 264061da546Spatrick response_packet = _STRIP_CHECKSUM_REGEX.sub("", response_packet) 265061da546Spatrick 266*f6aab3d8Srobert for tid in response_packet.split(","): 267*f6aab3d8Srobert if not tid: 268*f6aab3d8Srobert continue 269*f6aab3d8Srobert if tid.startswith("p"): 270*f6aab3d8Srobert pid, _, tid = tid.partition(".") 271*f6aab3d8Srobert yield (int(pid[1:], 16), int(tid, 16)) 272*f6aab3d8Srobert else: 273*f6aab3d8Srobert yield int(tid, 16) 274061da546Spatrick 275061da546Spatrick 276061da546Spatrickdef unpack_endian_binary_string(endian, value_string): 277061da546Spatrick """Unpack a gdb-remote binary (post-unescaped, i.e. not escaped) response to an unsigned int given endianness of the inferior.""" 278061da546Spatrick if not endian: 279061da546Spatrick raise Exception("endian cannot be None") 280061da546Spatrick if not value_string or len(value_string) < 1: 281061da546Spatrick raise Exception("value_string cannot be None or empty") 282061da546Spatrick 283061da546Spatrick if endian == 'little': 284061da546Spatrick value = 0 285061da546Spatrick i = 0 286061da546Spatrick while len(value_string) > 0: 287061da546Spatrick value += (ord(value_string[0]) << i) 288061da546Spatrick value_string = value_string[1:] 289061da546Spatrick i += 8 290061da546Spatrick return value 291061da546Spatrick elif endian == 'big': 292061da546Spatrick value = 0 293061da546Spatrick while len(value_string) > 0: 294061da546Spatrick value = (value << 8) + ord(value_string[0]) 295061da546Spatrick value_string = value_string[1:] 296061da546Spatrick return value 297061da546Spatrick else: 298061da546Spatrick # pdp is valid but need to add parse code once needed. 299061da546Spatrick raise Exception("unsupported endian:{}".format(endian)) 300061da546Spatrick 301061da546Spatrick 302061da546Spatrickdef unpack_register_hex_unsigned(endian, value_string): 303061da546Spatrick """Unpack a gdb-remote $p-style response to an unsigned int given endianness of inferior.""" 304061da546Spatrick if not endian: 305061da546Spatrick raise Exception("endian cannot be None") 306061da546Spatrick if not value_string or len(value_string) < 1: 307061da546Spatrick raise Exception("value_string cannot be None or empty") 308061da546Spatrick 309061da546Spatrick if endian == 'little': 310061da546Spatrick value = 0 311061da546Spatrick i = 0 312061da546Spatrick while len(value_string) > 0: 313061da546Spatrick value += (int(value_string[0:2], 16) << i) 314061da546Spatrick value_string = value_string[2:] 315061da546Spatrick i += 8 316061da546Spatrick return value 317061da546Spatrick elif endian == 'big': 318061da546Spatrick return int(value_string, 16) 319061da546Spatrick else: 320061da546Spatrick # pdp is valid but need to add parse code once needed. 321061da546Spatrick raise Exception("unsupported endian:{}".format(endian)) 322061da546Spatrick 323061da546Spatrick 324061da546Spatrickdef pack_register_hex(endian, value, byte_size=None): 325061da546Spatrick """Unpack a gdb-remote $p-style response to an unsigned int given endianness of inferior.""" 326061da546Spatrick if not endian: 327061da546Spatrick raise Exception("endian cannot be None") 328061da546Spatrick 329061da546Spatrick if endian == 'little': 330061da546Spatrick # Create the litt-endian return value. 331061da546Spatrick retval = "" 332061da546Spatrick while value != 0: 333061da546Spatrick retval = retval + "{:02x}".format(value & 0xff) 334061da546Spatrick value = value >> 8 335061da546Spatrick if byte_size: 336061da546Spatrick # Add zero-fill to the right/end (MSB side) of the value. 337061da546Spatrick retval += "00" * (byte_size - len(retval) // 2) 338061da546Spatrick return retval 339061da546Spatrick 340061da546Spatrick elif endian == 'big': 341061da546Spatrick retval = "" 342061da546Spatrick while value != 0: 343061da546Spatrick retval = "{:02x}".format(value & 0xff) + retval 344061da546Spatrick value = value >> 8 345061da546Spatrick if byte_size: 346061da546Spatrick # Add zero-fill to the left/front (MSB side) of the value. 347061da546Spatrick retval = ("00" * (byte_size - len(retval) // 2)) + retval 348061da546Spatrick return retval 349061da546Spatrick 350061da546Spatrick else: 351061da546Spatrick # pdp is valid but need to add parse code once needed. 352061da546Spatrick raise Exception("unsupported endian:{}".format(endian)) 353061da546Spatrick 354061da546Spatrick 355061da546Spatrickclass GdbRemoteEntryBase(object): 356061da546Spatrick 357061da546Spatrick def is_output_matcher(self): 358061da546Spatrick return False 359061da546Spatrick 360061da546Spatrick 361061da546Spatrickclass GdbRemoteEntry(GdbRemoteEntryBase): 362061da546Spatrick 363061da546Spatrick def __init__( 364061da546Spatrick self, 365061da546Spatrick is_send_to_remote=True, 366061da546Spatrick exact_payload=None, 367061da546Spatrick regex=None, 368be691f3bSpatrick capture=None): 369061da546Spatrick """Create an entry representing one piece of the I/O to/from a gdb remote debug monitor. 370061da546Spatrick 371061da546Spatrick Args: 372061da546Spatrick 373061da546Spatrick is_send_to_remote: True if this entry is a message to be 374061da546Spatrick sent to the gdbremote debug monitor; False if this 375061da546Spatrick entry represents text to be matched against the reply 376061da546Spatrick from the gdbremote debug monitor. 377061da546Spatrick 378061da546Spatrick exact_payload: if not None, then this packet is an exact 379061da546Spatrick send (when sending to the remote) or an exact match of 380061da546Spatrick the response from the gdbremote. The checksums are 381061da546Spatrick ignored on exact match requests since negotiation of 382061da546Spatrick no-ack makes the checksum content essentially 383061da546Spatrick undefined. 384061da546Spatrick 385be691f3bSpatrick regex: currently only valid for receives from gdbremote. When 386be691f3bSpatrick specified (and only if exact_payload is None), indicates the 387be691f3bSpatrick gdbremote response must match the given regex. Match groups in 388be691f3bSpatrick the regex can be used for the matching portion (see capture 389be691f3bSpatrick arg). It is perfectly valid to have just a regex arg without a 390be691f3bSpatrick capture arg. This arg only makes sense if exact_payload is not 391061da546Spatrick specified. 392061da546Spatrick 393061da546Spatrick capture: if specified, is a dictionary of regex match 394061da546Spatrick group indices (should start with 1) to variable names 395061da546Spatrick that will store the capture group indicated by the 396061da546Spatrick index. For example, {1:"thread_id"} will store capture 397061da546Spatrick group 1's content in the context dictionary where 398061da546Spatrick "thread_id" is the key and the match group value is 399be691f3bSpatrick the value. This arg only makes sense when regex is specified. 400061da546Spatrick """ 401061da546Spatrick self._is_send_to_remote = is_send_to_remote 402061da546Spatrick self.exact_payload = exact_payload 403061da546Spatrick self.regex = regex 404061da546Spatrick self.capture = capture 405061da546Spatrick 406061da546Spatrick def is_send_to_remote(self): 407061da546Spatrick return self._is_send_to_remote 408061da546Spatrick 409061da546Spatrick def is_consumed(self): 410061da546Spatrick # For now, all packets are consumed after first use. 411061da546Spatrick return True 412061da546Spatrick 413061da546Spatrick def get_send_packet(self): 414061da546Spatrick if not self.is_send_to_remote(): 415061da546Spatrick raise Exception( 416061da546Spatrick "get_send_packet() called on GdbRemoteEntry that is not a send-to-remote packet") 417061da546Spatrick if not self.exact_payload: 418061da546Spatrick raise Exception( 419061da546Spatrick "get_send_packet() called on GdbRemoteEntry but it doesn't have an exact payload") 420061da546Spatrick return self.exact_payload 421061da546Spatrick 422061da546Spatrick def _assert_exact_payload_match(self, asserter, actual_packet): 423061da546Spatrick assert_packets_equal(asserter, actual_packet, self.exact_payload) 424061da546Spatrick return None 425061da546Spatrick 426061da546Spatrick def _assert_regex_match(self, asserter, actual_packet, context): 427061da546Spatrick # Ensure the actual packet matches from the start of the actual packet. 428061da546Spatrick match = self.regex.match(actual_packet) 429061da546Spatrick if not match: 430061da546Spatrick asserter.fail( 431061da546Spatrick "regex '{}' failed to match against content '{}'".format( 432061da546Spatrick self.regex.pattern, actual_packet)) 433061da546Spatrick 434061da546Spatrick if self.capture: 435061da546Spatrick # Handle captures. 436061da546Spatrick for group_index, var_name in list(self.capture.items()): 437061da546Spatrick capture_text = match.group(group_index) 438061da546Spatrick # It is okay for capture text to be None - which it will be if it is a group that can match nothing. 439061da546Spatrick # The user must be okay with it since the regex itself matched 440061da546Spatrick # above. 441061da546Spatrick context[var_name] = capture_text 442061da546Spatrick 443061da546Spatrick return context 444061da546Spatrick 445061da546Spatrick def assert_match(self, asserter, actual_packet, context=None): 446061da546Spatrick # This only makes sense for matching lines coming from the 447061da546Spatrick # remote debug monitor. 448061da546Spatrick if self.is_send_to_remote(): 449061da546Spatrick raise Exception( 450061da546Spatrick "Attempted to match a packet being sent to the remote debug monitor, doesn't make sense.") 451061da546Spatrick 452061da546Spatrick # Create a new context if needed. 453061da546Spatrick if not context: 454061da546Spatrick context = {} 455061da546Spatrick 456061da546Spatrick # If this is an exact payload, ensure they match exactly, 457061da546Spatrick # ignoring the packet checksum which is optional for no-ack 458061da546Spatrick # mode. 459061da546Spatrick if self.exact_payload: 460061da546Spatrick self._assert_exact_payload_match(asserter, actual_packet) 461061da546Spatrick return context 462061da546Spatrick elif self.regex: 463061da546Spatrick return self._assert_regex_match(asserter, actual_packet, context) 464061da546Spatrick else: 465061da546Spatrick raise Exception( 466061da546Spatrick "Don't know how to match a remote-sent packet when exact_payload isn't specified.") 467061da546Spatrick 468061da546Spatrick 469061da546Spatrickclass MultiResponseGdbRemoteEntry(GdbRemoteEntryBase): 470061da546Spatrick """Represents a query/response style packet. 471061da546Spatrick 472061da546Spatrick Assumes the first item is sent to the gdb remote. 473061da546Spatrick An end sequence regex indicates the end of the query/response 474061da546Spatrick packet sequence. All responses up through (but not including) the 475061da546Spatrick end response are stored in a context variable. 476061da546Spatrick 477061da546Spatrick Settings accepted from params: 478061da546Spatrick 479061da546Spatrick next_query or query: required. The typical query packet without the $ prefix or #xx suffix. 480061da546Spatrick If there is a special first packet to start the iteration query, see the 481061da546Spatrick first_query key. 482061da546Spatrick 483061da546Spatrick first_query: optional. If the first query requires a special query command, specify 484061da546Spatrick it with this key. Do not specify the $ prefix or #xx suffix. 485061da546Spatrick 486061da546Spatrick append_iteration_suffix: defaults to False. Specify True if the 0-based iteration 487061da546Spatrick index should be appended as a suffix to the command. e.g. qRegisterInfo with 488061da546Spatrick this key set true will generate query packets of qRegisterInfo0, qRegisterInfo1, 489061da546Spatrick etc. 490061da546Spatrick 491061da546Spatrick end_regex: required. Specifies a compiled regex object that will match the full text 492061da546Spatrick of any response that signals an end to the iteration. It must include the 493061da546Spatrick initial $ and ending #xx and must match the whole packet. 494061da546Spatrick 495061da546Spatrick save_key: required. Specifies the key within the context where an array will be stored. 496061da546Spatrick Each packet received from the gdb remote that does not match the end_regex will get 497061da546Spatrick appended to the array stored within the context at that key. 498061da546Spatrick 499061da546Spatrick runaway_response_count: optional. Defaults to 10000. If this many responses are retrieved, 500061da546Spatrick assume there is something wrong with either the response collection or the ending 501061da546Spatrick detection regex and throw an exception. 502061da546Spatrick """ 503061da546Spatrick 504061da546Spatrick def __init__(self, params): 505061da546Spatrick self._next_query = params.get("next_query", params.get("query")) 506061da546Spatrick if not self._next_query: 507061da546Spatrick raise "either next_query or query key must be specified for MultiResponseGdbRemoteEntry" 508061da546Spatrick 509061da546Spatrick self._first_query = params.get("first_query", self._next_query) 510061da546Spatrick self._append_iteration_suffix = params.get( 511061da546Spatrick "append_iteration_suffix", False) 512061da546Spatrick self._iteration = 0 513061da546Spatrick self._end_regex = params["end_regex"] 514061da546Spatrick self._save_key = params["save_key"] 515061da546Spatrick self._runaway_response_count = params.get( 516061da546Spatrick "runaway_response_count", 10000) 517061da546Spatrick self._is_send_to_remote = True 518061da546Spatrick self._end_matched = False 519061da546Spatrick 520061da546Spatrick def is_send_to_remote(self): 521061da546Spatrick return self._is_send_to_remote 522061da546Spatrick 523061da546Spatrick def get_send_packet(self): 524061da546Spatrick if not self.is_send_to_remote(): 525061da546Spatrick raise Exception( 526061da546Spatrick "get_send_packet() called on MultiResponseGdbRemoteEntry that is not in the send state") 527061da546Spatrick if self._end_matched: 528061da546Spatrick raise Exception( 529061da546Spatrick "get_send_packet() called on MultiResponseGdbRemoteEntry but end of query/response sequence has already been seen.") 530061da546Spatrick 531061da546Spatrick # Choose the first or next query for the base payload. 532061da546Spatrick if self._iteration == 0 and self._first_query: 533061da546Spatrick payload = self._first_query 534061da546Spatrick else: 535061da546Spatrick payload = self._next_query 536061da546Spatrick 537061da546Spatrick # Append the suffix as needed. 538061da546Spatrick if self._append_iteration_suffix: 539061da546Spatrick payload += "%x" % self._iteration 540061da546Spatrick 541061da546Spatrick # Keep track of the iteration. 542061da546Spatrick self._iteration += 1 543061da546Spatrick 544061da546Spatrick # Now that we've given the query packet, flip the mode to 545061da546Spatrick # receive/match. 546061da546Spatrick self._is_send_to_remote = False 547061da546Spatrick 548061da546Spatrick # Return the result, converted to packet form. 549061da546Spatrick return gdbremote_packet_encode_string(payload) 550061da546Spatrick 551061da546Spatrick def is_consumed(self): 552061da546Spatrick return self._end_matched 553061da546Spatrick 554061da546Spatrick def assert_match(self, asserter, actual_packet, context=None): 555061da546Spatrick # This only makes sense for matching lines coming from the remote debug 556061da546Spatrick # monitor. 557061da546Spatrick if self.is_send_to_remote(): 558061da546Spatrick raise Exception( 559061da546Spatrick "assert_match() called on MultiResponseGdbRemoteEntry but state is set to send a query packet.") 560061da546Spatrick 561061da546Spatrick if self._end_matched: 562061da546Spatrick raise Exception( 563061da546Spatrick "assert_match() called on MultiResponseGdbRemoteEntry but end of query/response sequence has already been seen.") 564061da546Spatrick 565061da546Spatrick # Set up a context as needed. 566061da546Spatrick if not context: 567061da546Spatrick context = {} 568061da546Spatrick 569061da546Spatrick # Check if the packet matches the end condition. 570061da546Spatrick match = self._end_regex.match(actual_packet) 571061da546Spatrick if match: 572061da546Spatrick # We're done iterating. 573061da546Spatrick self._end_matched = True 574061da546Spatrick return context 575061da546Spatrick 576061da546Spatrick # Not done iterating - save the packet. 577061da546Spatrick context[self._save_key] = context.get(self._save_key, []) 578061da546Spatrick context[self._save_key].append(actual_packet) 579061da546Spatrick 580061da546Spatrick # Check for a runaway response cycle. 581061da546Spatrick if len(context[self._save_key]) >= self._runaway_response_count: 582061da546Spatrick raise Exception( 583061da546Spatrick "runaway query/response cycle detected: %d responses captured so far. Last response: %s" % 584061da546Spatrick (len( 585061da546Spatrick context[ 586061da546Spatrick self._save_key]), context[ 587061da546Spatrick self._save_key][ 588061da546Spatrick -1])) 589061da546Spatrick 590061da546Spatrick # Flip the mode to send for generating the query. 591061da546Spatrick self._is_send_to_remote = True 592061da546Spatrick return context 593061da546Spatrick 594061da546Spatrick 595061da546Spatrickclass MatchRemoteOutputEntry(GdbRemoteEntryBase): 596061da546Spatrick """Waits for output from the debug monitor to match a regex or time out. 597061da546Spatrick 598061da546Spatrick This entry type tries to match each time new gdb remote output is accumulated 599061da546Spatrick using a provided regex. If the output does not match the regex within the 600061da546Spatrick given timeframe, the command fails the playback session. If the regex does 601061da546Spatrick match, any capture fields are recorded in the context. 602061da546Spatrick 603061da546Spatrick Settings accepted from params: 604061da546Spatrick 605061da546Spatrick regex: required. Specifies a compiled regex object that must either succeed 606061da546Spatrick with re.match or re.search (see regex_mode below) within the given timeout 607061da546Spatrick (see timeout_seconds below) or cause the playback to fail. 608061da546Spatrick 609061da546Spatrick regex_mode: optional. Available values: "match" or "search". If "match", the entire 610061da546Spatrick stub output as collected so far must match the regex. If search, then the regex 611061da546Spatrick must match starting somewhere within the output text accumulated thus far. 612061da546Spatrick Default: "match" (i.e. the regex must match the entirety of the accumulated output 613061da546Spatrick buffer, so unexpected text will generally fail the match). 614061da546Spatrick 615061da546Spatrick capture: optional. If specified, is a dictionary of regex match group indices (should start 616061da546Spatrick with 1) to variable names that will store the capture group indicated by the 617061da546Spatrick index. For example, {1:"thread_id"} will store capture group 1's content in the 618061da546Spatrick context dictionary where "thread_id" is the key and the match group value is 619be691f3bSpatrick the value. This arg only makes sense when regex is specified. 620061da546Spatrick """ 621061da546Spatrick 622061da546Spatrick def __init__(self, regex=None, regex_mode="match", capture=None): 623061da546Spatrick self._regex = regex 624061da546Spatrick self._regex_mode = regex_mode 625061da546Spatrick self._capture = capture 626061da546Spatrick self._matched = False 627061da546Spatrick 628061da546Spatrick if not self._regex: 629061da546Spatrick raise Exception("regex cannot be None") 630061da546Spatrick 631061da546Spatrick if not self._regex_mode in ["match", "search"]: 632061da546Spatrick raise Exception( 633061da546Spatrick "unsupported regex mode \"{}\": must be \"match\" or \"search\"".format( 634061da546Spatrick self._regex_mode)) 635061da546Spatrick 636061da546Spatrick def is_output_matcher(self): 637061da546Spatrick return True 638061da546Spatrick 639061da546Spatrick def is_send_to_remote(self): 640061da546Spatrick # This is always a "wait for remote" command. 641061da546Spatrick return False 642061da546Spatrick 643061da546Spatrick def is_consumed(self): 644061da546Spatrick return self._matched 645061da546Spatrick 646061da546Spatrick def assert_match(self, asserter, accumulated_output, context): 647061da546Spatrick # Validate args. 648061da546Spatrick if not accumulated_output: 649061da546Spatrick raise Exception("accumulated_output cannot be none") 650061da546Spatrick if not context: 651061da546Spatrick raise Exception("context cannot be none") 652061da546Spatrick 653061da546Spatrick # Validate that we haven't already matched. 654061da546Spatrick if self._matched: 655061da546Spatrick raise Exception( 656061da546Spatrick "invalid state - already matched, attempting to match again") 657061da546Spatrick 658061da546Spatrick # If we don't have any content yet, we don't match. 659061da546Spatrick if len(accumulated_output) < 1: 660061da546Spatrick return context 661061da546Spatrick 662061da546Spatrick # Check if we match 663061da546Spatrick if self._regex_mode == "match": 664061da546Spatrick match = self._regex.match(accumulated_output) 665061da546Spatrick elif self._regex_mode == "search": 666061da546Spatrick match = self._regex.search(accumulated_output) 667061da546Spatrick else: 668061da546Spatrick raise Exception( 669061da546Spatrick "Unexpected regex mode: {}".format( 670061da546Spatrick self._regex_mode)) 671061da546Spatrick 672061da546Spatrick # If we don't match, wait to try again after next $O content, or time 673061da546Spatrick # out. 674061da546Spatrick if not match: 675061da546Spatrick # print("re pattern \"{}\" did not match against \"{}\"".format(self._regex.pattern, accumulated_output)) 676061da546Spatrick return context 677061da546Spatrick 678061da546Spatrick # We do match. 679061da546Spatrick self._matched = True 680061da546Spatrick # print("re pattern \"{}\" matched against \"{}\"".format(self._regex.pattern, accumulated_output)) 681061da546Spatrick 682061da546Spatrick # Collect up any captures into the context. 683061da546Spatrick if self._capture: 684061da546Spatrick # Handle captures. 685061da546Spatrick for group_index, var_name in list(self._capture.items()): 686061da546Spatrick capture_text = match.group(group_index) 687061da546Spatrick if not capture_text: 688061da546Spatrick raise Exception( 689061da546Spatrick "No content for group index {}".format(group_index)) 690061da546Spatrick context[var_name] = capture_text 691061da546Spatrick 692061da546Spatrick return context 693061da546Spatrick 694061da546Spatrick 695061da546Spatrickclass GdbRemoteTestSequence(object): 696061da546Spatrick 697061da546Spatrick _LOG_LINE_REGEX = re.compile(r'^.*(read|send)\s+packet:\s+(.+)$') 698061da546Spatrick 699061da546Spatrick def __init__(self, logger): 700061da546Spatrick self.entries = [] 701061da546Spatrick self.logger = logger 702061da546Spatrick 703be691f3bSpatrick def __len__(self): 704be691f3bSpatrick return len(self.entries) 705be691f3bSpatrick 706061da546Spatrick def add_log_lines(self, log_lines, remote_input_is_read): 707061da546Spatrick for line in log_lines: 708061da546Spatrick if isinstance(line, str): 709061da546Spatrick # Handle log line import 710061da546Spatrick # if self.logger: 711061da546Spatrick # self.logger.debug("processing log line: {}".format(line)) 712061da546Spatrick match = self._LOG_LINE_REGEX.match(line) 713061da546Spatrick if match: 714061da546Spatrick playback_packet = match.group(2) 715061da546Spatrick direction = match.group(1) 716061da546Spatrick if _is_packet_lldb_gdbserver_input( 717061da546Spatrick direction, remote_input_is_read): 718061da546Spatrick # Handle as something to send to the remote debug monitor. 719061da546Spatrick # if self.logger: 720061da546Spatrick # self.logger.info("processed packet to send to remote: {}".format(playback_packet)) 721061da546Spatrick self.entries.append( 722061da546Spatrick GdbRemoteEntry( 723061da546Spatrick is_send_to_remote=True, 724061da546Spatrick exact_payload=playback_packet)) 725061da546Spatrick else: 726061da546Spatrick # Log line represents content to be expected from the remote debug monitor. 727061da546Spatrick # if self.logger: 728061da546Spatrick # self.logger.info("receiving packet from llgs, should match: {}".format(playback_packet)) 729061da546Spatrick self.entries.append( 730061da546Spatrick GdbRemoteEntry( 731061da546Spatrick is_send_to_remote=False, 732061da546Spatrick exact_payload=playback_packet)) 733061da546Spatrick else: 734061da546Spatrick raise Exception( 735061da546Spatrick "failed to interpret log line: {}".format(line)) 736061da546Spatrick elif isinstance(line, dict): 737061da546Spatrick entry_type = line.get("type", "regex_capture") 738061da546Spatrick if entry_type == "regex_capture": 739061da546Spatrick # Handle more explicit control over details via dictionary. 740061da546Spatrick direction = line.get("direction", None) 741061da546Spatrick regex = line.get("regex", None) 742061da546Spatrick capture = line.get("capture", None) 743061da546Spatrick 744061da546Spatrick # Compile the regex. 745061da546Spatrick if regex and (isinstance(regex, str)): 746*f6aab3d8Srobert regex = re.compile(regex, re.DOTALL) 747061da546Spatrick 748061da546Spatrick if _is_packet_lldb_gdbserver_input( 749061da546Spatrick direction, remote_input_is_read): 750061da546Spatrick # Handle as something to send to the remote debug monitor. 751061da546Spatrick # if self.logger: 752061da546Spatrick # self.logger.info("processed dict sequence to send to remote") 753061da546Spatrick self.entries.append( 754061da546Spatrick GdbRemoteEntry( 755061da546Spatrick is_send_to_remote=True, 756061da546Spatrick regex=regex, 757be691f3bSpatrick capture=capture)) 758061da546Spatrick else: 759061da546Spatrick # Log line represents content to be expected from the remote debug monitor. 760061da546Spatrick # if self.logger: 761061da546Spatrick # self.logger.info("processed dict sequence to match receiving from remote") 762061da546Spatrick self.entries.append( 763061da546Spatrick GdbRemoteEntry( 764061da546Spatrick is_send_to_remote=False, 765061da546Spatrick regex=regex, 766be691f3bSpatrick capture=capture)) 767061da546Spatrick elif entry_type == "multi_response": 768061da546Spatrick self.entries.append(MultiResponseGdbRemoteEntry(line)) 769061da546Spatrick elif entry_type == "output_match": 770061da546Spatrick 771061da546Spatrick regex = line.get("regex", None) 772061da546Spatrick # Compile the regex. 773061da546Spatrick if regex and (isinstance(regex, str)): 774061da546Spatrick regex = re.compile(regex, re.DOTALL) 775061da546Spatrick 776061da546Spatrick regex_mode = line.get("regex_mode", "match") 777061da546Spatrick capture = line.get("capture", None) 778061da546Spatrick self.entries.append( 779061da546Spatrick MatchRemoteOutputEntry( 780061da546Spatrick regex=regex, 781061da546Spatrick regex_mode=regex_mode, 782061da546Spatrick capture=capture)) 783061da546Spatrick else: 784061da546Spatrick raise Exception("unknown entry type \"%s\"" % entry_type) 785061da546Spatrick 786061da546Spatrick 787061da546Spatrickdef process_is_running(pid, unknown_value=True): 788061da546Spatrick """If possible, validate that the given pid represents a running process on the local system. 789061da546Spatrick 790061da546Spatrick Args: 791061da546Spatrick 792061da546Spatrick pid: an OS-specific representation of a process id. Should be an integral value. 793061da546Spatrick 794061da546Spatrick unknown_value: value used when we cannot determine how to check running local 795061da546Spatrick processes on the OS. 796061da546Spatrick 797061da546Spatrick Returns: 798061da546Spatrick 799061da546Spatrick If we can figure out how to check running process ids on the given OS: 800061da546Spatrick return True if the process is running, or False otherwise. 801061da546Spatrick 802061da546Spatrick If we don't know how to check running process ids on the given OS: 803061da546Spatrick return the value provided by the unknown_value arg. 804061da546Spatrick """ 805*f6aab3d8Srobert if not isinstance(pid, int): 806061da546Spatrick raise Exception( 807061da546Spatrick "pid must be an integral type (actual type: %s)" % str( 808061da546Spatrick type(pid))) 809061da546Spatrick 810061da546Spatrick process_ids = [] 811061da546Spatrick 812061da546Spatrick if lldb.remote_platform: 813061da546Spatrick # Don't know how to get list of running process IDs on a remote 814061da546Spatrick # platform 815061da546Spatrick return unknown_value 816061da546Spatrick elif platform.system() in ['Darwin', 'Linux', 'FreeBSD', 'NetBSD']: 817061da546Spatrick # Build the list of running process ids 818061da546Spatrick output = subprocess.check_output( 819061da546Spatrick "ps ax | awk '{ print $1; }'", shell=True).decode("utf-8") 820061da546Spatrick text_process_ids = output.split('\n')[1:] 821061da546Spatrick # Convert text pids to ints 822061da546Spatrick process_ids = [int(text_pid) 823061da546Spatrick for text_pid in text_process_ids if text_pid != ''] 824061da546Spatrick elif platform.system() == 'Windows': 825061da546Spatrick output = subprocess.check_output( 826061da546Spatrick "for /f \"tokens=2 delims=,\" %F in ('tasklist /nh /fi \"PID ne 0\" /fo csv') do @echo %~F", shell=True).decode("utf-8") 827061da546Spatrick text_process_ids = output.split('\n')[1:] 828061da546Spatrick process_ids = [int(text_pid) 829061da546Spatrick for text_pid in text_process_ids if text_pid != ''] 830061da546Spatrick # elif {your_platform_here}: 831061da546Spatrick # fill in process_ids as a list of int type process IDs running on 832061da546Spatrick # the local system. 833061da546Spatrick else: 834061da546Spatrick # Don't know how to get list of running process IDs on this 835061da546Spatrick # OS, so return the "don't know" value. 836061da546Spatrick return unknown_value 837061da546Spatrick 838061da546Spatrick # Check if the pid is in the process_ids 839061da546Spatrick return pid in process_ids 840061da546Spatrick 841*f6aab3d8Srobert 842be691f3bSpatrickdef _handle_output_packet_string(packet_contents): 843*f6aab3d8Srobert # Warning: in non-stop mode, we currently handle only the first output 844*f6aab3d8Srobert # packet since we'd need to inject vStdio packets 845*f6aab3d8Srobert if not packet_contents.startswith((b"$O", b"%Stdio:O")): 846be691f3bSpatrick return None 847*f6aab3d8Srobert elif packet_contents == b"$OK": 848be691f3bSpatrick return None 849061da546Spatrick else: 850*f6aab3d8Srobert return binascii.unhexlify(packet_contents.partition(b"O")[2]) 851*f6aab3d8Srobert 852be691f3bSpatrick 853be691f3bSpatrickclass Server(object): 854be691f3bSpatrick 855*f6aab3d8Srobert _GDB_REMOTE_PACKET_REGEX = re.compile(br'^([\$%][^\#]*)#[0-9a-fA-F]{2}') 856be691f3bSpatrick 857be691f3bSpatrick class ChecksumMismatch(Exception): 858be691f3bSpatrick pass 859be691f3bSpatrick 860be691f3bSpatrick def __init__(self, sock, proc = None): 861be691f3bSpatrick self._accumulated_output = b"" 862be691f3bSpatrick self._receive_buffer = b"" 863be691f3bSpatrick self._normal_queue = [] 864be691f3bSpatrick self._output_queue = [] 865be691f3bSpatrick self._sock = sock 866be691f3bSpatrick self._proc = proc 867be691f3bSpatrick 868be691f3bSpatrick def send_raw(self, frame): 869be691f3bSpatrick self._sock.sendall(frame) 870be691f3bSpatrick 871be691f3bSpatrick def send_ack(self): 872be691f3bSpatrick self.send_raw(b"+") 873be691f3bSpatrick 874be691f3bSpatrick def send_packet(self, packet): 875be691f3bSpatrick self.send_raw(b'$%s#%02x'%(packet, self._checksum(packet))) 876be691f3bSpatrick 877be691f3bSpatrick @staticmethod 878be691f3bSpatrick def _checksum(packet): 879be691f3bSpatrick checksum = 0 880*f6aab3d8Srobert for c in iter(packet): 881be691f3bSpatrick checksum += c 882be691f3bSpatrick return checksum % 256 883be691f3bSpatrick 884be691f3bSpatrick def _read(self, q): 885be691f3bSpatrick while not q: 886be691f3bSpatrick new_bytes = self._sock.recv(4096) 887be691f3bSpatrick self._process_new_bytes(new_bytes) 888be691f3bSpatrick return q.pop(0) 889be691f3bSpatrick 890be691f3bSpatrick def _process_new_bytes(self, new_bytes): 891be691f3bSpatrick # Add new bytes to our accumulated unprocessed packet bytes. 892be691f3bSpatrick self._receive_buffer += new_bytes 893be691f3bSpatrick 894be691f3bSpatrick # Parse fully-formed packets into individual packets. 895be691f3bSpatrick has_more = len(self._receive_buffer) > 0 896be691f3bSpatrick while has_more: 897be691f3bSpatrick if len(self._receive_buffer) <= 0: 898be691f3bSpatrick has_more = False 899be691f3bSpatrick # handle '+' ack 900be691f3bSpatrick elif self._receive_buffer[0:1] == b"+": 901be691f3bSpatrick self._normal_queue += [b"+"] 902be691f3bSpatrick self._receive_buffer = self._receive_buffer[1:] 903be691f3bSpatrick else: 904be691f3bSpatrick packet_match = self._GDB_REMOTE_PACKET_REGEX.match( 905be691f3bSpatrick self._receive_buffer) 906be691f3bSpatrick if packet_match: 907be691f3bSpatrick # Our receive buffer matches a packet at the 908be691f3bSpatrick # start of the receive buffer. 909be691f3bSpatrick new_output_content = _handle_output_packet_string( 910be691f3bSpatrick packet_match.group(1)) 911be691f3bSpatrick if new_output_content: 912be691f3bSpatrick # This was an $O packet with new content. 913be691f3bSpatrick self._accumulated_output += new_output_content 914be691f3bSpatrick self._output_queue += [self._accumulated_output] 915be691f3bSpatrick else: 916be691f3bSpatrick # Any packet other than $O. 917be691f3bSpatrick self._normal_queue += [packet_match.group(0)] 918be691f3bSpatrick 919be691f3bSpatrick # Remove the parsed packet from the receive 920be691f3bSpatrick # buffer. 921be691f3bSpatrick self._receive_buffer = self._receive_buffer[ 922be691f3bSpatrick len(packet_match.group(0)):] 923be691f3bSpatrick else: 924be691f3bSpatrick # We don't have enough in the receive bufferto make a full 925be691f3bSpatrick # packet. Stop trying until we read more. 926be691f3bSpatrick has_more = False 927be691f3bSpatrick 928be691f3bSpatrick def get_raw_output_packet(self): 929be691f3bSpatrick return self._read(self._output_queue) 930be691f3bSpatrick 931be691f3bSpatrick def get_raw_normal_packet(self): 932be691f3bSpatrick return self._read(self._normal_queue) 933be691f3bSpatrick 934be691f3bSpatrick @staticmethod 935be691f3bSpatrick def _get_payload(frame): 936be691f3bSpatrick payload = frame[1:-3] 937be691f3bSpatrick checksum = int(frame[-2:], 16) 938be691f3bSpatrick if checksum != Server._checksum(payload): 939be691f3bSpatrick raise ChecksumMismatch 940be691f3bSpatrick return payload 941be691f3bSpatrick 942be691f3bSpatrick def get_normal_packet(self): 943be691f3bSpatrick frame = self.get_raw_normal_packet() 944be691f3bSpatrick if frame == b"+": return frame 945be691f3bSpatrick return self._get_payload(frame) 946be691f3bSpatrick 947be691f3bSpatrick def get_accumulated_output(self): 948be691f3bSpatrick return self._accumulated_output 949be691f3bSpatrick 950be691f3bSpatrick def consume_accumulated_output(self): 951be691f3bSpatrick output = self._accumulated_output 952be691f3bSpatrick self._accumulated_output = b"" 953be691f3bSpatrick return output 954be691f3bSpatrick 955be691f3bSpatrick def __str__(self): 956be691f3bSpatrick return dedent("""\ 957be691f3bSpatrick server '{}' on '{}' 958be691f3bSpatrick _receive_buffer: {} 959be691f3bSpatrick _normal_queue: {} 960be691f3bSpatrick _output_queue: {} 961be691f3bSpatrick _accumulated_output: {} 962be691f3bSpatrick """).format(self._proc, self._sock, self._receive_buffer, 963be691f3bSpatrick self._normal_queue, self._output_queue, 964be691f3bSpatrick self._accumulated_output) 965