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