xref: /llvm-project/lldb/packages/Python/lldbsuite/test/lldbtest.py (revision 22561cfb443267905d4190f0e2a738e6b412457f)
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(command):
176    """A generic "Command '%s' did not return successfully" message generator."""
177    return f"Command '{command}' did not return successfully"
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        # Optimistically set __errored__, __failed__, __expected__ to False
869        # initially.  If the test errored/failed, the session info
870        # is then dumped into a session specific file for diagnosis.
871        self.__cleanup_errored__ = False
872        self.__errored__ = False
873        self.__failed__ = False
874        self.__expected__ = False
875        # We are also interested in unexpected success.
876        self.__unexpected__ = False
877        # And skipped tests.
878        self.__skipped__ = False
879
880        # See addTearDownHook(self, hook) which allows the client to add a hook
881        # function to be run during tearDown() time.
882        self.hooks = []
883
884        # See HideStdout(self).
885        self.sys_stdout_hidden = False
886
887        if self.platformContext:
888            # set environment variable names for finding shared libraries
889            self.dylibPath = self.platformContext.shlib_environment_var
890
891        # Create the debugger instance.
892        self.dbg = lldb.SBDebugger.Create()
893        # Copy selected platform from a global instance if it exists.
894        if lldb.selected_platform is not None:
895            self.dbg.SetSelectedPlatform(lldb.selected_platform)
896
897        if not self.dbg:
898            raise Exception("Invalid debugger instance")
899
900        # Retrieve the associated command interpreter instance.
901        self.ci = self.dbg.GetCommandInterpreter()
902        if not self.ci:
903            raise Exception("Could not get the command interpreter")
904
905        # And the result object.
906        self.res = lldb.SBCommandReturnObject()
907
908        self.setPlatformWorkingDir()
909        self.enableLogChannelsForCurrentTest()
910
911        self.lib_lldb = None
912        self.framework_dir = None
913        self.darwinWithFramework = False
914
915        if sys.platform.startswith("darwin") and configuration.lldb_framework_path:
916            framework = configuration.lldb_framework_path
917            lib = os.path.join(framework, "LLDB")
918            if os.path.exists(lib):
919                self.framework_dir = os.path.dirname(framework)
920                self.lib_lldb = lib
921                self.darwinWithFramework = self.platformIsDarwin()
922
923    def setAsync(self, value):
924        """Sets async mode to True/False and ensures it is reset after the testcase completes."""
925        old_async = self.dbg.GetAsync()
926        self.dbg.SetAsync(value)
927        self.addTearDownHook(lambda: self.dbg.SetAsync(old_async))
928
929    def cleanupSubprocesses(self):
930        # Terminate subprocesses in reverse order from how they were created.
931        for p in reversed(self.subprocesses):
932            p.terminate()
933            del p
934        del self.subprocesses[:]
935
936    def spawnSubprocess(self, executable, args=[], extra_env=None, install_remote=True):
937        """Creates a subprocess.Popen object with the specified executable and arguments,
938        saves it in self.subprocesses, and returns the object.
939        """
940        proc = (
941            _RemoteProcess(install_remote)
942            if lldb.remote_platform
943            else _LocalProcess(self.TraceOn())
944        )
945        proc.launch(executable, args, extra_env=extra_env)
946        self.subprocesses.append(proc)
947        return proc
948
949    def runCmd(self, cmd, msg=None, check=True, trace=False, inHistory=False):
950        """
951        Ask the command interpreter to handle the command and then check its
952        return status.
953        """
954        # Fail fast if 'cmd' is not meaningful.
955        if cmd is None:
956            raise Exception("Bad 'cmd' parameter encountered")
957
958        trace = True if traceAlways else trace
959
960        if cmd.startswith("target create "):
961            cmd = cmd.replace("target create ", "file ")
962
963        running = cmd.startswith("run") or cmd.startswith("process launch")
964
965        for i in range(self.maxLaunchCount if running else 1):
966            with recording(self, trace) as sbuf:
967                print("runCmd:", cmd, file=sbuf)
968                if not check:
969                    print("check of return status not required", file=sbuf)
970
971            self.ci.HandleCommand(cmd, self.res, inHistory)
972
973            with recording(self, trace) as sbuf:
974                if self.res.Succeeded():
975                    print("output:", self.res.GetOutput(), file=sbuf)
976                else:
977                    print("runCmd failed!", file=sbuf)
978                    print(self.res.GetError(), file=sbuf)
979
980            if self.res.Succeeded():
981                break
982            elif running:
983                # For process launch, wait some time before possible next try.
984                time.sleep(self.timeWaitNextLaunch)
985                with recording(self, trace) as sbuf:
986                    print("Command '" + cmd + "' failed!", file=sbuf)
987
988        if check:
989            if not msg:
990                msg = CMD_MSG(cmd)
991            output = ""
992            if self.res.GetOutput():
993                output += "\nCommand output:\n" + self.res.GetOutput()
994            if self.res.GetError():
995                output += "\nError output:\n" + self.res.GetError()
996            self.assertTrue(self.res.Succeeded(), msg + output)
997
998    def HideStdout(self):
999        """Hide output to stdout from the user.
1000
1001        During test execution, there might be cases where we don't want to show the
1002        standard output to the user.  For example,
1003
1004            self.runCmd(r'''sc print("\n\n\tHello!\n")''')
1005
1006        tests whether command abbreviation for 'script' works or not.  There is no
1007        need to show the 'Hello' output to the user as long as the 'script' command
1008        succeeds and we are not in TraceOn() mode (see the '-t' option).
1009
1010        In this case, the test method calls self.HideStdout(self) to redirect the
1011        sys.stdout to a null device, and restores the sys.stdout upon teardown.
1012
1013        Note that you should only call this method at most once during a test case
1014        execution.  Any subsequent call has no effect at all."""
1015        if self.sys_stdout_hidden:
1016            return
1017
1018        self.sys_stdout_hidden = True
1019        old_stdout = sys.stdout
1020        sys.stdout = open(os.devnull, "w")
1021
1022        def restore_stdout():
1023            sys.stdout = old_stdout
1024
1025        self.addTearDownHook(restore_stdout)
1026
1027    # =======================================================================
1028    # Methods for customized teardown cleanups as well as execution of hooks.
1029    # =======================================================================
1030
1031    def setTearDownCleanup(self, dictionary=None):
1032        """Register a cleanup action at tearDown() time with a dictionary"""
1033        self.dict = dictionary
1034        self.doTearDownCleanup = True
1035
1036    def addTearDownCleanup(self, dictionary):
1037        """Add a cleanup action at tearDown() time with a dictionary"""
1038        self.dicts.append(dictionary)
1039        self.doTearDownCleanups = True
1040
1041    def addTearDownHook(self, hook):
1042        """
1043        Add a function to be run during tearDown() time.
1044
1045        Hooks are executed in a first come first serve manner.
1046        """
1047        if callable(hook):
1048            with recording(self, traceAlways) as sbuf:
1049                print("Adding tearDown hook:", getsource_if_available(hook), file=sbuf)
1050            self.hooks.append(hook)
1051
1052        return self
1053
1054    def deletePexpectChild(self):
1055        # This is for the case of directly spawning 'lldb' and interacting with it
1056        # using pexpect.
1057        if self.child and self.child.isalive():
1058            import pexpect
1059
1060            with recording(self, traceAlways) as sbuf:
1061                print("tearing down the child process....", file=sbuf)
1062            try:
1063                if self.child_in_script_interpreter:
1064                    self.child.sendline("quit()")
1065                    self.child.expect_exact(self.child_prompt)
1066                self.child.sendline("settings set interpreter.prompt-on-quit false")
1067                self.child.sendline("quit")
1068                self.child.expect(pexpect.EOF)
1069            except (ValueError, pexpect.ExceptionPexpect):
1070                # child is already terminated
1071                pass
1072            except OSError as exception:
1073                import errno
1074
1075                if exception.errno != errno.EIO:
1076                    # unexpected error
1077                    raise
1078                # child is already terminated
1079            finally:
1080                # Give it one final blow to make sure the child is terminated.
1081                self.child.close()
1082
1083    def tearDown(self):
1084        """Fixture for unittest test case teardown."""
1085        self.deletePexpectChild()
1086
1087        # Check and run any hook functions.
1088        for hook in reversed(self.hooks):
1089            with recording(self, traceAlways) as sbuf:
1090                print(
1091                    "Executing tearDown hook:", getsource_if_available(hook), file=sbuf
1092                )
1093            if funcutils.requires_self(hook):
1094                hook(self)
1095            else:
1096                hook()  # try the plain call and hope it works
1097
1098        del self.hooks
1099
1100        # Perform registered teardown cleanup.
1101        if doCleanup and self.doTearDownCleanup:
1102            self.cleanup(dictionary=self.dict)
1103
1104        # In rare cases where there are multiple teardown cleanups added.
1105        if doCleanup and self.doTearDownCleanups:
1106            if self.dicts:
1107                for dict in reversed(self.dicts):
1108                    self.cleanup(dictionary=dict)
1109
1110        # Remove subprocesses created by the test.
1111        self.cleanupSubprocesses()
1112
1113        # This must be the last statement, otherwise teardown hooks or other
1114        # lines might depend on this still being active.
1115        lldb.SBDebugger.Destroy(self.dbg)
1116        del self.dbg
1117
1118        # All modules should be orphaned now so that they can be cleared from
1119        # the shared module cache.
1120        lldb.SBModule.GarbageCollectAllocatedModules()
1121
1122        # Assert that the global module cache is empty.
1123        # FIXME: This assert fails on Windows.
1124        if self.getPlatform() != "windows":
1125            self.assertEqual(lldb.SBModule.GetNumberAllocatedModules(), 0)
1126
1127    # =========================================================
1128    # Various callbacks to allow introspection of test progress
1129    # =========================================================
1130
1131    def markError(self):
1132        """Callback invoked when an error (unexpected exception) errored."""
1133        self.__errored__ = True
1134        with recording(self, False) as sbuf:
1135            # False because there's no need to write "ERROR" to the stderr twice.
1136            # Once by the Python unittest framework, and a second time by us.
1137            print("ERROR", file=sbuf)
1138
1139    def markCleanupError(self):
1140        """Callback invoked when an error occurs while a test is cleaning up."""
1141        self.__cleanup_errored__ = True
1142        with recording(self, False) as sbuf:
1143            # False because there's no need to write "CLEANUP_ERROR" to the stderr twice.
1144            # Once by the Python unittest framework, and a second time by us.
1145            print("CLEANUP_ERROR", file=sbuf)
1146
1147    def markFailure(self):
1148        """Callback invoked when a failure (test assertion failure) occurred."""
1149        self.__failed__ = True
1150        with recording(self, False) as sbuf:
1151            # False because there's no need to write "FAIL" to the stderr twice.
1152            # Once by the Python unittest framework, and a second time by us.
1153            print("FAIL", file=sbuf)
1154
1155    def markExpectedFailure(self, err):
1156        """Callback invoked when an expected failure/error occurred."""
1157        self.__expected__ = True
1158        with recording(self, False) as sbuf:
1159            # False because there's no need to write "expected failure" to the
1160            # stderr twice.
1161            # Once by the Python unittest framework, and a second time by us.
1162            print("expected failure", file=sbuf)
1163
1164    def markSkippedTest(self):
1165        """Callback invoked when a test is skipped."""
1166        self.__skipped__ = True
1167        with recording(self, False) as sbuf:
1168            # False because there's no need to write "skipped test" to the
1169            # stderr twice.
1170            # Once by the Python unittest framework, and a second time by us.
1171            print("skipped test", file=sbuf)
1172
1173    def markUnexpectedSuccess(self):
1174        """Callback invoked when an unexpected success occurred."""
1175        self.__unexpected__ = True
1176        with recording(self, False) as sbuf:
1177            # False because there's no need to write "unexpected success" to the
1178            # stderr twice.
1179            # Once by the Python unittest framework, and a second time by us.
1180            print("unexpected success", file=sbuf)
1181
1182    def getRerunArgs(self):
1183        return " -f %s.%s" % (self.__class__.__name__, self._testMethodName)
1184
1185    def getLogBasenameForCurrentTest(self, prefix="Incomplete"):
1186        """
1187        returns a partial path that can be used as the beginning of the name of multiple
1188        log files pertaining to this test
1189        """
1190        return os.path.join(self.getBuildDir(), prefix)
1191
1192    def dumpSessionInfo(self):
1193        """
1194        Dump the debugger interactions leading to a test error/failure.  This
1195        allows for more convenient postmortem analysis.
1196
1197        See also LLDBTestResult (dotest.py) which is a singlton class derived
1198        from TextTestResult and overwrites addError, addFailure, and
1199        addExpectedFailure methods to allow us to to mark the test instance as
1200        such.
1201        """
1202
1203        # We are here because self.tearDown() detected that this test instance
1204        # either errored or failed.  The lldb.test_result singleton contains
1205        # two lists (errors and failures) which get populated by the unittest
1206        # framework.  Look over there for stack trace information.
1207        #
1208        # The lists contain 2-tuples of TestCase instances and strings holding
1209        # formatted tracebacks.
1210        #
1211        # See http://docs.python.org/library/unittest.html#unittest.TestResult.
1212
1213        # output tracebacks into session
1214        pairs = []
1215        if self.__errored__:
1216            pairs = configuration.test_result.errors
1217            prefix = "Error"
1218        elif self.__cleanup_errored__:
1219            pairs = configuration.test_result.cleanup_errors
1220            prefix = "CleanupError"
1221        elif self.__failed__:
1222            pairs = configuration.test_result.failures
1223            prefix = "Failure"
1224        elif self.__expected__:
1225            pairs = configuration.test_result.expectedFailures
1226            prefix = "ExpectedFailure"
1227        elif self.__skipped__:
1228            prefix = "SkippedTest"
1229        elif self.__unexpected__:
1230            prefix = "UnexpectedSuccess"
1231        else:
1232            prefix = "Success"
1233
1234        session_file = self.getLogBasenameForCurrentTest() + ".log"
1235
1236        # Python 3 doesn't support unbuffered I/O in text mode.  Open buffered.
1237        session = encoded_file.open(session_file, "utf-8", mode="w")
1238
1239        if not self.__unexpected__ and not self.__skipped__:
1240            for test, traceback in pairs:
1241                if test is self:
1242                    print(traceback, file=session)
1243
1244        import datetime
1245
1246        print(
1247            "Session info generated @",
1248            datetime.datetime.now().ctime(),
1249            file=session,
1250        )
1251        session.close()
1252        del session
1253
1254        # process the log files
1255        if prefix != "Success" or lldbtest_config.log_success:
1256            # keep all log files, rename them to include prefix
1257            src_log_basename = self.getLogBasenameForCurrentTest()
1258            dst_log_basename = self.getLogBasenameForCurrentTest(prefix)
1259            for src in self.log_files:
1260                if os.path.isfile(src):
1261                    dst = src.replace(src_log_basename, dst_log_basename)
1262                    if os.name == "nt" and os.path.isfile(dst):
1263                        # On Windows, renaming a -> b will throw an exception if
1264                        # b exists.  On non-Windows platforms it silently
1265                        # replaces the destination.  Ultimately this means that
1266                        # atomic renames are not guaranteed to be possible on
1267                        # Windows, but we need this to work anyway, so just
1268                        # remove the destination first if it already exists.
1269                        remove_file(dst)
1270
1271                    lldbutil.mkdir_p(os.path.dirname(dst))
1272                    os.rename(src, dst)
1273        else:
1274            # success!  (and we don't want log files) delete log files
1275            for log_file in self.log_files:
1276                if os.path.isfile(log_file):
1277                    remove_file(log_file)
1278
1279    # ====================================================
1280    # Config. methods supported through a plugin interface
1281    # (enables reading of the current test configuration)
1282    # ====================================================
1283
1284    def hasXMLSupport(self):
1285        """Returns True if lldb was built with XML support. Use this check to
1286        enable parts of tests, if you want to skip a whole test use skipIfXmlSupportMissing
1287        instead."""
1288        return (
1289            lldb.SBDebugger.GetBuildConfiguration()
1290            .GetValueForKey("xml")
1291            .GetValueForKey("value")
1292            .GetBooleanValue(False)
1293        )
1294
1295    def isMIPS(self):
1296        """Returns true if the architecture is MIPS."""
1297        arch = self.getArchitecture()
1298        if re.match("mips", arch):
1299            return True
1300        return False
1301
1302    def isPPC64le(self):
1303        """Returns true if the architecture is PPC64LE."""
1304        arch = self.getArchitecture()
1305        if re.match("powerpc64le", arch):
1306            return True
1307        return False
1308
1309    def getCPUInfo(self):
1310        triple = self.dbg.GetSelectedPlatform().GetTriple()
1311
1312        # TODO other platforms, please implement this function
1313        if not re.match(".*-.*-linux", triple):
1314            return ""
1315
1316        # Need to do something different for non-Linux/Android targets
1317        cpuinfo_path = self.getBuildArtifact("cpuinfo")
1318        if configuration.lldb_platform_name:
1319            self.runCmd(
1320                'platform get-file "/proc/cpuinfo" ' + cpuinfo_path, check=False
1321            )
1322            if not self.res.Succeeded():
1323                if self.TraceOn():
1324                    print(
1325                        'Failed to get /proc/cpuinfo from remote: "{}"'.format(
1326                            self.res.GetOutput().strip()
1327                        )
1328                    )
1329                    print("All cpuinfo feature checks will fail.")
1330                return ""
1331        else:
1332            cpuinfo_path = "/proc/cpuinfo"
1333
1334        try:
1335            with open(cpuinfo_path, "r") as f:
1336                cpuinfo = f.read()
1337        except:
1338            return ""
1339
1340        return cpuinfo
1341
1342    def isAArch64(self):
1343        """Returns true if the architecture is AArch64."""
1344        arch = self.getArchitecture().lower()
1345        return arch in ["aarch64", "arm64", "arm64e"]
1346
1347    def isAArch64SVE(self):
1348        return self.isAArch64() and "sve" in self.getCPUInfo()
1349
1350    def isAArch64SME(self):
1351        return self.isAArch64() and "sme" in self.getCPUInfo()
1352
1353    def isAArch64SME2(self):
1354        # If you have sme2, you also have sme.
1355        return self.isAArch64() and "sme2" in self.getCPUInfo()
1356
1357    def isAArch64SMEFA64(self):
1358        # smefa64 allows the use of the full A64 instruction set in streaming
1359        # mode. This is required by certain test programs to setup register
1360        # state.
1361        cpuinfo = self.getCPUInfo()
1362        return self.isAArch64() and "sme" in cpuinfo and "smefa64" in cpuinfo
1363
1364    def isAArch64MTE(self):
1365        return self.isAArch64() and "mte" in self.getCPUInfo()
1366
1367    def isAArch64GCS(self):
1368        return self.isAArch64() and "gcs" in self.getCPUInfo()
1369
1370    def isAArch64PAuth(self):
1371        if self.getArchitecture() == "arm64e":
1372            return True
1373        return self.isAArch64() and "paca" in self.getCPUInfo()
1374
1375    def isAArch64FPMR(self):
1376        return self.isAArch64() and "fpmr" in self.getCPUInfo()
1377
1378    def isAArch64Windows(self):
1379        """Returns true if the architecture is AArch64 and platform windows."""
1380        if self.getPlatform() == "windows":
1381            arch = self.getArchitecture().lower()
1382            return arch in ["aarch64", "arm64", "arm64e"]
1383        return False
1384
1385    def isLoongArch(self):
1386        """Returns true if the architecture is LoongArch."""
1387        arch = self.getArchitecture().lower()
1388        return arch in ["loongarch64", "loongarch32"]
1389
1390    def isLoongArchLSX(self):
1391        return self.isLoongArch() and "lsx" in self.getCPUInfo()
1392
1393    def isLoongArchLASX(self):
1394        return self.isLoongArch() and "lasx" in self.getCPUInfo()
1395
1396    def getArchitecture(self):
1397        """Returns the architecture in effect the test suite is running with."""
1398        return lldbplatformutil.getArchitecture()
1399
1400    def getLldbArchitecture(self):
1401        """Returns the architecture of the lldb binary."""
1402        return lldbplatformutil.getLLDBArchitecture()
1403
1404    def getCompiler(self):
1405        """Returns the compiler in effect the test suite is running with."""
1406        return lldbplatformutil.getCompiler()
1407
1408    def getCompilerVersion(self):
1409        """Returns a string that represents the compiler version.
1410        Supports: llvm, clang.
1411        """
1412        return lldbplatformutil.getCompilerVersion()
1413
1414    def getDwarfVersion(self):
1415        """Returns the dwarf version generated by clang or '0'."""
1416        return lldbplatformutil.getDwarfVersion()
1417
1418    def platformIsDarwin(self):
1419        """Returns true if the OS triple for the selected platform is any valid apple OS"""
1420        return lldbplatformutil.platformIsDarwin()
1421
1422    def hasDarwinFramework(self):
1423        return self.darwinWithFramework
1424
1425    def getPlatform(self):
1426        """Returns the target platform the test suite is running on."""
1427        return lldbplatformutil.getPlatform()
1428
1429    def isIntelCompiler(self):
1430        """Returns true if using an Intel (ICC) compiler, false otherwise."""
1431        return any([x in self.getCompiler() for x in ["icc", "icpc", "icl"]])
1432
1433    def expectedCompilerVersion(self, compiler_version):
1434        """Returns True iff compiler_version[1] matches the current compiler version.
1435        Use compiler_version[0] to specify the operator used to determine if a match has occurred.
1436        Any operator other than the following defaults to an equality test:
1437          '>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not'
1438
1439        If the current compiler version cannot be determined, we assume it is close to the top
1440        of trunk, so any less-than or equal-to comparisons will return False, and any
1441        greater-than or not-equal-to comparisons will return True.
1442        """
1443        return lldbplatformutil.expectedCompilerVersion(compiler_version)
1444
1445    def expectedCompiler(self, compilers):
1446        """Returns True iff any element of compilers is a sub-string of the current compiler."""
1447        return lldbplatformutil.expectedCompiler(compilers)
1448
1449    def expectedArch(self, archs):
1450        """Returns True iff any element of archs is a sub-string of the current architecture."""
1451        if archs is None:
1452            return True
1453
1454        for arch in archs:
1455            if arch in self.getArchitecture():
1456                return True
1457
1458        return False
1459
1460    def getRunOptions(self):
1461        """Command line option for -A and -C to run this test again, called from
1462        self.dumpSessionInfo()."""
1463        arch = self.getArchitecture()
1464        comp = self.getCompiler()
1465        option_str = ""
1466        if arch:
1467            option_str = "-A " + arch
1468        if comp:
1469            option_str += " -C " + comp
1470        return option_str
1471
1472    def getDebugInfo(self):
1473        method = getattr(self, self.testMethodName)
1474        return getattr(method, "debug_info", None)
1475
1476    def build(
1477        self,
1478        debug_info=None,
1479        architecture=None,
1480        compiler=None,
1481        dictionary=None,
1482        make_targets=None,
1483    ):
1484        """Platform specific way to build binaries."""
1485        if not architecture and configuration.arch:
1486            architecture = configuration.arch
1487
1488        if debug_info is None:
1489            debug_info = self.getDebugInfo()
1490
1491        dictionary = lldbplatformutil.finalize_build_dictionary(dictionary)
1492
1493        testdir = self.mydir
1494        testname = self.getBuildDirBasename()
1495
1496        module = builder_module()
1497        command = builder_module().getBuildCommand(
1498            debug_info,
1499            architecture,
1500            compiler,
1501            dictionary,
1502            testdir,
1503            testname,
1504            make_targets,
1505        )
1506        if command is None:
1507            raise Exception("Don't know how to build binary")
1508
1509        self.runBuildCommand(command)
1510
1511    def runBuildCommand(self, command):
1512        self.trace(seven.join_for_shell(command))
1513        try:
1514            output = check_output(command, stderr=STDOUT, errors="replace")
1515        except CalledProcessError as cpe:
1516            raise build_exception.BuildError(cpe)
1517        self.trace(output)
1518
1519    # ==================================================
1520    # Build methods supported through a plugin interface
1521    # ==================================================
1522
1523    def getstdlibFlag(self):
1524        """Returns the proper -stdlib flag, or empty if not required."""
1525        if (
1526            self.platformIsDarwin()
1527            or self.getPlatform() == "freebsd"
1528            or self.getPlatform() == "openbsd"
1529        ):
1530            stdlibflag = "-stdlib=libc++"
1531        else:  # this includes NetBSD
1532            stdlibflag = ""
1533        return stdlibflag
1534
1535    def getstdFlag(self):
1536        """Returns the proper stdflag."""
1537        if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion():
1538            stdflag = "-std=c++0x"
1539        else:
1540            stdflag = "-std=c++11"
1541        return stdflag
1542
1543    def buildDriver(self, sources, exe_name, defines=None):
1544        """Platform-specific way to build a program that links with LLDB (via the liblldb.so
1545        or LLDB.framework).
1546        """
1547        if defines is None:
1548            defines = []
1549
1550        stdflag = self.getstdFlag()
1551        stdlibflag = self.getstdlibFlag()
1552        defines = " ".join(["-D{}={}".format(name, value) for name, value in defines])
1553
1554        lib_dir = configuration.lldb_libs_dir
1555        if self.hasDarwinFramework():
1556            d = {
1557                "CXX_SOURCES": sources,
1558                "EXE": exe_name,
1559                "CFLAGS_EXTRAS": "%s %s %s" % (stdflag, stdlibflag, defines),
1560                "FRAMEWORK_INCLUDES": "-F%s" % self.framework_dir,
1561                "LD_EXTRAS": "%s -Wl,-rpath,%s" % (self.lib_lldb, self.framework_dir),
1562            }
1563        elif sys.platform.startswith("win"):
1564            d = {
1565                "CXX_SOURCES": sources,
1566                "EXE": exe_name,
1567                "CFLAGS_EXTRAS": "%s %s -I%s -I%s %s"
1568                % (
1569                    stdflag,
1570                    stdlibflag,
1571                    os.path.join(os.environ["LLDB_SRC"], "include"),
1572                    os.path.join(configuration.lldb_obj_root, "include"),
1573                    defines,
1574                ),
1575                "LD_EXTRAS": "-L%s -lliblldb" % lib_dir,
1576            }
1577        else:
1578            d = {
1579                "CXX_SOURCES": sources,
1580                "EXE": exe_name,
1581                "CFLAGS_EXTRAS": "%s %s -I%s -I%s %s"
1582                % (
1583                    stdflag,
1584                    stdlibflag,
1585                    os.path.join(os.environ["LLDB_SRC"], "include"),
1586                    os.path.join(configuration.lldb_obj_root, "include"),
1587                    defines,
1588                ),
1589                "LD_EXTRAS": "-L%s -llldb -Wl,-rpath,%s" % (lib_dir, lib_dir),
1590            }
1591        if self.TraceOn():
1592            print("Building LLDB Driver (%s) from sources %s" % (exe_name, sources))
1593
1594        self.build(dictionary=d)
1595
1596    def buildLibrary(self, sources, lib_name):
1597        """Platform specific way to build a default library."""
1598
1599        stdflag = self.getstdFlag()
1600
1601        lib_dir = configuration.lldb_libs_dir
1602        if self.hasDarwinFramework():
1603            d = {
1604                "DYLIB_CXX_SOURCES": sources,
1605                "DYLIB_NAME": lib_name,
1606                "CFLAGS_EXTRAS": "%s -stdlib=libc++ -I%s"
1607                % (stdflag, os.path.join(configuration.lldb_obj_root, "include")),
1608                "FRAMEWORK_INCLUDES": "-F%s" % self.framework_dir,
1609                "LD_EXTRAS": "%s -Wl,-rpath,%s -dynamiclib"
1610                % (self.lib_lldb, self.framework_dir),
1611            }
1612        elif self.getPlatform() == "windows":
1613            d = {
1614                "DYLIB_CXX_SOURCES": sources,
1615                "DYLIB_NAME": lib_name,
1616                "CFLAGS_EXTRAS": "%s -I%s -I%s"
1617                % (
1618                    stdflag,
1619                    os.path.join(os.environ["LLDB_SRC"], "include"),
1620                    os.path.join(configuration.lldb_obj_root, "include"),
1621                ),
1622                "LD_EXTRAS": "-shared -l%s\\liblldb.lib" % lib_dir,
1623            }
1624        else:
1625            d = {
1626                "DYLIB_CXX_SOURCES": sources,
1627                "DYLIB_NAME": lib_name,
1628                "CFLAGS_EXTRAS": "%s -I%s -I%s -fPIC"
1629                % (
1630                    stdflag,
1631                    os.path.join(os.environ["LLDB_SRC"], "include"),
1632                    os.path.join(configuration.lldb_obj_root, "include"),
1633                ),
1634                "LD_EXTRAS": "-shared -L%s -llldb -Wl,-rpath,%s" % (lib_dir, lib_dir),
1635            }
1636        if self.TraceOn():
1637            print("Building LLDB Library (%s) from sources %s" % (lib_name, sources))
1638
1639        self.build(dictionary=d)
1640
1641    def buildProgram(self, sources, exe_name):
1642        """Platform specific way to build an executable from C/C++ sources."""
1643        d = {"CXX_SOURCES": sources, "EXE": exe_name}
1644        self.build(dictionary=d)
1645
1646    def findBuiltClang(self):
1647        """Tries to find and use Clang from the build directory as the compiler (instead of the system compiler)."""
1648        paths_to_try = [
1649            "llvm-build/Release+Asserts/x86_64/bin/clang",
1650            "llvm-build/Debug+Asserts/x86_64/bin/clang",
1651            "llvm-build/Release/x86_64/bin/clang",
1652            "llvm-build/Debug/x86_64/bin/clang",
1653        ]
1654        lldb_root_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")
1655        for p in paths_to_try:
1656            path = os.path.join(lldb_root_path, p)
1657            if os.path.exists(path):
1658                return path
1659
1660        # Tries to find clang at the same folder as the lldb
1661        lldb_dir = os.path.dirname(lldbtest_config.lldbExec)
1662        path = shutil.which("clang", path=lldb_dir)
1663        if path is not None:
1664            return path
1665
1666        return os.environ["CC"]
1667
1668    def yaml2obj(self, yaml_path, obj_path, max_size=None):
1669        """
1670        Create an object file at the given path from a yaml file.
1671
1672        Throws subprocess.CalledProcessError if the object could not be created.
1673        """
1674        yaml2obj_bin = configuration.get_yaml2obj_path()
1675        if not yaml2obj_bin:
1676            self.assertTrue(False, "No valid yaml2obj executable specified")
1677        command = [yaml2obj_bin, "-o=%s" % obj_path, yaml_path]
1678        if max_size is not None:
1679            command += ["--max-size=%d" % max_size]
1680        self.runBuildCommand(command)
1681
1682    def cleanup(self, dictionary=None):
1683        """Platform specific way to do cleanup after build."""
1684        module = builder_module()
1685        if not module.cleanup(dictionary):
1686            raise Exception(
1687                "Don't know how to do cleanup with dictionary: " + dictionary
1688            )
1689
1690    def invoke(self, obj, name, trace=False):
1691        """Use reflection to call a method dynamically with no argument."""
1692        trace = True if traceAlways else trace
1693
1694        method = getattr(obj, name)
1695        import inspect
1696
1697        self.assertTrue(
1698            inspect.ismethod(method), name + "is a method name of object: " + str(obj)
1699        )
1700        result = method()
1701        with recording(self, trace) as sbuf:
1702            print(str(method) + ":", result, file=sbuf)
1703        return result
1704
1705    def getLibcPlusPlusLibs(self):
1706        if self.getPlatform() in ("freebsd", "linux", "netbsd", "openbsd"):
1707            return ["libc++.so.1"]
1708        else:
1709            return ["libc++.1.dylib", "libc++abi."]
1710
1711    def run_platform_command(self, cmd):
1712        platform = self.dbg.GetSelectedPlatform()
1713        shell_command = lldb.SBPlatformShellCommand(cmd)
1714        err = platform.Run(shell_command)
1715        return (err, shell_command.GetStatus(), shell_command.GetOutput())
1716
1717    def get_stats(self, options=None):
1718        """
1719        Get the output of the "statistics dump" with optional extra options
1720        and return the JSON as a python dictionary.
1721        """
1722        return_obj = lldb.SBCommandReturnObject()
1723        command = "statistics dump "
1724        if options is not None:
1725            command += options
1726        self.ci.HandleCommand(command, return_obj, False)
1727        metrics_json = return_obj.GetOutput()
1728        return json.loads(metrics_json)
1729
1730
1731# Metaclass for TestBase to change the list of test metods when a new TestCase is loaded.
1732# We change the test methods to create a new test method for each test for each debug info we are
1733# testing. The name of the new test method will be '<original-name>_<debug-info>' and with adding
1734# the new test method we remove the old method at the same time. This functionality can be
1735# supressed by at test case level setting the class attribute NO_DEBUG_INFO_TESTCASE or at test
1736# level by using the decorator @no_debug_info_test.
1737
1738
1739class LLDBTestCaseFactory(type):
1740    def __new__(cls, name, bases, attrs):
1741        original_testcase = super(LLDBTestCaseFactory, cls).__new__(
1742            cls, name, bases, attrs
1743        )
1744        if original_testcase.NO_DEBUG_INFO_TESTCASE:
1745            return original_testcase
1746
1747        # Default implementation for skip/xfail reason based on the debug category,
1748        # where "None" means to run the test as usual.
1749        def no_reason(_):
1750            return None
1751
1752        newattrs = {}
1753        for attrname, attrvalue in attrs.items():
1754            if attrname.startswith("test") and not getattr(
1755                attrvalue, "__no_debug_info_test__", False
1756            ):
1757                # If any debug info categories were explicitly tagged, assume that list to be
1758                # authoritative.  If none were specified, try with all debug
1759                # info formats.
1760                all_dbginfo_categories = set(
1761                    test_categories.debug_info_categories.keys()
1762                )
1763                categories = (
1764                    set(getattr(attrvalue, "categories", [])) & all_dbginfo_categories
1765                )
1766                if not categories:
1767                    categories = [
1768                        category
1769                        for category, can_replicate in test_categories.debug_info_categories.items()
1770                        if can_replicate
1771                    ]
1772
1773                xfail_for_debug_info_cat_fn = getattr(
1774                    attrvalue, "__xfail_for_debug_info_cat_fn__", no_reason
1775                )
1776                skip_for_debug_info_cat_fn = getattr(
1777                    attrvalue, "__skip_for_debug_info_cat_fn__", no_reason
1778                )
1779                for cat in categories:
1780
1781                    @decorators.add_test_categories([cat])
1782                    @wraps(attrvalue)
1783                    def test_method(self, attrvalue=attrvalue):
1784                        return attrvalue(self)
1785
1786                    method_name = attrname + "_" + cat
1787                    test_method.__name__ = method_name
1788                    test_method.debug_info = cat
1789
1790                    xfail_reason = xfail_for_debug_info_cat_fn(cat)
1791                    if xfail_reason:
1792                        test_method = unittest.expectedFailure(test_method)
1793
1794                    skip_reason = skip_for_debug_info_cat_fn(cat)
1795                    if skip_reason:
1796                        test_method = unittest.skip(skip_reason)(test_method)
1797
1798                    newattrs[method_name] = test_method
1799
1800            else:
1801                newattrs[attrname] = attrvalue
1802        return super(LLDBTestCaseFactory, cls).__new__(cls, name, bases, newattrs)
1803
1804
1805# Setup the metaclass for this class to change the list of the test
1806# methods when a new class is loaded
1807
1808
1809class TestBase(Base, metaclass=LLDBTestCaseFactory):
1810    """
1811    This abstract base class is meant to be subclassed.  It provides default
1812    implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(),
1813    among other things.
1814
1815    Important things for test class writers:
1816
1817        - The setUp method sets up things to facilitate subsequent interactions
1818          with the debugger as part of the test.  These include:
1819              - populate the test method name
1820              - create/get a debugger set with synchronous mode (self.dbg)
1821              - get the command interpreter from with the debugger (self.ci)
1822              - create a result object for use with the command interpreter
1823                (self.res)
1824              - plus other stuffs
1825
1826        - The tearDown method tries to perform some necessary cleanup on behalf
1827          of the test to return the debugger to a good state for the next test.
1828          These include:
1829              - execute any tearDown hooks registered by the test method with
1830                TestBase.addTearDownHook(); examples can be found in
1831                settings/TestSettings.py
1832              - kill the inferior process associated with each target, if any,
1833                and, then delete the target from the debugger's target list
1834              - perform build cleanup before running the next test method in the
1835                same test class; examples of registering for this service can be
1836                found in types/TestIntegerTypes.py with the call:
1837                    - self.setTearDownCleanup(dictionary=d)
1838
1839        - Similarly setUpClass and tearDownClass perform classwise setup and
1840          teardown fixtures.  The tearDownClass method invokes a default build
1841          cleanup for the entire test class;  also, subclasses can implement the
1842          classmethod classCleanup(cls) to perform special class cleanup action.
1843
1844        - The instance methods runCmd and expect are used heavily by existing
1845          test cases to send a command to the command interpreter and to perform
1846          string/pattern matching on the output of such command execution.  The
1847          expect method also provides a mode to peform string/pattern matching
1848          without running a command.
1849
1850        - The build method is used to build the binaries used during a
1851          particular test scenario.  A plugin should be provided for the
1852          sys.platform running the test suite.  The Mac OS X implementation is
1853          located in builders/darwin.py.
1854    """
1855
1856    # Subclasses can set this to true (if they don't depend on debug info) to avoid running the
1857    # test multiple times with various debug info types.
1858    NO_DEBUG_INFO_TESTCASE = False
1859
1860    def generateSource(self, source):
1861        template = source + ".template"
1862        temp = os.path.join(self.getSourceDir(), template)
1863        with open(temp, "r") as f:
1864            content = f.read()
1865
1866        public_api_dir = os.path.join(os.environ["LLDB_SRC"], "include", "lldb", "API")
1867
1868        # Look under the include/lldb/API directory and add #include statements
1869        # for all the SB API headers.
1870        public_headers = os.listdir(public_api_dir)
1871        # For different platforms, the include statement can vary.
1872        if self.hasDarwinFramework():
1873            include_stmt = "'#include <%s>' % os.path.join('LLDB', header)"
1874        else:
1875            include_stmt = (
1876                "'#include <%s>' % os.path.join(r'" + public_api_dir + "', header)"
1877            )
1878        list = [
1879            eval(include_stmt)
1880            for header in public_headers
1881            if (header.startswith("SB") and header.endswith(".h"))
1882        ]
1883        includes = "\n".join(list)
1884        new_content = content.replace("%include_SB_APIs%", includes)
1885        new_content = new_content.replace("%SOURCE_DIR%", self.getSourceDir())
1886        src = os.path.join(self.getBuildDir(), source)
1887        with open(src, "w") as f:
1888            f.write(new_content)
1889
1890        self.addTearDownHook(lambda: os.remove(src))
1891
1892    def setUp(self):
1893        # Works with the test driver to conditionally skip tests via
1894        # decorators.
1895        Base.setUp(self)
1896
1897        for s in self.setUpCommands():
1898            self.runCmd(s)
1899
1900        # We want our debugger to be synchronous.
1901        self.dbg.SetAsync(False)
1902
1903        # Retrieve the associated command interpreter instance.
1904        self.ci = self.dbg.GetCommandInterpreter()
1905        if not self.ci:
1906            raise Exception("Could not get the command interpreter")
1907
1908        # And the result object.
1909        self.res = lldb.SBCommandReturnObject()
1910
1911    def registerSharedLibrariesWithTarget(self, target, shlibs):
1912        """If we are remotely running the test suite, register the shared libraries with the target so they get uploaded, otherwise do nothing
1913
1914        Any modules in the target that have their remote install file specification set will
1915        get uploaded to the remote host. This function registers the local copies of the
1916        shared libraries with the target and sets their remote install locations so they will
1917        be uploaded when the target is run.
1918        """
1919        if not shlibs or not self.platformContext:
1920            return None
1921
1922        shlib_environment_var = self.platformContext.shlib_environment_var
1923        shlib_prefix = self.platformContext.shlib_prefix
1924        shlib_extension = "." + self.platformContext.shlib_extension
1925
1926        dirs = []
1927        # Add any shared libraries to our target if remote so they get
1928        # uploaded into the working directory on the remote side
1929        for name in shlibs:
1930            # The path can be a full path to a shared library, or a make file name like "Foo" for
1931            # "libFoo.dylib" or "libFoo.so", or "Foo.so" for "Foo.so" or "libFoo.so", or just a
1932            # basename like "libFoo.so". So figure out which one it is and resolve the local copy
1933            # of the shared library accordingly
1934            if os.path.isfile(name):
1935                local_shlib_path = (
1936                    name  # name is the full path to the local shared library
1937                )
1938            else:
1939                # Check relative names
1940                local_shlib_path = os.path.join(
1941                    self.getBuildDir(), shlib_prefix + name + shlib_extension
1942                )
1943                if not os.path.exists(local_shlib_path):
1944                    local_shlib_path = os.path.join(
1945                        self.getBuildDir(), name + shlib_extension
1946                    )
1947                    if not os.path.exists(local_shlib_path):
1948                        local_shlib_path = os.path.join(self.getBuildDir(), name)
1949
1950                # Make sure we found the local shared library in the above code
1951                self.assertTrue(os.path.exists(local_shlib_path))
1952
1953            # Add the shared library to our target
1954            shlib_module = target.AddModule(local_shlib_path, None, None, None)
1955            if lldb.remote_platform:
1956                # We must set the remote install location if we want the shared library
1957                # to get uploaded to the remote target
1958                remote_shlib_path = lldbutil.append_to_process_working_directory(
1959                    self, os.path.basename(local_shlib_path)
1960                )
1961                shlib_module.SetRemoteInstallFileSpec(
1962                    lldb.SBFileSpec(remote_shlib_path, False)
1963                )
1964                dir_to_add = self.get_process_working_directory()
1965            else:
1966                dir_to_add = os.path.dirname(local_shlib_path)
1967
1968            if dir_to_add not in dirs:
1969                dirs.append(dir_to_add)
1970
1971        env_value = self.platformContext.shlib_path_separator.join(dirs)
1972        return ["%s=%s" % (shlib_environment_var, env_value)]
1973
1974    def registerSanitizerLibrariesWithTarget(self, target):
1975        runtimes = []
1976        for m in target.module_iter():
1977            libspec = m.GetFileSpec()
1978            if "clang_rt" in libspec.GetFilename():
1979                runtimes.append(
1980                    os.path.join(libspec.GetDirectory(), libspec.GetFilename())
1981                )
1982        return self.registerSharedLibrariesWithTarget(target, runtimes)
1983
1984    # utility methods that tests can use to access the current objects
1985    def target(self):
1986        if not self.dbg:
1987            raise Exception("Invalid debugger instance")
1988        return self.dbg.GetSelectedTarget()
1989
1990    def process(self):
1991        if not self.dbg:
1992            raise Exception("Invalid debugger instance")
1993        return self.dbg.GetSelectedTarget().GetProcess()
1994
1995    def thread(self):
1996        if not self.dbg:
1997            raise Exception("Invalid debugger instance")
1998        return self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread()
1999
2000    def frame(self):
2001        if not self.dbg:
2002            raise Exception("Invalid debugger instance")
2003        return (
2004            self.dbg.GetSelectedTarget()
2005            .GetProcess()
2006            .GetSelectedThread()
2007            .GetSelectedFrame()
2008        )
2009
2010    def get_process_working_directory(self):
2011        """Get the working directory that should be used when launching processes for local or remote processes."""
2012        if lldb.remote_platform:
2013            # Remote tests set the platform working directory up in
2014            # TestBase.setUp()
2015            return lldb.remote_platform.GetWorkingDirectory()
2016        else:
2017            # local tests change directory into each test subdirectory
2018            return self.getBuildDir()
2019
2020    def tearDown(self):
2021        # Ensure all the references to SB objects have gone away so that we can
2022        # be sure that all test-specific resources have been freed before we
2023        # attempt to delete the targets.
2024        gc.collect()
2025
2026        # Delete the target(s) from the debugger as a general cleanup step.
2027        # This includes terminating the process for each target, if any.
2028        # We'd like to reuse the debugger for our next test without incurring
2029        # the initialization overhead.
2030        targets = []
2031        for target in self.dbg:
2032            if target:
2033                targets.append(target)
2034                process = target.GetProcess()
2035                if process:
2036                    rc = self.invoke(process, "Kill")
2037                    assert rc.Success()
2038        for target in targets:
2039            self.dbg.DeleteTarget(target)
2040
2041        # Assert that all targets are deleted.
2042        self.assertEqual(self.dbg.GetNumTargets(), 0)
2043
2044        # Do this last, to make sure it's in reverse order from how we setup.
2045        Base.tearDown(self)
2046
2047    def switch_to_thread_with_stop_reason(self, stop_reason):
2048        """
2049        Run the 'thread list' command, and select the thread with stop reason as
2050        'stop_reason'.  If no such thread exists, no select action is done.
2051        """
2052        from .lldbutil import stop_reason_to_str
2053
2054        self.runCmd("thread list")
2055        output = self.res.GetOutput()
2056        thread_line_pattern = re.compile(
2057            "^[ *] thread #([0-9]+):.*stop reason = %s"
2058            % stop_reason_to_str(stop_reason)
2059        )
2060        for line in output.splitlines():
2061            matched = thread_line_pattern.match(line)
2062            if matched:
2063                self.runCmd("thread select %s" % matched.group(1))
2064
2065    def match(
2066        self, str, patterns, msg=None, trace=False, error=False, matching=True, exe=True
2067    ):
2068        """run command in str, and match the result against regexp in patterns returning the match object for the first matching pattern
2069
2070        Otherwise, all the arguments have the same meanings as for the expect function
2071        """
2072
2073        trace = True if traceAlways else trace
2074
2075        if exe:
2076            # First run the command.  If we are expecting error, set check=False.
2077            # Pass the assert message along since it provides more semantic
2078            # info.
2079            self.runCmd(str, msg=msg, trace=(True if trace else False), check=not error)
2080
2081            # Then compare the output against expected strings.
2082            output = self.res.GetError() if error else self.res.GetOutput()
2083
2084            # If error is True, the API client expects the command to fail!
2085            if error:
2086                self.assertFalse(
2087                    self.res.Succeeded(), "Command '" + str + "' is expected to fail!"
2088                )
2089        else:
2090            # No execution required, just compare str against the golden input.
2091            output = str
2092            with recording(self, trace) as sbuf:
2093                print("looking at:", output, file=sbuf)
2094
2095        # The heading says either "Expecting" or "Not expecting".
2096        heading = "Expecting" if matching else "Not expecting"
2097
2098        for pattern in patterns:
2099            # Match Objects always have a boolean value of True.
2100            match_object = re.search(pattern, output)
2101            matched = bool(match_object)
2102            with recording(self, trace) as sbuf:
2103                print("%s pattern: %s" % (heading, pattern), file=sbuf)
2104                print("Matched" if matched else "Not matched", file=sbuf)
2105            if matched:
2106                break
2107
2108        self.assertTrue(
2109            matched if matching else not matched,
2110            msg if msg else EXP_MSG(str, output, exe),
2111        )
2112
2113        return match_object
2114
2115    def check_completion_with_desc(
2116        self, str_input, match_desc_pairs, enforce_order=False
2117    ):
2118        """
2119        Checks that when the given input is completed at the given list of
2120        completions and descriptions is returned.
2121        :param str_input: The input that should be completed. The completion happens at the end of the string.
2122        :param match_desc_pairs: A list of pairs that indicate what completions have to be in the list of
2123                                 completions returned by LLDB. The first element of the pair is the completion
2124                                 string that LLDB should generate and the second element the description.
2125        :param enforce_order: True iff the order in which the completions are returned by LLDB
2126                              should match the order of the match_desc_pairs pairs.
2127        """
2128        interp = self.dbg.GetCommandInterpreter()
2129        match_strings = lldb.SBStringList()
2130        description_strings = lldb.SBStringList()
2131        num_matches = interp.HandleCompletionWithDescriptions(
2132            str_input, len(str_input), 0, -1, match_strings, description_strings
2133        )
2134        self.assertEqual(len(description_strings), len(match_strings))
2135
2136        # The index of the last matched description in description_strings or
2137        # -1 if no description has been matched yet.
2138        last_found_index = -1
2139        out_of_order_errors = ""
2140        missing_pairs = []
2141        for pair in match_desc_pairs:
2142            found_pair = False
2143            for i in range(num_matches + 1):
2144                match_candidate = match_strings.GetStringAtIndex(i)
2145                description_candidate = description_strings.GetStringAtIndex(i)
2146                if match_candidate == pair[0] and description_candidate == pair[1]:
2147                    found_pair = True
2148                    if enforce_order and last_found_index > i:
2149                        new_err = (
2150                            "Found completion "
2151                            + pair[0]
2152                            + " at index "
2153                            + str(i)
2154                            + " in returned completion list but "
2155                            + "should have been after completion "
2156                            + match_strings.GetStringAtIndex(last_found_index)
2157                            + " (index:"
2158                            + str(last_found_index)
2159                            + ")\n"
2160                        )
2161                        out_of_order_errors += new_err
2162                    last_found_index = i
2163                    break
2164            if not found_pair:
2165                missing_pairs.append(pair)
2166
2167        error_msg = ""
2168        got_failure = False
2169        if len(missing_pairs):
2170            got_failure = True
2171            error_msg += "Missing pairs:\n"
2172            for pair in missing_pairs:
2173                error_msg += " [" + pair[0] + ":" + pair[1] + "]\n"
2174        if len(out_of_order_errors):
2175            got_failure = True
2176            error_msg += out_of_order_errors
2177        if got_failure:
2178            error_msg += (
2179                "Got the following " + str(num_matches) + " completions back:\n"
2180            )
2181            for i in range(num_matches + 1):
2182                match_candidate = match_strings.GetStringAtIndex(i)
2183                description_candidate = description_strings.GetStringAtIndex(i)
2184                error_msg += (
2185                    "["
2186                    + match_candidate
2187                    + ":"
2188                    + description_candidate
2189                    + "] index "
2190                    + str(i)
2191                    + "\n"
2192                )
2193            self.assertFalse(got_failure, error_msg)
2194
2195    def complete_from_to(self, str_input, patterns):
2196        """Test that the completion mechanism completes str_input to patterns,
2197        where patterns could be a single pattern-string or a list of
2198        pattern-strings.
2199
2200        If there is only one pattern and it is exactly equal to str_input, this
2201        assumes that there should be no completions provided and that the result
2202        should be the same as the input."""
2203
2204        # Patterns should not be None in order to proceed.
2205        self.assertFalse(patterns is None)
2206        # And should be either a string or list of strings.  Check for list type
2207        # below, if not, make a list out of the singleton string.  If patterns
2208        # is not a string or not a list of strings, there'll be runtime errors
2209        # later on.
2210        if not isinstance(patterns, list):
2211            patterns = [patterns]
2212
2213        interp = self.dbg.GetCommandInterpreter()
2214        match_strings = lldb.SBStringList()
2215        num_matches = interp.HandleCompletion(
2216            str_input, len(str_input), 0, -1, match_strings
2217        )
2218        common_match = match_strings.GetStringAtIndex(0)
2219        if num_matches == 0:
2220            compare_string = str_input
2221        else:
2222            if common_match is not None and len(common_match) > 0:
2223                compare_string = str_input + common_match
2224            else:
2225                compare_string = ""
2226                for idx in range(1, num_matches + 1):
2227                    compare_string += match_strings.GetStringAtIndex(idx) + "\n"
2228
2229        if len(patterns) == 1 and str_input == patterns[0] and num_matches:
2230            self.fail("Expected no completions but got:\n" + compare_string)
2231
2232        for p in patterns:
2233            self.expect(
2234                compare_string,
2235                msg=COMPLETION_MSG(str_input, p, match_strings),
2236                exe=False,
2237                substrs=[p],
2238            )
2239
2240    def completions_match(self, command, completions):
2241        """Checks that the completions for the given command are equal to the
2242        given list of completions"""
2243        interp = self.dbg.GetCommandInterpreter()
2244        match_strings = lldb.SBStringList()
2245        interp.HandleCompletion(command, len(command), 0, -1, match_strings)
2246        # match_strings is a 1-indexed list, so we have to slice...
2247        self.assertCountEqual(
2248            completions, list(match_strings)[1:], "List of returned completion is wrong"
2249        )
2250
2251    def completions_contain(self, command, completions):
2252        """Checks that the completions for the given command contain the given
2253        list of completions."""
2254        interp = self.dbg.GetCommandInterpreter()
2255        match_strings = lldb.SBStringList()
2256        interp.HandleCompletion(command, len(command), 0, -1, match_strings)
2257        for completion in completions:
2258            # match_strings is a 1-indexed list, so we have to slice...
2259            self.assertIn(
2260                completion, list(match_strings)[1:], "Couldn't find expected completion"
2261            )
2262
2263    def filecheck(
2264        self, command, check_file, filecheck_options="", expect_cmd_failure=False
2265    ):
2266        # Run the command.
2267        self.runCmd(
2268            command,
2269            check=(not expect_cmd_failure),
2270            msg="FileCheck'ing result of `{0}`".format(command),
2271        )
2272
2273        self.assertTrue((not expect_cmd_failure) == self.res.Succeeded())
2274
2275        # Get the error text if there was an error, and the regular text if not.
2276        output = self.res.GetOutput() if self.res.Succeeded() else self.res.GetError()
2277
2278        # Assemble the absolute path to the check file. As a convenience for
2279        # LLDB inline tests, assume that the check file is a relative path to
2280        # a file within the inline test directory.
2281        if check_file.endswith(".pyc"):
2282            check_file = check_file[:-1]
2283        check_file_abs = os.path.abspath(check_file)
2284
2285        # Run FileCheck.
2286        filecheck_bin = configuration.get_filecheck_path()
2287        if not filecheck_bin:
2288            self.assertTrue(False, "No valid FileCheck executable specified")
2289        filecheck_args = [filecheck_bin, check_file_abs]
2290        if filecheck_options:
2291            filecheck_args.append(filecheck_options)
2292        subproc = Popen(
2293            filecheck_args,
2294            stdin=PIPE,
2295            stdout=PIPE,
2296            stderr=PIPE,
2297            universal_newlines=True,
2298        )
2299        cmd_stdout, cmd_stderr = subproc.communicate(input=output)
2300        cmd_status = subproc.returncode
2301
2302        filecheck_cmd = " ".join(filecheck_args)
2303        filecheck_trace = """
2304--- FileCheck trace (code={0}) ---
2305{1}
2306
2307FileCheck input:
2308{2}
2309
2310FileCheck output:
2311{3}
2312{4}
2313""".format(
2314            cmd_status, filecheck_cmd, output, cmd_stdout, cmd_stderr
2315        )
2316
2317        trace = cmd_status != 0 or traceAlways
2318        with recording(self, trace) as sbuf:
2319            print(filecheck_trace, file=sbuf)
2320
2321        self.assertTrue(cmd_status == 0)
2322
2323    def expect(
2324        self,
2325        string,
2326        msg=None,
2327        patterns=None,
2328        startstr=None,
2329        endstr=None,
2330        substrs=None,
2331        trace=False,
2332        error=False,
2333        ordered=True,
2334        matching=True,
2335        exe=True,
2336        inHistory=False,
2337    ):
2338        """
2339        Similar to runCmd; with additional expect style output matching ability.
2340
2341        Ask the command interpreter to handle the command and then check its
2342        return status.  The 'msg' parameter specifies an informational assert
2343        message.  We expect the output from running the command to start with
2344        'startstr', matches the substrings contained in 'substrs', and regexp
2345        matches the patterns contained in 'patterns'.
2346
2347        When matching is true and ordered is true, which are both the default,
2348        the strings in the substrs array have to appear in the command output
2349        in the order in which they appear in the array.
2350
2351        If the keyword argument error is set to True, it signifies that the API
2352        client is expecting the command to fail.  In this case, the error stream
2353        from running the command is retrieved and compared against the golden
2354        input, instead.
2355
2356        If the keyword argument matching is set to False, it signifies that the API
2357        client is expecting the output of the command not to match the golden
2358        input.
2359
2360        Finally, the required argument 'string' represents the lldb command to be
2361        sent to the command interpreter.  In case the keyword argument 'exe' is
2362        set to False, the 'string' is treated as a string to be matched/not-matched
2363        against the golden input.
2364        """
2365        # Catch cases where `expect` has been miscalled. Specifically, prevent
2366        # this easy to make mistake:
2367        #     self.expect("lldb command", "some substr")
2368        # The `msg` parameter is used only when a failed match occurs. A failed
2369        # match can only occur when one of `patterns`, `startstr`, `endstr`, or
2370        # `substrs` has been given. Thus, if a `msg` is given, it's an error to
2371        # not also provide one of the matcher parameters.
2372        if msg and not (patterns or startstr or endstr or substrs or error):
2373            assert False, "expect() missing a matcher argument"
2374
2375        # Check `patterns` and `substrs` are not accidentally given as strings.
2376        assert not isinstance(patterns, str), "patterns must be a collection of strings"
2377        assert not isinstance(substrs, str), "substrs must be a collection of strings"
2378
2379        trace = True if traceAlways else trace
2380
2381        if exe:
2382            # First run the command.  If we are expecting error, set check=False.
2383            # Pass the assert message along since it provides more semantic
2384            # info.
2385            self.runCmd(
2386                string,
2387                msg=msg,
2388                trace=(True if trace else False),
2389                check=not error,
2390                inHistory=inHistory,
2391            )
2392
2393            # Then compare the output against expected strings.
2394            output = self.res.GetError() if error else self.res.GetOutput()
2395
2396            # If error is True, the API client expects the command to fail!
2397            if error:
2398                self.assertFalse(
2399                    self.res.Succeeded(),
2400                    "Command '" + string + "' is expected to fail!",
2401                )
2402        else:
2403            # No execution required, just compare string against the golden input.
2404            if isinstance(string, lldb.SBCommandReturnObject):
2405                output = string.GetOutput()
2406            else:
2407                output = string
2408            with recording(self, trace) as sbuf:
2409                print("looking at:", output, file=sbuf)
2410
2411        expecting_str = "Expecting" if matching else "Not expecting"
2412
2413        def found_str(matched):
2414            return "was found" if matched else "was not found"
2415
2416        # To be used as assert fail message and/or trace content
2417        log_lines = [
2418            "{}:".format("Ran command" if exe else "Checking string"),
2419            '"{}"'.format(string),
2420            # Space out command and output
2421            "",
2422        ]
2423        if exe:
2424            # Newline before output to make large strings more readable
2425            log_lines.append("Got output:\n{}".format(output))
2426
2427        # Assume that we start matched if we want a match
2428        # Meaning if you have no conditions, matching or
2429        # not matching will always pass
2430        matched = matching
2431
2432        # We will stop checking on first failure
2433        if startstr:
2434            matched = output.startswith(startstr)
2435            log_lines.append(
2436                '{} start string: "{}" ({})'.format(
2437                    expecting_str, startstr, found_str(matched)
2438                )
2439            )
2440
2441        if endstr and matched == matching:
2442            matched = output.endswith(endstr)
2443            log_lines.append(
2444                '{} end string: "{}" ({})'.format(
2445                    expecting_str, endstr, found_str(matched)
2446                )
2447            )
2448
2449        if substrs and matched == matching:
2450            start = 0
2451            for substr in substrs:
2452                index = output[start:].find(substr)
2453                start = start + index + len(substr) if ordered and matching else 0
2454                matched = index != -1
2455                log_lines.append(
2456                    '{} sub string: "{}" ({})'.format(
2457                        expecting_str, substr, found_str(matched)
2458                    )
2459                )
2460
2461                if matched != matching:
2462                    break
2463
2464        if patterns and matched == matching:
2465            for pattern in patterns:
2466                matched = re.search(pattern, output)
2467
2468                pattern_line = '{} regex pattern: "{}" ({}'.format(
2469                    expecting_str, pattern, found_str(matched)
2470                )
2471                if matched:
2472                    pattern_line += ', matched "{}"'.format(matched.group(0))
2473                pattern_line += ")"
2474                log_lines.append(pattern_line)
2475
2476                # Convert to bool because match objects
2477                # are True-ish but is not True itself
2478                matched = bool(matched)
2479                if matched != matching:
2480                    break
2481
2482        # If a check failed, add any extra assert message
2483        if msg is not None and matched != matching:
2484            log_lines.append(msg)
2485
2486        log_msg = "\n".join(log_lines)
2487        with recording(self, trace) as sbuf:
2488            print(log_msg, file=sbuf)
2489        if matched != matching:
2490            self.fail(log_msg)
2491
2492    def expect_expr(
2493        self,
2494        expr,
2495        result_summary=None,
2496        result_value=None,
2497        result_type=None,
2498        result_children=None,
2499    ):
2500        """
2501        Evaluates the given expression and verifies the result.
2502        :param expr: The expression as a string.
2503        :param result_summary: The summary that the expression should have. None if the summary should not be checked.
2504        :param result_value: The value that the expression should have. None if the value should not be checked.
2505        :param result_type: The type that the expression result should have. None if the type should not be checked.
2506        :param result_children: The expected children of the expression result
2507                                as a list of ValueChecks. None if the children shouldn't be checked.
2508        """
2509        self.assertTrue(
2510            expr.strip() == expr,
2511            "Expression contains trailing/leading whitespace: '" + expr + "'",
2512        )
2513
2514        frame = self.frame()
2515        options = lldb.SBExpressionOptions()
2516
2517        # Disable fix-its that tests don't pass by accident.
2518        options.SetAutoApplyFixIts(False)
2519
2520        # Set the usual default options for normal expressions.
2521        options.SetIgnoreBreakpoints(True)
2522
2523        if self.frame().IsValid():
2524            options.SetLanguage(frame.GuessLanguage())
2525            eval_result = self.frame().EvaluateExpression(expr, options)
2526        else:
2527            target = self.target()
2528            # If there is no selected target, run the expression in the dummy
2529            # target.
2530            if not target.IsValid():
2531                target = self.dbg.GetDummyTarget()
2532            eval_result = target.EvaluateExpression(expr, options)
2533
2534        value_check = ValueCheck(
2535            type=result_type,
2536            value=result_value,
2537            summary=result_summary,
2538            children=result_children,
2539        )
2540        value_check.check_value(self, eval_result, str(eval_result))
2541        return eval_result
2542
2543    def expect_var_path(
2544        self, var_path, summary=None, value=None, type=None, children=None
2545    ):
2546        """
2547        Evaluates the given variable path and verifies the result.
2548        See also 'frame variable' and SBFrame.GetValueForVariablePath.
2549        :param var_path: The variable path as a string.
2550        :param summary: The summary that the variable should have. None if the summary should not be checked.
2551        :param value: The value that the variable should have. None if the value should not be checked.
2552        :param type: The type that the variable result should have. None if the type should not be checked.
2553        :param children: The expected children of the variable  as a list of ValueChecks.
2554                         None if the children shouldn't be checked.
2555        """
2556        self.assertTrue(
2557            var_path.strip() == var_path,
2558            "Expression contains trailing/leading whitespace: '" + var_path + "'",
2559        )
2560
2561        frame = self.frame()
2562        eval_result = frame.GetValueForVariablePath(var_path)
2563
2564        value_check = ValueCheck(
2565            type=type, value=value, summary=summary, children=children
2566        )
2567        value_check.check_value(self, eval_result, str(eval_result))
2568        return eval_result
2569
2570    """Assert that an lldb.SBError is in the "success" state."""
2571
2572    def assertSuccess(self, obj, msg=None):
2573        if not obj.Success():
2574            error = obj.GetCString()
2575            self.fail(self._formatMessage(msg, "'{}' is not success".format(error)))
2576
2577    """Assert that an lldb.SBError is in the "failure" state."""
2578
2579    def assertFailure(self, obj, error_str=None, msg=None):
2580        if obj.Success():
2581            self.fail(self._formatMessage(msg, "Error not in a fail state"))
2582
2583        if error_str is None:
2584            return
2585
2586        error = obj.GetCString()
2587        self.assertEqual(error, error_str, msg)
2588
2589    """Assert that a command return object is successful"""
2590
2591    def assertCommandReturn(self, obj, msg=None):
2592        if not obj.Succeeded():
2593            error = obj.GetError()
2594            self.fail(self._formatMessage(msg, "'{}' is not success".format(error)))
2595
2596    """Assert two states are equal"""
2597
2598    def assertState(self, first, second, msg=None):
2599        if first != second:
2600            error = "{} ({}) != {} ({})".format(
2601                lldbutil.state_type_to_str(first),
2602                first,
2603                lldbutil.state_type_to_str(second),
2604                second,
2605            )
2606            self.fail(self._formatMessage(msg, error))
2607
2608    """Assert two stop reasons are equal"""
2609
2610    def assertStopReason(self, first, second, msg=None):
2611        if first != second:
2612            error = "{} ({}) != {} ({})".format(
2613                lldbutil.stop_reason_to_str(first),
2614                first,
2615                lldbutil.stop_reason_to_str(second),
2616                second,
2617            )
2618            self.fail(self._formatMessage(msg, error))
2619
2620    def createTestTarget(self, file_path=None, msg=None, load_dependent_modules=True):
2621        """
2622        Creates a target from the file found at the given file path.
2623        Asserts that the resulting target is valid.
2624        :param file_path: The file path that should be used to create the target.
2625                          The default argument opens the current default test
2626                          executable in the current test directory.
2627        :param msg: A custom error message.
2628        """
2629        if file_path is None:
2630            file_path = self.getBuildArtifact("a.out")
2631        error = lldb.SBError()
2632        triple = ""
2633        platform = ""
2634        target = self.dbg.CreateTarget(
2635            file_path, triple, platform, load_dependent_modules, error
2636        )
2637        if error.Fail():
2638            err = "Couldn't create target for path '{}': {}".format(
2639                file_path, str(error)
2640            )
2641            self.fail(self._formatMessage(msg, err))
2642
2643        self.assertTrue(target.IsValid(), "Got invalid target without error")
2644        return target
2645
2646    # =================================================
2647    # Misc. helper methods for debugging test execution
2648    # =================================================
2649
2650    def DebugSBValue(self, val):
2651        """Debug print a SBValue object, if traceAlways is True."""
2652        from .lldbutil import value_type_to_str
2653
2654        if not traceAlways:
2655            return
2656
2657        err = sys.stderr
2658        err.write(val.GetName() + ":\n")
2659        err.write("\t" + "TypeName         -> " + val.GetTypeName() + "\n")
2660        err.write("\t" + "ByteSize         -> " + str(val.GetByteSize()) + "\n")
2661        err.write("\t" + "NumChildren      -> " + str(val.GetNumChildren()) + "\n")
2662        err.write("\t" + "Value            -> " + str(val.GetValue()) + "\n")
2663        err.write("\t" + "ValueAsUnsigned  -> " + str(val.GetValueAsUnsigned()) + "\n")
2664        err.write(
2665            "\t" + "ValueType        -> " + value_type_to_str(val.GetValueType()) + "\n"
2666        )
2667        err.write("\t" + "Summary          -> " + str(val.GetSummary()) + "\n")
2668        err.write("\t" + "IsPointerType    -> " + str(val.TypeIsPointerType()) + "\n")
2669        err.write("\t" + "Location         -> " + val.GetLocation() + "\n")
2670
2671    def DebugSBType(self, type):
2672        """Debug print a SBType object, if traceAlways is True."""
2673        if not traceAlways:
2674            return
2675
2676        err = sys.stderr
2677        err.write(type.GetName() + ":\n")
2678        err.write("\t" + "ByteSize        -> " + str(type.GetByteSize()) + "\n")
2679        err.write("\t" + "IsAggregateType   -> " + str(type.IsAggregateType()) + "\n")
2680        err.write("\t" + "IsPointerType   -> " + str(type.IsPointerType()) + "\n")
2681        err.write("\t" + "IsReferenceType -> " + str(type.IsReferenceType()) + "\n")
2682
2683    def DebugPExpect(self, child):
2684        """Debug the spwaned pexpect object."""
2685        if not traceAlways:
2686            return
2687
2688        print(child)
2689
2690    @classmethod
2691    def RemoveTempFile(cls, file):
2692        if os.path.exists(file):
2693            remove_file(file)
2694
2695
2696# On Windows, the first attempt to delete a recently-touched file can fail
2697# because of a race with antimalware scanners.  This function will detect a
2698# failure and retry.
2699
2700
2701def remove_file(file, num_retries=1, sleep_duration=0.5):
2702    for i in range(num_retries + 1):
2703        try:
2704            os.remove(file)
2705            return True
2706        except:
2707            time.sleep(sleep_duration)
2708            continue
2709    return False
2710