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