1038 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			1038 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Python
		
	
	
	
| """
 | |
| A simple testing framework for lldb using python's unit testing framework.
 | |
| 
 | |
| Tests for lldb are written as python scripts which take advantage of the script
 | |
| bridging provided by LLDB.framework to interact with lldb core.
 | |
| 
 | |
| A specific naming pattern is followed by the .py script to be recognized as
 | |
| a module which implements a test scenario, namely, Test*.py.
 | |
| 
 | |
| To specify the directories where "Test*.py" python test scripts are located,
 | |
| you need to pass in a list of directory names.  By default, the current
 | |
| working directory is searched if nothing is specified on the command line.
 | |
| 
 | |
| Type:
 | |
| 
 | |
| ./dotest.py -h
 | |
| 
 | |
| for available options.
 | |
| """
 | |
| 
 | |
| from __future__ import absolute_import
 | |
| from __future__ import print_function
 | |
| 
 | |
| # System modules
 | |
| import atexit
 | |
| import datetime
 | |
| import errno
 | |
| import logging
 | |
| import os
 | |
| import platform
 | |
| import re
 | |
| import shutil
 | |
| import signal
 | |
| import subprocess
 | |
| import sys
 | |
| import tempfile
 | |
| 
 | |
| # Third-party modules
 | |
| import six
 | |
| import unittest2
 | |
| 
 | |
| # LLDB Modules
 | |
| import lldbsuite
 | |
| from . import configuration
 | |
| from . import dotest_args
 | |
| from . import lldbtest_config
 | |
| from . import test_categories
 | |
| from . import test_result
 | |
| from ..support import seven
 | |
| 
 | |
| 
 | |
| def is_exe(fpath):
 | |
|     """Returns true if fpath is an executable."""
 | |
|     if fpath == None:
 | |
|         return False
 | |
|     if sys.platform == 'win32':
 | |
|         if not fpath.endswith(".exe"):
 | |
|             fpath += ".exe"
 | |
|     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, _ = 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
 | |
| 
 | |
| 
 | |
| def usage(parser):
 | |
|     parser.print_help()
 | |
|     if configuration.verbose > 0:
 | |
|         print("""
 | |
| Examples:
 | |
| 
 | |
| This is an example of using the -f option to pinpoint to a specific test class
 | |
| and test method to be run:
 | |
| 
 | |
| $ ./dotest.py -f ClassTypesTestCase.test_with_dsym_and_run_command
 | |
| ----------------------------------------------------------------------
 | |
| Collected 1 test
 | |
| 
 | |
| test_with_dsym_and_run_command (TestClassTypes.ClassTypesTestCase)
 | |
| Test 'frame variable this' when stopped on a class constructor. ... ok
 | |
| 
 | |
| ----------------------------------------------------------------------
 | |
| Ran 1 test in 1.396s
 | |
| 
 | |
| OK
 | |
| 
 | |
| And this is an example of using the -p option to run a single file (the filename
 | |
| matches the pattern 'ObjC' and it happens to be 'TestObjCMethods.py'):
 | |
| 
 | |
| $ ./dotest.py -v -p ObjC
 | |
| ----------------------------------------------------------------------
 | |
| Collected 4 tests
 | |
| 
 | |
| test_break_with_dsym (TestObjCMethods.FoundationTestCase)
 | |
| Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok
 | |
| test_break_with_dwarf (TestObjCMethods.FoundationTestCase)
 | |
| Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok
 | |
| test_data_type_and_expr_with_dsym (TestObjCMethods.FoundationTestCase)
 | |
| Lookup objective-c data types and evaluate expressions. ... ok
 | |
| test_data_type_and_expr_with_dwarf (TestObjCMethods.FoundationTestCase)
 | |
| Lookup objective-c data types and evaluate expressions. ... ok
 | |
| 
 | |
| ----------------------------------------------------------------------
 | |
| Ran 4 tests in 16.661s
 | |
| 
 | |
| OK
 | |
| 
 | |
| Running of this script also sets up the LLDB_TEST environment variable so that
 | |
| individual test cases can locate their supporting files correctly.  The script
 | |
| tries to set up Python's search paths for modules by looking at the build tree
 | |
| relative to this script.  See also the '-i' option in the following example.
 | |
| 
 | |
| Finally, this is an example of using the lldb.py module distributed/installed by
 | |
| Xcode4 to run against the tests under the 'forward' directory, and with the '-w'
 | |
| option to add some delay between two tests.  It uses ARCH=x86_64 to specify that
 | |
| as the architecture and CC=clang to specify the compiler used for the test run:
 | |
| 
 | |
| $ PYTHONPATH=/Xcode4/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python ARCH=x86_64 CC=clang ./dotest.py -v -w -i forward
 | |
| 
 | |
| Session logs for test failures/errors will go into directory '2010-11-11-13_56_16'
 | |
| ----------------------------------------------------------------------
 | |
| Collected 2 tests
 | |
| 
 | |
| test_with_dsym_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase)
 | |
| Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok
 | |
| test_with_dwarf_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase)
 | |
| Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok
 | |
| 
 | |
| ----------------------------------------------------------------------
 | |
| Ran 2 tests in 5.659s
 | |
| 
 | |
| OK
 | |
| 
 | |
| The 'Session ...' verbiage is recently introduced (see also the '-s' option) to
 | |
| notify the directory containing the session logs for test failures or errors.
 | |
| In case there is any test failure/error, a similar message is appended at the
 | |
| end of the stderr output for your convenience.
 | |
| 
 | |
| ENABLING LOGS FROM TESTS
 | |
| 
 | |
| Option 1:
 | |
| 
 | |
| Writing logs into different files per test case::
 | |
| 
 | |
| $ ./dotest.py --channel "lldb all"
 | |
| 
 | |
| $ ./dotest.py --channel "lldb all" --channel "gdb-remote packets"
 | |
| 
 | |
| These log files are written to:
 | |
| 
 | |
| <session-dir>/<test-id>-host.log (logs from lldb host process)
 | |
| <session-dir>/<test-id>-server.log (logs from debugserver/lldb-server)
 | |
| <session-dir>/<test-id>-<test-result>.log (console logs)
 | |
| 
 | |
| By default, logs from successful runs are deleted.  Use the --log-success flag
 | |
| to create reference logs for debugging.
 | |
| 
 | |
| $ ./dotest.py --log-success
 | |
| 
 | |
| """)
 | |
