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