xref: /llvm-project/lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py (revision b7b9ccf44988edf49886743ae5c3cf4184db211f)
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        self._validate_checksums = True
867
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 = validate
879
880    @staticmethod
881    def _checksum(packet):
882        checksum = 0
883        for c in iter(packet):
884            checksum += c
885        return checksum % 256
886
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_bytes
896
897        # Parse fully-formed packets into individual packets.
898        has_more = len(self._receive_buffer) > 0
899        while has_more:
900            if len(self._receive_buffer) <= 0:
901                has_more = False
902            # handle '+' ack
903            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 the
910                    # 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_content
917                        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 receive
923                    # 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 full
929                    # packet. Stop trying until we read more.
930                    has_more = False
931
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 ChecksumMismatch
944        return payload
945
946    def get_normal_packet(self):
947        frame = self.get_raw_normal_packet()
948        if frame == b"+":
949            return frame
950        return self._get_payload(frame)
951
952    def get_accumulated_output(self):
953        return self._accumulated_output
954
955    def consume_accumulated_output(self):
956        output = self._accumulated_output
957        self._accumulated_output = b""
958        return output
959
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 ctypes
983    import ctypes.wintypes
984    from ctypes.wintypes import BOOL, DWORD, HANDLE, LPCWSTR, LPDWORD, LPVOID
985
986    kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
987
988    PIPE_ACCESS_INBOUND = 1
989    FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000
990    FILE_FLAG_OVERLAPPED = 0x40000000
991    PIPE_TYPE_BYTE = 0
992    PIPE_REJECT_REMOTE_CLIENTS = 8
993    INVALID_HANDLE_VALUE = -1
994    ERROR_ACCESS_DENIED = 5
995    ERROR_IO_PENDING = 997
996
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=None
1009            )
1010
1011    LPOVERLAPPED = ctypes.POINTER(OVERLAPPED)
1012
1013    CreateNamedPipe = kernel32.CreateNamedPipeW
1014    CreateNamedPipe.restype = HANDLE
1015    CreateNamedPipe.argtypes = (
1016        LPCWSTR,
1017        DWORD,
1018        DWORD,
1019        DWORD,
1020        DWORD,
1021        DWORD,
1022        DWORD,
1023        LPVOID,
1024    )
1025
1026    ConnectNamedPipe = kernel32.ConnectNamedPipe
1027    ConnectNamedPipe.restype = BOOL
1028    ConnectNamedPipe.argtypes = (HANDLE, LPOVERLAPPED)
1029
1030    CreateEvent = kernel32.CreateEventW
1031    CreateEvent.restype = HANDLE
1032    CreateEvent.argtypes = (LPVOID, BOOL, BOOL, LPCWSTR)
1033
1034    GetOverlappedResultEx = kernel32.GetOverlappedResultEx
1035    GetOverlappedResultEx.restype = BOOL
1036    GetOverlappedResultEx.argtypes = (HANDLE, LPOVERLAPPED, LPDWORD, DWORD, BOOL)
1037
1038    ReadFile = kernel32.ReadFile
1039    ReadFile.restype = BOOL
1040    ReadFile.argtypes = (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED)
1041
1042    CloseHandle = kernel32.CloseHandle
1043    CloseHandle.restype = BOOL
1044    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.name
1051                self._handle = CreateNamedPipe(
1052                    full_name,
1053                    PIPE_ACCESS_INBOUND
1054                    | FILE_FLAG_FIRST_PIPE_INSTANCE
1055                    | 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                    break
1065                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 == 0
1072            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._overlapped
1089            ):
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, True
1095            ):
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            pass
1113
1114        def read(self, size, timeout):
1115            (readers, _, _) = select.select([self._fd], [], [], timeout)
1116            if self._fd not in readers:
1117                raise TimeoutError
1118            return os.read(self._fd, size)
1119
1120        def close(self):
1121            os.close(self._fd)
1122