|     sys.exit(0)
 | |
| 
 | |
| 
 | |
| def parseExclusion(exclusion_file):
 | |
|     """Parse an exclusion file, of the following format, where
 | |
|        'skip files', 'skip methods', 'xfail files', and 'xfail methods'
 | |
|        are the possible list heading values:
 | |
| 
 | |
|        skip files
 | |
|        <file name>
 | |
|        <file name>
 | |
| 
 | |
|        xfail methods
 | |
|        <method name>
 | |
|     """
 | |
|     excl_type = None
 | |
| 
 | |
|     with open(exclusion_file) as f:
 | |
|         for line in f:
 | |
|             line = line.strip()
 | |
|             if not excl_type:
 | |
|                 excl_type = line
 | |
|                 continue
 | |
| 
 | |
|             if not line:
 | |
|                 excl_type = None
 | |
|             elif excl_type == 'skip':
 | |
|                 if not configuration.skip_tests:
 | |
|                     configuration.skip_tests = []
 | |
|                 configuration.skip_tests.append(line)
 | |
|             elif excl_type == 'xfail':
 | |
|                 if not configuration.xfail_tests:
 | |
|                     configuration.xfail_tests = []
 | |
|                 configuration.xfail_tests.append(line)
 | |
| 
 | |
| 
 | |
| def parseOptionsAndInitTestdirs():
 | |
|     """Initialize the list of directories containing our unittest scripts.
 | |
| 
 | |
|     '-h/--help as the first option prints out usage info and exit the program.
 | |
|     """
 | |
| 
 | |
|     do_help = False
 | |
| 
 | |
|     platform_system = platform.system()
 | |
|     platform_machine = platform.machine()
 | |
| 
 | |
|     try:
 | |
|         parser = dotest_args.create_parser()
 | |
|         args = parser.parse_args()
 | |
|     except:
 | |
|         raise
 | |
| 
 | |
|     if args.unset_env_varnames:
 | |
|         for env_var in args.unset_env_varnames:
 | |
|             if env_var in os.environ:
 | |
|                 # From Python Doc: When unsetenv() is supported, deletion of items in os.environ
 | |
|                 # is automatically translated into a corresponding call to
 | |
|                 # unsetenv().
 | |
|                 del os.environ[env_var]
 | |
|                 # os.unsetenv(env_var)
 | |
| 
 | |
|     if args.set_env_vars:
 | |
|         for env_var in args.set_env_vars:
 | |
|             parts = env_var.split('=', 1)
 | |
|             if len(parts) == 1:
 | |
|                 os.environ[parts[0]] = ""
 | |
|             else:
 | |
|                 os.environ[parts[0]] = parts[1]
 | |
| 
 | |
|     if args.set_inferior_env_vars:
 | |
|         lldbtest_config.inferior_env = ' '.join(args.set_inferior_env_vars)
 | |
| 
 | |
|     if args.h:
 | |
|         do_help = True
 | |
| 
 | |
|     if args.compiler:
 | |
|         configuration.compiler = os.path.abspath(args.compiler)
 | |
|         if not is_exe(configuration.compiler):
 | |
|             configuration.compiler = which(args.compiler)
 | |
|         if not is_exe(configuration.compiler):
 | |
|             logging.error(
 | |
|                     '%s is not a valid compiler executable; aborting...',
 | |
|                     args.compiler)
 | |
|             sys.exit(-1)
 | |
|     else:
 | |
|         # Use a compiler appropriate appropriate for the Apple SDK if one was
 | |
|         # specified
 | |
|         if platform_system == 'Darwin' and args.apple_sdk:
 | |
|             configuration.compiler = seven.get_command_output(
 | |
|                 'xcrun -sdk "%s" -find clang 2> /dev/null' %
 | |
|                 (args.apple_sdk))
 | |
|         else:
 | |
|             # 'clang' on ubuntu 14.04 is 3.4 so we try clang-3.5 first
 | |
|             candidateCompilers = ['clang-3.5', 'clang', 'gcc']
 | |
|             for candidate in candidateCompilers:
 | |
|                 if which(candidate):
 | |
|                     configuration.compiler = candidate
 | |
|                     break
 | |
| 
 | |
|     if args.dsymutil:
 | |
|         configuration.dsymutil = args.dsymutil
 | |
|     elif platform_system == 'Darwin':
 | |
|         configuration.dsymutil = seven.get_command_output(
 | |
|             'xcrun -find -toolchain default dsymutil')
 | |
|     if args.llvm_tools_dir:
 | |
|         configuration.filecheck = shutil.which("FileCheck", path=args.llvm_tools_dir)
 | |
|         configuration.yaml2obj = shutil.which("yaml2obj", path=args.llvm_tools_dir)
 | |
| 
 | |
|     if not configuration.get_filecheck_path():
 | |
|         logging.warning('No valid FileCheck executable; some tests may fail...')
 | |
|         logging.warning('(Double-check the --llvm-tools-dir argument to dotest.py)')
 | |
| 
 | |
|     if args.channels:
 | |
|         lldbtest_config.channels = args.channels
 | |
| 
 | |
|     if args.log_success:
 | |
|         lldbtest_config.log_success = args.log_success
 | |
| 
 | |
|     if args.out_of_tree_debugserver:
 | |
|         lldbtest_config.out_of_tree_debugserver = args.out_of_tree_debugserver
 | |
| 
 | |
|     # Set SDKROOT if we are using an Apple SDK
 | |
|     if platform_system == 'Darwin' and args.apple_sdk:
 | |
