1"""Module for supporting unit testing of the lldb-server debug monitor exe. 2""" 3 4import binascii 5import os 6import os.path 7import platform 8import re 9import socket 10import subprocess 11from lldbsuite.support import seven 12from lldbsuite.test.lldbtest import * 13from lldbsuite.test import configuration 14from textwrap import dedent 15import shutil 16 17 18def _get_support_exe(basename): 19 support_dir = lldb.SBHostOS.GetLLDBPath(lldb.ePathTypeSupportExecutableDir) 20 21 return shutil.which(basename, path=support_dir.GetDirectory()) 22 23 24def get_lldb_server_exe(): 25 """Return the lldb-server exe path. 26 27 Returns: 28 A path to the lldb-server exe if it is found to exist; otherwise, 29 returns None. 30 """ 31 32 return _get_support_exe("lldb-server") 33 34 35def get_debugserver_exe(): 36 """Return the debugserver exe path. 37 38 Returns: 39 A path to the debugserver exe if it is found to exist; otherwise, 40 returns None. 41 """ 42 if ( 43 configuration.arch 44 and configuration.arch == "x86_64" 45 and platform.machine().startswith("arm64") 46 ): 47 return "/Library/Apple/usr/libexec/oah/debugserver" 48 49 return _get_support_exe("debugserver") 50 51 52_LOG_LINE_REGEX = re.compile( 53 r"^(lldb-server|debugserver)\s+<\s*(\d+)>\s+(read|send)\s+packet:\s+(.+)$" 54) 55 56 57def _is_packet_lldb_gdbserver_input(packet_type, llgs_input_is_read): 58 """Return whether a given packet is input for lldb-gdbserver. 59 60 Args: 61 packet_type: a string indicating 'send' or 'receive', from a 62 gdbremote packet protocol log. 63 64 llgs_input_is_read: true if lldb-gdbserver input (content sent to 65 lldb-gdbserver) is listed as 'read' or 'send' in the packet 66 log entry. 67 68 Returns: 69 True if the packet should be considered input for lldb-gdbserver; False 70 otherwise. 71 """ 72 if packet_type == "read": 73 # when llgs is the read side, then a read packet is meant for 74 # input to llgs (when captured from the llgs/debugserver exe). 75 return llgs_input_is_read 76 elif packet_type == "send": 77 # when llgs is the send side, then a send packet is meant to 78 # be input to llgs (when captured from the lldb exe). 79 return not llgs_input_is_read 80 else: 81 # don't understand what type of packet this is 82 raise "Unknown packet type: {}".format(packet_type) 83 84 85_STRIP_CHECKSUM_REGEX = re.compile(r"#[0-9a-fA-F]{2}$") 86_STRIP_COMMAND_PREFIX_REGEX = re.compile(r"^\$") 87_STRIP_COMMAND_PREFIX_M_REGEX = re.compile(r"^\$m") 88 89 90def assert_packets_equal(asserter, actual_packet, expected_packet): 91 # strip off the checksum digits of the packet. When we're in 92 # no-ack mode, the # checksum is ignored, and should not be cause 93 # for a mismatched packet. 94 actual_stripped = _STRIP_CHECKSUM_REGEX.sub("", actual_packet) 95 expected_stripped = _STRIP_CHECKSUM_REGEX.sub("", expected_packet) 96 asserter.assertEqual(actual_stripped, expected_stripped) 97 98 99def expect_lldb_gdbserver_replay( 100 asserter, server, test_sequence, timeout_seconds, logger=None 101): 102 """Replay socket communication with lldb-gdbserver and verify responses. 103 104 Args: 105 asserter: the object providing assertEqual(first, second, msg=None), e.g. TestCase instance. 106 107 test_sequence: a GdbRemoteTestSequence instance that describes 108 the messages sent to the gdb remote and the responses 109 expected from it. 110 111 timeout_seconds: any response taking more than this number of 112 seconds will cause an exception to be raised. 113 114 logger: a Python logger instance. 115 116 Returns: 117 The context dictionary from running the given gdbremote 118 protocol sequence. This will contain any of the capture 119 elements specified to any GdbRemoteEntry instances in 120 test_sequence. 121 122 The context will also contain an entry, context["O_content"] 123 which contains the text from the inferior received via $O 124 packets. $O packets should not attempt to be matched 125 directly since they are not entirely deterministic as to 126 how many arrive and how much text is in each one. 127 128 context["O_count"] will contain an integer of the number of 129 O packets received. 130 """ 131 132 # Ensure we have some work to do. 133 if len(test_sequence.entries) < 1: 134 return {} 135 136 context = {"O_count": 0, "O_content": ""} 137 138 # Grab the first sequence entry. 139 sequence_entry = test_sequence.entries.pop(0) 140 141 # While we have an active sequence entry, send messages 142 # destined for the stub and collect/match/process responses 143 # expected from the stub. 144 while sequence_entry: 145 if sequence_entry.is_send_to_remote(): 146 # This is an entry to send to the remote debug monitor. 147 send_packet = sequence_entry.get_send_packet() 148 if logger: 149 if len(send_packet) == 1 and send_packet[0] == chr(3): 150 packet_desc = "^C" 151 else: 152 packet_desc = send_packet 153 logger.info("sending packet to remote: {}".format(packet_desc)) 154 server.send_raw(send_packet.encode()) 155 else: 156 # This is an entry expecting to receive content from the remote 157 # debug monitor. 158 159 # We'll pull from (and wait on) the queue appropriate for the type of matcher. 160 # We keep separate queues for process output (coming from non-deterministic 161 # $O packet division) and for all other packets. 162 try: 163 if sequence_entry.is_output_matcher(): 164 # Grab next entry from the output queue. 165 content = server.get_raw_output_packet() 166 else: 167 content = server.get_raw_normal_packet() 168 content = seven.bitcast_to_string(content) 169 except socket.timeout: 170 asserter.fail( 171 "timed out while waiting for '{}':\n{}".format( 172 sequence_entry, server 173 ) 174 ) 175 176 # Give the sequence entry the opportunity to match the content. 177 # Output matchers might match or pass after more output accumulates. 178 # Other packet types generally must match. 179 asserter.assertIsNotNone(content) 180 context = sequence_entry.assert_match(asserter, content, context=context) 181 182 # Move on to next sequence entry as needed. Some sequence entries support executing multiple 183 # times in different states (for looping over query/response 184 # packets). 185 if sequence_entry.is_consumed(): 186 if len(test_sequence.entries) > 0: 187 sequence_entry = test_sequence.entries.pop(0) 188 else: 189 sequence_entry = None 190 191 # Fill in the O_content entries. 192 context["O_count"] = 1 193 context["O_content"] = server.consume_accumulated_output() 194 195 return context 196 197 198def gdbremote_hex_encode_string(str): 199 output = "" 200 for c in str: 201 output += "{0:02x}".format(ord(c)) 202 return output 203 204 205def gdbremote_hex_decode_string(str): 206 return str.decode("hex") 207 208 209def gdbremote_packet_encode_string(str): 210 checksum = 0 211 for c in str: 212 checksum += ord(c) 213 return "$" + str + "#{0:02x}".format(checksum % 256) 214 215 216def build_gdbremote_A_packet(args_list): 217 """Given a list of args, create a properly-formed $A packet containing each arg.""" 218 payload = "A" 219 220 # build the arg content 221 arg_index = 0 222 for arg in args_list: 223 # Comma-separate the args. 224 if arg_index > 0: 225 payload += "," 226 227 # Hex-encode the arg. 228 hex_arg = gdbremote_hex_encode_string(arg) 229 230 # Build the A entry. 231 payload += "{},{},{}".format(len(hex_arg), arg_index, hex_arg) 232 233 # Next arg index, please. 234 arg_index += 1 235 236 # return the packetized payload 237 return gdbremote_packet_encode_string(payload) 238 239 240def parse_reg_info_response(response_packet): 241 if not response_packet: 242 raise Exception("response_packet cannot be None") 243 244 # Strip off prefix $ and suffix #xx if present. 245 response_packet = _STRIP_COMMAND_PREFIX_REGEX.sub("", response_packet) 246 response_packet = _STRIP_CHECKSUM_REGEX.sub("", response_packet) 247 248 # Build keyval pairs 249 values = {} 250 for kv in response_packet.split(";"): 251 if len(kv) < 1: 252 continue 253 (key, val) = kv.split(":") 254 values[key] = val 255 256 return values 257 258 259def parse_threadinfo_response(response_packet): 260 if not response_packet: 261 raise Exception("response_packet cannot be None") 262 263 # Strip off prefix $ and suffix #xx if present. 264 response_packet = _STRIP_COMMAND_PREFIX_M_REGEX.sub("", response_packet) 265 response_packet = _STRIP_CHECKSUM_REGEX.sub("", response_packet) 266 267 for tid in response_packet.split(","): 268 if not tid: 269 continue 270 if tid.startswith("p"): 271 pid, _, tid = tid.partition(".") 272 yield (int(pid[1:], 16), int(tid, 16)) 273 else: 274 yield int(tid, 16) 275 276 277def unpack_endian_binary_string(endian, value_string): 278 """Unpack a gdb-remote binary (post-unescaped, i.e. not escaped) response to an unsigned int given endianness of the inferior.""" 279 if not endian: 280 raise Exception("endian cannot be None") 281 if not value_string or len(value_string) < 1: 282 raise Exception("value_string cannot be None or empty") 283 284 if endian == "little": 285 value = 0 286 i = 0 287 while len(value_string) > 0: 288 value += ord(value_string[0]) << i 289 value_string = value_string[1:] 290 i += 8 291 return value 292 elif endian == "big": 293 value = 0 294 while len(value_string) > 0: 295 value = (value << 8) + ord(value_string[0]) 296 value_string = value_string[1:] 297 return value 298 else: 299 # pdp is valid but need to add parse code once needed. 300 raise Exception("unsupported endian:{}".format(endian)) 301 302 303def unpack_register_hex_unsigned(endian, value_string): 304 """Unpack a gdb-remote $p-style response to an unsigned int given endianness of inferior.""" 305 if not endian: 306 raise Exception("endian cannot be None") 307 if not value_string or len(value_string) < 1: 308 raise Exception("value_string cannot be None or empty") 309 310 if endian == "little": 311 value = 0 312 i = 0 313 while len(value_string) > 0: 314 value += int(value_string[0:2], 16) << i 315 value_string = value_string[2:] 316 i += 8 317 return value 318 elif endian == "big": 319 return int(value_string, 16) 320 else: 321 # pdp is valid but need to add parse code once needed. 322 raise Exception("unsupported endian:{}".format(endian)) 323 324 325def pack_register_hex(endian, value, byte_size=None): 326 """Unpack a gdb-remote $p-style response to an unsigned int given endianness of inferior.""" 327 if not endian: 328 raise Exception("endian cannot be None") 329 330 if endian == "little": 331 # Create the litt-endian return value. 332 retval = "" 333 while value != 0: 334 retval = retval + "{:02x}".format(value & 0xFF) 335 value = value >> 8 336 if byte_size: 337 # Add zero-fill to the right/end (MSB side) of the value. 338 retval += "00" * (byte_size - len(retval) // 2) 339 return retval 340 341 elif endian == "big": 342 retval = "" 343 while value != 0: 344 retval = "{:02x}".format(value & 0xFF) + retval 345 value = value >> 8 346 if byte_size: 347 # Add zero-fill to the left/front (MSB side) of the value. 348 retval = ("00" * (byte_size - len(retval) // 2)) + retval 349 return retval 350 351 else: 352 # pdp is valid but need to add parse code once needed. 353 raise Exception("unsupported endian:{}".format(endian)) 354 355 356class GdbRemoteEntryBase(object): 357 def is_output_matcher(self): 358 return False 359 360 361class GdbRemoteEntry(GdbRemoteEntryBase): 362 def __init__( 363 self, is_send_to_remote=True, exact_payload=None, regex=None, capture=None 364 ): 365 """Create an entry representing one piece of the I/O to/from a gdb remote debug monitor. 366 367 Args: 368 369 is_send_to_remote: True if this entry is a message to be 370 sent to the gdbremote debug monitor; False if this 371 entry represents text to be matched against the reply 372 from the gdbremote debug monitor. 373 374 exact_payload: if not None, then this packet is an exact 375 send (when sending to the remote) or an exact match of 376 the response from the gdbremote. The checksums are 377 ignored on exact match requests since negotiation of 378 no-ack makes the checksum content essentially 379 undefined. 380 381 regex: currently only valid for receives from gdbremote. When 382 specified (and only if exact_payload is None), indicates the 383 gdbremote response must match the given regex. Match groups in 384 the regex can be used for the matching portion (see capture 385 arg). It is perfectly valid to have just a regex arg without a 386 capture arg. This arg only makes sense if exact_payload is not 387 specified. 388 389 capture: if specified, is a dictionary of regex match 390 group indices (should start with 1) to variable names 391 that will store the capture group indicated by the 392 index. For example, {1:"thread_id"} will store capture 393 group 1's content in the context dictionary where 394 "thread_id" is the key and the match group value is 395 the value. This arg only makes sense when regex is specified. 396 """ 397 self._is_send_to_remote = is_send_to_remote 398 self.exact_payload = exact_payload 399 self.regex = regex 400 self.capture = capture 401 402 def is_send_to_remote(self): 403 return self._is_send_to_remote 404 405 def is_consumed(self): 406 # For now, all packets are consumed after first use. 407 return True 408 409 def get_send_packet(self): 410 if not self.is_send_to_remote(): 411 raise Exception( 412 "get_send_packet() called on GdbRemoteEntry that is not a send-to-remote packet" 413 ) 414 if not self.exact_payload: 415 raise Exception( 416 "get_send_packet() called on GdbRemoteEntry but it doesn't have an exact payload" 417 ) 418 return self.exact_payload 419 420 def _assert_exact_payload_match(self, asserter, actual_packet): 421 assert_packets_equal(asserter, actual_packet, self.exact_payload) 422 return None 423 424 def _assert_regex_match(self, asserter, actual_packet, context): 425 # Ensure the actual packet matches from the start of the actual packet. 426 match = self.regex.match(actual_packet) 427 if not match: 428 asserter.fail( 429 "regex '{}' failed to match against content '{}'".format( 430 self.regex.pattern, actual_packet 431 ) 432 ) 433 434 if self.capture: 435 # Handle captures. 436 for group_index, var_name in list(self.capture.items()): 437 capture_text = match.group(group_index) 438 # It is okay for capture text to be None - which it will be if it is a group that can match nothing. 439 # The user must be okay with it since the regex itself matched 440 # above. 441 context[var_name] = capture_text 442 443 return context 444 445 def assert_match(self, asserter, actual_packet, context=None): 446 # This only makes sense for matching lines coming from the 447 # remote debug monitor. 448 if self.is_send_to_remote(): 449 raise Exception( 450 "Attempted to match a packet being sent to the remote debug monitor, doesn't make sense." 451 ) 452 453 # Create a new context if needed. 454 if not context: 455 context = {} 456 457 # If this is an exact payload, ensure they match exactly, 458 # ignoring the packet checksum which is optional for no-ack 459 # mode. 460 if self.exact_payload: 461 self._assert_exact_payload_match(asserter, actual_packet) 462 return context 463 elif self.regex: 464 return self._assert_regex_match(asserter, actual_packet, context) 465 else: 466 raise Exception( 467 "Don't know how to match a remote-sent packet when exact_payload isn't specified." 468 ) 469 470 471class MultiResponseGdbRemoteEntry(GdbRemoteEntryBase): 472 """Represents a query/response style packet. 473 474 Assumes the first item is sent to the gdb remote. 475 An end sequence regex indicates the end of the query/response 476 packet sequence. All responses up through (but not including) the 477 end response are stored in a context variable. 478 479 Settings accepted from params: 480 481 next_query or query: required. The typical query packet without the $ prefix or #xx suffix. 482 If there is a special first packet to start the iteration query, see the 483 first_query key. 484 485 first_query: optional. If the first query requires a special query command, specify 486 it with this key. Do not specify the $ prefix or #xx suffix. 487 488 append_iteration_suffix: defaults to False. Specify True if the 0-based iteration 489 index should be appended as a suffix to the command. e.g. qRegisterInfo with 490 this key set true will generate query packets of qRegisterInfo0, qRegisterInfo1, 491 etc. 492 493 end_regex: required. Specifies a compiled regex object that will match the full text 494 of any response that signals an end to the iteration. It must include the 495 initial $ and ending #xx and must match the whole packet. 496 497 save_key: required. Specifies the key within the context where an array will be stored. 498 Each packet received from the gdb remote that does not match the end_regex will get 499 appended to the array stored within the context at that key. 500 501 runaway_response_count: optional. Defaults to 10000. If this many responses are retrieved, 502 assume there is something wrong with either the response collection or the ending 503 detection regex and throw an exception. 504 """ 505 506 def __init__(self, params): 507 self._next_query = params.get("next_query", params.get("query")) 508 if not self._next_query: 509 raise "either next_query or query key must be specified for MultiResponseGdbRemoteEntry" 510 511 self._first_query = params.get("first_query", self._next_query) 512 self._append_iteration_suffix = params.get("append_iteration_suffix", False) 513 self._iteration = 0 514 self._end_regex = params["end_regex"] 515 self._save_key = params["save_key"] 516 self._runaway_response_count = params.get("runaway_response_count", 10000) 517 self._is_send_to_remote = True 518 self._end_matched = False 519 520 def is_send_to_remote(self): 521 return self._is_send_to_remote 522 523 def get_send_packet(self): 524 if not self.is_send_to_remote(): 525 raise Exception( 526 "get_send_packet() called on MultiResponseGdbRemoteEntry that is not in the send state" 527 ) 528 if self._end_matched: 529 raise Exception( 530 "get_send_packet() called on MultiResponseGdbRemoteEntry but end of query/response sequence has already been seen." 531 ) 532 533 # Choose the first or next query for the base payload. 534 if self._iteration == 0 and self._first_query: 535 payload = self._first_query 536 else: 537 payload = self._next_query 538 539 # Append the suffix as needed. 540 if self._append_iteration_suffix: 541 payload += "%x" % self._iteration 542 543 # Keep track of the iteration. 544 self._iteration += 1 545 546 # Now that we've given the query packet, flip the mode to 547 # receive/match. 548 self._is_send_to_remote = False 549 550 # Return the result, converted to packet form. 551 return gdbremote_packet_encode_string(payload) 552 553 def is_consumed(self): 554 return self._end_matched 555 556 def assert_match(self, asserter, actual_packet, context=None): 557 # This only makes sense for matching lines coming from the remote debug 558 # monitor. 559 if self.is_send_to_remote(): 560 raise Exception( 561 "assert_match() called on MultiResponseGdbRemoteEntry but state is set to send a query packet." 562 ) 563 564 if self._end_matched: 565 raise Exception( 566 "assert_match() called on MultiResponseGdbRemoteEntry but end of query/response sequence has already been seen." 567 ) 568 569 # Set up a context as needed. 570 if not context: 571 context = {} 572 573 # Check if the packet matches the end condition. 574 match = self._end_regex.match(actual_packet) 575 if match: 576 # We're done iterating. 577 self._end_matched = True 578 return context 579 580 # Not done iterating - save the packet. 581 context[self._save_key] = context.get(self._save_key, []) 582 context[self._save_key].append(actual_packet) 583 584 # Check for a runaway response cycle. 585 if len(context[self._save_key]) >= self._runaway_response_count: 586 raise Exception( 587 "runaway query/response cycle detected: %d responses captured so far. Last response: %s" 588 % (len(context[self._save_key]), context[self._save_key][-1]) 589 ) 590 591 # Flip the mode to send for generating the query. 592 self._is_send_to_remote = True 593 return context 594 595 596class MatchRemoteOutputEntry(GdbRemoteEntryBase): 597 """Waits for output from the debug monitor to match a regex or time out. 598 599 This entry type tries to match each time new gdb remote output is accumulated 600 using a provided regex. If the output does not match the regex within the 601 given timeframe, the command fails the playback session. If the regex does 602 match, any capture fields are recorded in the context. 603 604 Settings accepted from params: 605 606 regex: required. Specifies a compiled regex object that must either succeed 607 with re.match or re.search (see regex_mode below) within the given timeout 608 (see timeout_seconds below) or cause the playback to fail. 609 610 regex_mode: optional. Available values: "match" or "search". If "match", the entire 611 stub output as collected so far must match the regex. If search, then the regex 612 must match starting somewhere within the output text accumulated thus far. 613 Default: "match" (i.e. the regex must match the entirety of the accumulated output 614 buffer, so unexpected text will generally fail the match). 615 616 capture: optional. If specified, is a dictionary of regex match group indices (should start 617 with 1) to variable names that will store the capture group indicated by the 618 index. For example, {1:"thread_id"} will store capture group 1's content in the 619 context dictionary where "thread_id" is the key and the match group value is 620 the value. This arg only makes sense when regex is specified. 621 """ 622 623 def __init__(self, regex=None, regex_mode="match", capture=None): 624 self._regex = regex 625 self._regex_mode = regex_mode 626 self._capture = capture 627 self._matched = False 628 629 if not self._regex: 630 raise Exception("regex cannot be None") 631 632 if not self._regex_mode in ["match", "search"]: 633 raise Exception( 634 'unsupported regex mode "{}": must be "match" or "search"'.format( 635 self._regex_mode 636 ) 637 ) 638 639 def is_output_matcher(self): 640 return True 641 642 def is_send_to_remote(self): 643 # This is always a "wait for remote" command. 644 return False 645 646 def is_consumed(self): 647 return self._matched 648 649 def assert_match(self, asserter, accumulated_output, context): 650 # Validate args. 651 if not accumulated_output: 652 raise Exception("accumulated_output cannot be none") 653 if not context: 654 raise Exception("context cannot be none") 655 656 # Validate that we haven't already matched. 657 if self._matched: 658 raise Exception( 659 "invalid state - already matched, attempting to match again" 660 ) 661 662 # If we don't have any content yet, we don't match. 663 if len(accumulated_output) < 1: 664 return context 665 666 # Check if we match 667 if self._regex_mode == "match": 668 match = self._regex.match(accumulated_output) 669 elif self._regex_mode == "search": 670 match = self._regex.search(accumulated_output) 671 else: 672 raise Exception("Unexpected regex mode: {}".format(self._regex_mode)) 673 674 # If we don't match, wait to try again after next $O content, or time 675 # out. 676 if not match: 677 # print("re pattern \"{}\" did not match against \"{}\"".format(self._regex.pattern, accumulated_output)) 678 return context 679 680 # We do match. 681 self._matched = True 682 # print("re pattern \"{}\" matched against \"{}\"".format(self._regex.pattern, accumulated_output)) 683 684 # Collect up any captures into the context. 685 if self._capture: 686 # Handle captures. 687 for group_index, var_name in list(self._capture.items()): 688 capture_text = match.group(group_index) 689 if not capture_text: 690 raise Exception("No content for group index {}".format(group_index)) 691 context[var_name] = capture_text 692 693 return context 694 695 696class GdbRemoteTestSequence(object): 697 _LOG_LINE_REGEX = re.compile(r"^.*(read|send)\s+packet:\s+(.+)$") 698 699 def __init__(self, logger): 700 self.entries = [] 701 self.logger = logger 702 703 def __len__(self): 704 return len(self.entries) 705 706 def add_log_lines(self, log_lines, remote_input_is_read): 707 for line in log_lines: 708 if isinstance(line, str): 709 # Handle log line import 710 # if self.logger: 711 # self.logger.debug("processing log line: {}".format(line)) 712 match = self._LOG_LINE_REGEX.match(line) 713 if match: 714 playback_packet = match.group(2) 715 direction = match.group(1) 716 if _is_packet_lldb_gdbserver_input(direction, remote_input_is_read): 717 # Handle as something to send to the remote debug monitor. 718 # if self.logger: 719 # self.logger.info("processed packet to send to remote: {}".format(playback_packet)) 720 self.entries.append( 721 GdbRemoteEntry( 722 is_send_to_remote=True, exact_payload=playback_packet 723 ) 724 ) 725 else: 726 # Log line represents content to be expected from the remote debug monitor. 727 # if self.logger: 728 # self.logger.info("receiving packet from llgs, should match: {}".format(playback_packet)) 729 self.entries.append( 730 GdbRemoteEntry( 731 is_send_to_remote=False, exact_payload=playback_packet 732 ) 733 ) 734 else: 735 raise Exception("failed to interpret log line: {}".format(line)) 736 elif isinstance(line, dict): 737 entry_type = line.get("type", "regex_capture") 738 if entry_type == "regex_capture": 739 # Handle more explicit control over details via dictionary. 740 direction = line.get("direction", None) 741 regex = line.get("regex", None) 742 capture = line.get("capture", None) 743 744 # Compile the regex. 745 if regex and (isinstance(regex, str)): 746 regex = re.compile(regex, re.DOTALL) 747 748 if _is_packet_lldb_gdbserver_input(direction, remote_input_is_read): 749 # Handle as something to send to the remote debug monitor. 750 # if self.logger: 751 # self.logger.info("processed dict sequence to send to remote") 752 self.entries.append( 753 GdbRemoteEntry( 754 is_send_to_remote=True, regex=regex, capture=capture 755 ) 756 ) 757 else: 758 # Log line represents content to be expected from the remote debug monitor. 759 # if self.logger: 760 # self.logger.info("processed dict sequence to match receiving from remote") 761 self.entries.append( 762 GdbRemoteEntry( 763 is_send_to_remote=False, regex=regex, capture=capture 764 ) 765 ) 766 elif entry_type == "multi_response": 767 self.entries.append(MultiResponseGdbRemoteEntry(line)) 768 elif entry_type == "output_match": 769 regex = line.get("regex", None) 770 # Compile the regex. 771 if regex and (isinstance(regex, str)): 772 regex = re.compile(regex, re.DOTALL) 773 774 regex_mode = line.get("regex_mode", "match") 775 capture = line.get("capture", None) 776 self.entries.append( 777 MatchRemoteOutputEntry( 778 regex=regex, regex_mode=regex_mode, capture=capture 779 ) 780 ) 781 else: 782 raise Exception('unknown entry type "%s"' % entry_type) 783 784 785def process_is_running(pid, unknown_value=True): 786 """If possible, validate that the given pid represents a running process on the local system. 787 788 Args: 789 790 pid: an OS-specific representation of a process id. Should be an integral value. 791 792 unknown_value: value used when we cannot determine how to check running local 793 processes on the OS. 794 795 Returns: 796 797 If we can figure out how to check running process ids on the given OS: 798 return True if the process is running, or False otherwise. 799 800 If we don't know how to check running process ids on the given OS: 801 return the value provided by the unknown_value arg. 802 """ 803 if not isinstance(pid, int): 804 raise Exception( 805 "pid must be an integral type (actual type: %s)" % str(type(pid)) 806 ) 807 808 process_ids = [] 809 810 if lldb.remote_platform: 811 # Don't know how to get list of running process IDs on a remote 812 # platform 813 return unknown_value 814 elif platform.system() in ["Darwin", "Linux", "FreeBSD", "NetBSD"]: 815 # Build the list of running process ids 816 output = subprocess.check_output( 817 "ps ax | awk '{ print $1; }'", shell=True 818 ).decode("utf-8") 819 text_process_ids = output.split("\n")[1:] 820 # Convert text pids to ints 821 process_ids = [int(text_pid) for text_pid in text_process_ids if text_pid != ""] 822 elif platform.system() == "Windows": 823 output = subprocess.check_output( 824 'for /f "tokens=2 delims=," %F in (\'tasklist /nh /fi "PID ne 0" /fo csv\') do @echo %~F', 825 shell=True, 826 ).decode("utf-8") 827 text_process_ids = output.split("\n")[1:] 828 process_ids = [int(text_pid) for text_pid in text_process_ids if text_pid != ""] 829 # elif {your_platform_here}: 830 # fill in process_ids as a list of int type process IDs running on 831 # the local system. 832 else: 833 # Don't know how to get list of running process IDs on this 834 # OS, so return the "don't know" value. 835 return unknown_value 836 837 # Check if the pid is in the process_ids 838 return pid in process_ids 839 840 841def _handle_output_packet_string(packet_contents): 842 # Warning: in non-stop mode, we currently handle only the first output 843 # packet since we'd need to inject vStdio packets 844 if not packet_contents.startswith((b"$O", b"%Stdio:O")): 845 return None 846 elif packet_contents == b"$OK": 847 return None 848 else: 849 return binascii.unhexlify(packet_contents.partition(b"O")[2]) 850 851 852class Server(object): 853 _GDB_REMOTE_PACKET_REGEX = re.compile(rb"^([\$%][^\#]*)#[0-9a-fA-F]{2}") 854 855 class ChecksumMismatch(Exception): 856 pass 857 858 def __init__(self, sock, proc=None): 859 self._accumulated_output = b"" 860 self._receive_buffer = b"" 861 self._normal_queue = [] 862 self._output_queue = [] 863 self._sock = sock 864 self._proc = proc 865 866 def send_raw(self, frame): 867 self._sock.sendall(frame) 868 869 def send_ack(self): 870 self.send_raw(b"+") 871 872 def send_packet(self, packet): 873 self.send_raw(b"$%s#%02x" % (packet, self._checksum(packet))) 874 875 @staticmethod 876 def _checksum(packet): 877 checksum = 0 878 for c in iter(packet): 879 checksum += c 880 return checksum % 256 881 882 def _read(self, q): 883 while not q: 884 new_bytes = self._sock.recv(4096) 885 self._process_new_bytes(new_bytes) 886 return q.pop(0) 887 888 def _process_new_bytes(self, new_bytes): 889 # Add new bytes to our accumulated unprocessed packet bytes. 890 self._receive_buffer += new_bytes 891 892 # Parse fully-formed packets into individual packets. 893 has_more = len(self._receive_buffer) > 0 894 while has_more: 895 if len(self._receive_buffer) <= 0: 896 has_more = False 897 # handle '+' ack 898 elif self._receive_buffer[0:1] == b"+": 899 self._normal_queue += [b"+"] 900 self._receive_buffer = self._receive_buffer[1:] 901 else: 902 packet_match = self._GDB_REMOTE_PACKET_REGEX.match(self._receive_buffer) 903 if packet_match: 904 # Our receive buffer matches a packet at the 905 # start of the receive buffer. 906 new_output_content = _handle_output_packet_string( 907 packet_match.group(1) 908 ) 909 if new_output_content: 910 # This was an $O packet with new content. 911 self._accumulated_output += new_output_content 912 self._output_queue += [self._accumulated_output] 913 else: 914 # Any packet other than $O. 915 self._normal_queue += [packet_match.group(0)] 916 917 # Remove the parsed packet from the receive 918 # buffer. 919 self._receive_buffer = self._receive_buffer[ 920 len(packet_match.group(0)) : 921 ] 922 else: 923 # We don't have enough in the receive bufferto make a full 924 # packet. Stop trying until we read more. 925 has_more = False 926 927 def get_raw_output_packet(self): 928 return self._read(self._output_queue) 929 930 def get_raw_normal_packet(self): 931 return self._read(self._normal_queue) 932 933 @staticmethod 934 def _get_payload(frame): 935 payload = frame[1:-3] 936 checksum = int(frame[-2:], 16) 937 if checksum != Server._checksum(payload): 938 raise ChecksumMismatch 939 return payload 940 941 def get_normal_packet(self): 942 frame = self.get_raw_normal_packet() 943 if frame == b"+": 944 return frame 945 return self._get_payload(frame) 946 947 def get_accumulated_output(self): 948 return self._accumulated_output 949 950 def consume_accumulated_output(self): 951 output = self._accumulated_output 952 self._accumulated_output = b"" 953 return output 954 955 def __str__(self): 956 return dedent( 957 """\ 958 server '{}' on '{}' 959 _receive_buffer: {} 960 _normal_queue: {} 961 _output_queue: {} 962 _accumulated_output: {} 963 """ 964 ).format( 965 self._proc, 966 self._sock, 967 self._receive_buffer, 968 self._normal_queue, 969 self._output_queue, 970 self._accumulated_output, 971 ) 972