|  | """ | 
|  | LLDB module which provides the abstract base class of lldb test case. | 
|  |  | 
|  | The concrete subclass can override lldbtest.TesBase in order to inherit the | 
|  | common behavior for unitest.TestCase.setUp/tearDown implemented in this file. | 
|  |  | 
|  | The subclass should override the attribute mydir in order for the python runtime | 
|  | to locate the individual test cases when running as part of a large test suite | 
|  | or when running each test case as a separate python invocation. | 
|  |  | 
|  | ./dotest.py provides a test driver which sets up the environment to run the | 
|  | entire of part of the test suite .  Example: | 
|  |  | 
|  | # Exercises the test suite in the types directory.... | 
|  | /Volumes/data/lldb/svn/ToT/test $ ./dotest.py -A x86_64 types | 
|  | ... | 
|  |  | 
|  | Session logs for test failures/errors/unexpected successes will go into directory '2012-05-16-13_35_42' | 
|  | Command invoked: python ./dotest.py -A x86_64 types | 
|  | compilers=['clang'] | 
|  |  | 
|  | Configuration: arch=x86_64 compiler=clang | 
|  | ---------------------------------------------------------------------- | 
|  | Collected 72 tests | 
|  |  | 
|  | ........................................................................ | 
|  | ---------------------------------------------------------------------- | 
|  | Ran 72 tests in 135.468s | 
|  |  | 
|  | OK | 
|  | $ | 
|  | """ | 
|  |  | 
|  | from __future__ import absolute_import | 
|  | from __future__ import print_function | 
|  |  | 
|  | # System modules | 
|  | import abc | 
|  | import collections | 
|  | from functools import wraps | 
|  | import gc | 
|  | import glob | 
|  | import inspect | 
|  | import io | 
|  | import os.path | 
|  | import re | 
|  | import shutil | 
|  | import signal | 
|  | from subprocess import * | 
|  | import sys | 
|  | import time | 
|  | import traceback | 
|  | import types | 
|  | import distutils.spawn | 
|  |  | 
|  | # Third-party modules | 
|  | import unittest2 | 
|  | from six import add_metaclass | 
|  | from six import StringIO as SixStringIO | 
|  | import six | 
|  |  | 
|  | # LLDB modules | 
|  | import use_lldb_suite | 
|  | import lldb | 
|  | from . import configuration | 
|  | from . import decorators | 
|  | from . import lldbplatformutil | 
|  | from . import lldbtest_config | 
|  | from . import lldbutil | 
|  | from . import test_categories | 
|  | from lldbsuite.support import encoded_file | 
|  | from lldbsuite.support import funcutils | 
|  |  | 
|  | # See also dotest.parseOptionsAndInitTestdirs(), where the environment variables | 
|  | # LLDB_COMMAND_TRACE is set from '-t' option. | 
|  |  | 
|  | # By default, traceAlways is False. | 
|  | if "LLDB_COMMAND_TRACE" in os.environ and os.environ[ | 
|  | "LLDB_COMMAND_TRACE"] == "YES": | 
|  | traceAlways = True | 
|  | else: | 
|  | traceAlways = False | 
|  |  | 
|  | # By default, doCleanup is True. | 
|  | if "LLDB_DO_CLEANUP" in os.environ and os.environ["LLDB_DO_CLEANUP"] == "NO": | 
|  | doCleanup = False | 
|  | else: | 
|  | doCleanup = True | 
|  |  | 
|  |  | 
|  | # | 
|  | # Some commonly used assert messages. | 
|  | # | 
|  |  | 
|  | COMMAND_FAILED_AS_EXPECTED = "Command has failed as expected" | 
|  |  | 
|  | CURRENT_EXECUTABLE_SET = "Current executable set successfully" | 
|  |  | 
|  | PROCESS_IS_VALID = "Process is valid" | 
|  |  | 
|  | PROCESS_KILLED = "Process is killed successfully" | 
|  |  | 
|  | PROCESS_EXITED = "Process exited successfully" | 
|  |  | 
|  | PROCESS_STOPPED = "Process status should be stopped" | 
|  |  | 
|  | RUN_SUCCEEDED = "Process is launched successfully" | 
|  |  | 
|  | RUN_COMPLETED = "Process exited successfully" | 
|  |  | 
|  | BACKTRACE_DISPLAYED_CORRECTLY = "Backtrace displayed correctly" | 
|  |  | 
|  | BREAKPOINT_CREATED = "Breakpoint created successfully" | 
|  |  | 
|  | BREAKPOINT_STATE_CORRECT = "Breakpoint state is correct" | 
|  |  | 
|  | BREAKPOINT_PENDING_CREATED = "Pending breakpoint created successfully" | 
|  |  | 
|  | BREAKPOINT_HIT_ONCE = "Breakpoint resolved with hit cout = 1" | 
|  |  | 
|  | BREAKPOINT_HIT_TWICE = "Breakpoint resolved with hit cout = 2" | 
|  |  | 
|  | BREAKPOINT_HIT_THRICE = "Breakpoint resolved with hit cout = 3" | 
|  |  | 
|  | MISSING_EXPECTED_REGISTERS = "At least one expected register is unavailable." | 
|  |  | 
|  | OBJECT_PRINTED_CORRECTLY = "Object printed correctly" | 
|  |  | 
|  | SOURCE_DISPLAYED_CORRECTLY = "Source code displayed correctly" | 
|  |  | 
|  | STEP_OUT_SUCCEEDED = "Thread step-out succeeded" | 
|  |  | 
|  | STOPPED_DUE_TO_EXC_BAD_ACCESS = "Process should be stopped due to bad access exception" | 
|  |  | 
|  | STOPPED_DUE_TO_ASSERT = "Process should be stopped due to an assertion" | 
|  |  | 
|  | STOPPED_DUE_TO_BREAKPOINT = "Process should be stopped due to breakpoint" | 
|  |  | 
|  | STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS = "%s, %s" % ( | 
|  | STOPPED_DUE_TO_BREAKPOINT, "instead, the actual stop reason is: '%s'") | 
|  |  | 
|  | STOPPED_DUE_TO_BREAKPOINT_CONDITION = "Stopped due to breakpoint condition" | 
|  |  | 
|  | STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT = "Stopped due to breakpoint and ignore count" | 
|  |  | 
|  | STOPPED_DUE_TO_SIGNAL = "Process state is stopped due to signal" | 
|  |  | 
|  | STOPPED_DUE_TO_STEP_IN = "Process state is stopped due to step in" | 
|  |  | 
|  | STOPPED_DUE_TO_WATCHPOINT = "Process should be stopped due to watchpoint" | 
|  |  | 
|  | DATA_TYPES_DISPLAYED_CORRECTLY = "Data type(s) displayed correctly" | 
|  |  | 
|  | VALID_BREAKPOINT = "Got a valid breakpoint" | 
|  |  | 
|  | VALID_BREAKPOINT_LOCATION = "Got a valid breakpoint location" | 
|  |  | 
|  | VALID_COMMAND_INTERPRETER = "Got a valid command interpreter" | 
|  |  | 
|  | VALID_FILESPEC = "Got a valid filespec" | 
|  |  | 
|  | VALID_MODULE = "Got a valid module" | 
|  |  | 
|  | VALID_PROCESS = "Got a valid process" | 
|  |  | 
|  | VALID_SYMBOL = "Got a valid symbol" | 
|  |  | 
|  | VALID_TARGET = "Got a valid target" | 
|  |  | 
|  | VALID_PLATFORM = "Got a valid platform" | 
|  |  | 
|  | VALID_TYPE = "Got a valid type" | 
|  |  | 
|  | VALID_VARIABLE = "Got a valid variable" | 
|  |  | 
|  | VARIABLES_DISPLAYED_CORRECTLY = "Variable(s) displayed correctly" | 
|  |  | 
|  | WATCHPOINT_CREATED = "Watchpoint created successfully" | 
|  |  | 
|  |  | 
|  | def CMD_MSG(str): | 
|  | '''A generic "Command '%s' returns successfully" message generator.''' | 
|  | return "Command '%s' returns successfully" % str | 
|  |  | 
|  |  | 
|  | def COMPLETION_MSG(str_before, str_after, completions): | 
|  | '''A generic message generator for the completion mechanism.''' | 
|  | return ("'%s' successfully completes to '%s', but completions were:\n%s" | 
|  | % (str_before, str_after, "\n".join(completions))) | 
|  |  | 
|  |  | 
|  | def EXP_MSG(str, actual, exe): | 
|  | '''A generic "'%s' returns expected result" message generator if exe. | 
|  | Otherwise, it generates "'%s' matches expected result" message.''' | 
|  |  | 
|  | return "'%s' %s expected result, got '%s'" % ( | 
|  | str, 'returns' if exe else 'matches', actual.strip()) | 
|  |  | 
|  |  | 
|  | def SETTING_MSG(setting): | 
|  | '''A generic "Value of setting '%s' is correct" message generator.''' | 
|  | return "Value of setting '%s' is correct" % setting | 
|  |  | 
|  |  | 
|  | def EnvArray(): | 
|  | """Returns an env variable array from the os.environ map object.""" | 
|  | return list(map(lambda k, | 
|  | v: k + "=" + v, | 
|  | list(os.environ.keys()), | 
|  | list(os.environ.values()))) | 
|  |  | 
|  |  | 
|  | def line_number(filename, string_to_match): | 
|  | """Helper function to return the line number of the first matched string.""" | 
|  | with io.open(filename, mode='r', encoding="utf-8") as f: | 
|  | for i, line in enumerate(f): | 
|  | if line.find(string_to_match) != -1: | 
|  | # Found our match. | 
|  | return i + 1 | 
|  | raise Exception( | 
|  | "Unable to find '%s' within file %s" % | 
|  | (string_to_match, filename)) | 
|  |  | 
|  | def get_line(filename, line_number): | 
|  | """Return the text of the line at the 1-based line number.""" | 
|  | with io.open(filename, mode='r', encoding="utf-8") as f: | 
|  | return f.readlines()[line_number - 1] | 
|  |  | 
|  | def pointer_size(): | 
|  | """Return the pointer size of the host system.""" | 
|  | import ctypes | 
|  | a_pointer = ctypes.c_void_p(0xffff) | 
|  | return 8 * ctypes.sizeof(a_pointer) | 
|  |  | 
|  |  | 
|  | def is_exe(fpath): | 
|  | """Returns true if fpath is an executable.""" | 
|  | return os.path.isfile(fpath) and os.access(fpath, os.X_OK) | 
|  |  | 
|  |  | 
|  | def which(program): | 
|  | """Returns the full path to a program; None otherwise.""" | 
|  | fpath, fname = os.path.split(program) | 
|  | if fpath: | 
|  | if is_exe(program): | 
|  | return program | 
|  | else: | 
|  | for path in os.environ["PATH"].split(os.pathsep): | 
|  | exe_file = os.path.join(path, program) | 
|  | if is_exe(exe_file): | 
|  | return exe_file | 
|  | return None | 
|  |  | 
|  |  | 
|  | class recording(SixStringIO): | 
|  | """ | 
|  | A nice little context manager for recording the debugger interactions into | 
|  | our session object.  If trace flag is ON, it also emits the interactions | 
|  | into the stderr. | 
|  | """ | 
|  |  | 
|  | def __init__(self, test, trace): | 
|  | """Create a SixStringIO instance; record the session obj and trace flag.""" | 
|  | SixStringIO.__init__(self) | 
|  | # The test might not have undergone the 'setUp(self)' phase yet, so that | 
|  | # the attribute 'session' might not even exist yet. | 
|  | self.session = getattr(test, "session", None) if test else None | 
|  | self.trace = trace | 
|  |  | 
|  | def __enter__(self): | 
|  | """ | 
|  | Context management protocol on entry to the body of the with statement. | 
|  | Just return the SixStringIO object. | 
|  | """ | 
|  | return self | 
|  |  | 
|  | def __exit__(self, type, value, tb): | 
|  | """ | 
|  | Context management protocol on exit from the body of the with statement. | 
|  | If trace is ON, it emits the recordings into stderr.  Always add the | 
|  | recordings to our session object.  And close the SixStringIO object, too. | 
|  | """ | 
|  | if self.trace: | 
|  | print(self.getvalue(), file=sys.stderr) | 
|  | if self.session: | 
|  | print(self.getvalue(), file=self.session) | 
|  | self.close() | 
|  |  | 
|  |  | 
|  | @add_metaclass(abc.ABCMeta) | 
|  | class _BaseProcess(object): | 
|  |  | 
|  | @abc.abstractproperty | 
|  | def pid(self): | 
|  | """Returns process PID if has been launched already.""" | 
|  |  | 
|  | @abc.abstractmethod | 
|  | def launch(self, executable, args): | 
|  | """Launches new process with given executable and args.""" | 
|  |  | 
|  | @abc.abstractmethod | 
|  | def terminate(self): | 
|  | """Terminates previously launched process..""" | 
|  |  | 
|  |  | 
|  | class _LocalProcess(_BaseProcess): | 
|  |  | 
|  | def __init__(self, trace_on): | 
|  | self._proc = None | 
|  | self._trace_on = trace_on | 
|  | self._delayafterterminate = 0.1 | 
|  |  | 
|  | @property | 
|  | def pid(self): | 
|  | return self._proc.pid | 
|  |  | 
|  | def launch(self, executable, args): | 
|  | self._proc = Popen( | 
|  | [executable] + args, | 
|  | stdout=open( | 
|  | os.devnull) if not self._trace_on else None, | 
|  | stdin=PIPE) | 
|  |  | 
|  | def terminate(self): | 
|  | if self._proc.poll() is None: | 
|  | # Terminate _proc like it does the pexpect | 
|  | signals_to_try = [ | 
|  | sig for sig in [ | 
|  | 'SIGHUP', | 
|  | 'SIGCONT', | 
|  | 'SIGINT'] if sig in dir(signal)] | 
|  | for sig in signals_to_try: | 
|  | try: | 
|  | self._proc.send_signal(getattr(signal, sig)) | 
|  | time.sleep(self._delayafterterminate) | 
|  | if self._proc.poll() is not None: | 
|  | return | 
|  | except ValueError: | 
|  | pass  # Windows says SIGINT is not a valid signal to send | 
|  | self._proc.terminate() | 
|  | time.sleep(self._delayafterterminate) | 
|  | if self._proc.poll() is not None: | 
|  | return | 
|  | self._proc.kill() | 
|  | time.sleep(self._delayafterterminate) | 
|  |  | 
|  | def poll(self): | 
|  | return self._proc.poll() | 
|  |  | 
|  |  | 
|  | class _RemoteProcess(_BaseProcess): | 
|  |  | 
|  | def __init__(self, install_remote): | 
|  | self._pid = None | 
|  | self._install_remote = install_remote | 
|  |  | 
|  | @property | 
|  | def pid(self): | 
|  | return self._pid | 
|  |  | 
|  | def launch(self, executable, args): | 
|  | if self._install_remote: | 
|  | src_path = executable | 
|  | dst_path = lldbutil.join_remote_paths( | 
|  | lldb.remote_platform.GetWorkingDirectory(), os.path.basename(executable)) | 
|  |  | 
|  | dst_file_spec = lldb.SBFileSpec(dst_path, False) | 
|  | err = lldb.remote_platform.Install( | 
|  | lldb.SBFileSpec(src_path, True), dst_file_spec) | 
|  | if err.Fail(): | 
|  | raise Exception( | 
|  | "remote_platform.Install('%s', '%s') failed: %s" % | 
|  | (src_path, dst_path, err)) | 
|  | else: | 
|  | dst_path = executable | 
|  | dst_file_spec = lldb.SBFileSpec(executable, False) | 
|  |  | 
|  | launch_info = lldb.SBLaunchInfo(args) | 
|  | launch_info.SetExecutableFile(dst_file_spec, True) | 
|  | launch_info.SetWorkingDirectory( | 
|  | lldb.remote_platform.GetWorkingDirectory()) | 
|  |  | 
|  | # Redirect stdout and stderr to /dev/null | 
|  | launch_info.AddSuppressFileAction(1, False, True) | 
|  | launch_info.AddSuppressFileAction(2, False, True) | 
|  |  | 
|  | err = lldb.remote_platform.Launch(launch_info) | 
|  | if err.Fail(): | 
|  | raise Exception( | 
|  | "remote_platform.Launch('%s', '%s') failed: %s" % | 
|  | (dst_path, args, err)) | 
|  | self._pid = launch_info.GetProcessID() | 
|  |  | 
|  | def terminate(self): | 
|  | lldb.remote_platform.Kill(self._pid) | 
|  |  | 
|  | # From 2.7's subprocess.check_output() convenience function. | 
|  | # Return a tuple (stdoutdata, stderrdata). | 
|  |  | 
|  |  | 
|  | def system(commands, **kwargs): | 
|  | r"""Run an os command with arguments and return its output as a byte string. | 
|  |  | 
|  | If the exit code was non-zero it raises a CalledProcessError.  The | 
|  | CalledProcessError object will have the return code in the returncode | 
|  | attribute and output in the output attribute. | 
|  |  | 
|  | The arguments are the same as for the Popen constructor.  Example: | 
|  |  | 
|  | >>> check_output(["ls", "-l", "/dev/null"]) | 
|  | 'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n' | 
|  |  | 
|  | The stdout argument is not allowed as it is used internally. | 
|  | To capture standard error in the result, use stderr=STDOUT. | 
|  |  | 
|  | >>> check_output(["/bin/sh", "-c", | 
|  | ...               "ls -l non_existent_file ; exit 0"], | 
|  | ...              stderr=STDOUT) | 
|  | 'ls: non_existent_file: No such file or directory\n' | 
|  | """ | 
|  |  | 
|  | # Assign the sender object to variable 'test' and remove it from kwargs. | 
|  | test = kwargs.pop('sender', None) | 
|  |  | 
|  | # [['make', 'clean', 'foo'], ['make', 'foo']] -> ['make clean foo', 'make foo'] | 
|  | commandList = [' '.join(x) for x in commands] | 
|  | output = "" | 
|  | error = "" | 
|  | for shellCommand in commandList: | 
|  | if 'stdout' in kwargs: | 
|  | raise ValueError( | 
|  | 'stdout argument not allowed, it will be overridden.') | 
|  | if 'shell' in kwargs and kwargs['shell'] == False: | 
|  | raise ValueError('shell=False not allowed') | 
|  | process = Popen( | 
|  | shellCommand, | 
|  | stdout=PIPE, | 
|  | stderr=PIPE, | 
|  | shell=True, | 
|  | universal_newlines=True, | 
|  | **kwargs) | 
|  | pid = process.pid | 
|  | this_output, this_error = process.communicate() | 
|  | retcode = process.poll() | 
|  |  | 
|  | # Enable trace on failure return while tracking down FreeBSD buildbot | 
|  | # issues | 
|  | trace = traceAlways | 
|  | if not trace and retcode and sys.platform.startswith("freebsd"): | 
|  | trace = True | 
|  |  | 
|  | with recording(test, trace) as sbuf: | 
|  | print(file=sbuf) | 
|  | print("os command:", shellCommand, file=sbuf) | 
|  | print("with pid:", pid, file=sbuf) | 
|  | print("stdout:", this_output, file=sbuf) | 
|  | print("stderr:", this_error, file=sbuf) | 
|  | print("retcode:", retcode, file=sbuf) | 
|  | print(file=sbuf) | 
|  |  | 
|  | if retcode: | 
|  | cmd = kwargs.get("args") | 
|  | if cmd is None: | 
|  | cmd = shellCommand | 
|  | cpe = CalledProcessError(retcode, cmd) | 
|  | # Ensure caller can access the stdout/stderr. | 
|  | cpe.lldb_extensions = { | 
|  | "stdout_content": this_output, | 
|  | "stderr_content": this_error, | 
|  | "command": shellCommand | 
|  | } | 
|  | raise cpe | 
|  | output = output + this_output | 
|  | error = error + this_error | 
|  | return (output, error) | 
|  |  | 
|  |  | 
|  | def getsource_if_available(obj): | 
|  | """ | 
|  | Return the text of the source code for an object if available.  Otherwise, | 
|  | a print representation is returned. | 
|  | """ | 
|  | import inspect | 
|  | try: | 
|  | return inspect.getsource(obj) | 
|  | except: | 
|  | return repr(obj) | 
|  |  | 
|  |  | 
|  | def builder_module(): | 
|  | if sys.platform.startswith("freebsd"): | 
|  | return __import__("builder_freebsd") | 
|  | if sys.platform.startswith("openbsd"): | 
|  | return __import__("builder_openbsd") | 
|  | if sys.platform.startswith("netbsd"): | 
|  | return __import__("builder_netbsd") | 
|  | if sys.platform.startswith("linux"): | 
|  | # sys.platform with Python-3.x returns 'linux', but with | 
|  | # Python-2.x it returns 'linux2'. | 
|  | return __import__("builder_linux") | 
|  | return __import__("builder_" + sys.platform) | 
|  |  | 
|  |  | 
|  | class Base(unittest2.TestCase): | 
|  | """ | 
|  | Abstract base for performing lldb (see TestBase) or other generic tests (see | 
|  | BenchBase for one example).  lldbtest.Base works with the test driver to | 
|  | accomplish things. | 
|  |  | 
|  | """ | 
|  |  | 
|  | # The concrete subclass should override this attribute. | 
|  | mydir = None | 
|  |  | 
|  | # Keep track of the old current working directory. | 
|  | oldcwd = None | 
|  |  | 
|  | @staticmethod | 
|  | def compute_mydir(test_file): | 
|  | '''Subclasses should call this function to correctly calculate the | 
|  | required "mydir" attribute as follows: | 
|  |  | 
|  | mydir = TestBase.compute_mydir(__file__) | 
|  | ''' | 
|  | # /abs/path/to/packages/group/subdir/mytest.py -> group/subdir | 
|  | rel_prefix = test_file[len(os.environ["LLDB_TEST"]) + 1:] | 
|  | return os.path.dirname(rel_prefix) | 
|  |  | 
|  | def TraceOn(self): | 
|  | """Returns True if we are in trace mode (tracing detailed test execution).""" | 
|  | return traceAlways | 
|  |  | 
|  | @classmethod | 
|  | def setUpClass(cls): | 
|  | """ | 
|  | Python unittest framework class setup fixture. | 
|  | Do current directory manipulation. | 
|  | """ | 
|  | # Fail fast if 'mydir' attribute is not overridden. | 
|  | if not cls.mydir or len(cls.mydir) == 0: | 
|  | raise Exception("Subclasses must override the 'mydir' attribute.") | 
|  |  | 
|  | # Save old working directory. | 
|  | cls.oldcwd = os.getcwd() | 
|  |  | 
|  | # Change current working directory if ${LLDB_TEST} is defined. | 
|  | # See also dotest.py which sets up ${LLDB_TEST}. | 
|  | if ("LLDB_TEST" in os.environ): | 
|  | full_dir = os.path.join(os.environ["LLDB_TEST"], | 
|  | cls.mydir) | 
|  | if traceAlways: | 
|  | print("Change dir to:", full_dir, file=sys.stderr) | 
|  | os.chdir(full_dir) | 
|  |  | 
|  | # Set platform context. | 
|  | cls.platformContext = lldbplatformutil.createPlatformContext() | 
|  |  | 
|  | @classmethod | 
|  | def tearDownClass(cls): | 
|  | """ | 
|  | Python unittest framework class teardown fixture. | 
|  | Do class-wide cleanup. | 
|  | """ | 
|  |  | 
|  | if doCleanup: | 
|  | # First, let's do the platform-specific cleanup. | 
|  | module = builder_module() | 
|  | module.cleanup() | 
|  |  | 
|  | # Subclass might have specific cleanup function defined. | 
|  | if getattr(cls, "classCleanup", None): | 
|  | if traceAlways: | 
|  | print( | 
|  | "Call class-specific cleanup function for class:", | 
|  | cls, | 
|  | file=sys.stderr) | 
|  | try: | 
|  | cls.classCleanup() | 
|  | except: | 
|  | exc_type, exc_value, exc_tb = sys.exc_info() | 
|  | traceback.print_exception(exc_type, exc_value, exc_tb) | 
|  |  | 
|  | # Restore old working directory. | 
|  | if traceAlways: | 
|  | print("Restore dir to:", cls.oldcwd, file=sys.stderr) | 
|  | os.chdir(cls.oldcwd) | 
|  |  | 
|  | @classmethod | 
|  | def skipLongRunningTest(cls): | 
|  | """ | 
|  | By default, we skip long running test case. | 
|  | This can be overridden by passing '-l' to the test driver (dotest.py). | 
|  | """ | 
|  | if "LLDB_SKIP_LONG_RUNNING_TEST" in os.environ and "NO" == os.environ[ | 
|  | "LLDB_SKIP_LONG_RUNNING_TEST"]: | 
|  | return False | 
|  | else: | 
|  | return True | 
|  |  | 
|  | def enableLogChannelsForCurrentTest(self): | 
|  | if len(lldbtest_config.channels) == 0: | 
|  | return | 
|  |  | 
|  | # if debug channels are specified in lldbtest_config.channels, | 
|  | # create a new set of log files for every test | 
|  | log_basename = self.getLogBasenameForCurrentTest() | 
|  |  | 
|  | # confirm that the file is writeable | 
|  | host_log_path = "{}-host.log".format(log_basename) | 
|  | open(host_log_path, 'w').close() | 
|  |  | 
|  | log_enable = "log enable -Tpn -f {} ".format(host_log_path) | 
|  | for channel_with_categories in lldbtest_config.channels: | 
|  | channel_then_categories = channel_with_categories.split(' ', 1) | 
|  | channel = channel_then_categories[0] | 
|  | if len(channel_then_categories) > 1: | 
|  | categories = channel_then_categories[1] | 
|  | else: | 
|  | categories = "default" | 
|  |  | 
|  | if channel == "gdb-remote" and lldb.remote_platform is None: | 
|  | # communicate gdb-remote categories to debugserver | 
|  | os.environ["LLDB_DEBUGSERVER_LOG_FLAGS"] = categories | 
|  |  | 
|  | self.ci.HandleCommand( | 
|  | log_enable + channel_with_categories, self.res) | 
|  | if not self.res.Succeeded(): | 
|  | raise Exception( | 
|  | 'log enable failed (check LLDB_LOG_OPTION env variable)') | 
|  |  | 
|  | # Communicate log path name to debugserver & lldb-server | 
|  | # For remote debugging, these variables need to be set when starting the platform | 
|  | # instance. | 
|  | if lldb.remote_platform is None: | 
|  | server_log_path = "{}-server.log".format(log_basename) | 
|  | open(server_log_path, 'w').close() | 
|  | os.environ["LLDB_DEBUGSERVER_LOG_FILE"] = server_log_path | 
|  |  | 
|  | # Communicate channels to lldb-server | 
|  | os.environ["LLDB_SERVER_LOG_CHANNELS"] = ":".join( | 
|  | lldbtest_config.channels) | 
|  |  | 
|  | self.addTearDownHook(self.disableLogChannelsForCurrentTest) | 
|  |  | 
|  | def disableLogChannelsForCurrentTest(self): | 
|  | # close all log files that we opened | 
|  | for channel_and_categories in lldbtest_config.channels: | 
|  | # channel format - <channel-name> [<category0> [<category1> ...]] | 
|  | channel = channel_and_categories.split(' ', 1)[0] | 
|  | self.ci.HandleCommand("log disable " + channel, self.res) | 
|  | if not self.res.Succeeded(): | 
|  | raise Exception( | 
|  | 'log disable failed (check LLDB_LOG_OPTION env variable)') | 
|  |  | 
|  | # Retrieve the server log (if any) from the remote system. It is assumed the server log | 
|  | # is writing to the "server.log" file in the current test directory. This can be | 
|  | # achieved by setting LLDB_DEBUGSERVER_LOG_FILE="server.log" when starting remote | 
|  | # platform. If the remote logging is not enabled, then just let the Get() command silently | 
|  | # fail. | 
|  | if lldb.remote_platform: | 
|  | lldb.remote_platform.Get( | 
|  | lldb.SBFileSpec("server.log"), lldb.SBFileSpec( | 
|  | self.getLogBasenameForCurrentTest() + "-server.log")) | 
|  |  | 
|  | def setPlatformWorkingDir(self): | 
|  | if not lldb.remote_platform or not configuration.lldb_platform_working_dir: | 
|  | return | 
|  |  | 
|  | components = self.mydir.split(os.path.sep) + [str(self.test_number), self.getBuildDirBasename()] | 
|  | remote_test_dir = configuration.lldb_platform_working_dir | 
|  | for c in components: | 
|  | remote_test_dir = lldbutil.join_remote_paths(remote_test_dir, c) | 
|  | error = lldb.remote_platform.MakeDirectory( | 
|  | remote_test_dir, 448)  # 448 = 0o700 | 
|  | if error.Fail(): | 
|  | raise Exception("making remote directory '%s': %s" % ( | 
|  | remote_test_dir, error)) | 
|  |  | 
|  | lldb.remote_platform.SetWorkingDirectory(remote_test_dir) | 
|  |  | 
|  | # This function removes all files from the current working directory while leaving | 
|  | # the directories in place. The cleaup is required to reduce the disk space required | 
|  | # by the test suite while leaving the directories untouched is neccessary because | 
|  | # sub-directories might belong to an other test | 
|  | def clean_working_directory(): | 
|  | # TODO: Make it working on Windows when we need it for remote debugging support | 
|  | # TODO: Replace the heuristic to remove the files with a logic what collects the | 
|  | # list of files we have to remove during test runs. | 
|  | shell_cmd = lldb.SBPlatformShellCommand( | 
|  | "rm %s/*" % remote_test_dir) | 
|  | lldb.remote_platform.Run(shell_cmd) | 
|  | self.addTearDownHook(clean_working_directory) | 
|  |  | 
|  | def getSourceDir(self): | 
|  | """Return the full path to the current test.""" | 
|  | return os.path.join(os.environ["LLDB_TEST"], self.mydir) | 
|  |  | 
|  | def getBuildDirBasename(self): | 
|  | return self.__class__.__module__ + "." + self.testMethodName | 
|  |  | 
|  | def getBuildDir(self): | 
|  | """Return the full path to the current test.""" | 
|  | return os.path.join(os.environ["LLDB_BUILD"], self.mydir, | 
|  | self.getBuildDirBasename()) | 
|  |  | 
|  |  | 
|  | def makeBuildDir(self): | 
|  | """Create the test-specific working directory, deleting any previous | 
|  | contents.""" | 
|  | # See also dotest.py which sets up ${LLDB_BUILD}. | 
|  | bdir = self.getBuildDir() | 
|  | if os.path.isdir(bdir): | 
|  | shutil.rmtree(bdir) | 
|  | lldbutil.mkdir_p(bdir) | 
|  |  | 
|  | def getBuildArtifact(self, name="a.out"): | 
|  | """Return absolute path to an artifact in the test's build directory.""" | 
|  | return os.path.join(self.getBuildDir(), name) | 
|  |  | 
|  | def getSourcePath(self, name): | 
|  | """Return absolute path to a file in the test's source directory.""" | 
|  | return os.path.join(self.getSourceDir(), name) | 
|  |  | 
|  | def setUp(self): | 
|  | """Fixture for unittest test case setup. | 
|  |  | 
|  | It works with the test driver to conditionally skip tests and does other | 
|  | initializations.""" | 
|  | #import traceback | 
|  | # traceback.print_stack() | 
|  |  | 
|  | if "LIBCXX_PATH" in os.environ: | 
|  | self.libcxxPath = os.environ["LIBCXX_PATH"] | 
|  | else: | 
|  | self.libcxxPath = None | 
|  |  | 
|  | if "LLDBMI_EXEC" in os.environ: | 
|  | self.lldbMiExec = os.environ["LLDBMI_EXEC"] | 
|  | else: | 
|  | self.lldbMiExec = None | 
|  |  | 
|  | if "LLDBVSCODE_EXEC" in os.environ: | 
|  | self.lldbVSCodeExec = os.environ["LLDBVSCODE_EXEC"] | 
|  | else: | 
|  | self.lldbVSCodeExec = None | 
|  |  | 
|  | # If we spawn an lldb process for test (via pexpect), do not load the | 
|  | # init file unless told otherwise. | 
|  | if "NO_LLDBINIT" in os.environ and "NO" == os.environ["NO_LLDBINIT"]: | 
|  | self.lldbOption = "" | 
|  | else: | 
|  | self.lldbOption = "--no-lldbinit" | 
|  |  | 
|  | # Assign the test method name to self.testMethodName. | 
|  | # | 
|  | # For an example of the use of this attribute, look at test/types dir. | 
|  | # There are a bunch of test cases under test/types and we don't want the | 
|  | # module cacheing subsystem to be confused with executable name "a.out" | 
|  | # used for all the test cases. | 
|  | self.testMethodName = self._testMethodName | 
|  |  | 
|  | # This is for the case of directly spawning 'lldb'/'gdb' and interacting | 
|  | # with it using pexpect. | 
|  | self.child = None | 
|  | self.child_prompt = "(lldb) " | 
|  | # If the child is interacting with the embedded script interpreter, | 
|  | # there are two exits required during tear down, first to quit the | 
|  | # embedded script interpreter and second to quit the lldb command | 
|  | # interpreter. | 
|  | self.child_in_script_interpreter = False | 
|  |  | 
|  | # These are for customized teardown cleanup. | 
|  | self.dict = None | 
|  | self.doTearDownCleanup = False | 
|  | # And in rare cases where there are multiple teardown cleanups. | 
|  | self.dicts = [] | 
|  | self.doTearDownCleanups = False | 
|  |  | 
|  | # List of spawned subproces.Popen objects | 
|  | self.subprocesses = [] | 
|  |  | 
|  | # List of forked process PIDs | 
|  | self.forkedProcessPids = [] | 
|  |  | 
|  | # Create a string buffer to record the session info, to be dumped into a | 
|  | # test case specific file if test failure is encountered. | 
|  | self.log_basename = self.getLogBasenameForCurrentTest() | 
|  |  | 
|  | session_file = "{}.log".format(self.log_basename) | 
|  | # Python 3 doesn't support unbuffered I/O in text mode.  Open buffered. | 
|  | self.session = encoded_file.open(session_file, "utf-8", mode="w") | 
|  |  | 
|  | # Optimistically set __errored__, __failed__, __expected__ to False | 
|  | # initially.  If the test errored/failed, the session info | 
|  | # (self.session) is then dumped into a session specific file for | 
|  | # diagnosis. | 
|  | self.__cleanup_errored__ = False | 
|  | self.__errored__ = False | 
|  | self.__failed__ = False | 
|  | self.__expected__ = False | 
|  | # We are also interested in unexpected success. | 
|  | self.__unexpected__ = False | 
|  | # And skipped tests. | 
|  | self.__skipped__ = False | 
|  |  | 
|  | # See addTearDownHook(self, hook) which allows the client to add a hook | 
|  | # function to be run during tearDown() time. | 
|  | self.hooks = [] | 
|  |  | 
|  | # See HideStdout(self). | 
|  | self.sys_stdout_hidden = False | 
|  |  | 
|  | if self.platformContext: | 
|  | # set environment variable names for finding shared libraries | 
|  | self.dylibPath = self.platformContext.shlib_environment_var | 
|  |  | 
|  | # Create the debugger instance if necessary. | 
|  | try: | 
|  | self.dbg = lldb.DBG | 
|  | except AttributeError: | 
|  | self.dbg = lldb.SBDebugger.Create() | 
|  |  | 
|  | if not self.dbg: | 
|  | raise Exception('Invalid debugger instance') | 
|  |  | 
|  | # Retrieve the associated command interpreter instance. | 
|  | self.ci = self.dbg.GetCommandInterpreter() | 
|  | if not self.ci: | 
|  | raise Exception('Could not get the command interpreter') | 
|  |  | 
|  | # And the result object. | 
|  | self.res = lldb.SBCommandReturnObject() | 
|  |  | 
|  | self.setPlatformWorkingDir() | 
|  | self.enableLogChannelsForCurrentTest() | 
|  |  | 
|  | lib_dir = os.environ["LLDB_LIB_DIR"] | 
|  | self.dsym = None | 
|  | self.framework_dir = None | 
|  | self.darwinWithFramework = self.platformIsDarwin() | 
|  | if sys.platform.startswith("darwin"): | 
|  | # Handle the framework environment variable if it is set | 
|  | if hasattr(lldbtest_config, 'lldbFrameworkPath'): | 
|  | framework_path = lldbtest_config.lldbFrameworkPath | 
|  | # Framework dir should be the directory containing the framework | 
|  | self.framework_dir = framework_path[:framework_path.rfind('LLDB.framework')] | 
|  | # If a framework dir was not specified assume the Xcode build | 
|  | # directory layout where the framework is in LLDB_LIB_DIR. | 
|  | else: | 
|  | self.framework_dir = lib_dir | 
|  | self.dsym = os.path.join(self.framework_dir, 'LLDB.framework', 'LLDB') | 
|  | # If the framework binary doesn't exist, assume we didn't actually | 
|  | # build a framework, and fallback to standard *nix behavior by | 
|  | # setting framework_dir and dsym to None. | 
|  | if not os.path.exists(self.dsym): | 
|  | self.framework_dir = None | 
|  | self.dsym = None | 
|  | self.darwinWithFramework = False | 
|  | self.makeBuildDir() | 
|  |  | 
|  | def setAsync(self, value): | 
|  | """ Sets async mode to True/False and ensures it is reset after the testcase completes.""" | 
|  | old_async = self.dbg.GetAsync() | 
|  | self.dbg.SetAsync(value) | 
|  | self.addTearDownHook(lambda: self.dbg.SetAsync(old_async)) | 
|  |  | 
|  | def cleanupSubprocesses(self): | 
|  | # Ensure any subprocesses are cleaned up | 
|  | for p in self.subprocesses: | 
|  | p.terminate() | 
|  | del p | 
|  | del self.subprocesses[:] | 
|  | # Ensure any forked processes are cleaned up | 
|  | for pid in self.forkedProcessPids: | 
|  | if os.path.exists("/proc/" + str(pid)): | 
|  | os.kill(pid, signal.SIGTERM) | 
|  |  | 
|  | def spawnSubprocess(self, executable, args=[], install_remote=True): | 
|  | """ Creates a subprocess.Popen object with the specified executable and arguments, | 
|  | saves it in self.subprocesses, and returns the object. | 
|  | NOTE: if using this function, ensure you also call: | 
|  |  | 
|  | self.addTearDownHook(self.cleanupSubprocesses) | 
|  |  | 
|  | otherwise the test suite will leak processes. | 
|  | """ | 
|  | proc = _RemoteProcess( | 
|  | install_remote) if lldb.remote_platform else _LocalProcess(self.TraceOn()) | 
|  | proc.launch(executable, args) | 
|  | self.subprocesses.append(proc) | 
|  | return proc | 
|  |  | 
|  | def forkSubprocess(self, executable, args=[]): | 
|  | """ Fork a subprocess with its own group ID. | 
|  | NOTE: if using this function, ensure you also call: | 
|  |  | 
|  | self.addTearDownHook(self.cleanupSubprocesses) | 
|  |  | 
|  | otherwise the test suite will leak processes. | 
|  | """ | 
|  | child_pid = os.fork() | 
|  | if child_pid == 0: | 
|  | # If more I/O support is required, this can be beefed up. | 
|  | fd = os.open(os.devnull, os.O_RDWR) | 
|  | os.dup2(fd, 1) | 
|  | os.dup2(fd, 2) | 
|  | # This call causes the child to have its of group ID | 
|  | os.setpgid(0, 0) | 
|  | os.execvp(executable, [executable] + args) | 
|  | # Give the child time to get through the execvp() call | 
|  | time.sleep(0.1) | 
|  | self.forkedProcessPids.append(child_pid) | 
|  | return child_pid | 
|  |  | 
|  | def HideStdout(self): | 
|  | """Hide output to stdout from the user. | 
|  |  | 
|  | During test execution, there might be cases where we don't want to show the | 
|  | standard output to the user.  For example, | 
|  |  | 
|  | self.runCmd(r'''sc print("\n\n\tHello!\n")''') | 
|  |  | 
|  | tests whether command abbreviation for 'script' works or not.  There is no | 
|  | need to show the 'Hello' output to the user as long as the 'script' command | 
|  | succeeds and we are not in TraceOn() mode (see the '-t' option). | 
|  |  | 
|  | In this case, the test method calls self.HideStdout(self) to redirect the | 
|  | sys.stdout to a null device, and restores the sys.stdout upon teardown. | 
|  |  | 
|  | Note that you should only call this method at most once during a test case | 
|  | execution.  Any subsequent call has no effect at all.""" | 
|  | if self.sys_stdout_hidden: | 
|  | return | 
|  |  | 
|  | self.sys_stdout_hidden = True | 
|  | old_stdout = sys.stdout | 
|  | sys.stdout = open(os.devnull, 'w') | 
|  |  | 
|  | def restore_stdout(): | 
|  | sys.stdout = old_stdout | 
|  | self.addTearDownHook(restore_stdout) | 
|  |  | 
|  | # ======================================================================= | 
|  | # Methods for customized teardown cleanups as well as execution of hooks. | 
|  | # ======================================================================= | 
|  |  | 
|  | def setTearDownCleanup(self, dictionary=None): | 
|  | """Register a cleanup action at tearDown() time with a dictinary""" | 
|  | self.dict = dictionary | 
|  | self.doTearDownCleanup = True | 
|  |  | 
|  | def addTearDownCleanup(self, dictionary): | 
|  | """Add a cleanup action at tearDown() time with a dictinary""" | 
|  | self.dicts.append(dictionary) | 
|  | self.doTearDownCleanups = True | 
|  |  | 
|  | def addTearDownHook(self, hook): | 
|  | """ | 
|  | Add a function to be run during tearDown() time. | 
|  |  | 
|  | Hooks are executed in a first come first serve manner. | 
|  | """ | 
|  | if six.callable(hook): | 
|  | with recording(self, traceAlways) as sbuf: | 
|  | print( | 
|  | "Adding tearDown hook:", | 
|  | getsource_if_available(hook), | 
|  | file=sbuf) | 
|  | self.hooks.append(hook) | 
|  |  | 
|  | return self | 
|  |  | 
|  | def deletePexpectChild(self): | 
|  | # This is for the case of directly spawning 'lldb' and interacting with it | 
|  | # using pexpect. | 
|  | if self.child and self.child.isalive(): | 
|  | import pexpect | 
|  | with recording(self, traceAlways) as sbuf: | 
|  | print("tearing down the child process....", file=sbuf) | 
|  | try: | 
|  | if self.child_in_script_interpreter: | 
|  | self.child.sendline('quit()') | 
|  | self.child.expect_exact(self.child_prompt) | 
|  | self.child.sendline( | 
|  | 'settings set interpreter.prompt-on-quit false') | 
|  | self.child.sendline('quit') | 
|  | self.child.expect(pexpect.EOF) | 
|  | except (ValueError, pexpect.ExceptionPexpect): | 
|  | # child is already terminated | 
|  | pass | 
|  | except OSError as exception: | 
|  | import errno | 
|  | if exception.errno != errno.EIO: | 
|  | # unexpected error | 
|  | raise | 
|  | # child is already terminated | 
|  | pass | 
|  | finally: | 
|  | # Give it one final blow to make sure the child is terminated. | 
|  | self.child.close() | 
|  |  | 
|  | def tearDown(self): | 
|  | """Fixture for unittest test case teardown.""" | 
|  | #import traceback | 
|  | # traceback.print_stack() | 
|  |  | 
|  | self.deletePexpectChild() | 
|  |  | 
|  | # Check and run any hook functions. | 
|  | for hook in reversed(self.hooks): | 
|  | with recording(self, traceAlways) as sbuf: | 
|  | print( | 
|  | "Executing tearDown hook:", | 
|  | getsource_if_available(hook), | 
|  | file=sbuf) | 
|  | if funcutils.requires_self(hook): | 
|  | hook(self) | 
|  | else: | 
|  | hook()  # try the plain call and hope it works | 
|  |  | 
|  | del self.hooks | 
|  |  | 
|  | # Perform registered teardown cleanup. | 
|  | if doCleanup and self.doTearDownCleanup: | 
|  | self.cleanup(dictionary=self.dict) | 
|  |  | 
|  | # In rare cases where there are multiple teardown cleanups added. | 
|  | if doCleanup and self.doTearDownCleanups: | 
|  | if self.dicts: | 
|  | for dict in reversed(self.dicts): | 
|  | self.cleanup(dictionary=dict) | 
|  |  | 
|  | # ========================================================= | 
|  | # Various callbacks to allow introspection of test progress | 
|  | # ========================================================= | 
|  |  | 
|  | def markError(self): | 
|  | """Callback invoked when an error (unexpected exception) errored.""" | 
|  | self.__errored__ = True | 
|  | with recording(self, False) as sbuf: | 
|  | # False because there's no need to write "ERROR" to the stderr twice. | 
|  | # Once by the Python unittest framework, and a second time by us. | 
|  | print("ERROR", file=sbuf) | 
|  |  | 
|  | def markCleanupError(self): | 
|  | """Callback invoked when an error occurs while a test is cleaning up.""" | 
|  | self.__cleanup_errored__ = True | 
|  | with recording(self, False) as sbuf: | 
|  | # False because there's no need to write "CLEANUP_ERROR" to the stderr twice. | 
|  | # Once by the Python unittest framework, and a second time by us. | 
|  | print("CLEANUP_ERROR", file=sbuf) | 
|  |  | 
|  | def markFailure(self): | 
|  | """Callback invoked when a failure (test assertion failure) occurred.""" | 
|  | self.__failed__ = True | 
|  | with recording(self, False) as sbuf: | 
|  | # False because there's no need to write "FAIL" to the stderr twice. | 
|  | # Once by the Python unittest framework, and a second time by us. | 
|  | print("FAIL", file=sbuf) | 
|  |  | 
|  | def markExpectedFailure(self, err, bugnumber): | 
|  | """Callback invoked when an expected failure/error occurred.""" | 
|  | self.__expected__ = True | 
|  | with recording(self, False) as sbuf: | 
|  | # False because there's no need to write "expected failure" to the | 
|  | # stderr twice. | 
|  | # Once by the Python unittest framework, and a second time by us. | 
|  | if bugnumber is None: | 
|  | print("expected failure", file=sbuf) | 
|  | else: | 
|  | print( | 
|  | "expected failure (problem id:" + str(bugnumber) + ")", | 
|  | file=sbuf) | 
|  |  | 
|  | def markSkippedTest(self): | 
|  | """Callback invoked when a test is skipped.""" | 
|  | self.__skipped__ = True | 
|  | with recording(self, False) as sbuf: | 
|  | # False because there's no need to write "skipped test" to the | 
|  | # stderr twice. | 
|  | # Once by the Python unittest framework, and a second time by us. | 
|  | print("skipped test", file=sbuf) | 
|  |  | 
|  | def markUnexpectedSuccess(self, bugnumber): | 
|  | """Callback invoked when an unexpected success occurred.""" | 
|  | self.__unexpected__ = True | 
|  | with recording(self, False) as sbuf: | 
|  | # False because there's no need to write "unexpected success" to the | 
|  | # stderr twice. | 
|  | # Once by the Python unittest framework, and a second time by us. | 
|  | if bugnumber is None: | 
|  | print("unexpected success", file=sbuf) | 
|  | else: | 
|  | print( | 
|  | "unexpected success (problem id:" + str(bugnumber) + ")", | 
|  | file=sbuf) | 
|  |  | 
|  | def getRerunArgs(self): | 
|  | return " -f %s.%s" % (self.__class__.__name__, self._testMethodName) | 
|  |  | 
|  | def getLogBasenameForCurrentTest(self, prefix=None): | 
|  | """ | 
|  | returns a partial path that can be used as the beginning of the name of multiple | 
|  | log files pertaining to this test | 
|  |  | 
|  | <session-dir>/<arch>-<compiler>-<test-file>.<test-class>.<test-method> | 
|  | """ | 
|  | dname = os.path.join(os.environ["LLDB_TEST"], | 
|  | os.environ["LLDB_SESSION_DIRNAME"]) | 
|  | if not os.path.isdir(dname): | 
|  | os.mkdir(dname) | 
|  |  | 
|  | components = [] | 
|  | if prefix is not None: | 
|  | components.append(prefix) | 
|  | for c in configuration.session_file_format: | 
|  | if c == 'f': | 
|  | components.append(self.__class__.__module__) | 
|  | elif c == 'n': | 
|  | components.append(self.__class__.__name__) | 
|  | elif c == 'c': | 
|  | compiler = self.getCompiler() | 
|  |  | 
|  | if compiler[1] == ':': | 
|  | compiler = compiler[2:] | 
|  | if os.path.altsep is not None: | 
|  | compiler = compiler.replace(os.path.altsep, os.path.sep) | 
|  | path_components = [x for x in compiler.split(os.path.sep) if x != ""] | 
|  |  | 
|  | # Add at most 4 path components to avoid generating very long | 
|  | # filenames | 
|  | components.extend(path_components[-4:]) | 
|  | elif c == 'a': | 
|  | components.append(self.getArchitecture()) | 
|  | elif c == 'm': | 
|  | components.append(self.testMethodName) | 
|  | fname = "-".join(components) | 
|  |  | 
|  | return os.path.join(dname, fname) | 
|  |  | 
|  | def dumpSessionInfo(self): | 
|  | """ | 
|  | Dump the debugger interactions leading to a test error/failure.  This | 
|  | allows for more convenient postmortem analysis. | 
|  |  | 
|  | See also LLDBTestResult (dotest.py) which is a singlton class derived | 
|  | from TextTestResult and overwrites addError, addFailure, and | 
|  | addExpectedFailure methods to allow us to to mark the test instance as | 
|  | such. | 
|  | """ | 
|  |  | 
|  | # We are here because self.tearDown() detected that this test instance | 
|  | # either errored or failed.  The lldb.test_result singleton contains | 
|  | # two lists (erros and failures) which get populated by the unittest | 
|  | # framework.  Look over there for stack trace information. | 
|  | # | 
|  | # The lists contain 2-tuples of TestCase instances and strings holding | 
|  | # formatted tracebacks. | 
|  | # | 
|  | # See http://docs.python.org/library/unittest.html#unittest.TestResult. | 
|  |  | 
|  | # output tracebacks into session | 
|  | pairs = [] | 
|  | if self.__errored__: | 
|  | pairs = configuration.test_result.errors | 
|  | prefix = 'Error' | 
|  | elif self.__cleanup_errored__: | 
|  | pairs = configuration.test_result.cleanup_errors | 
|  | prefix = 'CleanupError' | 
|  | elif self.__failed__: | 
|  | pairs = configuration.test_result.failures | 
|  | prefix = 'Failure' | 
|  | elif self.__expected__: | 
|  | pairs = configuration.test_result.expectedFailures | 
|  | prefix = 'ExpectedFailure' | 
|  | elif self.__skipped__: | 
|  | prefix = 'SkippedTest' | 
|  | elif self.__unexpected__: | 
|  | prefix = 'UnexpectedSuccess' | 
|  | else: | 
|  | prefix = 'Success' | 
|  |  | 
|  | if not self.__unexpected__ and not self.__skipped__: | 
|  | for test, traceback in pairs: | 
|  | if test is self: | 
|  | print(traceback, file=self.session) | 
|  |  | 
|  | # put footer (timestamp/rerun instructions) into session | 
|  | testMethod = getattr(self, self._testMethodName) | 
|  | if getattr(testMethod, "__benchmarks_test__", False): | 
|  | benchmarks = True | 
|  | else: | 
|  | benchmarks = False | 
|  |  | 
|  | import datetime | 
|  | print( | 
|  | "Session info generated @", | 
|  | datetime.datetime.now().ctime(), | 
|  | file=self.session) | 
|  | print( | 
|  | "To rerun this test, issue the following command from the 'test' directory:\n", | 
|  | file=self.session) | 
|  | print( | 
|  | "./dotest.py %s -v %s %s" % | 
|  | (self.getRunOptions(), | 
|  | ('+b' if benchmarks else '-t'), | 
|  | self.getRerunArgs()), | 
|  | file=self.session) | 
|  | self.session.close() | 
|  | del self.session | 
|  |  | 
|  | # process the log files | 
|  | log_files_for_this_test = glob.glob(self.log_basename + "*") | 
|  |  | 
|  | if prefix != 'Success' or lldbtest_config.log_success: | 
|  | # keep all log files, rename them to include prefix | 
|  | dst_log_basename = self.getLogBasenameForCurrentTest(prefix) | 
|  | for src in log_files_for_this_test: | 
|  | if os.path.isfile(src): | 
|  | dst = src.replace(self.log_basename, dst_log_basename) | 
|  | if os.name == "nt" and os.path.isfile(dst): | 
|  | # On Windows, renaming a -> b will throw an exception if | 
|  | # b exists.  On non-Windows platforms it silently | 
|  | # replaces the destination.  Ultimately this means that | 
|  | # atomic renames are not guaranteed to be possible on | 
|  | # Windows, but we need this to work anyway, so just | 
|  | # remove the destination first if it already exists. | 
|  | remove_file(dst) | 
|  |  | 
|  | lldbutil.mkdir_p(os.path.dirname(dst)) | 
|  | os.rename(src, dst) | 
|  | else: | 
|  | # success!  (and we don't want log files) delete log files | 
|  | for log_file in log_files_for_this_test: | 
|  | remove_file(log_file) | 
|  |  | 
|  | # ==================================================== | 
|  | # Config. methods supported through a plugin interface | 
|  | # (enables reading of the current test configuration) | 
|  | # ==================================================== | 
|  |  | 
|  | def isMIPS(self): | 
|  | """Returns true if the architecture is MIPS.""" | 
|  | arch = self.getArchitecture() | 
|  | if re.match("mips", arch): | 
|  | return True | 
|  | return False | 
|  |  | 
|  | def isPPC64le(self): | 
|  | """Returns true if the architecture is PPC64LE.""" | 
|  | arch = self.getArchitecture() | 
|  | if re.match("powerpc64le", arch): | 
|  | return True | 
|  | return False | 
|  |  | 
|  | def getArchitecture(self): | 
|  | """Returns the architecture in effect the test suite is running with.""" | 
|  | module = builder_module() | 
|  | arch = module.getArchitecture() | 
|  | if arch == 'amd64': | 
|  | arch = 'x86_64' | 
|  | return arch | 
|  |  | 
|  | def getLldbArchitecture(self): | 
|  | """Returns the architecture of the lldb binary.""" | 
|  | if not hasattr(self, 'lldbArchitecture'): | 
|  |  | 
|  | # spawn local process | 
|  | command = [ | 
|  | lldbtest_config.lldbExec, | 
|  | "-o", | 
|  | "file " + lldbtest_config.lldbExec, | 
|  | "-o", | 
|  | "quit" | 
|  | ] | 
|  |  | 
|  | output = check_output(command) | 
|  | str = output.decode("utf-8") | 
|  |  | 
|  | for line in str.splitlines(): | 
|  | m = re.search( | 
|  | "Current executable set to '.*' \\((.*)\\)\\.", line) | 
|  | if m: | 
|  | self.lldbArchitecture = m.group(1) | 
|  | break | 
|  |  | 
|  | return self.lldbArchitecture | 
|  |  | 
|  | def getCompiler(self): | 
|  | """Returns the compiler in effect the test suite is running with.""" | 
|  | module = builder_module() | 
|  | return module.getCompiler() | 
|  |  | 
|  | def getCompilerBinary(self): | 
|  | """Returns the compiler binary the test suite is running with.""" | 
|  | return self.getCompiler().split()[0] | 
|  |  | 
|  | def getCompilerVersion(self): | 
|  | """ Returns a string that represents the compiler version. | 
|  | Supports: llvm, clang. | 
|  | """ | 
|  | version = 'unknown' | 
|  |  | 
|  | compiler = self.getCompilerBinary() | 
|  | version_output = system([[compiler, "-v"]])[1] | 
|  | for line in version_output.split(os.linesep): | 
|  | m = re.search('version ([0-9\.]+)', line) | 
|  | if m: | 
|  | version = m.group(1) | 
|  | return version | 
|  |  | 
|  | def platformIsDarwin(self): | 
|  | """Returns true if the OS triple for the selected platform is any valid apple OS""" | 
|  | return lldbplatformutil.platformIsDarwin() | 
|  |  | 
|  | def hasDarwinFramework(self): | 
|  | return self.darwinWithFramework | 
|  |  | 
|  | def getPlatform(self): | 
|  | """Returns the target platform the test suite is running on.""" | 
|  | return lldbplatformutil.getPlatform() | 
|  |  | 
|  | def isIntelCompiler(self): | 
|  | """ Returns true if using an Intel (ICC) compiler, false otherwise. """ | 
|  | return any([x in self.getCompiler() for x in ["icc", "icpc", "icl"]]) | 
|  |  | 
|  | def expectedCompilerVersion(self, compiler_version): | 
|  | """Returns True iff compiler_version[1] matches the current compiler version. | 
|  | Use compiler_version[0] to specify the operator used to determine if a match has occurred. | 
|  | Any operator other than the following defaults to an equality test: | 
|  | '>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not' | 
|  | """ | 
|  | if (compiler_version is None): | 
|  | return True | 
|  | operator = str(compiler_version[0]) | 
|  | version = compiler_version[1] | 
|  |  | 
|  | if (version is None): | 
|  | return True | 
|  | if (operator == '>'): | 
|  | return self.getCompilerVersion() > version | 
|  | if (operator == '>=' or operator == '=>'): | 
|  | return self.getCompilerVersion() >= version | 
|  | if (operator == '<'): | 
|  | return self.getCompilerVersion() < version | 
|  | if (operator == '<=' or operator == '=<'): | 
|  | return self.getCompilerVersion() <= version | 
|  | if (operator == '!=' or operator == '!' or operator == 'not'): | 
|  | return str(version) not in str(self.getCompilerVersion()) | 
|  | return str(version) in str(self.getCompilerVersion()) | 
|  |  | 
|  | def expectedCompiler(self, compilers): | 
|  | """Returns True iff any element of compilers is a sub-string of the current compiler.""" | 
|  | if (compilers is None): | 
|  | return True | 
|  |  | 
|  | for compiler in compilers: | 
|  | if compiler in self.getCompiler(): | 
|  | return True | 
|  |  | 
|  | return False | 
|  |  | 
|  | def expectedArch(self, archs): | 
|  | """Returns True iff any element of archs is a sub-string of the current architecture.""" | 
|  | if (archs is None): | 
|  | return True | 
|  |  | 
|  | for arch in archs: | 
|  | if arch in self.getArchitecture(): | 
|  | return True | 
|  |  | 
|  | return False | 
|  |  | 
|  | def getRunOptions(self): | 
|  | """Command line option for -A and -C to run this test again, called from | 
|  | self.dumpSessionInfo().""" | 
|  | arch = self.getArchitecture() | 
|  | comp = self.getCompiler() | 
|  | option_str = "" | 
|  | if arch: | 
|  | option_str = "-A " + arch | 
|  | if comp: | 
|  | option_str += " -C " + comp | 
|  | return option_str | 
|  |  | 
|  | def getDebugInfo(self): | 
|  | method = getattr(self, self.testMethodName) | 
|  | return getattr(method, "debug_info", None) | 
|  |  | 
|  | # ================================================== | 
|  | # Build methods supported through a plugin interface | 
|  | # ================================================== | 
|  |  | 
|  | def getstdlibFlag(self): | 
|  | """ Returns the proper -stdlib flag, or empty if not required.""" | 
|  | if self.platformIsDarwin() or self.getPlatform() == "freebsd" or self.getPlatform() == "openbsd": | 
|  | stdlibflag = "-stdlib=libc++" | 
|  | else:  # this includes NetBSD | 
|  | stdlibflag = "" | 
|  | return stdlibflag | 
|  |  | 
|  | def getstdFlag(self): | 
|  | """ Returns the proper stdflag. """ | 
|  | if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): | 
|  | stdflag = "-std=c++0x" | 
|  | else: | 
|  | stdflag = "-std=c++11" | 
|  | return stdflag | 
|  |  | 
|  | def buildDriver(self, sources, exe_name): | 
|  | """ Platform-specific way to build a program that links with LLDB (via the liblldb.so | 
|  | or LLDB.framework). | 
|  | """ | 
|  | stdflag = self.getstdFlag() | 
|  | stdlibflag = self.getstdlibFlag() | 
|  |  | 
|  | lib_dir = os.environ["LLDB_LIB_DIR"] | 
|  | if self.hasDarwinFramework(): | 
|  | d = {'CXX_SOURCES': sources, | 
|  | 'EXE': exe_name, | 
|  | 'CFLAGS_EXTRAS': "%s %s" % (stdflag, stdlibflag), | 
|  | 'FRAMEWORK_INCLUDES': "-F%s" % self.framework_dir, | 
|  | 'LD_EXTRAS': "%s -Wl,-rpath,%s" % (self.dsym, self.framework_dir), | 
|  | } | 
|  | elif sys.platform.startswith('win'): | 
|  | d = { | 
|  | 'CXX_SOURCES': sources, | 
|  | 'EXE': exe_name, | 
|  | 'CFLAGS_EXTRAS': "%s %s -I%s" % (stdflag, | 
|  | stdlibflag, | 
|  | os.path.join( | 
|  | os.environ["LLDB_SRC"], | 
|  | "include")), | 
|  | 'LD_EXTRAS': "-L%s -lliblldb" % os.environ["LLDB_IMPLIB_DIR"]} | 
|  | else: | 
|  | d = { | 
|  | 'CXX_SOURCES': sources, | 
|  | 'EXE': exe_name, | 
|  | 'CFLAGS_EXTRAS': "%s %s -I%s" % (stdflag, | 
|  | stdlibflag, | 
|  | os.path.join( | 
|  | os.environ["LLDB_SRC"], | 
|  | "include")), | 
|  | 'LD_EXTRAS': "-L%s/../lib -llldb -Wl,-rpath,%s/../lib" % (lib_dir, lib_dir)} | 
|  | if self.TraceOn(): | 
|  | print( | 
|  | "Building LLDB Driver (%s) from sources %s" % | 
|  | (exe_name, sources)) | 
|  |  | 
|  | self.buildDefault(dictionary=d) | 
|  |  | 
|  | def buildLibrary(self, sources, lib_name): | 
|  | """Platform specific way to build a default library. """ | 
|  |  | 
|  | stdflag = self.getstdFlag() | 
|  |  | 
|  | lib_dir = os.environ["LLDB_LIB_DIR"] | 
|  | if self.hasDarwinFramework(): | 
|  | d = {'DYLIB_CXX_SOURCES': sources, | 
|  | 'DYLIB_NAME': lib_name, | 
|  | 'CFLAGS_EXTRAS': "%s -stdlib=libc++" % stdflag, | 
|  | 'FRAMEWORK_INCLUDES': "-F%s" % self.framework_dir, | 
|  | 'LD_EXTRAS': "%s -Wl,-rpath,%s -dynamiclib" % (self.dsym, self.framework_dir), | 
|  | } | 
|  | elif self.getPlatform() == 'windows': | 
|  | d = { | 
|  | 'DYLIB_CXX_SOURCES': sources, | 
|  | 'DYLIB_NAME': lib_name, | 
|  | 'CFLAGS_EXTRAS': "%s -I%s " % (stdflag, | 
|  | os.path.join( | 
|  | os.environ["LLDB_SRC"], | 
|  | "include")), | 
|  | 'LD_EXTRAS': "-shared -l%s\liblldb.lib" % self.os.environ["LLDB_IMPLIB_DIR"]} | 
|  | else: | 
|  | d = { | 
|  | 'DYLIB_CXX_SOURCES': sources, | 
|  | 'DYLIB_NAME': lib_name, | 
|  | 'CFLAGS_EXTRAS': "%s -I%s -fPIC" % (stdflag, | 
|  | os.path.join( | 
|  | os.environ["LLDB_SRC"], | 
|  | "include")), | 
|  | 'LD_EXTRAS': "-shared -L%s/../lib -llldb -Wl,-rpath,%s/../lib" % (lib_dir, lib_dir)} | 
|  | if self.TraceOn(): | 
|  | print( | 
|  | "Building LLDB Library (%s) from sources %s" % | 
|  | (lib_name, sources)) | 
|  |  | 
|  | self.buildDefault(dictionary=d) | 
|  |  | 
|  | def buildProgram(self, sources, exe_name): | 
|  | """ Platform specific way to build an executable from C/C++ sources. """ | 
|  | d = {'CXX_SOURCES': sources, | 
|  | 'EXE': exe_name} | 
|  | self.buildDefault(dictionary=d) | 
|  |  | 
|  | def buildDefault( | 
|  | self, | 
|  | architecture=None, | 
|  | compiler=None, | 
|  | dictionary=None): | 
|  | """Platform specific way to build the default binaries.""" | 
|  | testdir = self.mydir | 
|  | testname = self.getBuildDirBasename() | 
|  | if self.getDebugInfo(): | 
|  | raise Exception("buildDefault tests must set NO_DEBUG_INFO_TESTCASE") | 
|  | module = builder_module() | 
|  | dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) | 
|  | if not module.buildDefault(self, architecture, compiler, | 
|  | dictionary, testdir, testname): | 
|  | raise Exception("Don't know how to build default binary") | 
|  |  | 
|  | def buildDsym( | 
|  | self, | 
|  | architecture=None, | 
|  | compiler=None, | 
|  | dictionary=None): | 
|  | """Platform specific way to build binaries with dsym info.""" | 
|  | testdir = self.mydir | 
|  | testname = self.getBuildDirBasename() | 
|  | if self.getDebugInfo() != "dsym": | 
|  | raise Exception("NO_DEBUG_INFO_TESTCASE must build with buildDefault") | 
|  |  | 
|  | module = builder_module() | 
|  | dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) | 
|  | if not module.buildDsym(self, architecture, compiler, | 
|  | dictionary, testdir, testname): | 
|  | raise Exception("Don't know how to build binary with dsym") | 
|  |  | 
|  | def buildDwarf( | 
|  | self, | 
|  | architecture=None, | 
|  | compiler=None, | 
|  | dictionary=None): | 
|  | """Platform specific way to build binaries with dwarf maps.""" | 
|  | testdir = self.mydir | 
|  | testname = self.getBuildDirBasename() | 
|  | if self.getDebugInfo() != "dwarf": | 
|  | raise Exception("NO_DEBUG_INFO_TESTCASE must build with buildDefault") | 
|  |  | 
|  | module = builder_module() | 
|  | dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) | 
|  | if not module.buildDwarf(self, architecture, compiler, | 
|  | dictionary, testdir, testname): | 
|  | raise Exception("Don't know how to build binary with dwarf") | 
|  |  | 
|  | def buildDwo( | 
|  | self, | 
|  | architecture=None, | 
|  | compiler=None, | 
|  | dictionary=None): | 
|  | """Platform specific way to build binaries with dwarf maps.""" | 
|  | testdir = self.mydir | 
|  | testname = self.getBuildDirBasename() | 
|  | if self.getDebugInfo() != "dwo": | 
|  | raise Exception("NO_DEBUG_INFO_TESTCASE must build with buildDefault") | 
|  |  | 
|  | module = builder_module() | 
|  | dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) | 
|  | if not module.buildDwo(self, architecture, compiler, | 
|  | dictionary, testdir, testname): | 
|  | raise Exception("Don't know how to build binary with dwo") | 
|  |  | 
|  | def buildGModules( | 
|  | self, | 
|  | architecture=None, | 
|  | compiler=None, | 
|  | dictionary=None): | 
|  | """Platform specific way to build binaries with gmodules info.""" | 
|  | testdir = self.mydir | 
|  | testname = self.getBuildDirBasename() | 
|  | if self.getDebugInfo() != "gmodules": | 
|  | raise Exception("NO_DEBUG_INFO_TESTCASE must build with buildDefault") | 
|  |  | 
|  | module = builder_module() | 
|  | dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) | 
|  | if not module.buildGModules(self, architecture, compiler, | 
|  | dictionary, testdir, testname): | 
|  | raise Exception("Don't know how to build binary with gmodules") | 
|  |  | 
|  | def signBinary(self, binary_path): | 
|  | if sys.platform.startswith("darwin"): | 
|  | codesign_cmd = "codesign --force --sign \"%s\" %s" % ( | 
|  | lldbtest_config.codesign_identity, binary_path) | 
|  | call(codesign_cmd, shell=True) | 
|  |  | 
|  | def findBuiltClang(self): | 
|  | """Tries to find and use Clang from the build directory as the compiler (instead of the system compiler).""" | 
|  | paths_to_try = [ | 
|  | "llvm-build/Release+Asserts/x86_64/bin/clang", | 
|  | "llvm-build/Debug+Asserts/x86_64/bin/clang", | 
|  | "llvm-build/Release/x86_64/bin/clang", | 
|  | "llvm-build/Debug/x86_64/bin/clang", | 
|  | ] | 
|  | lldb_root_path = os.path.join( | 
|  | os.path.dirname(__file__), "..", "..", "..", "..") | 
|  | for p in paths_to_try: | 
|  | path = os.path.join(lldb_root_path, p) | 
|  | if os.path.exists(path): | 
|  | return path | 
|  |  | 
|  | # Tries to find clang at the same folder as the lldb | 
|  | lldb_dir = os.path.dirname(lldbtest_config.lldbExec) | 
|  | path = distutils.spawn.find_executable("clang", lldb_dir) | 
|  | if path is not None: | 
|  | return path | 
|  |  | 
|  | return os.environ["CC"] | 
|  |  | 
|  | def findYaml2obj(self): | 
|  | """ | 
|  | Get the path to the yaml2obj executable, which can be used to create | 
|  | test object files from easy to write yaml instructions. | 
|  |  | 
|  | Throws an Exception if the executable cannot be found. | 
|  | """ | 
|  | # Tries to find yaml2obj at the same folder as clang | 
|  | clang_dir = os.path.dirname(self.findBuiltClang()) | 
|  | path = distutils.spawn.find_executable("yaml2obj", clang_dir) | 
|  | if path is not None: | 
|  | return path | 
|  | raise Exception("yaml2obj executable not found") | 
|  |  | 
|  |  | 
|  | def yaml2obj(self, yaml_path, obj_path): | 
|  | """ | 
|  | Create an object file at the given path from a yaml file. | 
|  |  | 
|  | Throws subprocess.CalledProcessError if the object could not be created. | 
|  | """ | 
|  | yaml2obj = self.findYaml2obj() | 
|  | command = [yaml2obj, "-o=%s" % obj_path, yaml_path] | 
|  | system([command]) | 
|  |  | 
|  | def getBuildFlags( | 
|  | self, | 
|  | use_cpp11=True, | 
|  | use_libcxx=False, | 
|  | use_libstdcxx=False): | 
|  | """ Returns a dictionary (which can be provided to build* functions above) which | 
|  | contains OS-specific build flags. | 
|  | """ | 
|  | cflags = "" | 
|  | ldflags = "" | 
|  |  | 
|  | # On Mac OS X, unless specifically requested to use libstdc++, use | 
|  | # libc++ | 
|  | if not use_libstdcxx and self.platformIsDarwin(): | 
|  | use_libcxx = True | 
|  |  | 
|  | if use_libcxx and self.libcxxPath: | 
|  | cflags += "-stdlib=libc++ " | 
|  | if self.libcxxPath: | 
|  | libcxxInclude = os.path.join(self.libcxxPath, "include") | 
|  | libcxxLib = os.path.join(self.libcxxPath, "lib") | 
|  | if os.path.isdir(libcxxInclude) and os.path.isdir(libcxxLib): | 
|  | cflags += "-nostdinc++ -I%s -L%s -Wl,-rpath,%s " % ( | 
|  | libcxxInclude, libcxxLib, libcxxLib) | 
|  |  | 
|  | if use_cpp11: | 
|  | cflags += "-std=" | 
|  | if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): | 
|  | cflags += "c++0x" | 
|  | else: | 
|  | cflags += "c++11" | 
|  | if self.platformIsDarwin() or self.getPlatform() == "freebsd": | 
|  | cflags += " -stdlib=libc++" | 
|  | elif self.getPlatform() == "openbsd": | 
|  | cflags += " -stdlib=libc++" | 
|  | elif self.getPlatform() == "netbsd": | 
|  | cflags += " -stdlib=libstdc++" | 
|  | elif "clang" in self.getCompiler(): | 
|  | cflags += " -stdlib=libstdc++" | 
|  |  | 
|  | return {'CFLAGS_EXTRAS': cflags, | 
|  | 'LD_EXTRAS': ldflags, | 
|  | } | 
|  |  | 
|  | def cleanup(self, dictionary=None): | 
|  | """Platform specific way to do cleanup after build.""" | 
|  | module = builder_module() | 
|  | if not module.cleanup(self, dictionary): | 
|  | raise Exception( | 
|  | "Don't know how to do cleanup with dictionary: " + | 
|  | dictionary) | 
|  |  | 
|  | def getLLDBLibraryEnvVal(self): | 
|  | """ Returns the path that the OS-specific library search environment variable | 
|  | (self.dylibPath) should be set to in order for a program to find the LLDB | 
|  | library. If an environment variable named self.dylibPath is already set, | 
|  | the new path is appended to it and returned. | 
|  | """ | 
|  | existing_library_path = os.environ[ | 
|  | self.dylibPath] if self.dylibPath in os.environ else None | 
|  | lib_dir = os.environ["LLDB_LIB_DIR"] | 
|  | if existing_library_path: | 
|  | return "%s:%s" % (existing_library_path, lib_dir) | 
|  | elif sys.platform.startswith("darwin"): | 
|  | return os.path.join(lib_dir, 'LLDB.framework') | 
|  | else: | 
|  | return lib_dir | 
|  |  | 
|  | def getLibcPlusPlusLibs(self): | 
|  | if self.getPlatform() in ('freebsd', 'linux', 'netbsd', 'openbsd'): | 
|  | return ['libc++.so.1'] | 
|  | else: | 
|  | return ['libc++.1.dylib', 'libc++abi.dylib'] | 
|  |  | 
|  | # Metaclass for TestBase to change the list of test metods when a new TestCase is loaded. | 
|  | # We change the test methods to create a new test method for each test for each debug info we are | 
|  | # testing. The name of the new test method will be '<original-name>_<debug-info>' and with adding | 
|  | # the new test method we remove the old method at the same time. This functionality can be | 
|  | # supressed by at test case level setting the class attribute NO_DEBUG_INFO_TESTCASE or at test | 
|  | # level by using the decorator @no_debug_info_test. | 
|  |  | 
|  |  | 
|  | class LLDBTestCaseFactory(type): | 
|  |  | 
|  | def __new__(cls, name, bases, attrs): | 
|  | original_testcase = super( | 
|  | LLDBTestCaseFactory, cls).__new__( | 
|  | cls, name, bases, attrs) | 
|  | if original_testcase.NO_DEBUG_INFO_TESTCASE: | 
|  | return original_testcase | 
|  |  | 
|  | newattrs = {} | 
|  | for attrname, attrvalue in attrs.items(): | 
|  | if attrname.startswith("test") and not getattr( | 
|  | attrvalue, "__no_debug_info_test__", False): | 
|  |  | 
|  | # If any debug info categories were explicitly tagged, assume that list to be | 
|  | # authoritative.  If none were specified, try with all debug | 
|  | # info formats. | 
|  | all_dbginfo_categories = set(test_categories.debug_info_categories) | 
|  | categories = set( | 
|  | getattr( | 
|  | attrvalue, | 
|  | "categories", | 
|  | [])) & all_dbginfo_categories | 
|  | if not categories: | 
|  | categories = all_dbginfo_categories | 
|  |  | 
|  | for cat in categories: | 
|  | @decorators.add_test_categories([cat]) | 
|  | @wraps(attrvalue) | 
|  | def test_method(self, attrvalue=attrvalue): | 
|  | return attrvalue(self) | 
|  |  | 
|  | method_name = attrname + "_" + cat | 
|  | test_method.__name__ = method_name | 
|  | test_method.debug_info = cat | 
|  | newattrs[method_name] = test_method | 
|  |  | 
|  | else: | 
|  | newattrs[attrname] = attrvalue | 
|  | return super( | 
|  | LLDBTestCaseFactory, | 
|  | cls).__new__( | 
|  | cls, | 
|  | name, | 
|  | bases, | 
|  | newattrs) | 
|  |  | 
|  | # Setup the metaclass for this class to change the list of the test | 
|  | # methods when a new class is loaded | 
|  |  | 
|  |  | 
|  | @add_metaclass(LLDBTestCaseFactory) | 
|  | class TestBase(Base): | 
|  | """ | 
|  | This abstract base class is meant to be subclassed.  It provides default | 
|  | implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(), | 
|  | among other things. | 
|  |  | 
|  | Important things for test class writers: | 
|  |  | 
|  | - Overwrite the mydir class attribute, otherwise your test class won't | 
|  | run.  It specifies the relative directory to the top level 'test' so | 
|  | the test harness can change to the correct working directory before | 
|  | running your test. | 
|  |  | 
|  | - The setUp method sets up things to facilitate subsequent interactions | 
|  | with the debugger as part of the test.  These include: | 
|  | - populate the test method name | 
|  | - create/get a debugger set with synchronous mode (self.dbg) | 
|  | - get the command interpreter from with the debugger (self.ci) | 
|  | - create a result object for use with the command interpreter | 
|  | (self.res) | 
|  | - plus other stuffs | 
|  |  | 
|  | - The tearDown method tries to perform some necessary cleanup on behalf | 
|  | of the test to return the debugger to a good state for the next test. | 
|  | These include: | 
|  | - execute any tearDown hooks registered by the test method with | 
|  | TestBase.addTearDownHook(); examples can be found in | 
|  | settings/TestSettings.py | 
|  | - kill the inferior process associated with each target, if any, | 
|  | and, then delete the target from the debugger's target list | 
|  | - perform build cleanup before running the next test method in the | 
|  | same test class; examples of registering for this service can be | 
|  | found in types/TestIntegerTypes.py with the call: | 
|  | - self.setTearDownCleanup(dictionary=d) | 
|  |  | 
|  | - Similarly setUpClass and tearDownClass perform classwise setup and | 
|  | teardown fixtures.  The tearDownClass method invokes a default build | 
|  | cleanup for the entire test class;  also, subclasses can implement the | 
|  | classmethod classCleanup(cls) to perform special class cleanup action. | 
|  |  | 
|  | - The instance methods runCmd and expect are used heavily by existing | 
|  | test cases to send a command to the command interpreter and to perform | 
|  | string/pattern matching on the output of such command execution.  The | 
|  | expect method also provides a mode to peform string/pattern matching | 
|  | without running a command. | 
|  |  | 
|  | - The build methods buildDefault, buildDsym, and buildDwarf are used to | 
|  | build the binaries used during a particular test scenario.  A plugin | 
|  | should be provided for the sys.platform running the test suite.  The | 
|  | Mac OS X implementation is located in plugins/darwin.py. | 
|  | """ | 
|  |  | 
|  | # Subclasses can set this to true (if they don't depend on debug info) to avoid running the | 
|  | # test multiple times with various debug info types. | 
|  | NO_DEBUG_INFO_TESTCASE = False | 
|  |  | 
|  | # Maximum allowed attempts when launching the inferior process. | 
|  | # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable. | 
|  | maxLaunchCount = 1 | 
|  |  | 
|  | # Time to wait before the next launching attempt in second(s). | 
|  | # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable. | 
|  | timeWaitNextLaunch = 1.0 | 
|  |  | 
|  | def generateSource(self, source): | 
|  | template = source + '.template' | 
|  | temp = os.path.join(self.getSourceDir(), template) | 
|  | with open(temp, 'r') as f: | 
|  | content = f.read() | 
|  |  | 
|  | public_api_dir = os.path.join( | 
|  | os.environ["LLDB_SRC"], "include", "lldb", "API") | 
|  |  | 
|  | # Look under the include/lldb/API directory and add #include statements | 
|  | # for all the SB API headers. | 
|  | public_headers = os.listdir(public_api_dir) | 
|  | # For different platforms, the include statement can vary. | 
|  | if self.hasDarwinFramework(): | 
|  | include_stmt = "'#include <%s>' % os.path.join('LLDB', header)" | 
|  | else: | 
|  | include_stmt = "'#include <%s>' % os.path.join('" + public_api_dir + "', header)" | 
|  | list = [eval(include_stmt) for header in public_headers if ( | 
|  | header.startswith("SB") and header.endswith(".h"))] | 
|  | includes = '\n'.join(list) | 
|  | new_content = content.replace('%include_SB_APIs%', includes) | 
|  | src = os.path.join(self.getBuildDir(), source) | 
|  | with open(src, 'w') as f: | 
|  | f.write(new_content) | 
|  |  | 
|  | self.addTearDownHook(lambda: os.remove(src)) | 
|  |  | 
|  | def setUp(self): | 
|  | #import traceback | 
|  | # traceback.print_stack() | 
|  |  | 
|  | # Works with the test driver to conditionally skip tests via | 
|  | # decorators. | 
|  | Base.setUp(self) | 
|  |  | 
|  | # Set the clang modules cache path used by LLDB. | 
|  | mod_cache = os.path.join(os.path.join(os.environ["LLDB_BUILD"], | 
|  | "module-cache-lldb")) | 
|  | self.runCmd('settings set symbols.clang-modules-cache-path "%s"' | 
|  | % mod_cache) | 
|  |  | 
|  | # Disable Spotlight lookup. The testsuite creates | 
|  | # different binaries with the same UUID, because they only | 
|  | # differ in the debug info, which is not being hashed. | 
|  | self.runCmd('settings set symbols.enable-external-lookup false') | 
|  |  | 
|  | if "LLDB_MAX_LAUNCH_COUNT" in os.environ: | 
|  | self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"]) | 
|  |  | 
|  | if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ: | 
|  | self.timeWaitNextLaunch = float( | 
|  | os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"]) | 
|  |  | 
|  | # We want our debugger to be synchronous. | 
|  | self.dbg.SetAsync(False) | 
|  |  | 
|  | # Retrieve the associated command interpreter instance. | 
|  | self.ci = self.dbg.GetCommandInterpreter() | 
|  | if not self.ci: | 
|  | raise Exception('Could not get the command interpreter') | 
|  |  | 
|  | # And the result object. | 
|  | self.res = lldb.SBCommandReturnObject() | 
|  |  | 
|  | def registerSharedLibrariesWithTarget(self, target, shlibs): | 
|  | '''If we are remotely running the test suite, register the shared libraries with the target so they get uploaded, otherwise do nothing | 
|  |  | 
|  | Any modules in the target that have their remote install file specification set will | 
|  | get uploaded to the remote host. This function registers the local copies of the | 
|  | shared libraries with the target and sets their remote install locations so they will | 
|  | be uploaded when the target is run. | 
|  | ''' | 
|  | if not shlibs or not self.platformContext: | 
|  | return None | 
|  |  | 
|  | shlib_environment_var = self.platformContext.shlib_environment_var | 
|  | shlib_prefix = self.platformContext.shlib_prefix | 
|  | shlib_extension = '.' + self.platformContext.shlib_extension | 
|  |  | 
|  | working_dir = self.get_process_working_directory() | 
|  | environment = ['%s=%s' % (shlib_environment_var, working_dir)] | 
|  | # Add any shared libraries to our target if remote so they get | 
|  | # uploaded into the working directory on the remote side | 
|  | for name in shlibs: | 
|  | # The path can be a full path to a shared library, or a make file name like "Foo" for | 
|  | # "libFoo.dylib" or "libFoo.so", or "Foo.so" for "Foo.so" or "libFoo.so", or just a | 
|  | # basename like "libFoo.so". So figure out which one it is and resolve the local copy | 
|  | # of the shared library accordingly | 
|  | if os.path.isfile(name): | 
|  | local_shlib_path = name  # name is the full path to the local shared library | 
|  | else: | 
|  | # Check relative names | 
|  | local_shlib_path = os.path.join( | 
|  | self.getBuildDir(), shlib_prefix + name + shlib_extension) | 
|  | if not os.path.exists(local_shlib_path): | 
|  | local_shlib_path = os.path.join( | 
|  | self.getBuildDir(), name + shlib_extension) | 
|  | if not os.path.exists(local_shlib_path): | 
|  | local_shlib_path = os.path.join(self.getBuildDir(), name) | 
|  |  | 
|  | # Make sure we found the local shared library in the above code | 
|  | self.assertTrue(os.path.exists(local_shlib_path)) | 
|  |  | 
|  | # Add the shared library to our target | 
|  | shlib_module = target.AddModule(local_shlib_path, None, None, None) | 
|  | if lldb.remote_platform: | 
|  | # We must set the remote install location if we want the shared library | 
|  | # to get uploaded to the remote target | 
|  | remote_shlib_path = lldbutil.append_to_process_working_directory(self, | 
|  | os.path.basename(local_shlib_path)) | 
|  | shlib_module.SetRemoteInstallFileSpec( | 
|  | lldb.SBFileSpec(remote_shlib_path, False)) | 
|  |  | 
|  | return environment | 
|  |  | 
|  | # utility methods that tests can use to access the current objects | 
|  | def target(self): | 
|  | if not self.dbg: | 
|  | raise Exception('Invalid debugger instance') | 
|  | return self.dbg.GetSelectedTarget() | 
|  |  | 
|  | def process(self): | 
|  | if not self.dbg: | 
|  | raise Exception('Invalid debugger instance') | 
|  | return self.dbg.GetSelectedTarget().GetProcess() | 
|  |  | 
|  | def thread(self): | 
|  | if not self.dbg: | 
|  | raise Exception('Invalid debugger instance') | 
|  | return self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread() | 
|  |  | 
|  | def frame(self): | 
|  | if not self.dbg: | 
|  | raise Exception('Invalid debugger instance') | 
|  | return self.dbg.GetSelectedTarget().GetProcess( | 
|  | ).GetSelectedThread().GetSelectedFrame() | 
|  |  | 
|  | def get_process_working_directory(self): | 
|  | '''Get the working directory that should be used when launching processes for local or remote processes.''' | 
|  | if lldb.remote_platform: | 
|  | # Remote tests set the platform working directory up in | 
|  | # TestBase.setUp() | 
|  | return lldb.remote_platform.GetWorkingDirectory() | 
|  | else: | 
|  | # local tests change directory into each test subdirectory | 
|  | return self.getBuildDir() | 
|  |  | 
|  | def tearDown(self): | 
|  | #import traceback | 
|  | # traceback.print_stack() | 
|  |  | 
|  | # Ensure all the references to SB objects have gone away so that we can | 
|  | # be sure that all test-specific resources have been freed before we | 
|  | # attempt to delete the targets. | 
|  | gc.collect() | 
|  |  | 
|  | # Delete the target(s) from the debugger as a general cleanup step. | 
|  | # This includes terminating the process for each target, if any. | 
|  | # We'd like to reuse the debugger for our next test without incurring | 
|  | # the initialization overhead. | 
|  | targets = [] | 
|  | for target in self.dbg: | 
|  | if target: | 
|  | targets.append(target) | 
|  | process = target.GetProcess() | 
|  | if process: | 
|  | rc = self.invoke(process, "Kill") | 
|  | self.assertTrue(rc.Success(), PROCESS_KILLED) | 
|  | for target in targets: | 
|  | self.dbg.DeleteTarget(target) | 
|  |  | 
|  | # Do this last, to make sure it's in reverse order from how we setup. | 
|  | Base.tearDown(self) | 
|  |  | 
|  | # This must be the last statement, otherwise teardown hooks or other | 
|  | # lines might depend on this still being active. | 
|  | del self.dbg | 
|  |  | 
|  | def switch_to_thread_with_stop_reason(self, stop_reason): | 
|  | """ | 
|  | Run the 'thread list' command, and select the thread with stop reason as | 
|  | 'stop_reason'.  If no such thread exists, no select action is done. | 
|  | """ | 
|  | from .lldbutil import stop_reason_to_str | 
|  | self.runCmd('thread list') | 
|  | output = self.res.GetOutput() | 
|  | thread_line_pattern = re.compile( | 
|  | "^[ *] thread #([0-9]+):.*stop reason = %s" % | 
|  | stop_reason_to_str(stop_reason)) | 
|  | for line in output.splitlines(): | 
|  | matched = thread_line_pattern.match(line) | 
|  | if matched: | 
|  | self.runCmd('thread select %s' % matched.group(1)) | 
|  |  | 
|  | def runCmd(self, cmd, msg=None, check=True, trace=False, inHistory=False): | 
|  | """ | 
|  | Ask the command interpreter to handle the command and then check its | 
|  | return status. | 
|  | """ | 
|  | # Fail fast if 'cmd' is not meaningful. | 
|  | if not cmd or len(cmd) == 0: | 
|  | raise Exception("Bad 'cmd' parameter encountered") | 
|  |  | 
|  | trace = (True if traceAlways else trace) | 
|  |  | 
|  | if cmd.startswith("target create "): | 
|  | cmd = cmd.replace("target create ", "file ") | 
|  |  | 
|  | running = (cmd.startswith("run") or cmd.startswith("process launch")) | 
|  |  | 
|  | for i in range(self.maxLaunchCount if running else 1): | 
|  | self.ci.HandleCommand(cmd, self.res, inHistory) | 
|  |  | 
|  | with recording(self, trace) as sbuf: | 
|  | print("runCmd:", cmd, file=sbuf) | 
|  | if not check: | 
|  | print("check of return status not required", file=sbuf) | 
|  | if self.res.Succeeded(): | 
|  | print("output:", self.res.GetOutput(), file=sbuf) | 
|  | else: | 
|  | print("runCmd failed!", file=sbuf) | 
|  | print(self.res.GetError(), file=sbuf) | 
|  |  | 
|  | if self.res.Succeeded(): | 
|  | break | 
|  | elif running: | 
|  | # For process launch, wait some time before possible next try. | 
|  | time.sleep(self.timeWaitNextLaunch) | 
|  | with recording(self, trace) as sbuf: | 
|  | print("Command '" + cmd + "' failed!", file=sbuf) | 
|  |  | 
|  | if check: | 
|  | output = "" | 
|  | if self.res.GetOutput(): | 
|  | output += "\nCommand output:\n" + self.res.GetOutput() | 
|  | if self.res.GetError(): | 
|  | output += "\nError output:\n" + self.res.GetError() | 
|  | if msg: | 
|  | msg += output | 
|  | if cmd: | 
|  | cmd += output | 
|  | self.assertTrue(self.res.Succeeded(), | 
|  | msg if (msg) else CMD_MSG(cmd)) | 
|  |  | 
|  | def match( | 
|  | self, | 
|  | str, | 
|  | patterns, | 
|  | msg=None, | 
|  | trace=False, | 
|  | error=False, | 
|  | matching=True, | 
|  | exe=True): | 
|  | """run command in str, and match the result against regexp in patterns returning the match object for the first matching pattern | 
|  |  | 
|  | Otherwise, all the arguments have the same meanings as for the expect function""" | 
|  |  | 
|  | trace = (True if traceAlways else trace) | 
|  |  | 
|  | if exe: | 
|  | # First run the command.  If we are expecting error, set check=False. | 
|  | # Pass the assert message along since it provides more semantic | 
|  | # info. | 
|  | self.runCmd( | 
|  | str, | 
|  | msg=msg, | 
|  | trace=( | 
|  | True if trace else False), | 
|  | check=not error) | 
|  |  | 
|  | # Then compare the output against expected strings. | 
|  | output = self.res.GetError() if error else self.res.GetOutput() | 
|  |  | 
|  | # If error is True, the API client expects the command to fail! | 
|  | if error: | 
|  | self.assertFalse(self.res.Succeeded(), | 
|  | "Command '" + str + "' is expected to fail!") | 
|  | else: | 
|  | # No execution required, just compare str against the golden input. | 
|  | output = str | 
|  | with recording(self, trace) as sbuf: | 
|  | print("looking at:", output, file=sbuf) | 
|  |  | 
|  | # The heading says either "Expecting" or "Not expecting". | 
|  | heading = "Expecting" if matching else "Not expecting" | 
|  |  | 
|  | for pattern in patterns: | 
|  | # Match Objects always have a boolean value of True. | 
|  | match_object = re.search(pattern, output) | 
|  | matched = bool(match_object) | 
|  | with recording(self, trace) as sbuf: | 
|  | print("%s pattern: %s" % (heading, pattern), file=sbuf) | 
|  | print("Matched" if matched else "Not matched", file=sbuf) | 
|  | if matched: | 
|  | break | 
|  |  | 
|  | self.assertTrue(matched if matching else not matched, | 
|  | msg if msg else EXP_MSG(str, output, exe)) | 
|  |  | 
|  | return match_object | 
|  |  | 
|  | def check_completion_with_desc(self, str_input, match_desc_pairs): | 
|  | interp = self.dbg.GetCommandInterpreter() | 
|  | match_strings = lldb.SBStringList() | 
|  | description_strings = lldb.SBStringList() | 
|  | num_matches = interp.HandleCompletionWithDescriptions(str_input, len(str_input), 0, -1, match_strings, description_strings) | 
|  | self.assertEqual(len(description_strings), len(match_strings)) | 
|  |  | 
|  | missing_pairs = [] | 
|  | for pair in match_desc_pairs: | 
|  | found_pair = False | 
|  | for i in range(num_matches + 1): | 
|  | match_candidate = match_strings.GetStringAtIndex(i) | 
|  | description_candidate = description_strings.GetStringAtIndex(i) | 
|  | if match_candidate == pair[0] and description_candidate == pair[1]: | 
|  | found_pair = True | 
|  | break | 
|  | if not found_pair: | 
|  | missing_pairs.append(pair) | 
|  |  | 
|  | if len(missing_pairs): | 
|  | error_msg = "Missing pairs:\n" | 
|  | for pair in missing_pairs: | 
|  | error_msg += " [" + pair[0] + ":" + pair[1] + "]\n" | 
|  | error_msg += "Got the following " + str(num_matches) + " completions back:\n" | 
|  | for i in range(num_matches + 1): | 
|  | match_candidate = match_strings.GetStringAtIndex(i) | 
|  | description_candidate = description_strings.GetStringAtIndex(i) | 
|  | error_msg += "[" + match_candidate + ":" + description_candidate + "]\n" | 
|  | self.assertEqual(0, len(missing_pairs), error_msg) | 
|  |  | 
|  | def complete_exactly(self, str_input, patterns): | 
|  | self.complete_from_to(str_input, patterns, True) | 
|  |  | 
|  | def complete_from_to(self, str_input, patterns, turn_off_re_match=False): | 
|  | """Test that the completion mechanism completes str_input to patterns, | 
|  | where patterns could be a pattern-string or a list of pattern-strings""" | 
|  | # Patterns should not be None in order to proceed. | 
|  | self.assertFalse(patterns is None) | 
|  | # And should be either a string or list of strings.  Check for list type | 
|  | # below, if not, make a list out of the singleton string.  If patterns | 
|  | # is not a string or not a list of strings, there'll be runtime errors | 
|  | # later on. | 
|  | if not isinstance(patterns, list): | 
|  | patterns = [patterns] | 
|  |  | 
|  | interp = self.dbg.GetCommandInterpreter() | 
|  | match_strings = lldb.SBStringList() | 
|  | num_matches = interp.HandleCompletion(str_input, len(str_input), 0, -1, match_strings) | 
|  | common_match = match_strings.GetStringAtIndex(0) | 
|  | if num_matches == 0: | 
|  | compare_string = str_input | 
|  | else: | 
|  | if common_match != None and len(common_match) > 0: | 
|  | compare_string = str_input + common_match | 
|  | else: | 
|  | compare_string = "" | 
|  | for idx in range(1, num_matches+1): | 
|  | compare_string += match_strings.GetStringAtIndex(idx) + "\n" | 
|  |  | 
|  | for p in patterns: | 
|  | if turn_off_re_match: | 
|  | self.expect( | 
|  | compare_string, msg=COMPLETION_MSG( | 
|  | str_input, p, match_strings), exe=False, substrs=[p]) | 
|  | else: | 
|  | self.expect( | 
|  | compare_string, msg=COMPLETION_MSG( | 
|  | str_input, p, match_strings), exe=False, patterns=[p]) | 
|  |  | 
|  | def filecheck( | 
|  | self, | 
|  | command, | 
|  | check_file, | 
|  | filecheck_options = ''): | 
|  | # Run the command. | 
|  | self.runCmd( | 
|  | command, | 
|  | msg="FileCheck'ing result of `{0}`".format(command)) | 
|  |  | 
|  | # Get the error text if there was an error, and the regular text if not. | 
|  | output = self.res.GetOutput() if self.res.Succeeded() \ | 
|  | else self.res.GetError() | 
|  |  | 
|  | # Assemble the absolute path to the check file. As a convenience for | 
|  | # LLDB inline tests, assume that the check file is a relative path to | 
|  | # a file within the inline test directory. | 
|  | if check_file.endswith('.pyc'): | 
|  | check_file = check_file[:-1] | 
|  | check_file_abs = os.path.abspath(check_file) | 
|  |  | 
|  | # Run FileCheck. | 
|  | filecheck_bin = configuration.get_filecheck_path() | 
|  | if not filecheck_bin: | 
|  | self.assertTrue(False, "No valid FileCheck executable specified") | 
|  | filecheck_args = [filecheck_bin, check_file_abs] | 
|  | if filecheck_options: | 
|  | filecheck_args.append(filecheck_options) | 
|  | subproc = Popen(filecheck_args, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines = True) | 
|  | cmd_stdout, cmd_stderr = subproc.communicate(input=output) | 
|  | cmd_status = subproc.returncode | 
|  |  | 
|  | filecheck_cmd = " ".join(filecheck_args) | 
|  | filecheck_trace = """ | 
|  | --- FileCheck trace (code={0}) --- | 
|  | {1} | 
|  |  | 
|  | FileCheck input: | 
|  | {2} | 
|  |  | 
|  | FileCheck output: | 
|  | {3} | 
|  | {4} | 
|  | """.format(cmd_status, filecheck_cmd, output, cmd_stdout, cmd_stderr) | 
|  |  | 
|  | trace = cmd_status != 0 or traceAlways | 
|  | with recording(self, trace) as sbuf: | 
|  | print(filecheck_trace, file=sbuf) | 
|  |  | 
|  | self.assertTrue(cmd_status == 0) | 
|  |  | 
|  | def expect( | 
|  | self, | 
|  | str, | 
|  | msg=None, | 
|  | patterns=None, | 
|  | startstr=None, | 
|  | endstr=None, | 
|  | substrs=None, | 
|  | trace=False, | 
|  | error=False, | 
|  | matching=True, | 
|  | exe=True, | 
|  | inHistory=False): | 
|  | """ | 
|  | Similar to runCmd; with additional expect style output matching ability. | 
|  |  | 
|  | Ask the command interpreter to handle the command and then check its | 
|  | return status.  The 'msg' parameter specifies an informational assert | 
|  | message.  We expect the output from running the command to start with | 
|  | 'startstr', matches the substrings contained in 'substrs', and regexp | 
|  | matches the patterns contained in 'patterns'. | 
|  |  | 
|  | If the keyword argument error is set to True, it signifies that the API | 
|  | client is expecting the command to fail.  In this case, the error stream | 
|  | from running the command is retrieved and compared against the golden | 
|  | input, instead. | 
|  |  | 
|  | If the keyword argument matching is set to False, it signifies that the API | 
|  | client is expecting the output of the command not to match the golden | 
|  | input. | 
|  |  | 
|  | Finally, the required argument 'str' represents the lldb command to be | 
|  | sent to the command interpreter.  In case the keyword argument 'exe' is | 
|  | set to False, the 'str' is treated as a string to be matched/not-matched | 
|  | against the golden input. | 
|  | """ | 
|  | trace = (True if traceAlways else trace) | 
|  |  | 
|  | if exe: | 
|  | # First run the command.  If we are expecting error, set check=False. | 
|  | # Pass the assert message along since it provides more semantic | 
|  | # info. | 
|  | self.runCmd( | 
|  | str, | 
|  | msg=msg, | 
|  | trace=( | 
|  | True if trace else False), | 
|  | check=not error, | 
|  | inHistory=inHistory) | 
|  |  | 
|  | # Then compare the output against expected strings. | 
|  | output = self.res.GetError() if error else self.res.GetOutput() | 
|  |  | 
|  | # If error is True, the API client expects the command to fail! | 
|  | if error: | 
|  | self.assertFalse(self.res.Succeeded(), | 
|  | "Command '" + str + "' is expected to fail!") | 
|  | else: | 
|  | # No execution required, just compare str against the golden input. | 
|  | if isinstance(str, lldb.SBCommandReturnObject): | 
|  | output = str.GetOutput() | 
|  | else: | 
|  | output = str | 
|  | with recording(self, trace) as sbuf: | 
|  | print("looking at:", output, file=sbuf) | 
|  |  | 
|  | if output is None: | 
|  | output = "" | 
|  | # The heading says either "Expecting" or "Not expecting". | 
|  | heading = "Expecting" if matching else "Not expecting" | 
|  |  | 
|  | # Start from the startstr, if specified. | 
|  | # If there's no startstr, set the initial state appropriately. | 
|  | matched = output.startswith(startstr) if startstr else ( | 
|  | True if matching else False) | 
|  |  | 
|  | if startstr: | 
|  | with recording(self, trace) as sbuf: | 
|  | print("%s start string: %s" % (heading, startstr), file=sbuf) | 
|  | print("Matched" if matched else "Not matched", file=sbuf) | 
|  |  | 
|  | # Look for endstr, if specified. | 
|  | keepgoing = matched if matching else not matched | 
|  | if endstr: | 
|  | matched = output.endswith(endstr) | 
|  | with recording(self, trace) as sbuf: | 
|  | print("%s end string: %s" % (heading, endstr), file=sbuf) | 
|  | print("Matched" if matched else "Not matched", file=sbuf) | 
|  |  | 
|  | # Look for sub strings, if specified. | 
|  | keepgoing = matched if matching else not matched | 
|  | if substrs and keepgoing: | 
|  | for substr in substrs: | 
|  | matched = output.find(substr) != -1 | 
|  | with recording(self, trace) as sbuf: | 
|  | print("%s sub string: %s" % (heading, substr), file=sbuf) | 
|  | print("Matched" if matched else "Not matched", file=sbuf) | 
|  | keepgoing = matched if matching else not matched | 
|  | if not keepgoing: | 
|  | break | 
|  |  | 
|  | # Search for regular expression patterns, if specified. | 
|  | keepgoing = matched if matching else not matched | 
|  | if patterns and keepgoing: | 
|  | for pattern in patterns: | 
|  | # Match Objects always have a boolean value of True. | 
|  | matched = bool(re.search(pattern, output)) | 
|  | with recording(self, trace) as sbuf: | 
|  | print("%s pattern: %s" % (heading, pattern), file=sbuf) | 
|  | print("Matched" if matched else "Not matched", file=sbuf) | 
|  | keepgoing = matched if matching else not matched | 
|  | if not keepgoing: | 
|  | break | 
|  |  | 
|  | self.assertTrue(matched if matching else not matched, | 
|  | msg if msg else EXP_MSG(str, output, exe)) | 
|  |  | 
|  | def invoke(self, obj, name, trace=False): | 
|  | """Use reflection to call a method dynamically with no argument.""" | 
|  | trace = (True if traceAlways else trace) | 
|  |  | 
|  | method = getattr(obj, name) | 
|  | import inspect | 
|  | self.assertTrue(inspect.ismethod(method), | 
|  | name + "is a method name of object: " + str(obj)) | 
|  | result = method() | 
|  | with recording(self, trace) as sbuf: | 
|  | print(str(method) + ":", result, file=sbuf) | 
|  | return result | 
|  |  | 
|  | def build( | 
|  | self, | 
|  | architecture=None, | 
|  | compiler=None, | 
|  | dictionary=None): | 
|  | """Platform specific way to build the default binaries.""" | 
|  | module = builder_module() | 
|  |  | 
|  | dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) | 
|  | if self.getDebugInfo() is None: | 
|  | return self.buildDefault(architecture, compiler, dictionary) | 
|  | elif self.getDebugInfo() == "dsym": | 
|  | return self.buildDsym(architecture, compiler, dictionary) | 
|  | elif self.getDebugInfo() == "dwarf": | 
|  | return self.buildDwarf(architecture, compiler, dictionary) | 
|  | elif self.getDebugInfo() == "dwo": | 
|  | return self.buildDwo(architecture, compiler, dictionary) | 
|  | elif self.getDebugInfo() == "gmodules": | 
|  | return self.buildGModules(architecture, compiler, dictionary) | 
|  | else: | 
|  | self.fail("Can't build for debug info: %s" % self.getDebugInfo()) | 
|  |  | 
|  | def run_platform_command(self, cmd): | 
|  | platform = self.dbg.GetSelectedPlatform() | 
|  | shell_command = lldb.SBPlatformShellCommand(cmd) | 
|  | err = platform.Run(shell_command) | 
|  | return (err, shell_command.GetStatus(), shell_command.GetOutput()) | 
|  |  | 
|  | # ================================================= | 
|  | # Misc. helper methods for debugging test execution | 
|  | # ================================================= | 
|  |  | 
|  | def DebugSBValue(self, val): | 
|  | """Debug print a SBValue object, if traceAlways is True.""" | 
|  | from .lldbutil import value_type_to_str | 
|  |  | 
|  | if not traceAlways: | 
|  | return | 
|  |  | 
|  | err = sys.stderr | 
|  | err.write(val.GetName() + ":\n") | 
|  | err.write('\t' + "TypeName         -> " + val.GetTypeName() + '\n') | 
|  | err.write('\t' + "ByteSize         -> " + | 
|  | str(val.GetByteSize()) + '\n') | 
|  | err.write('\t' + "NumChildren      -> " + | 
|  | str(val.GetNumChildren()) + '\n') | 
|  | err.write('\t' + "Value            -> " + str(val.GetValue()) + '\n') | 
|  | err.write('\t' + "ValueAsUnsigned  -> " + | 
|  | str(val.GetValueAsUnsigned()) + '\n') | 
|  | err.write( | 
|  | '\t' + | 
|  | "ValueType        -> " + | 
|  | value_type_to_str( | 
|  | val.GetValueType()) + | 
|  | '\n') | 
|  | err.write('\t' + "Summary          -> " + str(val.GetSummary()) + '\n') | 
|  | err.write('\t' + "IsPointerType    -> " + | 
|  | str(val.TypeIsPointerType()) + '\n') | 
|  | err.write('\t' + "Location         -> " + val.GetLocation() + '\n') | 
|  |  | 
|  | def DebugSBType(self, type): | 
|  | """Debug print a SBType object, if traceAlways is True.""" | 
|  | if not traceAlways: | 
|  | return | 
|  |  | 
|  | err = sys.stderr | 
|  | err.write(type.GetName() + ":\n") | 
|  | err.write('\t' + "ByteSize        -> " + | 
|  | str(type.GetByteSize()) + '\n') | 
|  | err.write('\t' + "IsPointerType   -> " + | 
|  | str(type.IsPointerType()) + '\n') | 
|  | err.write('\t' + "IsReferenceType -> " + | 
|  | str(type.IsReferenceType()) + '\n') | 
|  |  | 
|  | def DebugPExpect(self, child): | 
|  | """Debug the spwaned pexpect object.""" | 
|  | if not traceAlways: | 
|  | return | 
|  |  | 
|  | print(child) | 
|  |  | 
|  | @classmethod | 
|  | def RemoveTempFile(cls, file): | 
|  | if os.path.exists(file): | 
|  | remove_file(file) | 
|  |  | 
|  | # On Windows, the first attempt to delete a recently-touched file can fail | 
|  | # because of a race with antimalware scanners.  This function will detect a | 
|  | # failure and retry. | 
|  |  | 
|  |  | 
|  | def remove_file(file, num_retries=1, sleep_duration=0.5): | 
|  | for i in range(num_retries + 1): | 
|  | try: | 
|  | os.remove(file) | 
|  | return True | 
|  | except: | 
|  | time.sleep(sleep_duration) | 
|  | continue | 
|  | return False |