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