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