1"""
2A simple testing framework for lldb using python's unit testing framework.
3
4Tests for lldb are written as python scripts which take advantage of the script
5bridging provided by LLDB.framework to interact with lldb core.
6
7A specific naming pattern is followed by the .py script to be recognized as
8a module which implements a test scenario, namely, Test*.py.
9
10To specify the directories where "Test*.py" python test scripts are located,
11you need to pass in a list of directory names.  By default, the current
12working directory is searched if nothing is specified on the command line.
13
14Type:
15
16./dotest.py -h
17
18for available options.
19"""
20
21from __future__ import absolute_import
22from __future__ import print_function
23
24# System modules
25import atexit
26import datetime
27import errno
28import logging
29import os
30import platform
31import re
32import signal
33import subprocess
34import sys
35import tempfile
36
37# Third-party modules
38import six
39import unittest2
40
41# LLDB Modules
42import lldbsuite
43from . import configuration
44from . import dotest_args
45from . import lldbtest_config
46from . import test_categories
47from . import test_result
48from ..support import seven
49
50
51def is_exe(fpath):
52    """Returns true if fpath is an executable."""
53    if fpath == None:
54        return False
55    if sys.platform == 'win32':
56        if not fpath.endswith(".exe"):
57            fpath += ".exe"
58    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
59
60
61def which(program):
62    """Returns the full path to a program; None otherwise."""
63    fpath, _ = os.path.split(program)
64    if fpath:
65        if is_exe(program):
66            return program
67    else:
68        for path in os.environ["PATH"].split(os.pathsep):
69            exe_file = os.path.join(path, program)
70            if is_exe(exe_file):
71                return exe_file
72    return None
73
74
75def usage(parser):
76    parser.print_help()
77    if configuration.verbose > 0:
78        print("""
79Examples:
80
81This is an example of using the -f option to pinpoint to a specific test class
82and test method to be run:
83
84$ ./dotest.py -f ClassTypesTestCase.test_with_dsym_and_run_command
85----------------------------------------------------------------------
86Collected 1 test
87
88test_with_dsym_and_run_command (TestClassTypes.ClassTypesTestCase)
89Test 'frame variable this' when stopped on a class constructor. ... ok
90
91----------------------------------------------------------------------
92Ran 1 test in 1.396s
93
94OK
95
96And this is an example of using the -p option to run a single file (the filename
97matches the pattern 'ObjC' and it happens to be 'TestObjCMethods.py'):
98
99$ ./dotest.py -v -p ObjC
100----------------------------------------------------------------------
101Collected 4 tests
102
103test_break_with_dsym (TestObjCMethods.FoundationTestCase)
104Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok
105test_break_with_dwarf (TestObjCMethods.FoundationTestCase)
106Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok
107test_data_type_and_expr_with_dsym (TestObjCMethods.FoundationTestCase)
108Lookup objective-c data types and evaluate expressions. ... ok
109test_data_type_and_expr_with_dwarf (TestObjCMethods.FoundationTestCase)
110Lookup objective-c data types and evaluate expressions. ... ok
111
112----------------------------------------------------------------------
113Ran 4 tests in 16.661s
114
115OK
116
117Running of this script also sets up the LLDB_TEST environment variable so that
118individual test cases can locate their supporting files correctly.  The script
119tries to set up Python's search paths for modules by looking at the build tree
120relative to this script.  See also the '-i' option in the following example.
121
122Finally, this is an example of using the lldb.py module distributed/installed by
123Xcode4 to run against the tests under the 'forward' directory, and with the '-w'
124option to add some delay between two tests.  It uses ARCH=x86_64 to specify that
125as the architecture and CC=clang to specify the compiler used for the test run:
126
127$ PYTHONPATH=/Xcode4/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python ARCH=x86_64 CC=clang ./dotest.py -v -w -i forward
128
129Session logs for test failures/errors will go into directory '2010-11-11-13_56_16'
130----------------------------------------------------------------------
131Collected 2 tests
132
133test_with_dsym_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase)
134Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok
135test_with_dwarf_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase)
136Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok
137
138----------------------------------------------------------------------
139Ran 2 tests in 5.659s
140
141OK
142
143The 'Session ...' verbiage is recently introduced (see also the '-s' option) to
144notify the directory containing the session logs for test failures or errors.
145In case there is any test failure/error, a similar message is appended at the
146end of the stderr output for your convenience.
147
148ENABLING LOGS FROM TESTS
149
150Option 1:
151
152Writing logs into different files per test case::
153
154$ ./dotest.py --channel "lldb all"
155
156$ ./dotest.py --channel "lldb all" --channel "gdb-remote packets"
157
158These log files are written to:
159
160<session-dir>/<test-id>-host.log (logs from lldb host process)
161<session-dir>/<test-id>-server.log (logs from debugserver/lldb-server)
162<session-dir>/<test-id>-<test-result>.log (console logs)
163
164By default, logs from successful runs are deleted.  Use the --log-success flag
165to create reference logs for debugging.
166
167$ ./dotest.py --log-success
168
169""")
170    sys.exit(0)
171
172
173def parseExclusion(exclusion_file):
174    """Parse an exclusion file, of the following format, where
175       'skip files', 'skip methods', 'xfail files', and 'xfail methods'
176       are the possible list heading values:
177
178       skip files
179       <file name>
180       <file name>
181
182       xfail methods
183       <method name>
184    """
185    excl_type = None
186
187    with open(exclusion_file) as f:
188        for line in f:
189            line = line.strip()
190            if not excl_type:
191                excl_type = line
192                continue
193
194            if not line:
195                excl_type = None
196            elif excl_type == 'skip':
197                if not configuration.skip_tests:
198                    configuration.skip_tests = []
199                configuration.skip_tests.append(line)
200            elif excl_type == 'xfail':
201                if not configuration.xfail_tests:
202                    configuration.xfail_tests = []
203                configuration.xfail_tests.append(line)
204
205
206def parseOptionsAndInitTestdirs():
207    """Initialize the list of directories containing our unittest scripts.
208
209    '-h/--help as the first option prints out usage info and exit the program.
210    """
211
212    do_help = False
213
214    platform_system = platform.system()
215    platform_machine = platform.machine()
216
217    try:
218        parser = dotest_args.create_parser()
219        args = parser.parse_args()
220    except:
221        raise
222
223    if args.unset_env_varnames:
224        for env_var in args.unset_env_varnames:
225            if env_var in os.environ:
226                # From Python Doc: When unsetenv() is supported, deletion of items in os.environ
227                # is automatically translated into a corresponding call to
228                # unsetenv().
229                del os.environ[env_var]
230                # os.unsetenv(env_var)
231
232    if args.set_env_vars:
233        for env_var in args.set_env_vars:
234            parts = env_var.split('=', 1)
235            if len(parts) == 1:
236                os.environ[parts[0]] = ""
237            else:
238                os.environ[parts[0]] = parts[1]
239
240    if args.set_inferior_env_vars:
241        lldbtest_config.inferior_env = ' '.join(args.set_inferior_env_vars)
242
243    if args.h:
244        do_help = True
245
246    if args.compiler:
247        configuration.compiler = os.path.abspath(args.compiler)
248        if not is_exe(configuration.compiler):
249            configuration.compiler = which(args.compiler)
250        if not is_exe(configuration.compiler):
251            logging.error(
252                    '%s is not a valid compiler executable; aborting...',
253                    args.compiler)
254            sys.exit(-1)
255    else:
256        # Use a compiler appropriate appropriate for the Apple SDK if one was
257        # specified
258        if platform_system == 'Darwin' and args.apple_sdk:
259            configuration.compiler = seven.get_command_output(
260                'xcrun -sdk "%s" -find clang 2> /dev/null' %
261                (args.apple_sdk))
262        else:
263            # 'clang' on ubuntu 14.04 is 3.4 so we try clang-3.5 first
264            candidateCompilers = ['clang-3.5', 'clang', 'gcc']
265            for candidate in candidateCompilers:
266                if which(candidate):
267                    configuration.compiler = candidate
268                    break
269
270    if args.dsymutil:
271        configuration.dsymutil = args.dsymutil
272    elif platform_system == 'Darwin':
273        configuration.dsymutil = seven.get_command_output(
274            'xcrun -find -toolchain default dsymutil')
275
276
277    # The lldb-dotest script produced by the CMake build passes in a path to a
278    # working FileCheck and yaml2obj binary. So does one specific Xcode
279    # project target. However, when invoking dotest.py directly, a valid
280    # --filecheck and --yaml2obj option needs to be given.
281    if args.filecheck:
282        configuration.filecheck = os.path.abspath(args.filecheck)
283
284    if args.yaml2obj:
285        configuration.yaml2obj = os.path.abspath(args.yaml2obj)
286
287    if not configuration.get_filecheck_path():
288        logging.warning('No valid FileCheck executable; some tests may fail...')
289        logging.warning('(Double-check the --filecheck argument to dotest.py)')
290
291    if args.channels:
292        lldbtest_config.channels = args.channels
293
294    if args.log_success:
295        lldbtest_config.log_success = args.log_success
296
297    if args.out_of_tree_debugserver:
298        lldbtest_config.out_of_tree_debugserver = args.out_of_tree_debugserver
299
300    # Set SDKROOT if we are using an Apple SDK
301    if platform_system == 'Darwin' and args.apple_sdk:
302        configuration.sdkroot = seven.get_command_output(
303            'xcrun --sdk "%s" --show-sdk-path 2> /dev/null' %
304            (args.apple_sdk))
305        if not configuration.sdkroot:
306            logging.error(
307                    'No SDK found with the name %s; aborting...',
308                    args.apple_sdk)
309            sys.exit(-1)
310
311    if args.arch:
312        configuration.arch = args.arch
313    else:
314        configuration.arch = platform_machine
315
316    if args.categories_list:
317        configuration.categories_list = set(
318            test_categories.validate(
319                args.categories_list, False))
320        configuration.use_categories = True
321    else:
322        configuration.categories_list = []
323
324    if args.skip_categories:
325        configuration.skip_categories += test_categories.validate(
326            args.skip_categories, False)
327
328    if args.xfail_categories:
329        configuration.xfail_categories += test_categories.validate(
330            args.xfail_categories, False)
331
332    if args.E:
333        os.environ['CFLAGS_EXTRAS'] = args.E
334
335    if args.dwarf_version:
336        configuration.dwarf_version = args.dwarf_version
337        # We cannot modify CFLAGS_EXTRAS because they're used in test cases
338        # that explicitly require no debug info.
339        os.environ['CFLAGS'] = '-gdwarf-{}'.format(configuration.dwarf_version)
340
341    if args.settings:
342        for setting in args.settings:
343            if not len(setting) == 1 or not setting[0].count('='):
344                logging.error('"%s" is not a setting in the form "key=value"',
345                              setting[0])
346                sys.exit(-1)
347            setting_list = setting[0].split('=', 1)
348            configuration.settings.append((setting_list[0], setting_list[1]))
349
350    if args.d:
351        sys.stdout.write(
352            "Suspending the process %d to wait for debugger to attach...\n" %
353            os.getpid())
354        sys.stdout.flush()
355        os.kill(os.getpid(), signal.SIGSTOP)
356
357    if args.f:
358        if any([x.startswith('-') for x in args.f]):
359            usage(parser)
360        configuration.filters.extend(args.f)
361
362    if args.framework:
363        configuration.lldb_framework_path = args.framework
364
365    if args.executable:
366        # lldb executable is passed explicitly
367        lldbtest_config.lldbExec = os.path.realpath(args.executable)
368        if not is_exe(lldbtest_config.lldbExec):
369            lldbtest_config.lldbExec = which(args.executable)
370        if not is_exe(lldbtest_config.lldbExec):
371            logging.error(
372                    '%s is not a valid executable to test; aborting...',
373                    args.executable)
374            sys.exit(-1)
375
376    if args.server and args.out_of_tree_debugserver:
377        logging.warning('Both --server and --out-of-tree-debugserver are set')
378
379    if args.server and not args.out_of_tree_debugserver:
380        os.environ['LLDB_DEBUGSERVER_PATH'] = args.server
381
382    if args.excluded:
383        for excl_file in args.excluded:
384            parseExclusion(excl_file)
385
386    if args.p:
387        if args.p.startswith('-'):
388            usage(parser)
389        configuration.regexp = args.p
390
391    if args.t:
392        os.environ['LLDB_COMMAND_TRACE'] = 'YES'
393
394    if args.v:
395        configuration.verbose = 2
396
397    # argparse makes sure we have a number
398    if args.sharp:
399        configuration.count = args.sharp
400
401    if sys.platform.startswith('win32'):
402        os.environ['LLDB_DISABLE_CRASH_DIALOG'] = str(
403            args.disable_crash_dialog)
404        os.environ['LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE'] = str(True)
405
406    if do_help:
407        usage(parser)
408
409    # Reproducer arguments
410    if args.capture_path and args.replay_path:
411        logging.error('Cannot specify both a capture and a replay path.')
412        sys.exit(-1)
413
414    if args.capture_path:
415        configuration.capture_path = args.capture_path
416
417    if args.replay_path:
418        configuration.replay_path = args.replay_path
419    if args.lldb_platform_name:
420        configuration.lldb_platform_name = args.lldb_platform_name
421    if args.lldb_platform_url:
422        configuration.lldb_platform_url = args.lldb_platform_url
423    if args.lldb_platform_working_dir:
424        configuration.lldb_platform_working_dir = args.lldb_platform_working_dir
425    if platform_system == 'Darwin'  and args.apple_sdk:
426        configuration.apple_sdk = args.apple_sdk
427    if args.test_build_dir:
428        configuration.test_build_dir = args.test_build_dir
429    if args.lldb_module_cache_dir:
430        configuration.lldb_module_cache_dir = args.lldb_module_cache_dir
431    else:
432        configuration.lldb_module_cache_dir = os.path.join(
433            configuration.test_build_dir, 'module-cache-lldb')
434    if args.clang_module_cache_dir:
435        configuration.clang_module_cache_dir = args.clang_module_cache_dir
436    else:
437        configuration.clang_module_cache_dir = os.path.join(
438            configuration.test_build_dir, 'module-cache-clang')
439
440    if args.lldb_libs_dir:
441        configuration.lldb_libs_dir = args.lldb_libs_dir
442
443    if args.enabled_plugins:
444        configuration.enabled_plugins = args.enabled_plugins
445
446    # Gather all the dirs passed on the command line.
447    if len(args.args) > 0:
448        configuration.testdirs = [os.path.realpath(os.path.abspath(x)) for x in args.args]
449
450    lldbtest_config.codesign_identity = args.codesign_identity
451
452def registerFaulthandler():
453    try:
454        import faulthandler
455    except ImportError:
456        # faulthandler is not available until python3
457        return
458
459    faulthandler.enable()
460    # faulthandler.register is not available on Windows.
461    if getattr(faulthandler, 'register', None):
462        faulthandler.register(signal.SIGTERM, chain=True)
463
464def setupSysPath():
465    """
466    Add LLDB.framework/Resources/Python to the search paths for modules.
467    As a side effect, we also discover the 'lldb' executable and export it here.
468    """
469
470    # Get the directory containing the current script.
471    if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ:
472        scriptPath = os.environ["DOTEST_SCRIPT_DIR"]
473    else:
474        scriptPath = os.path.dirname(os.path.abspath(__file__))
475    if not scriptPath.endswith('test'):
476        print("This script expects to reside in lldb's test directory.")
477        sys.exit(-1)
478
479    os.environ["LLDB_TEST"] = scriptPath
480
481    # Set up the root build directory.
482    if not configuration.test_build_dir:
483        raise Exception("test_build_dir is not set")
484    configuration.test_build_dir = os.path.abspath(configuration.test_build_dir)
485
486    # Set up the LLDB_SRC environment variable, so that the tests can locate
487    # the LLDB source code.
488    os.environ["LLDB_SRC"] = lldbsuite.lldb_root
489
490    pluginPath = os.path.join(scriptPath, 'plugins')
491    toolsLLDBVSCode = os.path.join(scriptPath, 'tools', 'lldb-vscode')
492    toolsLLDBServerPath = os.path.join(scriptPath, 'tools', 'lldb-server')
493
494    # Insert script dir, plugin dir and lldb-server dir to the sys.path.
495    sys.path.insert(0, pluginPath)
496    # Adding test/tools/lldb-vscode to the path makes it easy to
497    # "import lldb_vscode_testcase" from the VSCode tests
498    sys.path.insert(0, toolsLLDBVSCode)
499    # Adding test/tools/lldb-server to the path makes it easy
500    sys.path.insert(0, toolsLLDBServerPath)
501    # to "import lldbgdbserverutils" from the lldb-server tests
502
503    # This is the root of the lldb git/svn checkout
504    # When this changes over to a package instead of a standalone script, this
505    # will be `lldbsuite.lldb_root`
506    lldbRootDirectory = lldbsuite.lldb_root
507
508    # Some of the tests can invoke the 'lldb' command directly.
509    # We'll try to locate the appropriate executable right here.
510
511    # The lldb executable can be set from the command line
512    # if it's not set, we try to find it now
513    # first, we try the environment
514    if not lldbtest_config.lldbExec:
515        # First, you can define an environment variable LLDB_EXEC specifying the
516        # full pathname of the lldb executable.
517        if "LLDB_EXEC" in os.environ:
518            lldbtest_config.lldbExec = os.environ["LLDB_EXEC"]
519
520    if not lldbtest_config.lldbExec:
521        # Last, check the path
522        lldbtest_config.lldbExec = which('lldb')
523
524    if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec):
525        print(
526            "'{}' is not a path to a valid executable".format(
527                lldbtest_config.lldbExec))
528        lldbtest_config.lldbExec = None
529
530    if not lldbtest_config.lldbExec:
531        print("The 'lldb' executable cannot be located.  Some of the tests may not be run as a result.")
532        sys.exit(-1)
533
534    os.system('%s -v' % lldbtest_config.lldbExec)
535
536    lldbDir = os.path.dirname(lldbtest_config.lldbExec)
537
538    lldbVSCodeExec = os.path.join(lldbDir, "lldb-vscode")
539    if is_exe(lldbVSCodeExec):
540        os.environ["LLDBVSCODE_EXEC"] = lldbVSCodeExec
541    else:
542        if not configuration.shouldSkipBecauseOfCategories(["lldb-vscode"]):
543            print(
544                "The 'lldb-vscode' executable cannot be located.  The lldb-vscode tests can not be run as a result.")
545            configuration.skip_categories.append("lldb-vscode")
546
547    lldbPythonDir = None  # The directory that contains 'lldb/__init__.py'
548
549    # If our lldb supports the -P option, use it to find the python path:
550    lldb_dash_p_result = subprocess.check_output([lldbtest_config.lldbExec, "-P"], universal_newlines=True)
551    if lldb_dash_p_result:
552        for line in lldb_dash_p_result.splitlines():
553            if os.path.isdir(line) and os.path.exists(os.path.join(line, 'lldb', '__init__.py')):
554                lldbPythonDir = line
555                break
556
557    if not lldbPythonDir:
558        print(
559            "Unable to load lldb extension module.  Possible reasons for this include:")
560        print("  1) LLDB was built with LLDB_ENABLE_PYTHON=0")
561        print(
562            "  2) PYTHONPATH and PYTHONHOME are not set correctly.  PYTHONHOME should refer to")
563        print(
564            "     the version of Python that LLDB built and linked against, and PYTHONPATH")
565        print(
566            "     should contain the Lib directory for the same python distro, as well as the")
567        print("     location of LLDB\'s site-packages folder.")
568        print(
569            "  3) A different version of Python than that which was built against is exported in")
570        print("     the system\'s PATH environment variable, causing conflicts.")
571        print(
572            "  4) The executable '%s' could not be found.  Please check " %
573            lldbtest_config.lldbExec)
574        print("     that it exists and is executable.")
575
576    if lldbPythonDir:
577        lldbPythonDir = os.path.normpath(lldbPythonDir)
578        # Some of the code that uses this path assumes it hasn't resolved the Versions... link.
579        # If the path we've constructed looks like that, then we'll strip out
580        # the Versions/A part.
581        (before, frameWithVersion, after) = lldbPythonDir.rpartition(
582            "LLDB.framework/Versions/A")
583        if frameWithVersion != "":
584            lldbPythonDir = before + "LLDB.framework" + after
585
586        lldbPythonDir = os.path.abspath(lldbPythonDir)
587
588        if "freebsd" in sys.platform or "linux" in sys.platform:
589            os.environ['LLDB_LIB_DIR'] = os.path.join(lldbPythonDir, '..', '..')
590
591        # If tests need to find LLDB_FRAMEWORK, now they can do it
592        os.environ["LLDB_FRAMEWORK"] = os.path.dirname(
593            os.path.dirname(lldbPythonDir))
594
595        # This is to locate the lldb.py module.  Insert it right after
596        # sys.path[0].
597        sys.path[1:1] = [lldbPythonDir]
598
599
600def visit_file(dir, name):
601    # Try to match the regexp pattern, if specified.
602    if configuration.regexp:
603        if not re.search(configuration.regexp, name):
604            # We didn't match the regex, we're done.
605            return
606
607    if configuration.skip_tests:
608        for file_regexp in configuration.skip_tests:
609            if re.search(file_regexp, name):
610                return
611
612    # We found a match for our test.  Add it to the suite.
613
614    # Update the sys.path first.
615    if not sys.path.count(dir):
616        sys.path.insert(0, dir)
617    base = os.path.splitext(name)[0]
618
619    # Thoroughly check the filterspec against the base module and admit
620    # the (base, filterspec) combination only when it makes sense.
621
622    def check(obj, parts):
623        for part in parts:
624            try:
625                parent, obj = obj, getattr(obj, part)
626            except AttributeError:
627                # The filterspec has failed.
628                return False
629        return True
630
631    module = __import__(base)
632
633    def iter_filters():
634        for filterspec in configuration.filters:
635            parts = filterspec.split('.')
636            if check(module, parts):
637                yield filterspec
638            elif parts[0] == base and len(parts) > 1 and check(module, parts[1:]):
639                yield '.'.join(parts[1:])
640            else:
641                for key,value in module.__dict__.items():
642                    if check(value, parts):
643                        yield key + '.' + filterspec
644
645    filtered = False
646    for filterspec in iter_filters():
647        filtered = True
648        print("adding filter spec %s to module %s" % (filterspec, repr(module)))
649        tests = unittest2.defaultTestLoader.loadTestsFromName(filterspec, module)
650        configuration.suite.addTests(tests)
651
652    # Forgo this module if the (base, filterspec) combo is invalid
653    if configuration.filters and not filtered:
654        return
655
656    if not filtered:
657        # Add the entire file's worth of tests since we're not filtered.
658        # Also the fail-over case when the filterspec branch
659        # (base, filterspec) combo doesn't make sense.
660        configuration.suite.addTests(
661            unittest2.defaultTestLoader.loadTestsFromName(base))
662
663
664def visit(prefix, dir, names):
665    """Visitor function for os.path.walk(path, visit, arg)."""
666
667    dir_components = set(dir.split(os.sep))
668    excluded_components = set(['.svn', '.git'])
669    if dir_components.intersection(excluded_components):
670        return
671
672    # Gather all the Python test file names that follow the Test*.py pattern.
673    python_test_files = [
674        name
675        for name in names
676        if name.endswith('.py') and name.startswith(prefix)]
677
678    # Visit all the python test files.
679    for name in python_test_files:
680        # Ensure we error out if we have multiple tests with the same
681        # base name.
682        # Future improvement: find all the places where we work with base
683        # names and convert to full paths.  We have directory structure
684        # to disambiguate these, so we shouldn't need this constraint.
685        if name in configuration.all_tests:
686            raise Exception("Found multiple tests with the name %s" % name)
687        configuration.all_tests.add(name)
688
689        # Run the relevant tests in the python file.
690        visit_file(dir, name)
691
692
693# ======================================== #
694#                                          #
695# Execution of the test driver starts here #
696#                                          #
697# ======================================== #
698
699
700def checkDsymForUUIDIsNotOn():
701    cmd = ["defaults", "read", "com.apple.DebugSymbols"]
702    process = subprocess.Popen(
703        cmd,
704        stdout=subprocess.PIPE,
705        stderr=subprocess.STDOUT)
706    cmd_output = process.stdout.read()
707    output_str = cmd_output.decode("utf-8")
708    if "DBGFileMappedPaths = " in output_str:
709        print("%s =>" % ' '.join(cmd))
710        print(output_str)
711        print(
712            "Disable automatic lookup and caching of dSYMs before running the test suite!")
713        print("Exiting...")
714        sys.exit(0)
715
716
717def exitTestSuite(exitCode=None):
718    # lldb.py does SBDebugger.Initialize().
719    # Call SBDebugger.Terminate() on exit.
720    import lldb
721    lldb.SBDebugger.Terminate()
722    if exitCode:
723        sys.exit(exitCode)
724
725
726def getVersionForSDK(sdk):
727    sdk = str.lower(sdk)
728    full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk)
729    basename = os.path.basename(full_path)
730    basename = os.path.splitext(basename)[0]
731    basename = str.lower(basename)
732    ver = basename.replace(sdk, '')
733    return ver
734
735
736def checkCompiler():
737    # Add some intervention here to sanity check that the compiler requested is sane.
738    # If found not to be an executable program, we abort.
739    c = configuration.compiler
740    if which(c):
741        return
742
743    if not sys.platform.startswith("darwin"):
744        raise Exception(c + " is not a valid compiler")
745
746    pipe = subprocess.Popen(
747        ['xcrun', '-find', c], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
748    cmd_output = pipe.stdout.read()
749    if not cmd_output or "not found" in cmd_output:
750        raise Exception(c + " is not a valid compiler")
751
752    configuration.compiler = cmd_output.split('\n')[0]
753    print("'xcrun -find %s' returning %s" % (c, configuration.compiler))
754
755def canRunLibcxxTests():
756    from lldbsuite.test import lldbplatformutil
757
758    platform = lldbplatformutil.getPlatform()
759
760    if lldbplatformutil.target_is_android() or lldbplatformutil.platformIsDarwin():
761        return True, "libc++ always present"
762
763    if platform == "linux":
764        if os.path.isdir("/usr/include/c++/v1"):
765            return True, "Headers found, let's hope they work"
766        with tempfile.NamedTemporaryFile() as f:
767            cmd = [configuration.compiler, "-xc++", "-stdlib=libc++", "-o", f.name, "-"]
768            p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
769            _, stderr = p.communicate("#include <algorithm>\nint main() {}")
770            if not p.returncode:
771                return True, "Compiling with -stdlib=libc++ works"
772            return False, "Compiling with -stdlib=libc++ fails with the error: %s" % stderr
773
774    return False, "Don't know how to build with libc++ on %s" % platform
775
776def checkLibcxxSupport():
777    result, reason = canRunLibcxxTests()
778    if result:
779        return # libc++ supported
780    if "libc++" in configuration.categories_list:
781        return # libc++ category explicitly requested, let it run.
782    print("Libc++ tests will not be run because: " + reason)
783    configuration.skip_categories.append("libc++")
784
785def canRunLibstdcxxTests():
786    from lldbsuite.test import lldbplatformutil
787
788    platform = lldbplatformutil.getPlatform()
789    if lldbplatformutil.target_is_android():
790        platform = "android"
791    if platform == "linux":
792        return True, "libstdcxx always present"
793    return False, "Don't know how to build with libstdcxx on %s" % platform
794
795def checkLibstdcxxSupport():
796    result, reason = canRunLibstdcxxTests()
797    if result:
798        return # libstdcxx supported
799    if "libstdcxx" in configuration.categories_list:
800        return # libstdcxx category explicitly requested, let it run.
801    print("libstdcxx tests will not be run because: " + reason)
802    configuration.skip_categories.append("libstdcxx")
803
804def canRunWatchpointTests():
805    from lldbsuite.test import lldbplatformutil
806
807    platform = lldbplatformutil.getPlatform()
808    if platform == "netbsd":
809        if os.geteuid() == 0:
810            return True, "root can always write dbregs"
811        try:
812            output = subprocess.check_output(["/sbin/sysctl", "-n",
813              "security.models.extensions.user_set_dbregs"]).decode().strip()
814            if output == "1":
815                return True, "security.models.extensions.user_set_dbregs enabled"
816        except subprocess.CalledProcessError:
817            pass
818        return False, "security.models.extensions.user_set_dbregs disabled"
819    return True, "watchpoint support available"
820
821def checkWatchpointSupport():
822    result, reason = canRunWatchpointTests()
823    if result:
824        return # watchpoints supported
825    if "watchpoint" in configuration.categories_list:
826        return # watchpoint category explicitly requested, let it run.
827    print("watchpoint tests will not be run because: " + reason)
828    configuration.skip_categories.append("watchpoint")
829
830def checkObjcSupport():
831    from lldbsuite.test import lldbplatformutil
832
833    if not lldbplatformutil.platformIsDarwin():
834        print("objc tests will be skipped because of unsupported platform")
835        configuration.skip_categories.append("objc")
836
837def checkDebugInfoSupport():
838    import lldb
839
840    platform = lldb.selected_platform.GetTriple().split('-')[2]
841    compiler = configuration.compiler
842    skipped = []
843    for cat in test_categories.debug_info_categories:
844        if cat in configuration.categories_list:
845            continue # Category explicitly requested, let it run.
846        if test_categories.is_supported_on_platform(cat, platform, compiler):
847            continue
848        configuration.skip_categories.append(cat)
849        skipped.append(cat)
850    if skipped:
851        print("Skipping following debug info categories:", skipped)
852
853def checkDebugServerSupport():
854    from lldbsuite.test import lldbplatformutil
855
856    if lldbplatformutil.platformIsDarwin():
857        configuration.skip_categories.append("llgs")
858    else:
859        configuration.skip_categories.append("debugserver")
860
861def run_suite():
862    # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults
863    # does not exist before proceeding to running the test suite.
864    if sys.platform.startswith("darwin"):
865        checkDsymForUUIDIsNotOn()
866
867    # Start the actions by first parsing the options while setting up the test
868    # directories, followed by setting up the search paths for lldb utilities;
869    # then, we walk the directory trees and collect the tests into our test suite.
870    #
871    parseOptionsAndInitTestdirs()
872
873    # Print a stack trace if the test hangs or is passed SIGTERM.
874    registerFaulthandler()
875
876    setupSysPath()
877
878    import lldbconfig
879    if configuration.capture_path or configuration.replay_path:
880        lldbconfig.INITIALIZE = False
881    import lldb
882
883    if configuration.capture_path:
884        lldb.SBReproducer.Capture(configuration.capture_path)
885        lldb.SBReproducer.SetAutoGenerate(True)
886    elif configuration.replay_path:
887        lldb.SBReproducer.PassiveReplay(configuration.replay_path)
888
889    if not lldbconfig.INITIALIZE:
890        lldb.SBDebugger.Initialize()
891
892    # Use host platform by default.
893    lldb.selected_platform = lldb.SBPlatform.GetHostPlatform()
894
895    # Now we can also import lldbutil
896    from lldbsuite.test import lldbutil
897
898    if configuration.lldb_platform_name:
899        print("Setting up remote platform '%s'" %
900              (configuration.lldb_platform_name))
901        lldb.remote_platform = lldb.SBPlatform(
902            configuration.lldb_platform_name)
903        if not lldb.remote_platform.IsValid():
904            print(
905                "error: unable to create the LLDB platform named '%s'." %
906                (configuration.lldb_platform_name))
907            exitTestSuite(1)
908        if configuration.lldb_platform_url:
909            # We must connect to a remote platform if a LLDB platform URL was
910            # specified
911            print(
912                "Connecting to remote platform '%s' at '%s'..." %
913                (configuration.lldb_platform_name, configuration.lldb_platform_url))
914            platform_connect_options = lldb.SBPlatformConnectOptions(
915                configuration.lldb_platform_url)
916            err = lldb.remote_platform.ConnectRemote(platform_connect_options)
917            if err.Success():
918                print("Connected.")
919            else:
920                print("error: failed to connect to remote platform using URL '%s': %s" % (
921                    configuration.lldb_platform_url, err))
922                exitTestSuite(1)
923        else:
924            configuration.lldb_platform_url = None
925
926    if configuration.lldb_platform_working_dir:
927        print("Setting remote platform working directory to '%s'..." %
928              (configuration.lldb_platform_working_dir))
929        error = lldb.remote_platform.MakeDirectory(
930            configuration.lldb_platform_working_dir, 448)  # 448 = 0o700
931        if error.Fail():
932            raise Exception("making remote directory '%s': %s" % (
933                configuration.lldb_platform_working_dir, error))
934
935        if not lldb.remote_platform.SetWorkingDirectory(
936                configuration.lldb_platform_working_dir):
937            raise Exception("failed to set working directory '%s'" % configuration.lldb_platform_working_dir)
938        lldb.selected_platform = lldb.remote_platform
939    else:
940        lldb.remote_platform = None
941        configuration.lldb_platform_working_dir = None
942        configuration.lldb_platform_url = None
943
944    # Set up the working directory.
945    # Note that it's not dotest's job to clean this directory.
946    lldbutil.mkdir_p(configuration.test_build_dir)
947
948    from . import lldbplatformutil
949    target_platform = lldbplatformutil.getPlatform()
950
951    checkLibcxxSupport()
952    checkLibstdcxxSupport()
953    checkWatchpointSupport()
954    checkDebugInfoSupport()
955    checkDebugServerSupport()
956    checkObjcSupport()
957
958    for testdir in configuration.testdirs:
959        for (dirpath, dirnames, filenames) in os.walk(testdir):
960            visit('Test', dirpath, filenames)
961
962    #
963    # Now that we have loaded all the test cases, run the whole test suite.
964    #
965
966    # Install the control-c handler.
967    unittest2.signals.installHandler()
968
969    #
970    # Invoke the default TextTestRunner to run the test suite
971    #
972    checkCompiler()
973
974    if configuration.verbose:
975        print("compiler=%s" % configuration.compiler)
976
977    # Iterating over all possible architecture and compiler combinations.
978    configString = "arch=%s compiler=%s" % (configuration.arch,
979                                            configuration.compiler)
980
981    # Output the configuration.
982    if configuration.verbose:
983        sys.stderr.write("\nConfiguration: " + configString + "\n")
984
985    # First, write out the number of collected test cases.
986    if configuration.verbose:
987        sys.stderr.write(configuration.separator + "\n")
988        sys.stderr.write(
989            "Collected %d test%s\n\n" %
990            (configuration.suite.countTestCases(),
991             configuration.suite.countTestCases() != 1 and "s" or ""))
992
993    if configuration.suite.countTestCases() == 0:
994        logging.error("did not discover any matching tests")
995        exitTestSuite(1)
996
997    # Invoke the test runner.
998    if configuration.count == 1:
999        result = unittest2.TextTestRunner(
1000            stream=sys.stderr,
1001            verbosity=configuration.verbose,
1002            resultclass=test_result.LLDBTestResult).run(
1003            configuration.suite)
1004    else:
1005        # We are invoking the same test suite more than once.  In this case,
1006        # mark __ignore_singleton__ flag as True so the signleton pattern is
1007        # not enforced.
1008        test_result.LLDBTestResult.__ignore_singleton__ = True
1009        for i in range(configuration.count):
1010
1011            result = unittest2.TextTestRunner(
1012                stream=sys.stderr,
1013                verbosity=configuration.verbose,
1014                resultclass=test_result.LLDBTestResult).run(
1015                configuration.suite)
1016
1017    configuration.failed = not result.wasSuccessful()
1018
1019    if configuration.sdir_has_content and configuration.verbose:
1020        sys.stderr.write(
1021            "Session logs for test failures/errors/unexpected successes"
1022            " can be found in the test build directory\n")
1023
1024    if configuration.use_categories and len(
1025            configuration.failures_per_category) > 0:
1026        sys.stderr.write("Failures per category:\n")
1027        for category in configuration.failures_per_category:
1028            sys.stderr.write(
1029                "%s - %d\n" %
1030                (category, configuration.failures_per_category[category]))
1031
1032    # Exiting.
1033    exitTestSuite(configuration.failed)
1034
1035if __name__ == "__main__":
1036    print(
1037        __file__ +
1038        " is for use as a module only.  It should not be run as a standalone script.")
1039    sys.exit(-1)
1040