|         configuration.sdkroot = seven.get_command_output(
 | |
|             'xcrun --sdk "%s" --show-sdk-path 2> /dev/null' %
 | |
|             (args.apple_sdk))
 | |
|         if not configuration.sdkroot:
 | |
|             logging.error(
 | |
|                     'No SDK found with the name %s; aborting...',
 | |
|                     args.apple_sdk)
 | |
|             sys.exit(-1)
 | |
| 
 | |
|     if args.arch:
 | |
|         configuration.arch = args.arch
 | |
|     else:
 | |
|         configuration.arch = platform_machine
 | |
| 
 | |
|     if args.categories_list:
 | |
|         configuration.categories_list = set(
 | |
|             test_categories.validate(
 | |
|                 args.categories_list, False))
 | |
|         configuration.use_categories = True
 | |
|     else:
 | |
|         configuration.categories_list = []
 | |
| 
 | |
|     if args.skip_categories:
 | |
|         configuration.skip_categories += test_categories.validate(
 | |
|             args.skip_categories, False)
 | |
| 
 | |
|     if args.xfail_categories:
 | |
|         configuration.xfail_categories += test_categories.validate(
 | |
|             args.xfail_categories, False)
 | |
| 
 | |
|     if args.E:
 | |
|         os.environ['CFLAGS_EXTRAS'] = args.E
 | |
| 
 | |
|     if args.dwarf_version:
 | |
|         configuration.dwarf_version = args.dwarf_version
 | |
|         # We cannot modify CFLAGS_EXTRAS because they're used in test cases
 | |
|         # that explicitly require no debug info.
 | |
|         os.environ['CFLAGS'] = '-gdwarf-{}'.format(configuration.dwarf_version)
 | |
| 
 | |
|     if args.settings:
 | |
|         for setting in args.settings:
 | |
|             if not len(setting) == 1 or not setting[0].count('='):
 | |
|                 logging.error('"%s" is not a setting in the form "key=value"',
 | |
|                               setting[0])
 | |
|                 sys.exit(-1)
 | |
|             setting_list = setting[0].split('=', 1)
 | |
|             configuration.settings.append((setting_list[0], setting_list[1]))
 | |
| 
 | |
|     if args.d:
 | |
|         sys.stdout.write(
 | |
|             "Suspending the process %d to wait for debugger to attach...\n" %
 | |
|             os.getpid())
 | |
|         sys.stdout.flush()
 | |
|         os.kill(os.getpid(), signal.SIGSTOP)
 | |
| 
 | |
|     if args.f:
 | |
|         if any([x.startswith('-') for x in args.f]):
 | |
|             usage(parser)
 | |
|         configuration.filters.extend(args.f)
 | |
| 
 | |
|     if args.framework:
 | |
|         configuration.lldb_framework_path = args.framework
 | |
| 
 | |
|     if args.executable:
 | |
|         # lldb executable is passed explicitly
 | |
|         lldbtest_config.lldbExec = os.path.realpath(args.executable)
 | |
|         if not is_exe(lldbtest_config.lldbExec):
 | |
|             lldbtest_config.lldbExec = which(args.executable)
 | |
|         if not is_exe(lldbtest_config.lldbExec):
 | |
|             logging.error(
 | |
|                     '%s is not a valid executable to test; aborting...',
 | |
|                     args.executable)
 | |
|             sys.exit(-1)
 | |
| 
 | |
|     if args.excluded:
 | |
|         for excl_file in args.excluded:
 | |
|             parseExclusion(excl_file)
 | |
| 
 | |
|     if args.p:
 | |
|         if args.p.startswith('-'):
 | |
|             usage(parser)
 | |
|         configuration.regexp = args.p
 | |
| 
 | |
|     if args.t:
 | |
|         os.environ['LLDB_COMMAND_TRACE'] = 'YES'
 | |
| 
 | |
|     if args.v:
 | |
|         configuration.verbose = 2
 | |
| 
 | |
|     # argparse makes sure we have a number
 | |
|     if args.sharp:
 | |
|         configuration.count = args.sharp
 | |
| 
 | |
|     if sys.platform.startswith('win32'):
 | |
|         os.environ['LLDB_DISABLE_CRASH_DIALOG'] = str(
 | |
|             args.disable_crash_dialog)
 | |
|         os.environ['LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE'] = str(True)
 | |
| 
 | |
|     if do_help:
 | |
|         usage(parser)
 | |
| 
 | |
|     # Reproducer arguments
 | |
|     if args.capture_path and args.replay_path:
 | |
|         logging.error('Cannot specify both a capture and a replay path.')
 | |
|         sys.exit(-1)
 | |
| 
 | |
|     if args.capture_path:
 | |
|         configuration.capture_path = args.capture_path
 | |
| 
 | |
|     if args.replay_path:
 | |
|         configuration.replay_path = args.replay_path
 | |
|     if args.lldb_platform_name:
 | |
|         configuration.lldb_platform_name = args.lldb_platform_name
 | |
|     if args.lldb_platform_url:
 | |
|         configuration.lldb_platform_url = args.lldb_platform_url
 | |
|     if args.lldb_platform_working_dir:
 | |
|         configuration.lldb_platform_working_dir = args.lldb_platform_working_dir
 | |
|     if platform_system == 'Darwin'  and args.apple_sdk:
 | |
|         configuration.apple_sdk = args.apple_sdk
 | |
|     if args.test_build_dir:
 | |
|         configuration.test_build_dir = args.test_build_dir
 | |
|     if args.lldb_module_cache_dir:
 | |
|         configuration.lldb_module_cache_dir = args.lldb_module_cache_dir
 | |
|     else:
 | |
|         configuration.lldb_module_cache_dir = os.path.join(
 | |
|             configuration.test_build_dir, 'module-cache-lldb')
 | |
|     if args.clang_module_cache_dir:
 | |
|         configuration.clang_module_cache_dir = args.clang_module_cache_dir
 | |
|     else:
 | |
