brintos

brintos / llvm-project-archived public Read only

0
0
Text · 41.0 KiB · fc552ef Raw
1122 lines · python
1"""Module for supporting unit testing of the lldb-server debug monitor exe.2"""3 4import binascii5import os6import os.path7import platform8import re9import socket10import subprocess11from lldbsuite.support import seven12from lldbsuite.test.lldbtest import *13from lldbsuite.test import configuration14from textwrap import dedent15import shutil16import select17import random18 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.arch45        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 a63            gdbremote packet protocol log.64 65        llgs_input_is_read: true if lldb-gdbserver input (content sent to66            lldb-gdbserver) is listed as 'read' or 'send' in the packet67            log entry.68 69    Returns:70        True if the packet should be considered input for lldb-gdbserver; False71        otherwise.72    """73    if packet_type == "read":74        # when llgs is the read side, then a read packet is meant for75        # input to llgs (when captured from the llgs/debugserver exe).76        return llgs_input_is_read77    elif packet_type == "send":78        # when llgs is the send side, then a send packet is meant to79        # be input to llgs (when captured from the lldb exe).80        return not llgs_input_is_read81    else:82        # don't understand what type of packet this is83        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 in93    # no-ack mode, the # checksum is ignored, and should not be cause94    # 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=None102):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 describes109            the messages sent to the gdb remote and the responses110            expected from it.111 112        timeout_seconds: any response taking more than this number of113           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 gdbremote119        protocol sequence.  This will contain any of the capture120        elements specified to any GdbRemoteEntry instances in121        test_sequence.122 123        The context will also contain an entry, context["O_content"]124        which contains the text from the inferior received via $O125        packets.  $O packets should not attempt to be matched126        directly since they are not entirely deterministic as to127        how many arrive and how much text is in each one.128 129        context["O_count"] will contain an integer of the number of130        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 messages143    # destined for the stub and collect/match/process responses144    # 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_packet154                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 remote158            # 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-deterministic162            # $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, server174                    )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 multiple184        # times in different states (for looping over query/response185        # 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 = None191 192    # Fill in the O_content entries.193    context["O_count"] = 1194    context["O_content"] = server.consume_accumulated_output()195 196    return context197 198 199def gdbremote_hex_encode_string(str):200    output = ""201    for c in str:202        output += "{0:02x}".format(ord(c))203    return output204 205 206def gdbremote_hex_decode_string(str):207    return str.decode("hex")208 209 210def gdbremote_packet_encode_string(str):211    checksum = 0212    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 content222    arg_index = 0223    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 += 1236 237    # return the packetized payload238    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 pairs250    values = {}251    for kv in response_packet.split(";"):252        if len(kv) < 1:253            continue254        (key, val) = kv.split(":")255        values[key] = val256 257    return values258 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            continue271        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 = 0287        i = 0288        while len(value_string) > 0:289            value += ord(value_string[0]) << i290            value_string = value_string[1:]291            i += 8292        return value293    elif endian == "big":294        value = 0295        while len(value_string) > 0:296            value = (value << 8) + ord(value_string[0])297            value_string = value_string[1:]298        return value299    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 = 0313        i = 0314        while len(value_string) > 0:315            value += int(value_string[0:2], 16) << i316            value_string = value_string[2:]317            i += 8318        return value319    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 >> 8337        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 retval341 342    elif endian == "big":343        retval = ""344        while value != 0:345            retval = "{:02x}".format(value & 0xFF) + retval346            value = value >> 8347        if byte_size:348            # Add zero-fill to the left/front (MSB side) of the value.349            retval = ("00" * (byte_size - len(retval) // 2)) + retval350        return retval351 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 False360 361 362class GdbRemoteEntry(GdbRemoteEntryBase):363    def __init__(364        self, is_send_to_remote=True, exact_payload=None, regex=None, capture=None365    ):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 be371                sent to the gdbremote debug monitor; False if this372                entry represents text to be matched against the reply373                from the gdbremote debug monitor.374 375            exact_payload: if not None, then this packet is an exact376                send (when sending to the remote) or an exact match of377                the response from the gdbremote. The checksums are378                ignored on exact match requests since negotiation of379                no-ack makes the checksum content essentially380                undefined.381 382            regex: currently only valid for receives from gdbremote.  When383                specified (and only if exact_payload is None), indicates the384                gdbremote response must match the given regex. Match groups in385                the regex can be used for the matching portion (see capture386                arg). It is perfectly valid to have just a regex arg without a387                capture arg. This arg only makes sense if exact_payload is not388                specified.389 390            capture: if specified, is a dictionary of regex match391                group indices (should start with 1) to variable names392                that will store the capture group indicated by the393                index. For example, {1:"thread_id"} will store capture394                group 1's content in the context dictionary where395                "thread_id" is the key and the match group value is396                the value. This arg only makes sense when regex is specified.397        """398        self._is_send_to_remote = is_send_to_remote399        self.exact_payload = exact_payload400        self.regex = regex401        self.capture = capture402 403    def is_send_to_remote(self):404        return self._is_send_to_remote405 406    def is_consumed(self):407        # For now, all packets are consumed after first use.408        return True409 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_payload420 421    def _assert_exact_payload_match(self, asserter, actual_packet):422        assert_packets_equal(asserter, actual_packet, self.exact_payload)423        return None424 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_packet432                )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 matched441                # above.442                context[var_name] = capture_text443 444        return context445 446    def assert_match(self, asserter, actual_packet, context=None):447        # This only makes sense for matching lines coming from the448        # 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-ack460        # mode.461        if self.exact_payload:462            self._assert_exact_payload_match(asserter, actual_packet)463            return context464        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/response477    packet sequence.  All responses up through (but not including) the478    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 the484            first_query key.485 486        first_query: optional. If the first query requires a special query command, specify487            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 iteration490            index should be appended as a suffix to the command.  e.g. qRegisterInfo with491            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 text495            of any response that signals an end to the iteration.  It must include the496            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 get500            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 ending504            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 = 0515        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 = True519        self._end_matched = False520 521    def is_send_to_remote(self):522        return self._is_send_to_remote523 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_query537        else:538            payload = self._next_query539 540        # Append the suffix as needed.541        if self._append_iteration_suffix:542            payload += "%x" % self._iteration543 544        # Keep track of the iteration.545        self._iteration += 1546 547        # Now that we've given the query packet, flip the mode to548        # receive/match.549        self._is_send_to_remote = False550 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_matched556 557    def assert_match(self, asserter, actual_packet, context=None):558        # This only makes sense for matching lines coming from the remote debug559        # 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 = True579            return context580 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 = True594        return context595 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 accumulated601    using a provided regex.  If the output does not match the regex within the602    given timeframe, the command fails the playback session.  If the regex does603    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 succeed608            with re.match or re.search (see regex_mode below) within the given timeout609            (see timeout_seconds below) or cause the playback to fail.610 611        regex_mode: optional. Available values: "match" or "search". If "match", the entire612            stub output as collected so far must match the regex.  If search, then the regex613            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 output615            buffer, so unexpected text will generally fail the match).616 617        capture: optional.  If specified, is a dictionary of regex match group indices (should start618            with 1) to variable names that will store the capture group indicated by the619            index. For example, {1:"thread_id"} will store capture group 1's content in the620            context dictionary where "thread_id" is the key and the match group value is621            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 = regex626        self._regex_mode = regex_mode627        self._capture = capture628        self._matched = False629 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_mode637                )638            )639 640    def is_output_matcher(self):641        return True642 643    def is_send_to_remote(self):644        # This is always a "wait for remote" command.645        return False646 647    def is_consumed(self):648        return self._matched649 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 context666 667        # Check if we match668        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 time676        # out.677        if not match:678            # print("re pattern \"{}\" did not match against \"{}\"".format(self._regex.pattern, accumulated_output))679            return context680 681        # We do match.682        self._matched = True683        # 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_text693 694        return context695 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 = logger703 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 import711                # 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_packet724                            )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_packet733                            )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=capture756                            )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=capture765                            )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=capture780                        )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 local794        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 remote813        # platform814        return unknown_value815    elif platform.system() in ["Darwin", "Linux", "FreeBSD", "NetBSD"]:816        # Build the list of running process ids817        output = subprocess.check_output(818            "ps ax | awk '{ print $1; }'", shell=True819        ).decode("utf-8")820        text_process_ids = output.split("\n")[1:]821        # Convert text pids to ints822        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 on832    #   the local system.833    else:834        # Don't know how to get list of running process IDs on this835        # OS, so return the "don't know" value.836        return unknown_value837 838    # Check if the pid is in the process_ids839    return pid in process_ids840 841 842def _handle_output_packet_string(packet_contents):843    # Warning: in non-stop mode, we currently handle only the first output844    # packet since we'd need to inject vStdio packets845    if not packet_contents.startswith((b"$O", b"%Stdio:O")):846        return None847    elif packet_contents == b"$OK":848        return None849    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        pass858 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 = sock865        self._proc = proc866        self._validate_checksums = True867 868    def send_raw(self, frame):869        self._sock.sendall(frame)870 871    def send_ack(self):872        self.send_raw(b"+")873 874    def send_packet(self, packet):875        self.send_raw(b"$%s#%02x" % (packet, self._checksum(packet)))876 877    def set_validate_checksums(self, validate):878        self._validate_checksums = validate879 880    @staticmethod881    def _checksum(packet):882        checksum = 0883        for c in iter(packet):884            checksum += c885        return checksum % 256886 887    def _read(self, q):888        while not q:889            new_bytes = self._sock.recv(4096)890            self._process_new_bytes(new_bytes)891        return q.pop(0)892 893    def _process_new_bytes(self, new_bytes):894        # Add new bytes to our accumulated unprocessed packet bytes.895        self._receive_buffer += new_bytes896 897        # Parse fully-formed packets into individual packets.898        has_more = len(self._receive_buffer) > 0899        while has_more:900            if len(self._receive_buffer) <= 0:901                has_more = False902            # handle '+' ack903            elif self._receive_buffer[0:1] == b"+":904                self._normal_queue += [b"+"]905                self._receive_buffer = self._receive_buffer[1:]906            else:907                packet_match = self._GDB_REMOTE_PACKET_REGEX.match(self._receive_buffer)908                if packet_match:909                    # Our receive buffer matches a packet at the910                    # start of the receive buffer.911                    new_output_content = _handle_output_packet_string(912                        packet_match.group(1)913                    )914                    if new_output_content:915                        # This was an $O packet with new content.916                        self._accumulated_output += new_output_content917                        self._output_queue += [self._accumulated_output]918                    else:919                        # Any packet other than $O.920                        self._normal_queue += [packet_match.group(0)]921 922                    # Remove the parsed packet from the receive923                    # buffer.924                    self._receive_buffer = self._receive_buffer[925                        len(packet_match.group(0)) :926                    ]927                else:928                    # We don't have enough in the receive bufferto make a full929                    # packet. Stop trying until we read more.930                    has_more = False931 932    def get_raw_output_packet(self):933        return self._read(self._output_queue)934 935    def get_raw_normal_packet(self):936        return self._read(self._normal_queue)937 938    def _get_payload(self, frame):939        payload = frame[1:-3]940        if self._validate_checksums:941            checksum = int(frame[-2:], 16)942            if checksum != Server._checksum(payload):943                raise ChecksumMismatch944        return payload945 946    def get_normal_packet(self):947        frame = self.get_raw_normal_packet()948        if frame == b"+":949            return frame950        return self._get_payload(frame)951 952    def get_accumulated_output(self):953        return self._accumulated_output954 955    def consume_accumulated_output(self):956        output = self._accumulated_output957        self._accumulated_output = b""958        return output959 960    def __str__(self):961        return dedent(962            """\963            server '{}' on '{}'964            _receive_buffer: {}965            _normal_queue: {}966            _output_queue: {}967            _accumulated_output: {}968            """969        ).format(970            self._proc,971            self._sock,972            self._receive_buffer,973            self._normal_queue,974            self._output_queue,975            self._accumulated_output,976        )977 978 979# A class representing a pipe for communicating with debug server.980# This class includes menthods to open the pipe and read the port number from it.981if lldbplatformutil.getHostPlatform() == "windows":982    import ctypes983    import ctypes.wintypes984    from ctypes.wintypes import BOOL, DWORD, HANDLE, LPCWSTR, LPDWORD, LPVOID985 986    kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)987 988    PIPE_ACCESS_INBOUND = 1989    FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000990    FILE_FLAG_OVERLAPPED = 0x40000000991    PIPE_TYPE_BYTE = 0992    PIPE_REJECT_REMOTE_CLIENTS = 8993    INVALID_HANDLE_VALUE = -1994    ERROR_ACCESS_DENIED = 5995    ERROR_IO_PENDING = 997996 997    class OVERLAPPED(ctypes.Structure):998        _fields_ = [999            ("Internal", LPVOID),1000            ("InternalHigh", LPVOID),1001            ("Offset", DWORD),1002            ("OffsetHigh", DWORD),1003            ("hEvent", HANDLE),1004        ]1005 1006        def __init__(self):1007            super(OVERLAPPED, self).__init__(1008                Internal=0, InternalHigh=0, Offset=0, OffsetHigh=0, hEvent=None1009            )1010 1011    LPOVERLAPPED = ctypes.POINTER(OVERLAPPED)1012 1013    CreateNamedPipe = kernel32.CreateNamedPipeW1014    CreateNamedPipe.restype = HANDLE1015    CreateNamedPipe.argtypes = (1016        LPCWSTR,1017        DWORD,1018        DWORD,1019        DWORD,1020        DWORD,1021        DWORD,1022        DWORD,1023        LPVOID,1024    )1025 1026    ConnectNamedPipe = kernel32.ConnectNamedPipe1027    ConnectNamedPipe.restype = BOOL1028    ConnectNamedPipe.argtypes = (HANDLE, LPOVERLAPPED)1029 1030    CreateEvent = kernel32.CreateEventW1031    CreateEvent.restype = HANDLE1032    CreateEvent.argtypes = (LPVOID, BOOL, BOOL, LPCWSTR)1033 1034    GetOverlappedResultEx = kernel32.GetOverlappedResultEx1035    GetOverlappedResultEx.restype = BOOL1036    GetOverlappedResultEx.argtypes = (HANDLE, LPOVERLAPPED, LPDWORD, DWORD, BOOL)1037 1038    ReadFile = kernel32.ReadFile1039    ReadFile.restype = BOOL1040    ReadFile.argtypes = (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED)1041 1042    CloseHandle = kernel32.CloseHandle1043    CloseHandle.restype = BOOL1044    CloseHandle.argtypes = (HANDLE,)1045 1046    class Pipe(object):1047        def __init__(self, prefix):1048            while True:1049                self.name = "lldb-" + str(random.randrange(10**10))1050                full_name = "\\\\.\\pipe\\" + self.name1051                self._handle = CreateNamedPipe(1052                    full_name,1053                    PIPE_ACCESS_INBOUND1054                    | FILE_FLAG_FIRST_PIPE_INSTANCE1055                    | FILE_FLAG_OVERLAPPED,1056                    PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,1057                    1,1058                    4096,1059                    4096,1060                    0,1061                    None,1062                )1063                if self._handle != INVALID_HANDLE_VALUE:1064                    break1065                if ctypes.get_last_error() != ERROR_ACCESS_DENIED:1066                    raise ctypes.WinError(ctypes.get_last_error())1067 1068            self._overlapped = OVERLAPPED()1069            self._overlapped.hEvent = CreateEvent(None, True, False, None)1070            result = ConnectNamedPipe(self._handle, self._overlapped)1071            assert result == 01072            if ctypes.get_last_error() != ERROR_IO_PENDING:1073                raise ctypes.WinError(ctypes.get_last_error())1074 1075        def finish_connection(self, timeout):1076            if not GetOverlappedResultEx(1077                self._handle,1078                self._overlapped,1079                ctypes.byref(DWORD(0)),1080                timeout * 1000,1081                True,1082            ):1083                raise ctypes.WinError(ctypes.get_last_error())1084 1085        def read(self, size, timeout):1086            buf = ctypes.create_string_buffer(size)1087            if not ReadFile(1088                self._handle, ctypes.byref(buf), size, None, self._overlapped1089            ):1090                if ctypes.get_last_error() != ERROR_IO_PENDING:1091                    raise ctypes.WinError(ctypes.get_last_error())1092            read = DWORD(0)1093            if not GetOverlappedResultEx(1094                self._handle, self._overlapped, ctypes.byref(read), timeout * 1000, True1095            ):1096                raise ctypes.WinError(ctypes.get_last_error())1097            return buf.raw[0 : read.value]1098 1099        def close(self):1100            CloseHandle(self._overlapped.hEvent)1101            CloseHandle(self._handle)1102 1103else:1104 1105    class Pipe(object):1106        def __init__(self, prefix):1107            self.name = os.path.join(prefix, "stub_port_number")1108            os.mkfifo(self.name)1109            self._fd = os.open(self.name, os.O_RDONLY | os.O_NONBLOCK)1110 1111        def finish_connection(self, timeout):1112            pass1113 1114        def read(self, size, timeout):1115            (readers, _, _) = select.select([self._fd], [], [], timeout)1116            if self._fd not in readers:1117                raise TimeoutError1118            return os.read(self._fd, size)1119 1120        def close(self):1121            os.close(self._fd)1122