xref: /llvm-project/lldb/packages/Python/lldbsuite/test/lldbtest.py (revision d5e1de6da96c1ab3b8cae68447e8ed3696a7006e)
1"""
2LLDB module which provides the abstract base class of lldb test case.
3
4The concrete subclass can override lldbtest.TestBase in order to inherit the
5common behavior for unitest.TestCase.setUp/tearDown implemented in this file.
6
7./dotest.py provides a test driver which sets up the environment to run the
8entire of part of the test suite .  Example:
9
10# Exercises the test suite in the types directory....
11/Volumes/data/lldb/svn/ToT/test $ ./dotest.py -A x86_64 types
12...
13
14Session logs for test failures/errors/unexpected successes will go into directory '2012-05-16-13_35_42'
15Command invoked: python ./dotest.py -A x86_64 types
16compilers=['clang']
17
18Configuration: arch=x86_64 compiler=clang
19----------------------------------------------------------------------
20Collected 72 tests
21
22........................................................................
23----------------------------------------------------------------------
24Ran 72 tests in 135.468s
25
26OK
27$
28"""
29
30# System modules
31import abc
32from functools import wraps
33import gc
34import io
35import json
36import os.path
37import re
38import shutil
39import signal
40from subprocess import *
41import sys
42import time
43import traceback
44
45# Third-party modules
46import unittest
47
48# LLDB modules
49import lldb
50from . import configuration
51from . import decorators
52from . import lldbplatformutil
53from . import lldbtest_config
54from . import lldbutil
55from . import test_categories
56from lldbsuite.support import encoded_file
57from lldbsuite.support import funcutils
58from lldbsuite.support import seven
59from lldbsuite.test_event import build_exception
60
61# See also dotest.parseOptionsAndInitTestdirs(), where the environment variables
62# LLDB_COMMAND_TRACE is set from '-t' option.
63
64# By default, traceAlways is False.
65if "LLDB_COMMAND_TRACE" in os.environ and os.environ["LLDB_COMMAND_TRACE"] == "YES":
66    traceAlways = True
67else:
68    traceAlways = False
69
70# By default, doCleanup is True.
71if "LLDB_DO_CLEANUP" in os.environ and os.environ["LLDB_DO_CLEANUP"] == "NO":
72    doCleanup = False
73else:
74    doCleanup = True
75
76
77#
78# Some commonly used assert messages.
79#
80
81COMMAND_FAILED_AS_EXPECTED = "Command has failed as expected"
82
83CURRENT_EXECUTABLE_SET = "Current executable set successfully"
84
85PROCESS_IS_VALID = "Process is valid"
86
87PROCESS_KILLED = "Process is killed successfully"
88
89PROCESS_EXITED = "Process exited successfully"
90
91PROCESS_STOPPED = "Process status should be stopped"
92
93RUN_SUCCEEDED = "Process is launched successfully"
94
95RUN_COMPLETED = "Process exited successfully"
96
97BACKTRACE_DISPLAYED_CORRECTLY = "Backtrace displayed correctly"
98
99BREAKPOINT_CREATED = "Breakpoint created successfully"
100
101BREAKPOINT_STATE_CORRECT = "Breakpoint state is correct"
102
103BREAKPOINT_PENDING_CREATED = "Pending breakpoint created successfully"
104
105BREAKPOINT_HIT_ONCE = "Breakpoint resolved with hit count = 1"
106
107BREAKPOINT_HIT_TWICE = "Breakpoint resolved with hit count = 2"
108
109BREAKPOINT_HIT_THRICE = "Breakpoint resolved with hit count = 3"
110
111MISSING_EXPECTED_REGISTERS = "At least one expected register is unavailable."
112
113OBJECT_PRINTED_CORRECTLY = "Object printed correctly"
114
115SOURCE_DISPLAYED_CORRECTLY = "Source code displayed correctly"
116
117STEP_IN_SUCCEEDED = "Thread step-in succeeded"
118
119STEP_OUT_SUCCEEDED = "Thread step-out succeeded"
120
121STOPPED_DUE_TO_EXC_BAD_ACCESS = "Process should be stopped due to bad access exception"
122
123STOPPED_DUE_TO_ASSERT = "Process should be stopped due to an assertion"
124
125STOPPED_DUE_TO_BREAKPOINT = "Process should be stopped due to breakpoint"
126
127STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS = "%s, %s" % (
128    STOPPED_DUE_TO_BREAKPOINT,
129    "instead, the actual stop reason is: '%s'",
130)
131
132STOPPED_DUE_TO_BREAKPOINT_CONDITION = "Stopped due to breakpoint condition"
133
134STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT = "Stopped due to breakpoint and ignore count"
135
136STOPPED_DUE_TO_BREAKPOINT_JITTED_CONDITION = (
137    "Stopped due to breakpoint jitted condition"
138)
139
140STOPPED_DUE_TO_SIGNAL = "Process state is stopped due to signal"
141
142STOPPED_DUE_TO_STEP_IN = "Process state is stopped due to step in"
143
144STOPPED_DUE_TO_WATCHPOINT = "Process should be stopped due to watchpoint"
145
146STOPPED_DUE_TO_HISTORY_BOUNDARY = "Process should be stopped due to history boundary"
147
148DATA_TYPES_DISPLAYED_CORRECTLY = "Data type(s) displayed correctly"
149
150VALID_BREAKPOINT = "Got a valid breakpoint"
151
152VALID_BREAKPOINT_LOCATION = "Got a valid breakpoint location"
153
154VALID_COMMAND_INTERPRETER = "Got a valid command interpreter"
155
156VALID_FILESPEC = "Got a valid filespec"
157
158VALID_MODULE = "Got a valid module"
159
160VALID_PROCESS = "Got a valid process"
161
162VALID_SYMBOL = "Got a valid symbol"
163
164VALID_TARGET = "Got a valid target"
165
166VALID_PLATFORM = "Got a valid platform"
167
168VALID_TYPE = "Got a valid type"
169
170VALID_VARIABLE = "Got a valid variable"
171
172VARIABLES_DISPLAYED_CORRECTLY = "Variable(s) displayed correctly"
173
174WATCHPOINT_CREATED = "Watchpoint created successfully"
175
176
177def CMD_MSG(command):
178    """A generic "Command '%s' did not return successfully" message generator."""
179    return f"Command '{command}' did not return successfully"
180
181
182def COMPLETION_MSG(str_before, str_after, completions):
183    """A generic assertion failed message generator for the completion mechanism."""
184    return "'%s' successfully completes to '%s', but completions were:\n%s" % (
185        str_before,
186        str_after,
187        "\n".join(completions),
188    )
189
190
191def EXP_MSG(str, actual, exe):
192    """A generic "'%s' returned unexpected result" message generator if exe.
193    Otherwise, it generates "'%s' does not match expected result" message."""
194
195    return "'%s' %s result, got '%s'" % (
196        str,
197        "returned unexpected" if exe else "does not match expected",
198        actual.strip(),
199    )
200
201
202def SETTING_MSG(setting):
203    """A generic "Value of setting '%s' is not correct" message generator."""
204    return "Value of setting '%s' is not correct" % setting
205
206
207def line_number(filename, string_to_match):
208    """Helper function to return the line number of the first matched string."""
209    with io.open(filename, mode="r", encoding="utf-8") as f:
210        for i, line in enumerate(f):
211            if line.find(string_to_match) != -1:
212                # Found our match.
213                return i + 1
214    raise Exception("Unable to find '%s' within file %s" % (string_to_match, filename))
215
216
217def get_line(filename, line_number):
218    """Return the text of the line at the 1-based line number."""
219    with io.open(filename, mode="r", encoding="utf-8") as f:
220        return f.readlines()[line_number - 1]
221
222
223def pointer_size():
224    """Return the pointer size of the host system."""
225    import ctypes
226
227    a_pointer = ctypes.c_void_p(0xFFFF)
228    return 8 * ctypes.sizeof(a_pointer)
229
230
231def is_exe(fpath):
232    """Returns true if fpath is an executable."""
233    if fpath is None:
234        return False
235    if sys.platform == "win32":
236        if not fpath.endswith(".exe"):
237            fpath += ".exe"
238    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
239
240
241def which(program):
242    """Returns the full path to a program; None otherwise."""
243    fpath, fname = os.path.split(program)
244    if fpath:
245        if is_exe(program):
246            return program
247    else:
248        for path in os.environ["PATH"].split(os.pathsep):
249            exe_file = os.path.join(path, program)
250            if is_exe(exe_file):
251                return exe_file
252    return None
253
254
255class ValueCheck:
256    def __init__(
257        self,
258        name=None,
259        value=None,
260        type=None,
261        summary=None,
262        children=None,
263        dereference=None,
264    ):
265        """
266        :param name: The name that the SBValue should have. None if the summary
267                     should not be checked.
268        :param summary: The summary that the SBValue should have. None if the
269                        summary should not be checked.
270        :param value: The value that the SBValue should have. None if the value
271                      should not be checked.
272        :param type: The type that the SBValue result should have. None if the
273                     type should not be checked.
274        :param children: A list of ValueChecks that need to match the children
275                         of this SBValue. None if children shouldn't be checked.
276                         The order of checks is the order of the checks in the
277                         list. The number of checks has to match the number of
278                         children.
279        :param dereference: A ValueCheck for the SBValue returned by the
280                            `Dereference` function.
281        """
282        self.expect_name = name
283        self.expect_value = value
284        self.expect_type = type
285        self.expect_summary = summary
286        self.children = children
287        self.dereference = dereference
288
289    def check_value(self, test_base, val, error_msg=None):
290        """
291        Checks that the given value matches the currently set properties
292        of this ValueCheck. If a match failed, the given TestBase will
293        be used to emit an error. A custom error message can be specified
294        that will be used to describe failed check for this SBValue (but
295        not errors in the child values).
296        """
297
298        this_error_msg = error_msg if error_msg else ""
299        this_error_msg += "\nChecking SBValue: " + str(val)
300
301        test_base.assertSuccess(val.GetError())
302
303        # Python 3.6 doesn't declare a `re.Pattern` type, get the dynamic type.
304        pattern_type = type(re.compile(""))
305
306        if self.expect_name:
307            test_base.assertEqual(self.expect_name, val.GetName(), this_error_msg)
308        if self.expect_value:
309            if isinstance(self.expect_value, pattern_type):
310                test_base.assertRegex(val.GetValue(), self.expect_value, this_error_msg)
311            else:
312                test_base.assertEqual(self.expect_value, val.GetValue(), this_error_msg)
313        if self.expect_type:
314            test_base.assertEqual(
315                self.expect_type, val.GetDisplayTypeName(), this_error_msg
316            )
317        if self.expect_summary:
318            if isinstance(self.expect_summary, pattern_type):
319                test_base.assertRegex(
320                    val.GetSummary(), self.expect_summary, this_error_msg
321                )
322            else:
323                test_base.assertEqual(
324                    self.expect_summary, val.GetSummary(), this_error_msg
325                )
326        if self.children is not None:
327            self.check_value_children(test_base, val, error_msg)
328
329        if self.dereference is not None:
330            self.dereference.check_value(test_base, val.Dereference(), error_msg)
331
332    def check_value_children(self, test_base, val, error_msg=None):
333        """
334        Checks that the children of a SBValue match a certain structure and
335        have certain properties.
336
337        :param test_base: The current test's TestBase object.
338        :param val: The SBValue to check.
339        """
340
341        this_error_msg = error_msg if error_msg else ""
342        this_error_msg += "\nChecking SBValue: " + str(val)
343
344        test_base.assertEqual(len(self.children), val.GetNumChildren(), this_error_msg)
345
346        for i in range(0, val.GetNumChildren()):
347            expected_child = self.children[i]
348            actual_child = val.GetChildAtIndex(i)
349            child_error = "Checking child with index " + str(i) + ":\n" + error_msg
350            expected_child.check_value(test_base, actual_child, child_error)
351
352
353class recording(io.StringIO):
354    """
355    A nice little context manager for recording the debugger interactions into
356    our session object.  If trace flag is ON, it also emits the interactions
357    into the stderr.
358    """
359
360    def __init__(self, test, trace):
361        """Create a io.StringIO instance; record the session obj and trace flag."""
362        io.StringIO.__init__(self)
363        # The test might not have undergone the 'setUp(self)' phase yet, so that
364        # the attribute 'session' might not even exist yet.
365        self.session = getattr(test, "session", None) if test else None
366        self.trace = trace
367
368    def __enter__(self):
369        """
370        Context management protocol on entry to the body of the with statement.
371        Just return the io.StringIO object.
372        """
373        return self
374
375    def __exit__(self, type, value, tb):
376        """
377        Context management protocol on exit from the body of the with statement.
378        If trace is ON, it emits the recordings into stderr.  Always add the
379        recordings to our session object.  And close the io.StringIO object, too.
380        """
381        if self.trace:
382            print(self.getvalue(), file=sys.stderr)
383        if self.session:
384            print(self.getvalue(), file=self.session)
385        self.close()
386
387
388class _BaseProcess(object, metaclass=abc.ABCMeta):
389    @abc.abstractproperty
390    def pid(self):
391        """Returns process PID if has been launched already."""
392
393    @abc.abstractmethod
394    def launch(self, executable, args, extra_env):
395        """Launches new process with given executable and args."""
396
397    @abc.abstractmethod
398    def terminate(self):
399        """Terminates previously launched process.."""
400
401
402class _LocalProcess(_BaseProcess):
403    def __init__(self, trace_on):
404        self._proc = None
405        self._trace_on = trace_on
406        self._delayafterterminate = 0.1
407
408    @property
409    def pid(self):
410        return self._proc.pid
411
412    def launch(self, executable, args, extra_env):
413        env = None
414        if extra_env:
415            env = dict(os.environ)
416            env.update([kv.split("=", 1) for kv in extra_env])
417
418        self._proc = Popen(
419            [executable] + args,
420            stdout=DEVNULL if not self._trace_on else None,
421            stdin=PIPE,
422            env=env,
423        )
424
425    def terminate(self):
426        if self._proc.poll() is None:
427            # Terminate _proc like it does the pexpect
428            signals_to_try = [
429                sig for sig in ["SIGHUP", "SIGCONT", "SIGINT"] if sig in dir(signal)
430            ]
431            for sig in signals_to_try:
432                try:
433                    self._proc.send_signal(getattr(signal, sig))
434                    time.sleep(self._delayafterterminate)
435                    if self._proc.poll() is not None:
436                        return
437                except ValueError:
438                    pass  # Windows says SIGINT is not a valid signal to send
439            self._proc.terminate()
440            time.sleep(self._delayafterterminate)
441            if self._proc.poll() is not None:
442                return
443            self._proc.kill()
444            time.sleep(self._delayafterterminate)
445
446    def poll(self):
447        return self._proc.poll()
448
449    def wait(self, timeout=None):
450        return self._proc.wait(timeout)
451
452
453class _RemoteProcess(_BaseProcess):
454    def __init__(self, install_remote):
455        self._pid = None
456        self._install_remote = install_remote
457
458    @property
459    def pid(self):
460        return self._pid
461
462    def launch(self, executable, args, extra_env):
463        if self._install_remote:
464            src_path = executable
465            dst_path = lldbutil.join_remote_paths(
466                lldb.remote_platform.GetWorkingDirectory(), os.path.basename(executable)
467            )
468
469            dst_file_spec = lldb.SBFileSpec(dst_path, False)
470            err = lldb.remote_platform.Install(
471                lldb.SBFileSpec(src_path, True), dst_file_spec
472            )
473            if err.Fail():
474                raise Exception(
475                    "remote_platform.Install('%s', '%s') failed: %s"
476                    % (src_path, dst_path, err)
477                )
478        else:
479            dst_path = executable
480            dst_file_spec = lldb.SBFileSpec(executable, False)
481
482        launch_info = lldb.SBLaunchInfo(args)
483        launch_info.SetExecutableFile(dst_file_spec, True)
484        launch_info.SetWorkingDirectory(lldb.remote_platform.GetWorkingDirectory())
485
486        # Redirect stdout and stderr to /dev/null
487        launch_info.AddSuppressFileAction(1, False, True)
488        launch_info.AddSuppressFileAction(2, False, True)
489
490        if extra_env:
491            launch_info.SetEnvironmentEntries(extra_env, True)
492
493        err = lldb.remote_platform.Launch(launch_info)
494        if err.Fail():
495            raise Exception(
496                "remote_platform.Launch('%s', '%s') failed: %s" % (dst_path, args, err)
497            )
498        self._pid = launch_info.GetProcessID()
499
500    def terminate(self):
501        lldb.remote_platform.Kill(self._pid)
502
503
504def getsource_if_available(obj):
505    """
506    Return the text of the source code for an object if available.  Otherwise,
507    a print representation is returned.
508    """
509    import inspect
510
511    try:
512        return inspect.getsource(obj)
513    except:
514        return repr(obj)
515
516
517def builder_module():
518    return lldbplatformutil.builder_module()
519
520
521class Base(unittest.TestCase):
522    """
523    Abstract base for performing lldb (see TestBase) or other generic tests (see
524    BenchBase for one example).  lldbtest.Base works with the test driver to
525    accomplish things.
526
527    """
528
529    # The concrete subclass should override this attribute.
530    mydir = None
531
532    # Keep track of the old current working directory.
533    oldcwd = None
534
535    # Maximum allowed attempts when launching the inferior process.
536    # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable.
537    maxLaunchCount = 1
538
539    # Time to wait before the next launching attempt in second(s).
540    # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable.
541    timeWaitNextLaunch = 1.0
542
543    @staticmethod
544    def compute_mydir(test_file):
545        """Subclasses should call this function to correctly calculate the
546        required "mydir" attribute as follows:
547
548         mydir = TestBase.compute_mydir(__file__)
549        """
550        # /abs/path/to/packages/group/subdir/mytest.py -> group/subdir
551        lldb_test_src = configuration.test_src_root
552        if not test_file.startswith(lldb_test_src):
553            raise Exception(
554                "Test file '%s' must reside within lldb_test_src "
555                "(which is '%s')." % (test_file, lldb_test_src)
556            )
557        return os.path.dirname(os.path.relpath(test_file, start=lldb_test_src))
558
559    def TraceOn(self):
560        """Returns True if we are in trace mode (tracing detailed test execution)."""
561        return traceAlways
562
563    def trace(self, *args, **kwargs):
564        with recording(self, self.TraceOn()) as sbuf:
565            print(*args, file=sbuf, **kwargs)
566
567    @classmethod
568    def setUpClass(cls):
569        """
570        Python unittest framework class setup fixture.
571        Do current directory manipulation.
572        """
573        # Fail fast if 'mydir' attribute is not overridden.
574        if not cls.mydir:
575            cls.mydir = Base.compute_mydir(sys.modules[cls.__module__].__file__)
576        if not cls.mydir:
577            raise Exception("Subclasses must override the 'mydir' attribute.")
578
579        # Save old working directory.
580        cls.oldcwd = os.getcwd()
581
582        full_dir = os.path.join(configuration.test_src_root, cls.mydir)
583        if traceAlways:
584            print("Change dir to:", full_dir, file=sys.stderr)
585        os.chdir(full_dir)
586
587        # Set platform context.
588        cls.platformContext = lldbplatformutil.createPlatformContext()
589
590    @classmethod
591    def tearDownClass(cls):
592        """
593        Python unittest framework class teardown fixture.
594        Do class-wide cleanup.
595        """
596
597        if doCleanup:
598            # First, let's do the platform-specific cleanup.
599            module = builder_module()
600            module.cleanup()
601
602            # Subclass might have specific cleanup function defined.
603            if getattr(cls, "classCleanup", None):
604                if traceAlways:
605                    print(
606                        "Call class-specific cleanup function for class:",
607                        cls,
608                        file=sys.stderr,
609                    )
610                try:
611                    cls.classCleanup()
612                except:
613                    exc_type, exc_value, exc_tb = sys.exc_info()
614                    traceback.print_exception(exc_type, exc_value, exc_tb)
615
616        # Restore old working directory.
617        if traceAlways:
618            print("Restore dir to:", cls.oldcwd, file=sys.stderr)
619        os.chdir(cls.oldcwd)
620
621    def enableLogChannelsForCurrentTest(self):
622        if len(lldbtest_config.channels) == 0:
623            return
624
625        # if debug channels are specified in lldbtest_config.channels,
626        # create a new set of log files for every test
627        log_basename = self.getLogBasenameForCurrentTest()
628
629        # confirm that the file is writeable
630        host_log_path = "{}-host.log".format(log_basename)
631        open(host_log_path, "w").close()
632        self.log_files.append(host_log_path)
633
634        log_enable = "log enable -Tpn -f {} ".format(host_log_path)
635        for channel_with_categories in lldbtest_config.channels:
636            channel_then_categories = channel_with_categories.split(" ", 1)
637            channel = channel_then_categories[0]
638            if len(channel_then_categories) > 1:
639                categories = channel_then_categories[1]
640            else:
641                categories = "default"
642
643            if channel == "gdb-remote" and lldb.remote_platform is None:
644                # communicate gdb-remote categories to debugserver
645                os.environ["LLDB_DEBUGSERVER_LOG_FLAGS"] = categories
646
647            self.ci.HandleCommand(log_enable + channel_with_categories, self.res)
648            if not self.res.Succeeded():
649                raise Exception(
650                    "log enable failed (check LLDB_LOG_OPTION env variable)"
651                )
652
653        # Communicate log path name to debugserver & lldb-server
654        # For remote debugging, these variables need to be set when starting the platform
655        # instance.
656        if lldb.remote_platform is None:
657            server_log_path = "{}-server.log".format(log_basename)
658            open(server_log_path, "w").close()
659            self.log_files.append(server_log_path)
660            os.environ["LLDB_DEBUGSERVER_LOG_FILE"] = server_log_path
661
662            # Communicate channels to lldb-server
663            os.environ["LLDB_SERVER_LOG_CHANNELS"] = ":".join(lldbtest_config.channels)
664
665        self.addTearDownHook(self.disableLogChannelsForCurrentTest)
666
667    def disableLogChannelsForCurrentTest(self):
668        # close all log files that we opened
669        for channel_and_categories in lldbtest_config.channels:
670            # channel format - <channel-name> [<category0> [<category1> ...]]
671            channel = channel_and_categories.split(" ", 1)[0]
672            self.ci.HandleCommand("log disable " + channel, self.res)
673            if not self.res.Succeeded():
674                raise Exception(
675                    "log disable failed (check LLDB_LOG_OPTION env variable)"
676                )
677
678        # Retrieve the server log (if any) from the remote system. It is assumed the server log
679        # is writing to the "server.log" file in the current test directory. This can be
680        # achieved by setting LLDB_DEBUGSERVER_LOG_FILE="server.log" when starting remote
681        # platform.
682        if lldb.remote_platform:
683            server_log_path = self.getLogBasenameForCurrentTest() + "-server.log"
684            if lldb.remote_platform.Get(
685                lldb.SBFileSpec("server.log"), lldb.SBFileSpec(server_log_path)
686            ).Success():
687                self.log_files.append(server_log_path)
688
689    def setPlatformWorkingDir(self):
690        if not lldb.remote_platform or not configuration.lldb_platform_working_dir:
691            return
692
693        components = self.mydir.split(os.path.sep) + [
694            str(self.test_number),
695            self.getBuildDirBasename(),
696        ]
697        remote_test_dir = configuration.lldb_platform_working_dir
698        for c in components:
699            remote_test_dir = lldbutil.join_remote_paths(remote_test_dir, c)
700            error = lldb.remote_platform.MakeDirectory(
701                remote_test_dir, 448
702            )  # 448 = 0o700
703            if error.Fail():
704                raise Exception(
705                    "making remote directory '%s': %s" % (remote_test_dir, error)
706                )
707
708        lldb.remote_platform.SetWorkingDirectory(remote_test_dir)
709
710        # This function removes all files from the current working directory while leaving
711        # the directories in place. The cleanup is required to reduce the disk space required
712        # by the test suite while leaving the directories untouched is neccessary because
713        # sub-directories might belong to an other test
714        def clean_working_directory():
715            # TODO: Make it working on Windows when we need it for remote debugging support
716            # TODO: Replace the heuristic to remove the files with a logic what collects the
717            # list of files we have to remove during test runs.
718            shell_cmd = lldb.SBPlatformShellCommand("rm %s/*" % remote_test_dir)
719            lldb.remote_platform.Run(shell_cmd)
720
721        self.addTearDownHook(clean_working_directory)
722
723    def getSourceDir(self):
724        """Return the full path to the current test."""
725        return os.path.join(configuration.test_src_root, self.mydir)
726
727    def getBuildDirBasename(self):
728        return self.__class__.__module__ + "." + self.testMethodName
729
730    def getBuildDir(self):
731        """Return the full path to the current test."""
732        return os.path.join(
733            configuration.test_build_dir, self.mydir, self.getBuildDirBasename()
734        )
735
736    def makeBuildDir(self):
737        """Create the test-specific working directory, deleting any previous
738        contents."""
739        bdir = self.getBuildDir()
740        if os.path.isdir(bdir):
741            shutil.rmtree(bdir)
742        lldbutil.mkdir_p(bdir)
743
744    def getBuildArtifact(self, name="a.out"):
745        """Return absolute path to an artifact in the test's build directory."""
746        return os.path.join(self.getBuildDir(), name)
747
748    def getSourcePath(self, name):
749        """Return absolute path to a file in the test's source directory."""
750        return os.path.join(self.getSourceDir(), name)
751
752    @classmethod
753    def setUpCommands(cls):
754        commands = [
755            # First of all, clear all settings to have clean state of global properties.
756            "settings clear -all",
757            # Disable Spotlight lookup. The testsuite creates
758            # different binaries with the same UUID, because they only
759            # differ in the debug info, which is not being hashed.
760            "settings set symbols.enable-external-lookup false",
761            # Inherit the TCC permissions from the inferior's parent.
762            "settings set target.inherit-tcc true",
763            # Based on https://discourse.llvm.org/t/running-lldb-in-a-container/76801/4
764            "settings set target.disable-aslr false",
765            # Kill rather than detach from the inferior if something goes wrong.
766            "settings set target.detach-on-error false",
767            # Disable fix-its by default so that incorrect expressions in tests don't
768            # pass just because Clang thinks it has a fix-it.
769            "settings set target.auto-apply-fixits false",
770            # Testsuite runs in parallel and the host can have also other load.
771            "settings set plugin.process.gdb-remote.packet-timeout 60",
772            'settings set symbols.clang-modules-cache-path "{}"'.format(
773                configuration.lldb_module_cache_dir
774            ),
775            "settings set use-color false",
776        ]
777
778        # Set any user-overridden settings.
779        for setting, value in configuration.settings:
780            commands.append("setting set %s %s" % (setting, value))
781
782        # Make sure that a sanitizer LLDB's environment doesn't get passed on.
783        if (
784            cls.platformContext
785            and cls.platformContext.shlib_environment_var in os.environ
786        ):
787            commands.append(
788                "settings set target.env-vars {}=".format(
789                    cls.platformContext.shlib_environment_var
790                )
791            )
792
793        # Set environment variables for the inferior.
794        if lldbtest_config.inferior_env:
795            commands.append(
796                "settings set target.env-vars {}".format(lldbtest_config.inferior_env)
797            )
798        return commands
799
800    def setUp(self):
801        """Fixture for unittest test case setup.
802
803        It works with the test driver to conditionally skip tests and does other
804        initializations."""
805        # import traceback
806        # traceback.print_stack()
807
808        if "LLDB_MAX_LAUNCH_COUNT" in os.environ:
809            self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"])
810
811        if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ:
812            self.timeWaitNextLaunch = float(os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"])
813
814        if "LIBCXX_PATH" in os.environ:
815            self.libcxxPath = os.environ["LIBCXX_PATH"]
816        else:
817            self.libcxxPath = None
818
819        if "LLDBDAP_EXEC" in os.environ:
820            self.lldbDAPExec = os.environ["LLDBDAP_EXEC"]
821        else:
822            self.lldbDAPExec = None
823
824        self.lldbOption = " ".join("-o '" + s + "'" for s in self.setUpCommands())
825
826        # If we spawn an lldb process for test (via pexpect), do not load the
827        # init file unless told otherwise.
828        if os.environ.get("NO_LLDBINIT") != "NO":
829            self.lldbOption += " --no-lldbinit"
830
831        # Assign the test method name to self.testMethodName.
832        #
833        # For an example of the use of this attribute, look at test/types dir.
834        # There are a bunch of test cases under test/types and we don't want the
835        # module cacheing subsystem to be confused with executable name "a.out"
836        # used for all the test cases.
837        self.testMethodName = self._testMethodName
838
839        # This is for the case of directly spawning 'lldb'/'gdb' and interacting
840        # with it using pexpect.
841        self.child = None
842        self.child_prompt = "(lldb) "
843        # If the child is interacting with the embedded script interpreter,
844        # there are two exits required during tear down, first to quit the
845        # embedded script interpreter and second to quit the lldb command
846        # interpreter.
847        self.child_in_script_interpreter = False
848
849        # These are for customized teardown cleanup.
850        self.dict = None
851        self.doTearDownCleanup = False
852        # And in rare cases where there are multiple teardown cleanups.
853        self.dicts = []
854        self.doTearDownCleanups = False
855
856        # List of spawned subproces.Popen objects
857        self.subprocesses = []
858
859        # List of log files produced by the current test.
860        self.log_files = []
861
862        # Create the build directory.
863        # The logs are stored in the build directory, so we have to create it
864        # before creating the first log file.
865        self.makeBuildDir()
866
867        session_file = self.getLogBasenameForCurrentTest() + ".log"
868        self.log_files.append(session_file)
869
870        # Python 3 doesn't support unbuffered I/O in text mode.  Open buffered.
871        self.session = encoded_file.open(session_file, "utf-8", mode="w")
872
873        # Optimistically set __errored__, __failed__, __expected__ to False
874        # initially.  If the test errored/failed, the session info
875        # (self.session) is then dumped into a session specific file for
876        # diagnosis.
877        self.__cleanup_errored__ = False
878        self.__errored__ = False
879        self.__failed__ = False
880        self.__expected__ = False
881        # We are also interested in unexpected success.
882        self.__unexpected__ = False
883        # And skipped tests.
884        self.__skipped__ = False
885
886        # See addTearDownHook(self, hook) which allows the client to add a hook
887        # function to be run during tearDown() time.
888        self.hooks = []
889
890        # See HideStdout(self).
891        self.sys_stdout_hidden = False
892
893        if self.platformContext:
894            # set environment variable names for finding shared libraries
895            self.dylibPath = self.platformContext.shlib_environment_var
896
897        # Create the debugger instance.
898        self.dbg = lldb.SBDebugger.Create()
899        # Copy selected platform from a global instance if it exists.
900        if lldb.selected_platform is not None:
901            self.dbg.SetSelectedPlatform(lldb.selected_platform)
902
903        if not self.dbg:
904            raise Exception("Invalid debugger instance")
905
906        # Retrieve the associated command interpreter instance.
907        self.ci = self.dbg.GetCommandInterpreter()
908        if not self.ci:
909            raise Exception("Could not get the command interpreter")
910
911        # And the result object.
912        self.res = lldb.SBCommandReturnObject()
913
914        self.setPlatformWorkingDir()
915        self.enableLogChannelsForCurrentTest()
916
917        self.lib_lldb = None
918        self.framework_dir = None
919        self.darwinWithFramework = False
920
921        if sys.platform.startswith("darwin") and configuration.lldb_framework_path:
922            framework = configuration.lldb_framework_path
923            lib = os.path.join(framework, "LLDB")
924            if os.path.exists(lib):
925                self.framework_dir = os.path.dirname(framework)
926                self.lib_lldb = lib
927                self.darwinWithFramework = self.platformIsDarwin()
928
929    def setAsync(self, value):
930        """Sets async mode to True/False and ensures it is reset after the testcase completes."""
931        old_async = self.dbg.GetAsync()
932        self.dbg.SetAsync(value)
933        self.addTearDownHook(lambda: self.dbg.SetAsync(old_async))
934
935    def cleanupSubprocesses(self):
936        # Terminate subprocesses in reverse order from how they were created.
937        for p in reversed(self.subprocesses):
938            p.terminate()
939            del p
940        del self.subprocesses[:]
941
942    def spawnSubprocess(self, executable, args=[], extra_env=None, install_remote=True):
943        """Creates a subprocess.Popen object with the specified executable and arguments,
944        saves it in self.subprocesses, and returns the object.
945        """
946        proc = (
947            _RemoteProcess(install_remote)
948            if lldb.remote_platform
949            else _LocalProcess(self.TraceOn())
950        )
951        proc.launch(executable, args, extra_env=extra_env)
952        self.subprocesses.append(proc)
953        return proc
954
955    def runCmd(self, cmd, msg=None, check=True, trace=False, inHistory=False):
956        """
957        Ask the command interpreter to handle the command and then check its
958        return status.
959        """
960        # Fail fast if 'cmd' is not meaningful.
961        if cmd is None:
962            raise Exception("Bad 'cmd' parameter encountered")
963
964        trace = True if traceAlways else trace
965
966        if cmd.startswith("target create "):
967            cmd = cmd.replace("target create ", "file ")
968
969        running = cmd.startswith("run") or cmd.startswith("process launch")
970
971        for i in range(self.maxLaunchCount if running else 1):
972            with recording(self, trace) as sbuf:
973                print("runCmd:", cmd, file=sbuf)
974                if not check:
975                    print("check of return status not required", file=sbuf)
976
977            self.ci.HandleCommand(cmd, self.res, inHistory)
978
979            with recording(self, trace) as sbuf:
980                if self.res.Succeeded():
981                    print("output:", self.res.GetOutput(), file=sbuf)
982                else:
983                    print("runCmd failed!", file=sbuf)
984                    print(self.res.GetError(), file=sbuf)
985
986            if self.res.Succeeded():
987                break
988            elif running:
989                # For process launch, wait some time before possible next try.
990                time.sleep(self.timeWaitNextLaunch)
991                with recording(self, trace) as sbuf:
992                    print("Command '" + cmd + "' failed!", file=sbuf)
993
994        if check:
995            if not msg:
996                msg = CMD_MSG(cmd)
997            output = ""
998            if self.res.GetOutput():
999                output += "\nCommand output:\n" + self.res.GetOutput()
1000            if self.res.GetError():
1001                output += "\nError output:\n" + self.res.GetError()
1002            self.assertTrue(self.res.Succeeded(), msg + output)
1003
1004    def HideStdout(self):
1005        """Hide output to stdout from the user.
1006
1007        During test execution, there might be cases where we don't want to show the
1008        standard output to the user.  For example,
1009
1010            self.runCmd(r'''sc print("\n\n\tHello!\n")''')
1011
1012        tests whether command abbreviation for 'script' works or not.  There is no
1013        need to show the 'Hello' output to the user as long as the 'script' command
1014        succeeds and we are not in TraceOn() mode (see the '-t' option).
1015
1016        In this case, the test method calls self.HideStdout(self) to redirect the
1017        sys.stdout to a null device, and restores the sys.stdout upon teardown.
1018
1019        Note that you should only call this method at most once during a test case
1020        execution.  Any subsequent call has no effect at all."""
1021        if self.sys_stdout_hidden:
1022            return
1023
1024        self.sys_stdout_hidden = True
1025        old_stdout = sys.stdout
1026        sys.stdout = open(os.devnull, "w")
1027
1028        def restore_stdout():
1029            sys.stdout = old_stdout
1030
1031        self.addTearDownHook(restore_stdout)
1032
1033    # =======================================================================
1034    # Methods for customized teardown cleanups as well as execution of hooks.
1035    # =======================================================================
1036
1037    def setTearDownCleanup(self, dictionary=None):
1038        """Register a cleanup action at tearDown() time with a dictionary"""
1039        self.dict = dictionary
1040        self.doTearDownCleanup = True
1041
1042    def addTearDownCleanup(self, dictionary):
1043        """Add a cleanup action at tearDown() time with a dictionary"""
1044        self.dicts.append(dictionary)
1045        self.doTearDownCleanups = True
1046
1047    def addTearDownHook(self, hook):
1048        """
1049        Add a function to be run during tearDown() time.
1050
1051        Hooks are executed in a first come first serve manner.
1052        """
1053        if callable(hook):
1054            with recording(self, traceAlways) as sbuf:
1055                print("Adding tearDown hook:", getsource_if_available(hook), file=sbuf)
1056            self.hooks.append(hook)
1057
1058        return self
1059
1060    def deletePexpectChild(self):
1061        # This is for the case of directly spawning 'lldb' and interacting with it
1062        # using pexpect.
1063        if self.child and self.child.isalive():
1064            import pexpect
1065
1066            with recording(self, traceAlways) as sbuf:
1067                print("tearing down the child process....", file=sbuf)
1068            try:
1069                if self.child_in_script_interpreter:
1070                    self.child.sendline("quit()")
1071                    self.child.expect_exact(self.child_prompt)
1072                self.child.sendline("settings set interpreter.prompt-on-quit false")
1073                self.child.sendline("quit")
1074                self.child.expect(pexpect.EOF)
1075            except (ValueError, pexpect.ExceptionPexpect):
1076                # child is already terminated
1077                pass
1078            except OSError as exception:
1079                import errno
1080
1081                if exception.errno != errno.EIO:
1082                    # unexpected error
1083                    raise
1084                # child is already terminated
1085            finally:
1086                # Give it one final blow to make sure the child is terminated.
1087                self.child.close()
1088
1089    def tearDown(self):
1090        """Fixture for unittest test case teardown."""
1091        self.deletePexpectChild()
1092
1093        # Check and run any hook functions.
1094        for hook in reversed(self.hooks):
1095            with recording(self, traceAlways) as sbuf:
1096                print(
1097                    "Executing tearDown hook:", getsource_if_available(hook), file=sbuf
1098                )
1099            if funcutils.requires_self(hook):
1100                hook(self)
1101            else:
1102                hook()  # try the plain call and hope it works
1103
1104        del self.hooks
1105
1106        # Perform registered teardown cleanup.
1107        if doCleanup and self.doTearDownCleanup:
1108            self.cleanup(dictionary=self.dict)
1109
1110        # In rare cases where there are multiple teardown cleanups added.
1111        if doCleanup and self.doTearDownCleanups:
1112            if self.dicts:
1113                for dict in reversed(self.dicts):
1114                    self.cleanup(dictionary=dict)
1115
1116        # Remove subprocesses created by the test.
1117        self.cleanupSubprocesses()
1118
1119        # This must be the last statement, otherwise teardown hooks or other
1120        # lines might depend on this still being active.
1121        lldb.SBDebugger.Destroy(self.dbg)
1122        del self.dbg
1123
1124        # All modules should be orphaned now so that they can be cleared from
1125        # the shared module cache.
1126        lldb.SBModule.GarbageCollectAllocatedModules()
1127
1128        # Assert that the global module cache is empty.
1129        # FIXME: This assert fails on Windows.
1130        if self.getPlatform() != "windows":
1131            self.assertEqual(lldb.SBModule.GetNumberAllocatedModules(), 0)
1132
1133    # =========================================================
1134    # Various callbacks to allow introspection of test progress
1135    # =========================================================
1136
1137    def markError(self):
1138        """Callback invoked when an error (unexpected exception) errored."""
1139        self.__errored__ = True
1140        with recording(self, False) as sbuf:
1141            # False because there's no need to write "ERROR" to the stderr twice.
1142            # Once by the Python unittest framework, and a second time by us.
1143            print("ERROR", file=sbuf)
1144
1145    def markCleanupError(self):
1146        """Callback invoked when an error occurs while a test is cleaning up."""
1147        self.__cleanup_errored__ = True
1148        with recording(self, False) as sbuf:
1149            # False because there's no need to write "CLEANUP_ERROR" to the stderr twice.
1150            # Once by the Python unittest framework, and a second time by us.
1151            print("CLEANUP_ERROR", file=sbuf)
1152
1153    def markFailure(self):
1154        """Callback invoked when a failure (test assertion failure) occurred."""
1155        self.__failed__ = True
1156        with recording(self, False) as sbuf:
1157            # False because there's no need to write "FAIL" to the stderr twice.
1158            # Once by the Python unittest framework, and a second time by us.
1159            print("FAIL", file=sbuf)
1160
1161    def markExpectedFailure(self, err):
1162        """Callback invoked when an expected failure/error occurred."""
1163        self.__expected__ = True
1164        with recording(self, False) as sbuf:
1165            # False because there's no need to write "expected failure" to the
1166            # stderr twice.
1167            # Once by the Python unittest framework, and a second time by us.
1168            print("expected failure", file=sbuf)
1169
1170    def markSkippedTest(self):
1171        """Callback invoked when a test is skipped."""
1172        self.__skipped__ = True
1173        with recording(self, False) as sbuf:
1174            # False because there's no need to write "skipped test" to the
1175            # stderr twice.
1176            # Once by the Python unittest framework, and a second time by us.
1177            print("skipped test", file=sbuf)
1178
1179    def markUnexpectedSuccess(self):
1180        """Callback invoked when an unexpected success occurred."""
1181        self.__unexpected__ = True
1182        with recording(self, False) as sbuf:
1183            # False because there's no need to write "unexpected success" to the
1184            # stderr twice.
1185            # Once by the Python unittest framework, and a second time by us.
1186            print("unexpected success", file=sbuf)
1187
1188    def getRerunArgs(self):
1189        return " -f %s.%s" % (self.__class__.__name__, self._testMethodName)
1190
1191    def getLogBasenameForCurrentTest(self, prefix="Incomplete"):
1192        """
1193        returns a partial path that can be used as the beginning of the name of multiple
1194        log files pertaining to this test
1195        """
1196        return os.path.join(self.getBuildDir(), prefix)
1197
1198    def dumpSessionInfo(self):
1199        """
1200        Dump the debugger interactions leading to a test error/failure.  This
1201        allows for more convenient postmortem analysis.
1202
1203        See also LLDBTestResult (dotest.py) which is a singlton class derived
1204        from TextTestResult and overwrites addError, addFailure, and
1205        addExpectedFailure methods to allow us to to mark the test instance as
1206        such.
1207        """
1208
1209        # We are here because self.tearDown() detected that this test instance
1210        # either errored or failed.  The lldb.test_result singleton contains
1211        # two lists (errors and failures) which get populated by the unittest
1212        # framework.  Look over there for stack trace information.
1213        #
1214        # The lists contain 2-tuples of TestCase instances and strings holding
1215        # formatted tracebacks.
1216        #
1217        # See http://docs.python.org/library/unittest.html#unittest.TestResult.
1218
1219        # output tracebacks into session
1220        pairs = []
1221        if self.__errored__:
1222            pairs = configuration.test_result.errors
1223            prefix = "Error"
1224        elif self.__cleanup_errored__:
1225            pairs = configuration.test_result.cleanup_errors
1226            prefix = "CleanupError"
1227        elif self.__failed__:
1228            pairs = configuration.test_result.failures
1229            prefix = "Failure"
1230        elif self.__expected__:
1231            pairs = configuration.test_result.expectedFailures
1232            prefix = "ExpectedFailure"
1233        elif self.__skipped__:
1234            prefix = "SkippedTest"
1235        elif self.__unexpected__:
1236            prefix = "UnexpectedSuccess"
1237        else:
1238            prefix = "Success"
1239
1240        if not self.__unexpected__ and not self.__skipped__:
1241            for test, traceback in pairs:
1242                if test is self:
1243                    print(traceback, file=self.session)
1244
1245        import datetime
1246
1247        print(
1248            "Session info generated @",
1249            datetime.datetime.now().ctime(),
1250            file=self.session,
1251        )
1252        self.session.close()
1253        del self.session
1254
1255        # process the log files
1256        if prefix != "Success" or lldbtest_config.log_success:
1257            # keep all log files, rename them to include prefix
1258            src_log_basename = self.getLogBasenameForCurrentTest()
1259            dst_log_basename = self.getLogBasenameForCurrentTest(prefix)
1260            for src in self.log_files:
1261                if os.path.isfile(src):
1262                    dst = src.replace(src_log_basename, dst_log_basename)
1263                    if os.name == "nt" and os.path.isfile(dst):
1264                        # On Windows, renaming a -> b will throw an exception if
1265                        # b exists.  On non-Windows platforms it silently
1266                        # replaces the destination.  Ultimately this means that
1267                        # atomic renames are not guaranteed to be possible on
1268                        # Windows, but we need this to work anyway, so just
1269                        # remove the destination first if it already exists.
1270                        remove_file(dst)
1271
1272                    lldbutil.mkdir_p(os.path.dirname(dst))
1273                    os.rename(src, dst)
1274        else:
1275            # success!  (and we don't want log files) delete log files
1276            for log_file in self.log_files:
1277                if os.path.isfile(log_file):
1278                    remove_file(log_file)
1279
1280    # ====================================================
1281    # Config. methods supported through a plugin interface
1282    # (enables reading of the current test configuration)
1283    # ====================================================
1284
1285    def hasXMLSupport(self):
1286        """Returns True if lldb was built with XML support. Use this check to
1287        enable parts of tests, if you want to skip a whole test use skipIfXmlSupportMissing
1288        instead."""
1289        return (
1290            lldb.SBDebugger.GetBuildConfiguration()
1291            .GetValueForKey("xml")
1292            .GetValueForKey("value")
1293            .GetBooleanValue(False)
1294        )
1295
1296    def isMIPS(self):
1297        """Returns true if the architecture is MIPS."""
1298        arch = self.getArchitecture()
1299        if re.match("mips", arch):
1300            return True
1301        return False
1302
1303    def isPPC64le(self):
1304        """Returns true if the architecture is PPC64LE."""
1305        arch = self.getArchitecture()
1306        if re.match("powerpc64le", arch):
1307            return True
1308        return False
1309
1310    def getCPUInfo(self):
1311        triple = self.dbg.GetSelectedPlatform().GetTriple()
1312
1313        # TODO other platforms, please implement this function
1314        if not re.match(".*-.*-linux", triple):
1315            return ""
1316
1317        # Need to do something different for non-Linux/Android targets
1318        cpuinfo_path = self.getBuildArtifact("cpuinfo")
1319        if configuration.lldb_platform_name:
1320            self.runCmd(
1321                'platform get-file "/proc/cpuinfo" ' + cpuinfo_path, check=False
1322            )
1323            if not self.res.Succeeded():
1324                if self.TraceOn():
1325                    print(
1326                        'Failed to get /proc/cpuinfo from remote: "{}"'.format(
1327                            self.res.GetOutput().strip()
1328                        )
1329                    )
1330                    print("All cpuinfo feature checks will fail.")
1331                return ""
1332        else:
1333            cpuinfo_path = "/proc/cpuinfo"
1334
1335        try:
1336            with open(cpuinfo_path, "r") as f:
1337                cpuinfo = f.read()
1338        except:
1339            return ""
1340
1341        return cpuinfo
1342
1343    def isAArch64(self):
1344        """Returns true if the architecture is AArch64."""
1345        arch = self.getArchitecture().lower()
1346        return arch in ["aarch64", "arm64", "arm64e"]
1347
1348    def isAArch64SVE(self):
1349        return self.isAArch64() and "sve" in self.getCPUInfo()
1350
1351    def isAArch64SME(self):
1352        return self.isAArch64() and "sme" in self.getCPUInfo()
1353
1354    def isAArch64SME2(self):
1355        # If you have sme2, you also have sme.
1356        return self.isAArch64() and "sme2" in self.getCPUInfo()
1357
1358    def isAArch64SMEFA64(self):
1359        # smefa64 allows the use of the full A64 instruction set in streaming
1360        # mode. This is required by certain test programs to setup register
1361        # state.
1362        cpuinfo = self.getCPUInfo()
1363        return self.isAArch64() and "sme" in cpuinfo and "smefa64" in cpuinfo
1364
1365    def isAArch64MTE(self):
1366        return self.isAArch64() and "mte" in self.getCPUInfo()
1367
1368    def isAArch64PAuth(self):
1369        if self.getArchitecture() == "arm64e":
1370            return True
1371        return self.isAArch64() and "paca" in self.getCPUInfo()
1372
1373    def isAArch64FPMR(self):
1374        return self.isAArch64() and "fpmr" in self.getCPUInfo()
1375
1376    def isAArch64Windows(self):
1377        """Returns true if the architecture is AArch64 and platform windows."""
1378        if self.getPlatform() == "windows":
1379            arch = self.getArchitecture().lower()
1380            return arch in ["aarch64", "arm64", "arm64e"]
1381        return False
1382
1383    def getArchitecture(self):
1384        """Returns the architecture in effect the test suite is running with."""
1385        return lldbplatformutil.getArchitecture()
1386
1387    def getLldbArchitecture(self):
1388        """Returns the architecture of the lldb binary."""
1389        return lldbplatformutil.getLLDBArchitecture()
1390
1391    def getCompiler(self):
1392        """Returns the compiler in effect the test suite is running with."""
1393        return lldbplatformutil.getCompiler()
1394
1395    def getCompilerVersion(self):
1396        """Returns a string that represents the compiler version.
1397        Supports: llvm, clang.
1398        """
1399        return lldbplatformutil.getCompilerVersion()
1400
1401    def getDwarfVersion(self):
1402        """Returns the dwarf version generated by clang or '0'."""
1403        return lldbplatformutil.getDwarfVersion()
1404
1405    def platformIsDarwin(self):
1406        """Returns true if the OS triple for the selected platform is any valid apple OS"""
1407        return lldbplatformutil.platformIsDarwin()
1408
1409    def hasDarwinFramework(self):
1410        return self.darwinWithFramework
1411
1412    def getPlatform(self):
1413        """Returns the target platform the test suite is running on."""
1414        return lldbplatformutil.getPlatform()
1415
1416    def isIntelCompiler(self):
1417        """Returns true if using an Intel (ICC) compiler, false otherwise."""
1418        return any([x in self.getCompiler() for x in ["icc", "icpc", "icl"]])
1419
1420    def expectedCompilerVersion(self, compiler_version):
1421        """Returns True iff compiler_version[1] matches the current compiler version.
1422        Use compiler_version[0] to specify the operator used to determine if a match has occurred.
1423        Any operator other than the following defaults to an equality test:
1424          '>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not'
1425
1426        If the current compiler version cannot be determined, we assume it is close to the top
1427        of trunk, so any less-than or equal-to comparisons will return False, and any
1428        greater-than or not-equal-to comparisons will return True.
1429        """
1430        return lldbplatformutil.expectedCompilerVersion(compiler_version)
1431
1432    def expectedCompiler(self, compilers):
1433        """Returns True iff any element of compilers is a sub-string of the current compiler."""
1434        return lldbplatformutil.expectedCompiler(compilers)
1435
1436    def expectedArch(self, archs):
1437        """Returns True iff any element of archs is a sub-string of the current architecture."""
1438        if archs is None:
1439            return True
1440
1441        for arch in archs:
1442            if arch in self.getArchitecture():
1443                return True
1444
1445        return False
1446
1447    def getRunOptions(self):
1448        """Command line option for -A and -C to run this test again, called from
1449        self.dumpSessionInfo()."""
1450        arch = self.getArchitecture()
1451        comp = self.getCompiler()
1452        option_str = ""
1453        if arch:
1454            option_str = "-A " + arch
1455        if comp:
1456            option_str += " -C " + comp
1457        return option_str
1458
1459    def getDebugInfo(self):
1460        method = getattr(self, self.testMethodName)
1461        return getattr(method, "debug_info", None)
1462
1463    def build(
1464        self,
1465        debug_info=None,
1466        architecture=None,
1467        compiler=None,
1468        dictionary=None,
1469        make_targets=None,
1470    ):
1471        """Platform specific way to build binaries."""
1472        if not architecture and configuration.arch:
1473            architecture = configuration.arch
1474
1475        if debug_info is None:
1476            debug_info = self.getDebugInfo()
1477
1478        dictionary = lldbplatformutil.finalize_build_dictionary(dictionary)
1479
1480        testdir = self.mydir
1481        testname = self.getBuildDirBasename()
1482
1483        module = builder_module()
1484        command = builder_module().getBuildCommand(
1485            debug_info,
1486            architecture,
1487            compiler,
1488            dictionary,
1489            testdir,
1490            testname,
1491            make_targets,
1492        )
1493        if command is None:
1494            raise Exception("Don't know how to build binary")
1495
1496        self.runBuildCommand(command)
1497
1498    def runBuildCommand(self, command):
1499        self.trace(seven.join_for_shell(command))
1500        try:
1501            output = check_output(command, stderr=STDOUT, errors="replace")
1502        except CalledProcessError as cpe:
1503            raise build_exception.BuildError(cpe)
1504        self.trace(output)
1505
1506    # ==================================================
1507    # Build methods supported through a plugin interface
1508    # ==================================================
1509
1510    def getstdlibFlag(self):
1511        """Returns the proper -stdlib flag, or empty if not required."""
1512        if (
1513            self.platformIsDarwin()
1514            or self.getPlatform() == "freebsd"
1515            or self.getPlatform() == "openbsd"
1516        ):
1517            stdlibflag = "-stdlib=libc++"
1518        else:  # this includes NetBSD
1519            stdlibflag = ""
1520        return stdlibflag
1521
1522    def getstdFlag(self):
1523        """Returns the proper stdflag."""
1524        if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion():
1525            stdflag = "-std=c++0x"
1526        else:
1527            stdflag = "-std=c++11"
1528        return stdflag
1529
1530    def buildDriver(self, sources, exe_name, defines=None):
1531        """Platform-specific way to build a program that links with LLDB (via the liblldb.so
1532        or LLDB.framework).
1533        """
1534        if defines is None:
1535            defines = []
1536
1537        stdflag = self.getstdFlag()
1538        stdlibflag = self.getstdlibFlag()
1539        defines = " ".join(["-D{}={}".format(name, value) for name, value in defines])
1540
1541        lib_dir = configuration.lldb_libs_dir
1542        if self.hasDarwinFramework():
1543            d = {
1544                "CXX_SOURCES": sources,
1545                "EXE": exe_name,
1546                "CFLAGS_EXTRAS": "%s %s %s" % (stdflag, stdlibflag, defines),
1547                "FRAMEWORK_INCLUDES": "-F%s" % self.framework_dir,
1548                "LD_EXTRAS": "%s -Wl,-rpath,%s" % (self.lib_lldb, self.framework_dir),
1549            }
1550        elif sys.platform.startswith("win"):
1551            d = {
1552                "CXX_SOURCES": sources,
1553                "EXE": exe_name,
1554                "CFLAGS_EXTRAS": "%s %s -I%s -I%s %s"
1555                % (
1556                    stdflag,
1557                    stdlibflag,
1558                    os.path.join(os.environ["LLDB_SRC"], "include"),
1559                    os.path.join(configuration.lldb_obj_root, "include"),
1560                    defines,
1561                ),
1562                "LD_EXTRAS": "-L%s -lliblldb" % lib_dir,
1563            }
1564        else:
1565            d = {
1566                "CXX_SOURCES": sources,
1567                "EXE": exe_name,
1568                "CFLAGS_EXTRAS": "%s %s -I%s -I%s %s"
1569                % (
1570                    stdflag,
1571                    stdlibflag,
1572                    os.path.join(os.environ["LLDB_SRC"], "include"),
1573                    os.path.join(configuration.lldb_obj_root, "include"),
1574                    defines,
1575                ),
1576                "LD_EXTRAS": "-L%s -llldb -Wl,-rpath,%s" % (lib_dir, lib_dir),
1577            }
1578        if self.TraceOn():
1579            print("Building LLDB Driver (%s) from sources %s" % (exe_name, sources))
1580
1581        self.build(dictionary=d)
1582
1583    def buildLibrary(self, sources, lib_name):
1584        """Platform specific way to build a default library."""
1585
1586        stdflag = self.getstdFlag()
1587
1588        lib_dir = configuration.lldb_libs_dir
1589        if self.hasDarwinFramework():
1590            d = {
1591                "DYLIB_CXX_SOURCES": sources,
1592                "DYLIB_NAME": lib_name,
1593                "CFLAGS_EXTRAS": "%s -stdlib=libc++ -I%s"
1594                % (stdflag, os.path.join(configuration.lldb_obj_root, "include")),
1595                "FRAMEWORK_INCLUDES": "-F%s" % self.framework_dir,
1596                "LD_EXTRAS": "%s -Wl,-rpath,%s -dynamiclib"
1597                % (self.lib_lldb, self.framework_dir),
1598            }
1599        elif self.getPlatform() == "windows":
1600            d = {
1601                "DYLIB_CXX_SOURCES": sources,
1602                "DYLIB_NAME": lib_name,
1603                "CFLAGS_EXTRAS": "%s -I%s -I%s"
1604                % (
1605                    stdflag,
1606                    os.path.join(os.environ["LLDB_SRC"], "include"),
1607                    os.path.join(configuration.lldb_obj_root, "include"),
1608                ),
1609                "LD_EXTRAS": "-shared -l%s\\liblldb.lib" % lib_dir,
1610            }
1611        else:
1612            d = {
1613                "DYLIB_CXX_SOURCES": sources,
1614                "DYLIB_NAME": lib_name,
1615                "CFLAGS_EXTRAS": "%s -I%s -I%s -fPIC"
1616                % (
1617                    stdflag,
1618                    os.path.join(os.environ["LLDB_SRC"], "include"),
1619                    os.path.join(configuration.lldb_obj_root, "include"),
1620                ),
1621                "LD_EXTRAS": "-shared -L%s -llldb -Wl,-rpath,%s" % (lib_dir, lib_dir),
1622            }
1623        if self.TraceOn():
1624            print("Building LLDB Library (%s) from sources %s" % (lib_name, sources))
1625
1626        self.build(dictionary=d)
1627
1628    def buildProgram(self, sources, exe_name):
1629        """Platform specific way to build an executable from C/C++ sources."""
1630        d = {"CXX_SOURCES": sources, "EXE": exe_name}
1631        self.build(dictionary=d)
1632
1633    def findBuiltClang(self):
1634        """Tries to find and use Clang from the build directory as the compiler (instead of the system compiler)."""
1635        paths_to_try = [
1636            "llvm-build/Release+Asserts/x86_64/bin/clang",
1637            "llvm-build/Debug+Asserts/x86_64/bin/clang",
1638            "llvm-build/Release/x86_64/bin/clang",
1639            "llvm-build/Debug/x86_64/bin/clang",
1640        ]
1641        lldb_root_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")
1642        for p in paths_to_try:
1643            path = os.path.join(lldb_root_path, p)
1644            if os.path.exists(path):
1645                return path
1646
1647        # Tries to find clang at the same folder as the lldb
1648        lldb_dir = os.path.dirname(lldbtest_config.lldbExec)
1649        path = shutil.which("clang", path=lldb_dir)
1650        if path is not None:
1651            return path
1652
1653        return os.environ["CC"]
1654
1655    def yaml2obj(self, yaml_path, obj_path, max_size=None):
1656        """
1657        Create an object file at the given path from a yaml file.
1658
1659        Throws subprocess.CalledProcessError if the object could not be created.
1660        """
1661        yaml2obj_bin = configuration.get_yaml2obj_path()
1662        if not yaml2obj_bin:
1663            self.assertTrue(False, "No valid yaml2obj executable specified")
1664        command = [yaml2obj_bin, "-o=%s" % obj_path, yaml_path]
1665        if max_size is not None:
1666            command += ["--max-size=%d" % max_size]
1667        self.runBuildCommand(command)
1668
1669    def cleanup(self, dictionary=None):
1670        """Platform specific way to do cleanup after build."""
1671        module = builder_module()
1672        if not module.cleanup(dictionary):
1673            raise Exception(
1674                "Don't know how to do cleanup with dictionary: " + dictionary
1675            )
1676
1677    def invoke(self, obj, name, trace=False):
1678        """Use reflection to call a method dynamically with no argument."""
1679        trace = True if traceAlways else trace
1680
1681        method = getattr(obj, name)
1682        import inspect
1683
1684        self.assertTrue(
1685            inspect.ismethod(method), name + "is a method name of object: " + str(obj)
1686        )
1687        result = method()
1688        with recording(self, trace) as sbuf:
1689            print(str(method) + ":", result, file=sbuf)
1690        return result
1691
1692    def getLibcPlusPlusLibs(self):
1693        if self.getPlatform() in ("freebsd", "linux", "netbsd", "openbsd"):
1694            return ["libc++.so.1"]
1695        else:
1696            return ["libc++.1.dylib", "libc++abi."]
1697
1698    def run_platform_command(self, cmd):
1699        platform = self.dbg.GetSelectedPlatform()
1700        shell_command = lldb.SBPlatformShellCommand(cmd)
1701        err = platform.Run(shell_command)
1702        return (err, shell_command.GetStatus(), shell_command.GetOutput())
1703
1704    def get_stats(self, options=None):
1705        """
1706        Get the output of the "statistics dump" with optional extra options
1707        and return the JSON as a python dictionary.
1708        """
1709        return_obj = lldb.SBCommandReturnObject()
1710        command = "statistics dump "
1711        if options is not None:
1712            command += options
1713        self.ci.HandleCommand(command, return_obj, False)
1714        metrics_json = return_obj.GetOutput()
1715        return json.loads(metrics_json)
1716
1717
1718# Metaclass for TestBase to change the list of test metods when a new TestCase is loaded.
1719# We change the test methods to create a new test method for each test for each debug info we are
1720# testing. The name of the new test method will be '<original-name>_<debug-info>' and with adding
1721# the new test method we remove the old method at the same time. This functionality can be
1722# supressed by at test case level setting the class attribute NO_DEBUG_INFO_TESTCASE or at test
1723# level by using the decorator @no_debug_info_test.
1724
1725
1726class LLDBTestCaseFactory(type):
1727    def __new__(cls, name, bases, attrs):
1728        original_testcase = super(LLDBTestCaseFactory, cls).__new__(
1729            cls, name, bases, attrs
1730        )
1731        if original_testcase.NO_DEBUG_INFO_TESTCASE:
1732            return original_testcase
1733
1734        # Default implementation for skip/xfail reason based on the debug category,
1735        # where "None" means to run the test as usual.
1736        def no_reason(_):
1737            return None
1738
1739        newattrs = {}
1740        for attrname, attrvalue in attrs.items():
1741            if attrname.startswith("test") and not getattr(
1742                attrvalue, "__no_debug_info_test__", False
1743            ):
1744                # If any debug info categories were explicitly tagged, assume that list to be
1745                # authoritative.  If none were specified, try with all debug
1746                # info formats.
1747                all_dbginfo_categories = set(
1748                    test_categories.debug_info_categories.keys()
1749                )
1750                categories = (
1751                    set(getattr(attrvalue, "categories", [])) & all_dbginfo_categories
1752                )
1753                if not categories:
1754                    categories = [
1755                        category
1756                        for category, can_replicate in test_categories.debug_info_categories.items()
1757                        if can_replicate
1758                    ]
1759
1760                xfail_for_debug_info_cat_fn = getattr(
1761                    attrvalue, "__xfail_for_debug_info_cat_fn__", no_reason
1762                )
1763                skip_for_debug_info_cat_fn = getattr(
1764                    attrvalue, "__skip_for_debug_info_cat_fn__", no_reason
1765                )
1766                for cat in categories:
1767
1768                    @decorators.add_test_categories([cat])
1769                    @wraps(attrvalue)
1770                    def test_method(self, attrvalue=attrvalue):
1771                        return attrvalue(self)
1772
1773                    method_name = attrname + "_" + cat
1774                    test_method.__name__ = method_name
1775                    test_method.debug_info = cat
1776
1777                    xfail_reason = xfail_for_debug_info_cat_fn(cat)
1778                    if xfail_reason:
1779                        test_method = unittest.expectedFailure(test_method)
1780
1781                    skip_reason = skip_for_debug_info_cat_fn(cat)
1782                    if skip_reason:
1783                        test_method = unittest.skip(skip_reason)(test_method)
1784
1785                    newattrs[method_name] = test_method
1786
1787            else:
1788                newattrs[attrname] = attrvalue
1789        return super(LLDBTestCaseFactory, cls).__new__(cls, name, bases, newattrs)
1790
1791
1792# Setup the metaclass for this class to change the list of the test
1793# methods when a new class is loaded
1794
1795
1796class TestBase(Base, metaclass=LLDBTestCaseFactory):
1797    """
1798    This abstract base class is meant to be subclassed.  It provides default
1799    implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(),
1800    among other things.
1801
1802    Important things for test class writers:
1803
1804        - The setUp method sets up things to facilitate subsequent interactions
1805          with the debugger as part of the test.  These include:
1806              - populate the test method name
1807              - create/get a debugger set with synchronous mode (self.dbg)
1808              - get the command interpreter from with the debugger (self.ci)
1809              - create a result object for use with the command interpreter
1810                (self.res)
1811              - plus other stuffs
1812
1813        - The tearDown method tries to perform some necessary cleanup on behalf
1814          of the test to return the debugger to a good state for the next test.
1815          These include:
1816              - execute any tearDown hooks registered by the test method with
1817                TestBase.addTearDownHook(); examples can be found in
1818                settings/TestSettings.py
1819              - kill the inferior process associated with each target, if any,
1820                and, then delete the target from the debugger's target list
1821              - perform build cleanup before running the next test method in the
1822                same test class; examples of registering for this service can be
1823                found in types/TestIntegerTypes.py with the call:
1824                    - self.setTearDownCleanup(dictionary=d)
1825
1826        - Similarly setUpClass and tearDownClass perform classwise setup and
1827          teardown fixtures.  The tearDownClass method invokes a default build
1828          cleanup for the entire test class;  also, subclasses can implement the
1829          classmethod classCleanup(cls) to perform special class cleanup action.
1830
1831        - The instance methods runCmd and expect are used heavily by existing
1832          test cases to send a command to the command interpreter and to perform
1833          string/pattern matching on the output of such command execution.  The
1834          expect method also provides a mode to peform string/pattern matching
1835          without running a command.
1836
1837        - The build method is used to build the binaries used during a
1838          particular test scenario.  A plugin should be provided for the
1839          sys.platform running the test suite.  The Mac OS X implementation is
1840          located in builders/darwin.py.
1841    """
1842
1843    # Subclasses can set this to true (if they don't depend on debug info) to avoid running the
1844    # test multiple times with various debug info types.
1845    NO_DEBUG_INFO_TESTCASE = False
1846
1847    def generateSource(self, source):
1848        template = source + ".template"
1849        temp = os.path.join(self.getSourceDir(), template)
1850        with open(temp, "r") as f:
1851            content = f.read()
1852
1853        public_api_dir = os.path.join(os.environ["LLDB_SRC"], "include", "lldb", "API")
1854
1855        # Look under the include/lldb/API directory and add #include statements
1856        # for all the SB API headers.
1857        public_headers = os.listdir(public_api_dir)
1858        # For different platforms, the include statement can vary.
1859        if self.hasDarwinFramework():
1860            include_stmt = "'#include <%s>' % os.path.join('LLDB', header)"
1861        else:
1862            include_stmt = (
1863                "'#include <%s>' % os.path.join(r'" + public_api_dir + "', header)"
1864            )
1865        list = [
1866            eval(include_stmt)
1867            for header in public_headers
1868            if (header.startswith("SB") and header.endswith(".h"))
1869        ]
1870        includes = "\n".join(list)
1871        new_content = content.replace("%include_SB_APIs%", includes)
1872        new_content = new_content.replace("%SOURCE_DIR%", self.getSourceDir())
1873        src = os.path.join(self.getBuildDir(), source)
1874        with open(src, "w") as f:
1875            f.write(new_content)
1876
1877        self.addTearDownHook(lambda: os.remove(src))
1878
1879    def setUp(self):
1880        # Works with the test driver to conditionally skip tests via
1881        # decorators.
1882        Base.setUp(self)
1883
1884        for s in self.setUpCommands():
1885            self.runCmd(s)
1886
1887        # We want our debugger to be synchronous.
1888        self.dbg.SetAsync(False)
1889
1890        # Retrieve the associated command interpreter instance.
1891        self.ci = self.dbg.GetCommandInterpreter()
1892        if not self.ci:
1893            raise Exception("Could not get the command interpreter")
1894
1895        # And the result object.
1896        self.res = lldb.SBCommandReturnObject()
1897
1898    def registerSharedLibrariesWithTarget(self, target, shlibs):
1899        """If we are remotely running the test suite, register the shared libraries with the target so they get uploaded, otherwise do nothing
1900
1901        Any modules in the target that have their remote install file specification set will
1902        get uploaded to the remote host. This function registers the local copies of the
1903        shared libraries with the target and sets their remote install locations so they will
1904        be uploaded when the target is run.
1905        """
1906        if not shlibs or not self.platformContext:
1907            return None
1908
1909        shlib_environment_var = self.platformContext.shlib_environment_var
1910        shlib_prefix = self.platformContext.shlib_prefix
1911        shlib_extension = "." + self.platformContext.shlib_extension
1912
1913        dirs = []
1914        # Add any shared libraries to our target if remote so they get
1915        # uploaded into the working directory on the remote side
1916        for name in shlibs:
1917            # The path can be a full path to a shared library, or a make file name like "Foo" for
1918            # "libFoo.dylib" or "libFoo.so", or "Foo.so" for "Foo.so" or "libFoo.so", or just a
1919            # basename like "libFoo.so". So figure out which one it is and resolve the local copy
1920            # of the shared library accordingly
1921            if os.path.isfile(name):
1922                local_shlib_path = (
1923                    name  # name is the full path to the local shared library
1924                )
1925            else:
1926                # Check relative names
1927                local_shlib_path = os.path.join(
1928                    self.getBuildDir(), shlib_prefix + name + shlib_extension
1929                )
1930                if not os.path.exists(local_shlib_path):
1931                    local_shlib_path = os.path.join(
1932                        self.getBuildDir(), name + shlib_extension
1933                    )
1934                    if not os.path.exists(local_shlib_path):
1935                        local_shlib_path = os.path.join(self.getBuildDir(), name)
1936
1937                # Make sure we found the local shared library in the above code
1938                self.assertTrue(os.path.exists(local_shlib_path))
1939
1940            # Add the shared library to our target
1941            shlib_module = target.AddModule(local_shlib_path, None, None, None)
1942            if lldb.remote_platform:
1943                # We must set the remote install location if we want the shared library
1944                # to get uploaded to the remote target
1945                remote_shlib_path = lldbutil.append_to_process_working_directory(
1946                    self, os.path.basename(local_shlib_path)
1947                )
1948                shlib_module.SetRemoteInstallFileSpec(
1949                    lldb.SBFileSpec(remote_shlib_path, False)
1950                )
1951                dir_to_add = self.get_process_working_directory()
1952            else:
1953                dir_to_add = os.path.dirname(local_shlib_path)
1954
1955            if dir_to_add not in dirs:
1956                dirs.append(dir_to_add)
1957
1958        env_value = self.platformContext.shlib_path_separator.join(dirs)
1959        return ["%s=%s" % (shlib_environment_var, env_value)]
1960
1961    def registerSanitizerLibrariesWithTarget(self, target):
1962        runtimes = []
1963        for m in target.module_iter():
1964            libspec = m.GetFileSpec()
1965            if "clang_rt" in libspec.GetFilename():
1966                runtimes.append(
1967                    os.path.join(libspec.GetDirectory(), libspec.GetFilename())
1968                )
1969        return self.registerSharedLibrariesWithTarget(target, runtimes)
1970
1971    # utility methods that tests can use to access the current objects
1972    def target(self):
1973        if not self.dbg:
1974            raise Exception("Invalid debugger instance")
1975        return self.dbg.GetSelectedTarget()
1976
1977    def process(self):
1978        if not self.dbg:
1979            raise Exception("Invalid debugger instance")
1980        return self.dbg.GetSelectedTarget().GetProcess()
1981
1982    def thread(self):
1983        if not self.dbg:
1984            raise Exception("Invalid debugger instance")
1985        return self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread()
1986
1987    def frame(self):
1988        if not self.dbg:
1989            raise Exception("Invalid debugger instance")
1990        return (
1991            self.dbg.GetSelectedTarget()
1992            .GetProcess()
1993            .GetSelectedThread()
1994            .GetSelectedFrame()
1995        )
1996
1997    def get_process_working_directory(self):
1998        """Get the working directory that should be used when launching processes for local or remote processes."""
1999        if lldb.remote_platform:
2000            # Remote tests set the platform working directory up in
2001            # TestBase.setUp()
2002            return lldb.remote_platform.GetWorkingDirectory()
2003        else:
2004            # local tests change directory into each test subdirectory
2005            return self.getBuildDir()
2006
2007    def tearDown(self):
2008        # Ensure all the references to SB objects have gone away so that we can
2009        # be sure that all test-specific resources have been freed before we
2010        # attempt to delete the targets.
2011        gc.collect()
2012
2013        # Delete the target(s) from the debugger as a general cleanup step.
2014        # This includes terminating the process for each target, if any.
2015        # We'd like to reuse the debugger for our next test without incurring
2016        # the initialization overhead.
2017        targets = []
2018        for target in self.dbg:
2019            if target:
2020                targets.append(target)
2021                process = target.GetProcess()
2022                if process:
2023                    rc = self.invoke(process, "Kill")
2024                    assert rc.Success()
2025        for target in targets:
2026            self.dbg.DeleteTarget(target)
2027
2028        # Assert that all targets are deleted.
2029        self.assertEqual(self.dbg.GetNumTargets(), 0)
2030
2031        # Do this last, to make sure it's in reverse order from how we setup.
2032        Base.tearDown(self)
2033
2034    def switch_to_thread_with_stop_reason(self, stop_reason):
2035        """
2036        Run the 'thread list' command, and select the thread with stop reason as
2037        'stop_reason'.  If no such thread exists, no select action is done.
2038        """
2039        from .lldbutil import stop_reason_to_str
2040
2041        self.runCmd("thread list")
2042        output = self.res.GetOutput()
2043        thread_line_pattern = re.compile(
2044            "^[ *] thread #([0-9]+):.*stop reason = %s"
2045            % stop_reason_to_str(stop_reason)
2046        )
2047        for line in output.splitlines():
2048            matched = thread_line_pattern.match(line)
2049            if matched:
2050                self.runCmd("thread select %s" % matched.group(1))
2051
2052    def match(
2053        self, str, patterns, msg=None, trace=False, error=False, matching=True, exe=True
2054    ):
2055        """run command in str, and match the result against regexp in patterns returning the match object for the first matching pattern
2056
2057        Otherwise, all the arguments have the same meanings as for the expect function
2058        """
2059
2060        trace = True if traceAlways else trace
2061
2062        if exe:
2063            # First run the command.  If we are expecting error, set check=False.
2064            # Pass the assert message along since it provides more semantic
2065            # info.
2066            self.runCmd(str, msg=msg, trace=(True if trace else False), check=not error)
2067
2068            # Then compare the output against expected strings.
2069            output = self.res.GetError() if error else self.res.GetOutput()
2070
2071            # If error is True, the API client expects the command to fail!
2072            if error:
2073                self.assertFalse(
2074                    self.res.Succeeded(), "Command '" + str + "' is expected to fail!"
2075                )
2076        else:
2077            # No execution required, just compare str against the golden input.
2078            output = str
2079            with recording(self, trace) as sbuf:
2080                print("looking at:", output, file=sbuf)
2081
2082        # The heading says either "Expecting" or "Not expecting".
2083        heading = "Expecting" if matching else "Not expecting"
2084
2085        for pattern in patterns:
2086            # Match Objects always have a boolean value of True.
2087            match_object = re.search(pattern, output)
2088            matched = bool(match_object)
2089            with recording(self, trace) as sbuf:
2090                print("%s pattern: %s" % (heading, pattern), file=sbuf)
2091                print("Matched" if matched else "Not matched", file=sbuf)
2092            if matched:
2093                break
2094
2095        self.assertTrue(
2096            matched if matching else not matched,
2097            msg if msg else EXP_MSG(str, output, exe),
2098        )
2099
2100        return match_object
2101
2102    def check_completion_with_desc(
2103        self, str_input, match_desc_pairs, enforce_order=False
2104    ):
2105        """
2106        Checks that when the given input is completed at the given list of
2107        completions and descriptions is returned.
2108        :param str_input: The input that should be completed. The completion happens at the end of the string.
2109        :param match_desc_pairs: A list of pairs that indicate what completions have to be in the list of
2110                                 completions returned by LLDB. The first element of the pair is the completion
2111                                 string that LLDB should generate and the second element the description.
2112        :param enforce_order: True iff the order in which the completions are returned by LLDB
2113                              should match the order of the match_desc_pairs pairs.
2114        """
2115        interp = self.dbg.GetCommandInterpreter()
2116        match_strings = lldb.SBStringList()
2117        description_strings = lldb.SBStringList()
2118        num_matches = interp.HandleCompletionWithDescriptions(
2119            str_input, len(str_input), 0, -1, match_strings, description_strings
2120        )
2121        self.assertEqual(len(description_strings), len(match_strings))
2122
2123        # The index of the last matched description in description_strings or
2124        # -1 if no description has been matched yet.
2125        last_found_index = -1
2126        out_of_order_errors = ""
2127        missing_pairs = []
2128        for pair in match_desc_pairs:
2129            found_pair = False
2130            for i in range(num_matches + 1):
2131                match_candidate = match_strings.GetStringAtIndex(i)
2132                description_candidate = description_strings.GetStringAtIndex(i)
2133                if match_candidate == pair[0] and description_candidate == pair[1]:
2134                    found_pair = True
2135                    if enforce_order and last_found_index > i:
2136                        new_err = (
2137                            "Found completion "
2138                            + pair[0]
2139                            + " at index "
2140                            + str(i)
2141                            + " in returned completion list but "
2142                            + "should have been after completion "
2143                            + match_strings.GetStringAtIndex(last_found_index)
2144                            + " (index:"
2145                            + str(last_found_index)
2146                            + ")\n"
2147                        )
2148                        out_of_order_errors += new_err
2149                    last_found_index = i
2150                    break
2151            if not found_pair:
2152                missing_pairs.append(pair)
2153
2154        error_msg = ""
2155        got_failure = False
2156        if len(missing_pairs):
2157            got_failure = True
2158            error_msg += "Missing pairs:\n"
2159            for pair in missing_pairs:
2160                error_msg += " [" + pair[0] + ":" + pair[1] + "]\n"
2161        if len(out_of_order_errors):
2162            got_failure = True
2163            error_msg += out_of_order_errors
2164        if got_failure:
2165            error_msg += (
2166                "Got the following " + str(num_matches) + " completions back:\n"
2167            )
2168            for i in range(num_matches + 1):
2169                match_candidate = match_strings.GetStringAtIndex(i)
2170                description_candidate = description_strings.GetStringAtIndex(i)
2171                error_msg += (
2172                    "["
2173                    + match_candidate
2174                    + ":"
2175                    + description_candidate
2176                    + "] index "
2177                    + str(i)
2178                    + "\n"
2179                )
2180            self.assertFalse(got_failure, error_msg)
2181
2182    def complete_from_to(self, str_input, patterns):
2183        """Test that the completion mechanism completes str_input to patterns,
2184        where patterns could be a single pattern-string or a list of
2185        pattern-strings.
2186
2187        If there is only one pattern and it is exactly equal to str_input, this
2188        assumes that there should be no completions provided and that the result
2189        should be the same as the input."""
2190
2191        # Patterns should not be None in order to proceed.
2192        self.assertFalse(patterns is None)
2193        # And should be either a string or list of strings.  Check for list type
2194        # below, if not, make a list out of the singleton string.  If patterns
2195        # is not a string or not a list of strings, there'll be runtime errors
2196        # later on.
2197        if not isinstance(patterns, list):
2198            patterns = [patterns]
2199
2200        interp = self.dbg.GetCommandInterpreter()
2201        match_strings = lldb.SBStringList()
2202        num_matches = interp.HandleCompletion(
2203            str_input, len(str_input), 0, -1, match_strings
2204        )
2205        common_match = match_strings.GetStringAtIndex(0)
2206        if num_matches == 0:
2207            compare_string = str_input
2208        else:
2209            if common_match is not None and len(common_match) > 0:
2210                compare_string = str_input + common_match
2211            else:
2212                compare_string = ""
2213                for idx in range(1, num_matches + 1):
2214                    compare_string += match_strings.GetStringAtIndex(idx) + "\n"
2215
2216        if len(patterns) == 1 and str_input == patterns[0] and num_matches:
2217            self.fail("Expected no completions but got:\n" + compare_string)
2218
2219        for p in patterns:
2220            self.expect(
2221                compare_string,
2222                msg=COMPLETION_MSG(str_input, p, match_strings),
2223                exe=False,
2224                substrs=[p],
2225            )
2226
2227    def completions_match(self, command, completions):
2228        """Checks that the completions for the given command are equal to the
2229        given list of completions"""
2230        interp = self.dbg.GetCommandInterpreter()
2231        match_strings = lldb.SBStringList()
2232        interp.HandleCompletion(command, len(command), 0, -1, match_strings)
2233        # match_strings is a 1-indexed list, so we have to slice...
2234        self.assertCountEqual(
2235            completions, list(match_strings)[1:], "List of returned completion is wrong"
2236        )
2237
2238    def completions_contain(self, command, completions):
2239        """Checks that the completions for the given command contain the given
2240        list of completions."""
2241        interp = self.dbg.GetCommandInterpreter()
2242        match_strings = lldb.SBStringList()
2243        interp.HandleCompletion(command, len(command), 0, -1, match_strings)
2244        for completion in completions:
2245            # match_strings is a 1-indexed list, so we have to slice...
2246            self.assertIn(
2247                completion, list(match_strings)[1:], "Couldn't find expected completion"
2248            )
2249
2250    def filecheck(
2251        self, command, check_file, filecheck_options="", expect_cmd_failure=False
2252    ):
2253        # Run the command.
2254        self.runCmd(
2255            command,
2256            check=(not expect_cmd_failure),
2257            msg="FileCheck'ing result of `{0}`".format(command),
2258        )
2259
2260        self.assertTrue((not expect_cmd_failure) == self.res.Succeeded())
2261
2262        # Get the error text if there was an error, and the regular text if not.
2263        output = self.res.GetOutput() if self.res.Succeeded() else self.res.GetError()
2264
2265        # Assemble the absolute path to the check file. As a convenience for
2266        # LLDB inline tests, assume that the check file is a relative path to
2267        # a file within the inline test directory.
2268        if check_file.endswith(".pyc"):
2269            check_file = check_file[:-1]
2270        check_file_abs = os.path.abspath(check_file)
2271
2272        # Run FileCheck.
2273        filecheck_bin = configuration.get_filecheck_path()
2274        if not filecheck_bin:
2275            self.assertTrue(False, "No valid FileCheck executable specified")
2276        filecheck_args = [filecheck_bin, check_file_abs]
2277        if filecheck_options:
2278            filecheck_args.append(filecheck_options)
2279        subproc = Popen(
2280            filecheck_args,
2281            stdin=PIPE,
2282            stdout=PIPE,
2283            stderr=PIPE,
2284            universal_newlines=True,
2285        )
2286        cmd_stdout, cmd_stderr = subproc.communicate(input=output)
2287        cmd_status = subproc.returncode
2288
2289        filecheck_cmd = " ".join(filecheck_args)
2290        filecheck_trace = """
2291--- FileCheck trace (code={0}) ---
2292{1}
2293
2294FileCheck input:
2295{2}
2296
2297FileCheck output:
2298{3}
2299{4}
2300""".format(
2301            cmd_status, filecheck_cmd, output, cmd_stdout, cmd_stderr
2302        )
2303
2304        trace = cmd_status != 0 or traceAlways
2305        with recording(self, trace) as sbuf:
2306            print(filecheck_trace, file=sbuf)
2307
2308        self.assertTrue(cmd_status == 0)
2309
2310    def expect(
2311        self,
2312        string,
2313        msg=None,
2314        patterns=None,
2315        startstr=None,
2316        endstr=None,
2317        substrs=None,
2318        trace=False,
2319        error=False,
2320        ordered=True,
2321        matching=True,
2322        exe=True,
2323        inHistory=False,
2324    ):
2325        """
2326        Similar to runCmd; with additional expect style output matching ability.
2327
2328        Ask the command interpreter to handle the command and then check its
2329        return status.  The 'msg' parameter specifies an informational assert
2330        message.  We expect the output from running the command to start with
2331        'startstr', matches the substrings contained in 'substrs', and regexp
2332        matches the patterns contained in 'patterns'.
2333
2334        When matching is true and ordered is true, which are both the default,
2335        the strings in the substrs array have to appear in the command output
2336        in the order in which they appear in the array.
2337
2338        If the keyword argument error is set to True, it signifies that the API
2339        client is expecting the command to fail.  In this case, the error stream
2340        from running the command is retrieved and compared against the golden
2341        input, instead.
2342
2343        If the keyword argument matching is set to False, it signifies that the API
2344        client is expecting the output of the command not to match the golden
2345        input.
2346
2347        Finally, the required argument 'string' represents the lldb command to be
2348        sent to the command interpreter.  In case the keyword argument 'exe' is
2349        set to False, the 'string' is treated as a string to be matched/not-matched
2350        against the golden input.
2351        """
2352        # Catch cases where `expect` has been miscalled. Specifically, prevent
2353        # this easy to make mistake:
2354        #     self.expect("lldb command", "some substr")
2355        # The `msg` parameter is used only when a failed match occurs. A failed
2356        # match can only occur when one of `patterns`, `startstr`, `endstr`, or
2357        # `substrs` has been given. Thus, if a `msg` is given, it's an error to
2358        # not also provide one of the matcher parameters.
2359        if msg and not (patterns or startstr or endstr or substrs or error):
2360            assert False, "expect() missing a matcher argument"
2361
2362        # Check `patterns` and `substrs` are not accidentally given as strings.
2363        assert not isinstance(patterns, str), "patterns must be a collection of strings"
2364        assert not isinstance(substrs, str), "substrs must be a collection of strings"
2365
2366        trace = True if traceAlways else trace
2367
2368        if exe:
2369            # First run the command.  If we are expecting error, set check=False.
2370            # Pass the assert message along since it provides more semantic
2371            # info.
2372            self.runCmd(
2373                string,
2374                msg=msg,
2375                trace=(True if trace else False),
2376                check=not error,
2377                inHistory=inHistory,
2378            )
2379
2380            # Then compare the output against expected strings.
2381            output = self.res.GetError() if error else self.res.GetOutput()
2382
2383            # If error is True, the API client expects the command to fail!
2384            if error:
2385                self.assertFalse(
2386                    self.res.Succeeded(),
2387                    "Command '" + string + "' is expected to fail!",
2388                )
2389        else:
2390            # No execution required, just compare string against the golden input.
2391            if isinstance(string, lldb.SBCommandReturnObject):
2392                output = string.GetOutput()
2393            else:
2394                output = string
2395            with recording(self, trace) as sbuf:
2396                print("looking at:", output, file=sbuf)
2397
2398        expecting_str = "Expecting" if matching else "Not expecting"
2399
2400        def found_str(matched):
2401            return "was found" if matched else "was not found"
2402
2403        # To be used as assert fail message and/or trace content
2404        log_lines = [
2405            "{}:".format("Ran command" if exe else "Checking string"),
2406            '"{}"'.format(string),
2407            # Space out command and output
2408            "",
2409        ]
2410        if exe:
2411            # Newline before output to make large strings more readable
2412            log_lines.append("Got output:\n{}".format(output))
2413
2414        # Assume that we start matched if we want a match
2415        # Meaning if you have no conditions, matching or
2416        # not matching will always pass
2417        matched = matching
2418
2419        # We will stop checking on first failure
2420        if startstr:
2421            matched = output.startswith(startstr)
2422            log_lines.append(
2423                '{} start string: "{}" ({})'.format(
2424                    expecting_str, startstr, found_str(matched)
2425                )
2426            )
2427
2428        if endstr and matched == matching:
2429            matched = output.endswith(endstr)
2430            log_lines.append(
2431                '{} end string: "{}" ({})'.format(
2432                    expecting_str, endstr, found_str(matched)
2433                )
2434            )
2435
2436        if substrs and matched == matching:
2437            start = 0
2438            for substr in substrs:
2439                index = output[start:].find(substr)
2440                start = start + index + len(substr) if ordered and matching else 0
2441                matched = index != -1
2442                log_lines.append(
2443                    '{} sub string: "{}" ({})'.format(
2444                        expecting_str, substr, found_str(matched)
2445                    )
2446                )
2447
2448                if matched != matching:
2449                    break
2450
2451        if patterns and matched == matching:
2452            for pattern in patterns:
2453                matched = re.search(pattern, output)
2454
2455                pattern_line = '{} regex pattern: "{}" ({}'.format(
2456                    expecting_str, pattern, found_str(matched)
2457                )
2458                if matched:
2459                    pattern_line += ', matched "{}"'.format(matched.group(0))
2460                pattern_line += ")"
2461                log_lines.append(pattern_line)
2462
2463                # Convert to bool because match objects
2464                # are True-ish but is not True itself
2465                matched = bool(matched)
2466                if matched != matching:
2467                    break
2468
2469        # If a check failed, add any extra assert message
2470        if msg is not None and matched != matching:
2471            log_lines.append(msg)
2472
2473        log_msg = "\n".join(log_lines)
2474        with recording(self, trace) as sbuf:
2475            print(log_msg, file=sbuf)
2476        if matched != matching:
2477            self.fail(log_msg)
2478
2479    def expect_expr(
2480        self,
2481        expr,
2482        result_summary=None,
2483        result_value=None,
2484        result_type=None,
2485        result_children=None,
2486    ):
2487        """
2488        Evaluates the given expression and verifies the result.
2489        :param expr: The expression as a string.
2490        :param result_summary: The summary that the expression should have. None if the summary should not be checked.
2491        :param result_value: The value that the expression should have. None if the value should not be checked.
2492        :param result_type: The type that the expression result should have. None if the type should not be checked.
2493        :param result_children: The expected children of the expression result
2494                                as a list of ValueChecks. None if the children shouldn't be checked.
2495        """
2496        self.assertTrue(
2497            expr.strip() == expr,
2498            "Expression contains trailing/leading whitespace: '" + expr + "'",
2499        )
2500
2501        frame = self.frame()
2502        options = lldb.SBExpressionOptions()
2503
2504        # Disable fix-its that tests don't pass by accident.
2505        options.SetAutoApplyFixIts(False)
2506
2507        # Set the usual default options for normal expressions.
2508        options.SetIgnoreBreakpoints(True)
2509
2510        if self.frame().IsValid():
2511            options.SetLanguage(frame.GuessLanguage())
2512            eval_result = self.frame().EvaluateExpression(expr, options)
2513        else:
2514            target = self.target()
2515            # If there is no selected target, run the expression in the dummy
2516            # target.
2517            if not target.IsValid():
2518                target = self.dbg.GetDummyTarget()
2519            eval_result = target.EvaluateExpression(expr, options)
2520
2521        value_check = ValueCheck(
2522            type=result_type,
2523            value=result_value,
2524            summary=result_summary,
2525            children=result_children,
2526        )
2527        value_check.check_value(self, eval_result, str(eval_result))
2528        return eval_result
2529
2530    def expect_var_path(
2531        self, var_path, summary=None, value=None, type=None, children=None
2532    ):
2533        """
2534        Evaluates the given variable path and verifies the result.
2535        See also 'frame variable' and SBFrame.GetValueForVariablePath.
2536        :param var_path: The variable path as a string.
2537        :param summary: The summary that the variable should have. None if the summary should not be checked.
2538        :param value: The value that the variable should have. None if the value should not be checked.
2539        :param type: The type that the variable result should have. None if the type should not be checked.
2540        :param children: The expected children of the variable  as a list of ValueChecks.
2541                         None if the children shouldn't be checked.
2542        """
2543        self.assertTrue(
2544            var_path.strip() == var_path,
2545            "Expression contains trailing/leading whitespace: '" + var_path + "'",
2546        )
2547
2548        frame = self.frame()
2549        eval_result = frame.GetValueForVariablePath(var_path)
2550
2551        value_check = ValueCheck(
2552            type=type, value=value, summary=summary, children=children
2553        )
2554        value_check.check_value(self, eval_result, str(eval_result))
2555        return eval_result
2556
2557    """Assert that an lldb.SBError is in the "success" state."""
2558
2559    def assertSuccess(self, obj, msg=None):
2560        if not obj.Success():
2561            error = obj.GetCString()
2562            self.fail(self._formatMessage(msg, "'{}' is not success".format(error)))
2563
2564    """Assert that an lldb.SBError is in the "failure" state."""
2565
2566    def assertFailure(self, obj, error_str=None, msg=None):
2567        if obj.Success():
2568            self.fail(self._formatMessage(msg, "Error not in a fail state"))
2569
2570        if error_str is None:
2571            return
2572
2573        error = obj.GetCString()
2574        self.assertEqual(error, error_str, msg)
2575
2576    """Assert that a command return object is successful"""
2577
2578    def assertCommandReturn(self, obj, msg=None):
2579        if not obj.Succeeded():
2580            error = obj.GetError()
2581            self.fail(self._formatMessage(msg, "'{}' is not success".format(error)))
2582
2583    """Assert two states are equal"""
2584
2585    def assertState(self, first, second, msg=None):
2586        if first != second:
2587            error = "{} ({}) != {} ({})".format(
2588                lldbutil.state_type_to_str(first),
2589                first,
2590                lldbutil.state_type_to_str(second),
2591                second,
2592            )
2593            self.fail(self._formatMessage(msg, error))
2594
2595    """Assert two stop reasons are equal"""
2596
2597    def assertStopReason(self, first, second, msg=None):
2598        if first != second:
2599            error = "{} ({}) != {} ({})".format(
2600                lldbutil.stop_reason_to_str(first),
2601                first,
2602                lldbutil.stop_reason_to_str(second),
2603                second,
2604            )
2605            self.fail(self._formatMessage(msg, error))
2606
2607    def createTestTarget(self, file_path=None, msg=None, load_dependent_modules=True):
2608        """
2609        Creates a target from the file found at the given file path.
2610        Asserts that the resulting target is valid.
2611        :param file_path: The file path that should be used to create the target.
2612                          The default argument opens the current default test
2613                          executable in the current test directory.
2614        :param msg: A custom error message.
2615        """
2616        if file_path is None:
2617            file_path = self.getBuildArtifact("a.out")
2618        error = lldb.SBError()
2619        triple = ""
2620        platform = ""
2621        target = self.dbg.CreateTarget(
2622            file_path, triple, platform, load_dependent_modules, error
2623        )
2624        if error.Fail():
2625            err = "Couldn't create target for path '{}': {}".format(
2626                file_path, str(error)
2627            )
2628            self.fail(self._formatMessage(msg, err))
2629
2630        self.assertTrue(target.IsValid(), "Got invalid target without error")
2631        return target
2632
2633    # =================================================
2634    # Misc. helper methods for debugging test execution
2635    # =================================================
2636
2637    def DebugSBValue(self, val):
2638        """Debug print a SBValue object, if traceAlways is True."""
2639        from .lldbutil import value_type_to_str
2640
2641        if not traceAlways:
2642            return
2643
2644        err = sys.stderr
2645        err.write(val.GetName() + ":\n")
2646        err.write("\t" + "TypeName         -> " + val.GetTypeName() + "\n")
2647        err.write("\t" + "ByteSize         -> " + str(val.GetByteSize()) + "\n")
2648        err.write("\t" + "NumChildren      -> " + str(val.GetNumChildren()) + "\n")
2649        err.write("\t" + "Value            -> " + str(val.GetValue()) + "\n")
2650        err.write("\t" + "ValueAsUnsigned  -> " + str(val.GetValueAsUnsigned()) + "\n")
2651        err.write(
2652            "\t" + "ValueType        -> " + value_type_to_str(val.GetValueType()) + "\n"
2653        )
2654        err.write("\t" + "Summary          -> " + str(val.GetSummary()) + "\n")
2655        err.write("\t" + "IsPointerType    -> " + str(val.TypeIsPointerType()) + "\n")
2656        err.write("\t" + "Location         -> " + val.GetLocation() + "\n")
2657
2658    def DebugSBType(self, type):
2659        """Debug print a SBType object, if traceAlways is True."""
2660        if not traceAlways:
2661            return
2662
2663        err = sys.stderr
2664        err.write(type.GetName() + ":\n")
2665        err.write("\t" + "ByteSize        -> " + str(type.GetByteSize()) + "\n")
2666        err.write("\t" + "IsAggregateType   -> " + str(type.IsAggregateType()) + "\n")
2667        err.write("\t" + "IsPointerType   -> " + str(type.IsPointerType()) + "\n")
2668        err.write("\t" + "IsReferenceType -> " + str(type.IsReferenceType()) + "\n")
2669
2670    def DebugPExpect(self, child):
2671        """Debug the spwaned pexpect object."""
2672        if not traceAlways:
2673            return
2674
2675        print(child)
2676
2677    @classmethod
2678    def RemoveTempFile(cls, file):
2679        if os.path.exists(file):
2680            remove_file(file)
2681
2682
2683# On Windows, the first attempt to delete a recently-touched file can fail
2684# because of a race with antimalware scanners.  This function will detect a
2685# failure and retry.
2686
2687
2688def remove_file(file, num_retries=1, sleep_duration=0.5):
2689    for i in range(num_retries + 1):
2690        try:
2691            os.remove(file)
2692            return True
2693        except:
2694            time.sleep(sleep_duration)
2695            continue
2696    return False
2697