|         configuration.clang_module_cache_dir = os.path.join(
 | |
|             configuration.test_build_dir, 'module-cache-clang')
 | |
| 
 | |
|     if args.lldb_libs_dir:
 | |
|         configuration.lldb_libs_dir = args.lldb_libs_dir
 | |
| 
 | |
|     if args.enabled_plugins:
 | |
|         configuration.enabled_plugins = args.enabled_plugins
 | |
| 
 | |
|     # Gather all the dirs passed on the command line.
 | |
|     if len(args.args) > 0:
 | |
|         configuration.testdirs = [os.path.realpath(os.path.abspath(x)) for x in args.args]
 | |
| 
 | |
|     lldbtest_config.codesign_identity = args.codesign_identity
 | |
| 
 | |
| def registerFaulthandler():
 | |
|     try:
 | |
|         import faulthandler
 | |
|     except ImportError:
 | |
|         # faulthandler is not available until python3
 | |
|         return
 | |
| 
 | |
|     faulthandler.enable()
 | |
|     # faulthandler.register is not available on Windows.
 | |
|     if getattr(faulthandler, 'register', None):
 | |
|         faulthandler.register(signal.SIGTERM, chain=True)
 | |
| 
 | |
| def setupSysPath():
 | |
|     """
 | |
|     Add LLDB.framework/Resources/Python to the search paths for modules.
 | |
|     As a side effect, we also discover the 'lldb' executable and export it here.
 | |
|     """
 | |
| 
 | |
|     # Get the directory containing the current script.
 | |
|     if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ:
 | |
|         scriptPath = os.environ["DOTEST_SCRIPT_DIR"]
 | |
|     else:
 | |
|         scriptPath = os.path.dirname(os.path.abspath(__file__))
 | |
|     if not scriptPath.endswith('test'):
 | |
|         print("This script expects to reside in lldb's test directory.")
 | |
|         sys.exit(-1)
 | |
| 
 | |
|     os.environ["LLDB_TEST"] = scriptPath
 | |
| 
 | |
|     # Set up the root build directory.
 | |
|     if not configuration.test_build_dir:
 | |
|         raise Exception("test_build_dir is not set")
 | |
|     configuration.test_build_dir = os.path.abspath(configuration.test_build_dir)
 | |
| 
 | |
|     # Set up the LLDB_SRC environment variable, so that the tests can locate
 | |
|     # the LLDB source code.
 | |
|     os.environ["LLDB_SRC"] = lldbsuite.lldb_root
 | |
| 
 | |
|     pluginPath = os.path.join(scriptPath, 'plugins')
 | |
|     toolsLLDBVSCode = os.path.join(scriptPath, 'tools', 'lldb-vscode')
 | |
|     toolsLLDBServerPath = os.path.join(scriptPath, 'tools', 'lldb-server')
 | |
| 
 | |
|     # Insert script dir, plugin dir and lldb-server dir to the sys.path.
 | |
|     sys.path.insert(0, pluginPath)
 | |
|     # Adding test/tools/lldb-vscode to the path makes it easy to
 | |
|     # "import lldb_vscode_testcase" from the VSCode tests
 | |
|     sys.path.insert(0, toolsLLDBVSCode)
 | |
|     # Adding test/tools/lldb-server to the path makes it easy
 | |
|     sys.path.insert(0, toolsLLDBServerPath)
 | |
|     # to "import lldbgdbserverutils" from the lldb-server tests
 | |
| 
 | |
|     # This is the root of the lldb git/svn checkout
 | |
|     # When this changes over to a package instead of a standalone script, this
 | |
|     # will be `lldbsuite.lldb_root`
 | |
|     lldbRootDirectory = lldbsuite.lldb_root
 | |
| 
 | |
|     # Some of the tests can invoke the 'lldb' command directly.
 | |
|     # We'll try to locate the appropriate executable right here.
 | |
| 
 | |
|     # The lldb executable can be set from the command line
 | |
|     # if it's not set, we try to find it now
 | |
|     # first, we try the environment
 | |
|     if not lldbtest_config.lldbExec:
 | |
|         # First, you can define an environment variable LLDB_EXEC specifying the
 | |
|         # full pathname of the lldb executable.
 | |
|         if "LLDB_EXEC" in os.environ:
 | |
|             lldbtest_config.lldbExec = os.environ["LLDB_EXEC"]
 | |
| 
 | |
|     if not lldbtest_config.lldbExec:
 | |
|         # Last, check the path
 | |
|         lldbtest_config.lldbExec = which('lldb')
 | |
| 
 | |
|     if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec):
 | |
|         print(
 | |
|             "'{}' is not a path to a valid executable".format(
 | |
|                 lldbtest_config.lldbExec))
 | |
|         lldbtest_config.lldbExec = None
 | |
| 
 | |
|     if not lldbtest_config.lldbExec:
 | |
|         print("The 'lldb' executable cannot be located.  Some of the tests may not be run as a result.")
 | |
|         sys.exit(-1)
 | |
| 
 | |
|     os.system('%s -v' % lldbtest_config.lldbExec)
 | |
| 
 | |
|     lldbDir = os.path.dirname(lldbtest_config.lldbExec)
 | |
| 
 | |
|     lldbVSCodeExec = os.path.join(lldbDir, "lldb-vscode")
 | |
|     if is_exe(lldbVSCodeExec):
 | |
|         os.environ["LLDBVSCODE_EXEC"] = lldbVSCodeExec
 | |
|     else:
 | |
|         if not configuration.shouldSkipBecauseOfCategories(["lldb-vscode"]):
 | |
|             print(
 | |
|                 "The 'lldb-vscode' executable cannot be located.  The lldb-vscode tests can not be run as a result.")
 | |
|             configuration.skip_categories.append("lldb-vscode")
 | |
| 
 | |
|     lldbPythonDir = None  # The directory that contains 'lldb/__init__.py'
 | |
| 
 | |
|     # If our lldb supports the -P option, use it to find the python path:
 | |
|     lldb_dash_p_result = subprocess.check_output([lldbtest_config.lldbExec, "-P"], universal_newlines=True)
 | |
