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