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