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