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