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