|     if lldb_dash_p_result:
 | |
|         for line in lldb_dash_p_result.splitlines():
 | |
|             if os.path.isdir(line) and os.path.exists(os.path.join(line, 'lldb', '__init__.py')):
 | |
|                 lldbPythonDir = line
 | |
|                 break
 | |
| 
 | |
|     if not lldbPythonDir:
 | |
|         print(
 | |
|             "Unable to load lldb extension module.  Possible reasons for this include:")
 | |
|         print("  1) LLDB was built with LLDB_ENABLE_PYTHON=0")
 | |
|         print(
 | |
|             "  2) PYTHONPATH and PYTHONHOME are not set correctly.  PYTHONHOME should refer to")
 | |
|         print(
 | |
|             "     the version of Python that LLDB built and linked against, and PYTHONPATH")
 | |
|         print(
 | |
|             "     should contain the Lib directory for the same python distro, as well as the")
 | |
|         print("     location of LLDB\'s site-packages folder.")
 | |
|         print(
 | |
|             "  3) A different version of Python than that which was built against is exported in")
 | |
|         print("     the system\'s PATH environment variable, causing conflicts.")
 | |
|         print(
 | |
|             "  4) The executable '%s' could not be found.  Please check " %
 | |
|             lldbtest_config.lldbExec)
 | |
|         print("     that it exists and is executable.")
 | |
| 
 | |
|     if lldbPythonDir:
 | |
|         lldbPythonDir = os.path.normpath(lldbPythonDir)
 | |
|         # Some of the code that uses this path assumes it hasn't resolved the Versions... link.
 | |
|         # If the path we've constructed looks like that, then we'll strip out
 | |
|         # the Versions/A part.
 | |
|         (before, frameWithVersion, after) = lldbPythonDir.rpartition(
 | |
|             "LLDB.framework/Versions/A")
 | |
|         if frameWithVersion != "":
 | |
|             lldbPythonDir = before + "LLDB.framework" + after
 | |
| 
 | |
|         lldbPythonDir = os.path.abspath(lldbPythonDir)
 | |
| 
 | |
|         if "freebsd" in sys.platform or "linux" in sys.platform:
 | |
|             os.environ['LLDB_LIB_DIR'] = os.path.join(lldbPythonDir, '..', '..')
 | |
| 
 | |
|         # If tests need to find LLDB_FRAMEWORK, now they can do it
 | |
|         os.environ["LLDB_FRAMEWORK"] = os.path.dirname(
 | |
|             os.path.dirname(lldbPythonDir))
 | |
| 
 | |
|         # This is to locate the lldb.py module.  Insert it right after
 | |
|         # sys.path[0].
 | |
|         sys.path[1:1] = [lldbPythonDir]
 | |
| 
 | |
| 
 | |
| def visit_file(dir, name):
 | |
|     # Try to match the regexp pattern, if specified.
 | |
|     if configuration.regexp:
 | |
|         if not re.search(configuration.regexp, name):
 | |
|             # We didn't match the regex, we're done.
 | |
|             return
 | |
| 
 | |
|     if configuration.skip_tests:
 | |
|         for file_regexp in configuration.skip_tests:
 | |
|             if re.search(file_regexp, name):
 | |
|                 return
 | |
| 
 | |
|     # We found a match for our test.  Add it to the suite.
 | |
| 
 | |
|     # Update the sys.path first.
 | |
|     if not sys.path.count(dir):
 | |
|         sys.path.insert(0, dir)
 | |
|     base = os.path.splitext(name)[0]
 | |
| 
 | |
|     # Thoroughly check the filterspec against the base module and admit
 | |
|     # the (base, filterspec) combination only when it makes sense.
 | |
| 
 | |
|     def check(obj, parts):
 | |
|         for part in parts:
 | |
|             try:
 | |
|                 parent, obj = obj, getattr(obj, part)
 | |
|             except AttributeError:
 | |
|                 # The filterspec has failed.
 | |
|                 return False
 | |
|         return True
 | |
| 
 | |
|     module = __import__(base)
 | |
| 
 | |
|     def iter_filters():
 | |
|         for filterspec in configuration.filters:
 | |
|             parts = filterspec.split('.')
 | |
|             if check(module, parts):
 | |
|                 yield filterspec
 | |
|             elif parts[0] == base and len(parts) > 1 and check(module, parts[1:]):
 | |
|                 yield '.'.join(parts[1:])
 | |
|             else:
 | |
|                 for key,value in module.__dict__.items():
 | |
|                     if check(value, parts):
 | |
|                         yield key + '.' + filterspec
 | |
| 
 | |
|     filtered = False
 | |
|     for filterspec in iter_filters():
 | |
|         filtered = True
 | |
|         print("adding filter spec %s to module %s" % (filterspec, repr(module)))
 | |
|         tests = unittest2.defaultTestLoader.loadTestsFromName(filterspec, module)
 | |
|         configuration.suite.addTests(tests)
 | |
| 
 | |
|     # Forgo this module if the (base, filterspec) combo is invalid
 | |
|     if configuration.filters and not filtered:
 | |
|         return
 | |
| 
 | |
|     if not filtered:
 | |
|         # Add the entire file's worth of tests since we're not filtered.
 | |
|         # Also the fail-over case when the filterspec branch
 | |
|         # (base, filterspec) combo doesn't make sense.
 | |
|         configuration.suite.addTests(
 | |
|             unittest2.defaultTestLoader.loadTestsFromName(base))
 | |
| 
 | |
| 
 | |
| def visit(prefix, dir, names):
 | |
|     """Visitor function for os.path.walk(path, visit, arg)."""
 | |
| 
 | |
|     dir_components = set(dir.split(os.sep))
 | |
|     excluded_components = set(['.svn', '.git'])
 | |
|     if dir_components.intersection(excluded_components):
 | |
|         return
 | |
| 
 | |
|     # Gather all the Python test file names that follow the Test*.py pattern.
 | |
