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