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