|     python_test_files = [
 | |
|         name
 | |
|         for name in names
 | |
|         if name.endswith('.py') and name.startswith(prefix)]
 | |
| 
 | |
|     # Visit all the python test files.
 | |
|     for name in python_test_files:
 | |
|         # Ensure we error out if we have multiple tests with the same
 | |
|         # base name.
 | |
|         # Future improvement: find all the places where we work with base
 | |
|         # names and convert to full paths.  We have directory structure
 | |
|         # to disambiguate these, so we shouldn't need this constraint.
 | |
|         if name in configuration.all_tests:
 | |
|             raise Exception("Found multiple tests with the name %s" % name)
 | |
|         configuration.all_tests.add(name)
 | |
| 
 | |
|         # Run the relevant tests in the python file.
 | |
|         visit_file(dir, name)
 | |
| 
 | |
| 
 | |
| # ======================================== #
 | |
| #                                          #
 | |
| # Execution of the test driver starts here #
 | |
| #                                          #
 | |
| # ======================================== #
 | |
| 
 | |
| 
 | |
| def checkDsymForUUIDIsNotOn():
 | |
|     cmd = ["defaults", "read", "com.apple.DebugSymbols"]
 | |
|     process = subprocess.Popen(
 | |
|         cmd,
 | |
|         stdout=subprocess.PIPE,
 | |
|         stderr=subprocess.STDOUT)
 | |
|     cmd_output = process.stdout.read()
 | |
|     output_str = cmd_output.decode("utf-8")
 | |
|     if "DBGFileMappedPaths = " in output_str:
 | |
|         print("%s =>" % ' '.join(cmd))
 | |
|         print(output_str)
 | |
|         print(
 | |
|             "Disable automatic lookup and caching of dSYMs before running the test suite!")
 | |
|         print("Exiting...")
 | |
|         sys.exit(0)
 | |
| 
 | |
| 
 | |
| def exitTestSuite(exitCode=None):
 | |
|     # lldb.py does SBDebugger.Initialize().
 | |
|     # Call SBDebugger.Terminate() on exit.
 | |
|     import lldb
 | |
|     lldb.SBDebugger.Terminate()
 | |
|     if exitCode:
 | |
|         sys.exit(exitCode)
 | |
| 
 | |
| 
 | |
| def getVersionForSDK(sdk):
 | |
|     sdk = str.lower(sdk)
 | |
|     full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk)
 | |
|     basename = os.path.basename(full_path)
 | |
|     basename = os.path.splitext(basename)[0]
 | |
|     basename = str.lower(basename)
 | |
|     ver = basename.replace(sdk, '')
 | |
|     return ver
 | |
| 
 | |
| 
 | |
| def checkCompiler():
 | |
|     # Add some intervention here to sanity check that the compiler requested is sane.
 | |
|     # If found not to be an executable program, we abort.
 | |
|     c = configuration.compiler
 | |
|     if which(c):
 | |
|         return
 | |
| 
 | |
|     if not sys.platform.startswith("darwin"):
 | |
|         raise Exception(c + " is not a valid compiler")
 | |
| 
 | |
