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