|     pipe = subprocess.Popen(
 | |
|         ['xcrun', '-find', c], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
 | |
|     cmd_output = pipe.stdout.read()
 | |
|     if not cmd_output or "not found" in cmd_output:
 | |
|         raise Exception(c + " is not a valid compiler")
 | |
| 
 | |
|     configuration.compiler = cmd_output.split('\n')[0]
 | |
|     print("'xcrun -find %s' returning %s" % (c, configuration.compiler))
 | |
| 
 | |
| def canRunLibcxxTests():
 | |
|     from lldbsuite.test import lldbplatformutil
 | |
| 
 | |
|     platform = lldbplatformutil.getPlatform()
 | |
| 
 | |
|     if lldbplatformutil.target_is_android() or lldbplatformutil.platformIsDarwin():
 | |
|         return True, "libc++ always present"
 | |
| 
 | |
|     if platform == "linux":
 | |
|         with tempfile.NamedTemporaryFile() as f:
 | |
|             cmd = [configuration.compiler, "-xc++", "-stdlib=libc++", "-o", f.name, "-"]
 | |
|             p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
 | |
|             _, stderr = p.communicate("#include <algorithm>\nint main() {}")
 | |
|             if not p.returncode:
 | |
|                 return True, "Compiling with -stdlib=libc++ works"
 | |
|             return False, "Compiling with -stdlib=libc++ fails with the error: %s" % stderr
 | |
| 
 | |
|     return False, "Don't know how to build with libc++ on %s" % platform
 | |
| 
 | |
| def checkLibcxxSupport():
 | |
|     result, reason = canRunLibcxxTests()
 | |
|     if result:
 | |
|         return # libc++ supported
 | |
|     if "libc++" in configuration.categories_list:
 | |
|         return # libc++ category explicitly requested, let it run.
 | |
|     print("Libc++ tests will not be run because: " + reason)
 | |
|     configuration.skip_categories.append("libc++")
 | |
| 
 | |
| def canRunLibstdcxxTests():
 | |
|     from lldbsuite.test import lldbplatformutil
 | |
| 
 | |
|     platform = lldbplatformutil.getPlatform()
 | |
|     if lldbplatformutil.target_is_android():
 | |
|         platform = "android"
 | |
|     if platform == "linux":
 | |
|         return True, "libstdcxx always present"
 | |
|     return False, "Don't know how to build with libstdcxx on %s" % platform
 | |
| 
 | |
| def checkLibstdcxxSupport():
 | |
|     result, reason = canRunLibstdcxxTests()
 | |
|     if result:
 | |
|         return # libstdcxx supported
 | |
|     if "libstdcxx" in configuration.categories_list:
 | |
|         return # libstdcxx category explicitly requested, let it run.
 | |
|     print("libstdcxx tests will not be run because: " + reason)
 | |
|     configuration.skip_categories.append("libstdcxx")
 | |
| 
 | |
| def canRunWatchpointTests():
 | |
|     from lldbsuite.test import lldbplatformutil
 | |
| 
 | |
|     platform = lldbplatformutil.getPlatform()
 | |
|     if platform == "netbsd":
 | |
|         if os.geteuid() == 0:
 | |
|             return True, "root can always write dbregs"
 | |
|         try:
 | |
|             output = subprocess.check_output(["/sbin/sysctl", "-n",
 | |
|               "security.models.extensions.user_set_dbregs"]).decode().strip()
 | |
|             if output == "1":
 | |
|                 return True, "security.models.extensions.user_set_dbregs enabled"
 | |
|         except subprocess.CalledProcessError:
 | |
|             pass
 | |
|         return False, "security.models.extensions.user_set_dbregs disabled"
 | |
|     elif platform == "freebsd" and configuration.arch == "aarch64":
 | |
|         import lldb
 | |
|         if lldb.SBPlatform.GetHostPlatform().GetOSMajorVersion() < 13:
 | |
|             return False, "Watchpoint support on arm64 requires FreeBSD 13.0"
 | |
|     return True, "watchpoint support available"
 | |
| 
 | |
| def checkWatchpointSupport():
 | |
|     result, reason = canRunWatchpointTests()
 | |
|     if result:
 | |
|         return # watchpoints supported
 | |
|     if "watchpoint" in configuration.categories_list:
 | |
|         return # watchpoint category explicitly requested, let it run.
 | |
|     print("watchpoint tests will not be run because: " + reason)
 | |
|     configuration.skip_categories.append("watchpoint")
 | |
| 
 | |
| def checkObjcSupport():
 | |
|     from lldbsuite.test import lldbplatformutil
 | |
| 
 | |
|     if not lldbplatformutil.platformIsDarwin():
 | |
|         print("objc tests will be skipped because of unsupported platform")
 | |
|         configuration.skip_categories.append("objc")
 | |
| 
 | |
| def checkDebugInfoSupport():
 | |
|     import lldb
 | |
| 
 | |
|     platform = lldb.selected_platform.GetTriple().split('-')[2]
 | |
|     compiler = configuration.compiler
 | |
|     skipped = []
 | |
|     for cat in test_categories.debug_info_categories:
 | |
|         if cat in configuration.categories_list:
 | |
|             continue # Category explicitly requested, let it run.
 | |
|         if test_categories.is_supported_on_platform(cat, platform, compiler):
 | |
|             continue
 | |
|         configuration.skip_categories.append(cat)
 | |
|         skipped.append(cat)
 | |
|     if skipped:
 | |
|         print("Skipping following debug info categories:", skipped)
 | |
| 
 | |
| def checkDebugServerSupport():
 | |
|     from lldbsuite.test import lldbplatformutil
 | |
|     import lldb
 | |
| 
 | |
|     skip_msg = "Skipping %s tests, as they are not compatible with remote testing on this platform"
 | |
|     if lldbplatformutil.platformIsDarwin():
 | |
|         configuration.skip_categories.append("llgs")
 | |
|         if lldb.remote_platform:
 | |
|             # <rdar://problem/34539270>
 | |
|             configuration.skip_categories.append("debugserver")
 | |
|             print(skip_msg%"debugserver");
 | |
|     else:
 | |
|         configuration.skip_categories.append("debugserver")
 | |
|         if lldb.remote_platform and lldbplatformutil.getPlatform() == "windows":
 | |
|             configuration.skip_categories.append("llgs")
 | |
|             print(skip_msg%"lldb-server");
 | |
| 
 | |
| def run_suite():
 | |
|     # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults
 | |
|     # does not exist before proceeding to running the test suite.
 | |
|     if sys.platform.startswith("darwin"):
 | |
|         checkDsymForUUIDIsNotOn()
 | |
| 
 | |
|     # Start the actions by first parsing the options while setting up the test
 | |
|     # directories, followed by setting up the search paths for lldb utilities;
 | |
|     # then, we walk the directory trees and collect the tests into our test suite.
 | |
|     #
 | |
|     parseOptionsAndInitTestdirs()
 | |
| 
 | |
|     # Print a stack trace if the test hangs or is passed SIGTERM.
 | |
|     registerFaulthandler()
 | |
| 
 | |
|     setupSysPath()
 | |
| 
 | |
|     import lldbconfig
 | |
|     if configuration.capture_path or configuration.replay_path:
 | |
|         lldbconfig.INITIALIZE = False
 | |
|     import lldb
 | |
| 
 | |
|     if configuration.capture_path:
 | |
|         lldb.SBReproducer.Capture(configuration.capture_path)
 | |
|         lldb.SBReproducer.SetAutoGenerate(True)
 | |
|     elif configuration.replay_path:
 | |
|         lldb.SBReproducer.PassiveReplay(configuration.replay_path)
 | |
| 
 | |
|     if not lldbconfig.INITIALIZE:
 | |
|         lldb.SBDebugger.Initialize()
 | |
| 
 | |
|     # Use host platform by default.
 | |
|     lldb.selected_platform = lldb.SBPlatform.GetHostPlatform()
 | |
| 
 | |
|     # Now we can also import lldbutil
 | |
|     from lldbsuite.test import lldbutil
 | |
| 
 | |
|     if configuration.lldb_platform_name:
 | |
|         print("Setting up remote platform '%s'" %
 | |
|               (configuration.lldb_platform_name))
 | |
|         lldb.remote_platform = lldb.SBPlatform(
 | |
|             configuration.lldb_platform_name)
 | |
|         if not lldb.remote_platform.IsValid():
 | |
|             print(
 | |
|                 "error: unable to create the LLDB platform named '%s'." %
 | |
|                 (configuration.lldb_platform_name))
 | |
|             exitTestSuite(1)
 | |
|         if configuration.lldb_platform_url:
 | |
|             # We must connect to a remote platform if a LLDB platform URL was
 | |
|             # specified
 | |
|             print(
 | |
|                 "Connecting to remote platform '%s' at '%s'..." %
 | |
|                 (configuration.lldb_platform_name, configuration.lldb_platform_url))
 | |
|             platform_connect_options = lldb.SBPlatformConnectOptions(
 | |
|                 configuration.lldb_platform_url)
 | |
|             err = lldb.remote_platform.ConnectRemote(platform_connect_options)
 | |
|             if err.Success():
 | |
|                 print("Connected.")
 | |
|             else:
 | |
|                 print("error: failed to connect to remote platform using URL '%s': %s" % (
 | |
|                     configuration.lldb_platform_url, err))
 | |
|                 exitTestSuite(1)
 | |
|         else:
 | |
|             configuration.lldb_platform_url = None
 | |
| 
 | |
|     if configuration.lldb_platform_working_dir:
 | |
|         print("Setting remote platform working directory to '%s'..." %
 | |
|               (configuration.lldb_platform_working_dir))
 | |
|         error = lldb.remote_platform.MakeDirectory(
 | |
|             configuration.lldb_platform_working_dir, 448)  # 448 = 0o700
 | |
|         if error.Fail():
 | |
|             raise Exception("making remote directory '%s': %s" % (
 | |
|                 configuration.lldb_platform_working_dir, error))
 | |
| 
 | |
|         if not lldb.remote_platform.SetWorkingDirectory(
 | |
|                 configuration.lldb_platform_working_dir):
 | |
|             raise Exception("failed to set working directory '%s'" % configuration.lldb_platform_working_dir)
 | |
|         lldb.selected_platform = lldb.remote_platform
 | |
|     else:
 | |
|         lldb.remote_platform = None
 | |
|         configuration.lldb_platform_working_dir = None
 | |
|         configuration.lldb_platform_url = None
 | |
| 
 | |
|     # Set up the working directory.
 | |
|     # Note that it's not dotest's job to clean this directory.
 | |
|     lldbutil.mkdir_p(configuration.test_build_dir)
 | |
| 
 | |
|     from . import lldbplatformutil
 | |
|     target_platform = lldbplatformutil.getPlatform()
 | |
| 
 | |
|     checkLibcxxSupport()
 | |
|     checkLibstdcxxSupport()
 | |
|     checkWatchpointSupport()
 | |
|     checkDebugInfoSupport()
 | |
|     checkDebugServerSupport()
 | |
|     checkObjcSupport()
 | |
| 
 | |
|     for testdir in configuration.testdirs:
 | |
|         for (dirpath, dirnames, filenames) in os.walk(testdir):
 | |
|             visit('Test', dirpath, filenames)
 | |
| 
 | |
|     #
 | |
|     # Now that we have loaded all the test cases, run the whole test suite.
 | |
|     #
 | |
| 
 | |
|     # Install the control-c handler.
 | |
|     unittest2.signals.installHandler()
 | |
| 
 | |
|     #
 | |
|     # Invoke the default TextTestRunner to run the test suite
 | |
|     #
 | |
|     checkCompiler()
 | |
| 
 | |
|     if configuration.verbose:
 | |
|         print("compiler=%s" % configuration.compiler)
 | |
| 
 | |
|     # Iterating over all possible architecture and compiler combinations.
 | |
|     configString = "arch=%s compiler=%s" % (configuration.arch,
 | |
|                                             configuration.compiler)
 | |
| 
 | |
|     # Output the configuration.
 | |
|     if configuration.verbose:
 | |
|         sys.stderr.write("\nConfiguration: " + configString + "\n")
 | |
| 
 | |
|     # First, write out the number of collected test cases.
 | |
|     if configuration.verbose:
 | |
|         sys.stderr.write(configuration.separator + "\n")
 | |
|         sys.stderr.write(
 | |
|             "Collected %d test%s\n\n" %
 | |
|             (configuration.suite.countTestCases(),
 | |
|              configuration.suite.countTestCases() != 1 and "s" or ""))
 | |
| 
 | |
|     if configuration.suite.countTestCases() == 0:
 | |
|         logging.error("did not discover any matching tests")
 | |
|         exitTestSuite(1)
 | |
| 
 | |
|     # Invoke the test runner.
 | |
|     if configuration.count == 1:
 | |
|         result = unittest2.TextTestRunner(
 | |
|             stream=sys.stderr,
 | |
|             verbosity=configuration.verbose,
 | |
|             resultclass=test_result.LLDBTestResult).run(
 | |
|             configuration.suite)
 | |
|     else:
 | |
|         # We are invoking the same test suite more than once.  In this case,
 | |
|         # mark __ignore_singleton__ flag as True so the signleton pattern is
 | |
|         # not enforced.
 | |
|         test_result.LLDBTestResult.__ignore_singleton__ = True
 | |
|         for i in range(configuration.count):
 | |
| 
 | |
|             result = unittest2.TextTestRunner(
 | |
|                 stream=sys.stderr,
 | |
|                 verbosity=configuration.verbose,
 | |
|                 resultclass=test_result.LLDBTestResult).run(
 | |
|                 configuration.suite)
 | |
| 
 | |
|     configuration.failed = not result.wasSuccessful()
 | |
| 
 | |
|     if configuration.sdir_has_content and configuration.verbose:
 | |
|         sys.stderr.write(
 | |
|             "Session logs for test failures/errors/unexpected successes"
 | |
|             " can be found in the test build directory\n")
 | |
| 
 | |
|     if configuration.use_categories and len(
 | |
|             configuration.failures_per_category) > 0:
 | |
|         sys.stderr.write("Failures per category:\n")
 | |
|         for category in configuration.failures_per_category:
 | |
|             sys.stderr.write(
 | |
|                 "%s - %d\n" %
 | |
|                 (category, configuration.failures_per_category[category]))
 | |
| 
 | |
|     # Exiting.
 | |
|     exitTestSuite(configuration.failed)
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     print(
 | |
|         __file__ +
 | |
|         " is for use as a module only.  It should not be run as a standalone script.")
 | |
|     sys.exit(-1)
 |