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 is 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.make: 270 configuration.make_path = args.make 271 272 if args.dsymutil: 273 configuration.dsymutil = args.dsymutil 274 elif platform_system == "Darwin": 275 configuration.dsymutil = seven.get_command_output( 276 "xcrun -find -toolchain default dsymutil" 277 ) 278 if args.llvm_tools_dir: 279 configuration.llvm_tools_dir = args.llvm_tools_dir 280 configuration.filecheck = shutil.which("FileCheck", path=args.llvm_tools_dir) 281 configuration.yaml2obj = shutil.which("yaml2obj", path=args.llvm_tools_dir) 282 283 if not configuration.get_filecheck_path(): 284 logging.warning("No valid FileCheck executable; some tests may fail...") 285 logging.warning("(Double-check the --llvm-tools-dir argument to dotest.py)") 286 287 if args.libcxx_include_dir or args.libcxx_library_dir: 288 if args.lldb_platform_name: 289 logging.warning( 290 "Custom libc++ is not supported for remote runs: ignoring --libcxx arguments" 291 ) 292 elif not (args.libcxx_include_dir and args.libcxx_library_dir): 293 logging.error( 294 "Custom libc++ requires both --libcxx-include-dir and --libcxx-library-dir" 295 ) 296 sys.exit(-1) 297 configuration.libcxx_include_dir = args.libcxx_include_dir 298 configuration.libcxx_include_target_dir = args.libcxx_include_target_dir 299 configuration.libcxx_library_dir = args.libcxx_library_dir 300 301 if args.channels: 302 lldbtest_config.channels = args.channels 303 304 if args.log_success: 305 lldbtest_config.log_success = args.log_success 306 307 if args.out_of_tree_debugserver: 308 lldbtest_config.out_of_tree_debugserver = args.out_of_tree_debugserver 309 310 # Set SDKROOT if we are using an Apple SDK 311 if args.sysroot is not None: 312 configuration.sdkroot = args.sysroot 313 elif platform_system == "Darwin" and args.apple_sdk: 314 configuration.sdkroot = seven.get_command_output( 315 'xcrun --sdk "%s" --show-sdk-path 2> /dev/null' % (args.apple_sdk) 316 ) 317 if not configuration.sdkroot: 318 logging.error("No SDK found with the name %s; aborting...", args.apple_sdk) 319 sys.exit(-1) 320 321 if args.arch: 322 configuration.arch = args.arch 323 else: 324 configuration.arch = platform_machine 325 326 if args.categories_list: 327 configuration.categories_list = set( 328 test_categories.validate(args.categories_list, False) 329 ) 330 configuration.use_categories = True 331 else: 332 configuration.categories_list = [] 333 334 if args.skip_categories: 335 configuration.skip_categories += test_categories.validate( 336 args.skip_categories, False 337 ) 338 339 if args.xfail_categories: 340 configuration.xfail_categories += test_categories.validate( 341 args.xfail_categories, False 342 ) 343 344 if args.E: 345 os.environ["CFLAGS_EXTRAS"] = args.E 346 347 if args.dwarf_version: 348 configuration.dwarf_version = args.dwarf_version 349 # We cannot modify CFLAGS_EXTRAS because they're used in test cases 350 # that explicitly require no debug info. 351 os.environ["CFLAGS"] = "-gdwarf-{}".format(configuration.dwarf_version) 352 353 if args.settings: 354 for setting in args.settings: 355 if not len(setting) == 1 or not setting[0].count("="): 356 logging.error( 357 '"%s" is not a setting in the form "key=value"', setting[0] 358 ) 359 sys.exit(-1) 360 setting_list = setting[0].split("=", 1) 361 configuration.settings.append((setting_list[0], setting_list[1])) 362 363 if args.d: 364 sys.stdout.write( 365 "Suspending the process %d to wait for debugger to attach...\n" 366 % os.getpid() 367 ) 368 sys.stdout.flush() 369 os.kill(os.getpid(), signal.SIGSTOP) 370 371 if args.f: 372 if any([x.startswith("-") for x in args.f]): 373 usage(parser) 374 configuration.filters.extend(args.f) 375 376 if args.framework: 377 configuration.lldb_framework_path = args.framework 378 379 if args.executable: 380 # lldb executable is passed explicitly 381 lldbtest_config.lldbExec = os.path.abspath(args.executable) 382 if not is_exe(lldbtest_config.lldbExec): 383 lldbtest_config.lldbExec = which(args.executable) 384 if not is_exe(lldbtest_config.lldbExec): 385 logging.error( 386 "%s is not a valid executable to test; aborting...", args.executable 387 ) 388 sys.exit(-1) 389 390 if args.excluded: 391 for excl_file in args.excluded: 392 parseExclusion(excl_file) 393 394 if args.p: 395 if args.p.startswith("-"): 396 usage(parser) 397 configuration.regexp = args.p 398 399 if args.t: 400 os.environ["LLDB_COMMAND_TRACE"] = "YES" 401 402 if args.v: 403 configuration.verbose = 2 404 405 # argparse makes sure we have a number 406 if args.sharp: 407 configuration.count = args.sharp 408 409 if sys.platform.startswith("win32"): 410 os.environ["LLDB_DISABLE_CRASH_DIALOG"] = str(args.disable_crash_dialog) 411 os.environ["LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE"] = str(True) 412 413 if do_help: 414 usage(parser) 415 416 if args.lldb_platform_name: 417 configuration.lldb_platform_name = args.lldb_platform_name 418 if args.lldb_platform_url: 419 configuration.lldb_platform_url = args.lldb_platform_url 420 if args.lldb_platform_working_dir: 421 configuration.lldb_platform_working_dir = args.lldb_platform_working_dir 422 if platform_system == "Darwin" and args.apple_sdk: 423 configuration.apple_sdk = args.apple_sdk 424 if args.test_build_dir: 425 configuration.test_build_dir = args.test_build_dir 426 if args.lldb_module_cache_dir: 427 configuration.lldb_module_cache_dir = args.lldb_module_cache_dir 428 else: 429 configuration.lldb_module_cache_dir = os.path.join( 430 configuration.test_build_dir, "module-cache-lldb" 431 ) 432 433 if args.clang_module_cache_dir: 434 configuration.clang_module_cache_dir = args.clang_module_cache_dir 435 else: 436 configuration.clang_module_cache_dir = os.path.join( 437 configuration.test_build_dir, "module-cache-clang" 438 ) 439 440 if args.lldb_libs_dir: 441 configuration.lldb_libs_dir = args.lldb_libs_dir 442 if args.lldb_obj_root: 443 configuration.lldb_obj_root = args.lldb_obj_root 444 445 if args.enabled_plugins: 446 configuration.enabled_plugins = args.enabled_plugins 447 448 # Gather all the dirs passed on the command line. 449 if len(args.args) > 0: 450 configuration.testdirs = [ 451 os.path.realpath(os.path.abspath(x)) for x in args.args 452 ] 453 454 455def registerFaulthandler(): 456 try: 457 import faulthandler 458 except ImportError: 459 # faulthandler is not available until python3 460 return 461 462 faulthandler.enable() 463 # faulthandler.register is not available on Windows. 464 if getattr(faulthandler, "register", None): 465 faulthandler.register(signal.SIGTERM, chain=True) 466 467 468def setupSysPath(): 469 """ 470 Add LLDB.framework/Resources/Python to the search paths for modules. 471 As a side effect, we also discover the 'lldb' executable and export it here. 472 """ 473 474 # Get the directory containing the current script. 475 if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ: 476 scriptPath = os.environ["DOTEST_SCRIPT_DIR"] 477 else: 478 scriptPath = os.path.dirname(os.path.abspath(__file__)) 479 if not scriptPath.endswith("test"): 480 print("This script expects to reside in lldb's test directory.") 481 sys.exit(-1) 482 483 os.environ["LLDB_TEST"] = scriptPath 484 485 # Set up the root build directory. 486 if not configuration.test_build_dir: 487 raise Exception("test_build_dir is not set") 488 configuration.test_build_dir = os.path.abspath(configuration.test_build_dir) 489 490 # Set up the LLDB_SRC environment variable, so that the tests can locate 491 # the LLDB source code. 492 os.environ["LLDB_SRC"] = lldbsuite.lldb_root 493 494 pluginPath = os.path.join(scriptPath, "plugins") 495 toolsLLDBDAP = os.path.join(scriptPath, "tools", "lldb-dap") 496 toolsLLDBServerPath = os.path.join(scriptPath, "tools", "lldb-server") 497 intelpt = os.path.join(scriptPath, "tools", "intelpt") 498 499 # Insert script dir, plugin dir and lldb-server dir to the sys.path. 500 sys.path.insert(0, pluginPath) 501 # Adding test/tools/lldb-dap to the path makes it easy to 502 # "import lldb_dap_testcase" from the DAP tests 503 sys.path.insert(0, toolsLLDBDAP) 504 # Adding test/tools/lldb-server to the path makes it easy 505 # to "import lldbgdbserverutils" from the lldb-server tests 506 sys.path.insert(0, toolsLLDBServerPath) 507 # Adding test/tools/intelpt to the path makes it easy 508 # to "import intelpt_testcase" from the lldb-server tests 509 sys.path.insert(0, intelpt) 510 511 # This is the root of the lldb git/svn checkout 512 # When this changes over to a package instead of a standalone script, this 513 # will be `lldbsuite.lldb_root` 514 lldbRootDirectory = lldbsuite.lldb_root 515 516 # Some of the tests can invoke the 'lldb' command directly. 517 # We'll try to locate the appropriate executable right here. 518 519 # The lldb executable can be set from the command line 520 # if it's not set, we try to find it now 521 # first, we try the environment 522 if not lldbtest_config.lldbExec: 523 # First, you can define an environment variable LLDB_EXEC specifying the 524 # full pathname of the lldb executable. 525 if "LLDB_EXEC" in os.environ: 526 lldbtest_config.lldbExec = os.environ["LLDB_EXEC"] 527 528 if not lldbtest_config.lldbExec: 529 # Last, check the path 530 lldbtest_config.lldbExec = which("lldb") 531 532 if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec): 533 print( 534 "'{}' is not a path to a valid executable".format(lldbtest_config.lldbExec) 535 ) 536 lldbtest_config.lldbExec = None 537 538 if not lldbtest_config.lldbExec: 539 print( 540 "The 'lldb' executable cannot be located. Some of the tests may not be run as a result." 541 ) 542 sys.exit(-1) 543 544 os.system("%s -v" % lldbtest_config.lldbExec) 545 546 lldbDir = os.path.dirname(lldbtest_config.lldbExec) 547 548 lldbDAPExec = os.path.join(lldbDir, "lldb-dap") 549 if is_exe(lldbDAPExec): 550 os.environ["LLDBDAP_EXEC"] = lldbDAPExec 551 552 lldbPythonDir = None # The directory that contains 'lldb/__init__.py' 553 554 # If our lldb supports the -P option, use it to find the python path: 555 lldb_dash_p_result = subprocess.check_output( 556 [lldbtest_config.lldbExec, "-P"], universal_newlines=True 557 ) 558 if lldb_dash_p_result: 559 for line in lldb_dash_p_result.splitlines(): 560 if os.path.isdir(line) and os.path.exists( 561 os.path.join(line, "lldb", "__init__.py") 562 ): 563 lldbPythonDir = line 564 break 565 566 if not lldbPythonDir: 567 print( 568 "Unable to load lldb extension module. Possible reasons for this include:" 569 ) 570 print(" 1) LLDB was built with LLDB_ENABLE_PYTHON=0") 571 print( 572 " 2) PYTHONPATH and PYTHONHOME are not set correctly. PYTHONHOME should refer to" 573 ) 574 print( 575 " the version of Python that LLDB built and linked against, and PYTHONPATH" 576 ) 577 print( 578 " should contain the Lib directory for the same python distro, as well as the" 579 ) 580 print(" location of LLDB's site-packages folder.") 581 print( 582 " 3) A different version of Python than that which was built against is exported in" 583 ) 584 print(" the system's PATH environment variable, causing conflicts.") 585 print( 586 " 4) The executable '%s' could not be found. Please check " 587 % lldbtest_config.lldbExec 588 ) 589 print(" that it exists and is executable.") 590 591 if lldbPythonDir: 592 lldbPythonDir = os.path.normpath(lldbPythonDir) 593 # Some of the code that uses this path assumes it hasn't resolved the Versions... link. 594 # If the path we've constructed looks like that, then we'll strip out 595 # the Versions/A part. 596 (before, frameWithVersion, after) = lldbPythonDir.rpartition( 597 "LLDB.framework/Versions/A" 598 ) 599 if frameWithVersion != "": 600 lldbPythonDir = before + "LLDB.framework" + after 601 602 lldbPythonDir = os.path.abspath(lldbPythonDir) 603 604 if "freebsd" in sys.platform or "linux" in sys.platform: 605 os.environ["LLDB_LIB_DIR"] = os.path.join(lldbPythonDir, "..", "..") 606 607 # If tests need to find LLDB_FRAMEWORK, now they can do it 608 os.environ["LLDB_FRAMEWORK"] = os.path.dirname(os.path.dirname(lldbPythonDir)) 609 610 # This is to locate the lldb.py module. Insert it right after 611 # sys.path[0]. 612 sys.path[1:1] = [lldbPythonDir] 613 614 615def visit_file(dir, name): 616 # Try to match the regexp pattern, if specified. 617 if configuration.regexp: 618 if not re.search(configuration.regexp, name): 619 # We didn't match the regex, we're done. 620 return 621 622 if configuration.skip_tests: 623 for file_regexp in configuration.skip_tests: 624 if re.search(file_regexp, name): 625 return 626 627 # We found a match for our test. Add it to the suite. 628 629 # Update the sys.path first. 630 if not sys.path.count(dir): 631 sys.path.insert(0, dir) 632 base = os.path.splitext(name)[0] 633 634 # Thoroughly check the filterspec against the base module and admit 635 # the (base, filterspec) combination only when it makes sense. 636 637 def check(obj, parts): 638 for part in parts: 639 try: 640 parent, obj = obj, getattr(obj, part) 641 except AttributeError: 642 # The filterspec has failed. 643 return False 644 return True 645 646 module = __import__(base) 647 648 def iter_filters(): 649 for filterspec in configuration.filters: 650 parts = filterspec.split(".") 651 if check(module, parts): 652 yield filterspec 653 elif parts[0] == base and len(parts) > 1 and check(module, parts[1:]): 654 yield ".".join(parts[1:]) 655 else: 656 for key, value in module.__dict__.items(): 657 if check(value, parts): 658 yield key + "." + filterspec 659 660 filtered = False 661 for filterspec in iter_filters(): 662 filtered = True 663 print("adding filter spec %s to module %s" % (filterspec, repr(module))) 664 tests = unittest.defaultTestLoader.loadTestsFromName(filterspec, module) 665 configuration.suite.addTests(tests) 666 667 # Forgo this module if the (base, filterspec) combo is invalid 668 if configuration.filters and not filtered: 669 return 670 671 if not filtered: 672 # Add the entire file's worth of tests since we're not filtered. 673 # Also the fail-over case when the filterspec branch 674 # (base, filterspec) combo doesn't make sense. 675 configuration.suite.addTests(unittest.defaultTestLoader.loadTestsFromName(base)) 676 677 678def visit(prefix, dir, names): 679 """Visitor function for os.path.walk(path, visit, arg).""" 680 681 dir_components = set(dir.split(os.sep)) 682 excluded_components = set([".svn", ".git"]) 683 if dir_components.intersection(excluded_components): 684 return 685 686 # Gather all the Python test file names that follow the Test*.py pattern. 687 python_test_files = [ 688 name for name in names if name.endswith(".py") and name.startswith(prefix) 689 ] 690 691 # Visit all the python test files. 692 for name in python_test_files: 693 # Ensure we error out if we have multiple tests with the same 694 # base name. 695 # Future improvement: find all the places where we work with base 696 # names and convert to full paths. We have directory structure 697 # to disambiguate these, so we shouldn't need this constraint. 698 if name in configuration.all_tests: 699 raise Exception("Found multiple tests with the name %s" % name) 700 configuration.all_tests.add(name) 701 702 # Run the relevant tests in the python file. 703 visit_file(dir, name) 704 705 706# ======================================== # 707# # 708# Execution of the test driver starts here # 709# # 710# ======================================== # 711 712 713def checkDsymForUUIDIsNotOn(): 714 cmd = ["defaults", "read", "com.apple.DebugSymbols"] 715 process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 716 cmd_output = process.stdout.read() 717 output_str = cmd_output.decode("utf-8") 718 if "DBGFileMappedPaths = " in output_str: 719 print("%s =>" % " ".join(cmd)) 720 print(output_str) 721 print( 722 "Disable automatic lookup and caching of dSYMs before running the test suite!" 723 ) 724 print("Exiting...") 725 sys.exit(0) 726 727 728def exitTestSuite(exitCode=None): 729 # lldb.py does SBDebugger.Initialize(). 730 # Call SBDebugger.Terminate() on exit. 731 import lldb 732 733 lldb.SBDebugger.Terminate() 734 if exitCode: 735 sys.exit(exitCode) 736 737 738def getVersionForSDK(sdk): 739 sdk = str.lower(sdk) 740 full_path = seven.get_command_output("xcrun -sdk %s --show-sdk-path" % sdk) 741 basename = os.path.basename(full_path) 742 basename = os.path.splitext(basename)[0] 743 basename = str.lower(basename) 744 ver = basename.replace(sdk, "") 745 return ver 746 747 748def checkCompiler(): 749 # Add some intervention here to sanity check that the compiler requested is sane. 750 # If found not to be an executable program, we abort. 751 c = configuration.compiler 752 if which(c): 753 return 754 755 if not sys.platform.startswith("darwin"): 756 raise Exception(c + " is not a valid compiler") 757 758 pipe = subprocess.Popen( 759 ["xcrun", "-find", c], stdout=subprocess.PIPE, stderr=subprocess.STDOUT 760 ) 761 cmd_output = pipe.stdout.read() 762 if not cmd_output or "not found" in cmd_output: 763 raise Exception(c + " is not a valid compiler") 764 765 configuration.compiler = cmd_output.split("\n")[0] 766 print("'xcrun -find %s' returning %s" % (c, configuration.compiler)) 767 768 769def canRunLibcxxTests(): 770 from lldbsuite.test import lldbplatformutil 771 772 platform = lldbplatformutil.getPlatform() 773 774 if lldbplatformutil.target_is_android() or lldbplatformutil.platformIsDarwin(): 775 return True, "libc++ always present" 776 777 if platform == "linux": 778 with tempfile.NamedTemporaryFile() as f: 779 cmd = [configuration.compiler, "-xc++", "-stdlib=libc++", "-o", f.name, "-"] 780 p = subprocess.Popen( 781 cmd, 782 stdin=subprocess.PIPE, 783 stdout=subprocess.PIPE, 784 stderr=subprocess.PIPE, 785 universal_newlines=True, 786 ) 787 _, stderr = p.communicate("#include <cassert>\nint main() {}") 788 if not p.returncode: 789 return True, "Compiling with -stdlib=libc++ works" 790 return ( 791 False, 792 "Compiling with -stdlib=libc++ fails with the error: %s" % stderr, 793 ) 794 795 return False, "Don't know how to build with libc++ on %s" % platform 796 797 798def checkLibcxxSupport(): 799 result, reason = canRunLibcxxTests() 800 if result: 801 return # libc++ supported 802 if "libc++" in configuration.categories_list: 803 return # libc++ category explicitly requested, let it run. 804 if configuration.verbose: 805 print("libc++ tests will not be run because: " + reason) 806 configuration.skip_categories.append("libc++") 807 808 809def canRunLibstdcxxTests(): 810 from lldbsuite.test import lldbplatformutil 811 812 platform = lldbplatformutil.getPlatform() 813 if lldbplatformutil.target_is_android(): 814 platform = "android" 815 if platform == "linux": 816 return True, "libstdcxx always present" 817 return False, "Don't know how to build with libstdcxx on %s" % platform 818 819 820def checkLibstdcxxSupport(): 821 result, reason = canRunLibstdcxxTests() 822 if result: 823 return # libstdcxx supported 824 if "libstdcxx" in configuration.categories_list: 825 return # libstdcxx category explicitly requested, let it run. 826 if configuration.verbose: 827 print("libstdcxx tests will not be run because: " + reason) 828 configuration.skip_categories.append("libstdcxx") 829 830 831def canRunWatchpointTests(): 832 from lldbsuite.test import lldbplatformutil 833 834 platform = lldbplatformutil.getPlatform() 835 if platform == "netbsd": 836 if os.geteuid() == 0: 837 return True, "root can always write dbregs" 838 try: 839 output = ( 840 subprocess.check_output( 841 ["/sbin/sysctl", "-n", "security.models.extensions.user_set_dbregs"] 842 ) 843 .decode() 844 .strip() 845 ) 846 if output == "1": 847 return True, "security.models.extensions.user_set_dbregs enabled" 848 except subprocess.CalledProcessError: 849 pass 850 return False, "security.models.extensions.user_set_dbregs disabled" 851 elif platform == "freebsd" and configuration.arch == "aarch64": 852 import lldb 853 854 if lldb.SBPlatform.GetHostPlatform().GetOSMajorVersion() < 13: 855 return False, "Watchpoint support on arm64 requires FreeBSD 13.0" 856 return True, "watchpoint support available" 857 858 859def checkWatchpointSupport(): 860 result, reason = canRunWatchpointTests() 861 if result: 862 return # watchpoints supported 863 if "watchpoint" in configuration.categories_list: 864 return # watchpoint category explicitly requested, let it run. 865 if configuration.verbose: 866 print("watchpoint tests will not be run because: " + reason) 867 configuration.skip_categories.append("watchpoint") 868 869 870def checkObjcSupport(): 871 from lldbsuite.test import lldbplatformutil 872 873 if not lldbplatformutil.platformIsDarwin(): 874 if configuration.verbose: 875 print("objc tests will be skipped because of unsupported platform") 876 configuration.skip_categories.append("objc") 877 878 879def checkDebugInfoSupport(): 880 from lldbsuite.test import lldbplatformutil 881 882 platform = lldbplatformutil.getPlatform() 883 compiler = configuration.compiler 884 for cat in test_categories.debug_info_categories: 885 if cat in configuration.categories_list: 886 continue # Category explicitly requested, let it run. 887 if test_categories.is_supported_on_platform(cat, platform, compiler): 888 continue 889 configuration.skip_categories.append(cat) 890 891 892def checkDebugServerSupport(): 893 from lldbsuite.test import lldbplatformutil 894 import lldb 895 896 skip_msg = "Skipping %s tests, as they are not compatible with remote testing on this platform" 897 if lldbplatformutil.platformIsDarwin(): 898 configuration.skip_categories.append("llgs") 899 if lldb.remote_platform: 900 # <rdar://problem/34539270> 901 configuration.skip_categories.append("debugserver") 902 if configuration.verbose: 903 print(skip_msg % "debugserver") 904 else: 905 configuration.skip_categories.append("debugserver") 906 if lldb.remote_platform and lldbplatformutil.getPlatform() == "windows": 907 configuration.skip_categories.append("llgs") 908 if configuration.verbose: 909 print(skip_msg % "lldb-server") 910 911 912def checkForkVForkSupport(): 913 from lldbsuite.test import lldbplatformutil 914 915 platform = lldbplatformutil.getPlatform() 916 if platform not in ["freebsd", "linux", "netbsd"]: 917 configuration.skip_categories.append("fork") 918 919 920def checkPexpectSupport(): 921 from lldbsuite.test import lldbplatformutil 922 923 platform = lldbplatformutil.getPlatform() 924 925 # llvm.org/pr22274: need a pexpect replacement for windows 926 if platform in ["windows"]: 927 if configuration.verbose: 928 print("pexpect tests will be skipped because of unsupported platform") 929 configuration.skip_categories.append("pexpect") 930 931 932def checkDAPSupport(): 933 import lldb 934 935 if "LLDBDAP_EXEC" not in os.environ: 936 msg = ( 937 "The 'lldb-dap' executable cannot be located and its tests will not be run." 938 ) 939 elif lldb.remote_platform: 940 msg = "lldb-dap tests are not compatible with remote platforms and will not be run." 941 else: 942 msg = None 943 944 if msg: 945 if configuration.verbose: 946 print(msg) 947 configuration.skip_categories.append("lldb-dap") 948 949 950def run_suite(): 951 # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults 952 # does not exist before proceeding to running the test suite. 953 if sys.platform.startswith("darwin"): 954 checkDsymForUUIDIsNotOn() 955 956 # Start the actions by first parsing the options while setting up the test 957 # directories, followed by setting up the search paths for lldb utilities; 958 # then, we walk the directory trees and collect the tests into our test suite. 959 # 960 parseOptionsAndInitTestdirs() 961 962 # Print a stack trace if the test hangs or is passed SIGTERM. 963 registerFaulthandler() 964 965 setupSysPath() 966 967 import lldb 968 969 lldb.SBDebugger.Initialize() 970 lldb.SBDebugger.PrintStackTraceOnError() 971 972 # Use host platform by default. 973 lldb.remote_platform = None 974 lldb.selected_platform = lldb.SBPlatform.GetHostPlatform() 975 976 # Now we can also import lldbutil 977 from lldbsuite.test import lldbutil 978 979 if configuration.lldb_platform_name: 980 print("Setting up remote platform '%s'" % (configuration.lldb_platform_name)) 981 lldb.remote_platform = lldb.SBPlatform(configuration.lldb_platform_name) 982 lldb.selected_platform = lldb.remote_platform 983 if not lldb.remote_platform.IsValid(): 984 print( 985 "error: unable to create the LLDB platform named '%s'." 986 % (configuration.lldb_platform_name) 987 ) 988 exitTestSuite(1) 989 if configuration.lldb_platform_url: 990 # We must connect to a remote platform if a LLDB platform URL was 991 # specified 992 print( 993 "Connecting to remote platform '%s' at '%s'..." 994 % (configuration.lldb_platform_name, configuration.lldb_platform_url) 995 ) 996 platform_connect_options = lldb.SBPlatformConnectOptions( 997 configuration.lldb_platform_url 998 ) 999 err = lldb.remote_platform.ConnectRemote(platform_connect_options) 1000 if err.Success(): 1001 print("Connected.") 1002 else: 1003 print( 1004 "error: failed to connect to remote platform using URL '%s': %s" 1005 % (configuration.lldb_platform_url, err) 1006 ) 1007 exitTestSuite(1) 1008 else: 1009 configuration.lldb_platform_url = None 1010 1011 if configuration.lldb_platform_working_dir: 1012 print( 1013 "Setting remote platform working directory to '%s'..." 1014 % (configuration.lldb_platform_working_dir) 1015 ) 1016 error = lldb.remote_platform.MakeDirectory( 1017 configuration.lldb_platform_working_dir, 448 1018 ) # 448 = 0o700 1019 if error.Fail(): 1020 raise Exception( 1021 "making remote directory '%s': %s" 1022 % (configuration.lldb_platform_working_dir, error) 1023 ) 1024 1025 if not lldb.remote_platform.SetWorkingDirectory( 1026 configuration.lldb_platform_working_dir 1027 ): 1028 raise Exception( 1029 "failed to set working directory '%s'" 1030 % configuration.lldb_platform_working_dir 1031 ) 1032 lldb.selected_platform = lldb.remote_platform 1033 else: 1034 lldb.remote_platform = None 1035 configuration.lldb_platform_working_dir = None 1036 configuration.lldb_platform_url = None 1037 1038 # Set up the working directory. 1039 # Note that it's not dotest's job to clean this directory. 1040 lldbutil.mkdir_p(configuration.test_build_dir) 1041 1042 checkLibcxxSupport() 1043 checkLibstdcxxSupport() 1044 checkWatchpointSupport() 1045 checkDebugInfoSupport() 1046 checkDebugServerSupport() 1047 checkObjcSupport() 1048 checkForkVForkSupport() 1049 checkPexpectSupport() 1050 checkDAPSupport() 1051 1052 skipped_categories_list = ", ".join(configuration.skip_categories) 1053 print( 1054 "Skipping the following test categories: {}".format( 1055 configuration.skip_categories 1056 ) 1057 ) 1058 1059 for testdir in configuration.testdirs: 1060 for dirpath, dirnames, filenames in os.walk(testdir): 1061 visit("Test", dirpath, filenames) 1062 1063 # 1064 # Now that we have loaded all the test cases, run the whole test suite. 1065 # 1066 1067 # Install the control-c handler. 1068 unittest.signals.installHandler() 1069 1070 # 1071 # Invoke the default TextTestRunner to run the test suite 1072 # 1073 checkCompiler() 1074 1075 if configuration.verbose: 1076 print("compiler=%s" % configuration.compiler) 1077 1078 # Iterating over all possible architecture and compiler combinations. 1079 configString = "arch=%s compiler=%s" % (configuration.arch, configuration.compiler) 1080 1081 # Output the configuration. 1082 if configuration.verbose: 1083 sys.stderr.write("\nConfiguration: " + configString + "\n") 1084 1085 # First, write out the number of collected test cases. 1086 if configuration.verbose: 1087 sys.stderr.write(configuration.separator + "\n") 1088 sys.stderr.write( 1089 "Collected %d test%s\n\n" 1090 % ( 1091 configuration.suite.countTestCases(), 1092 configuration.suite.countTestCases() != 1 and "s" or "", 1093 ) 1094 ) 1095 1096 if configuration.suite.countTestCases() == 0: 1097 logging.error("did not discover any matching tests") 1098 exitTestSuite(1) 1099 1100 # Invoke the test runner. 1101 if configuration.count == 1: 1102 result = unittest.TextTestRunner( 1103 stream=sys.stderr, 1104 verbosity=configuration.verbose, 1105 resultclass=test_result.LLDBTestResult, 1106 ).run(configuration.suite) 1107 else: 1108 # We are invoking the same test suite more than once. In this case, 1109 # mark __ignore_singleton__ flag as True so the signleton pattern is 1110 # not enforced. 1111 test_result.LLDBTestResult.__ignore_singleton__ = True 1112 for i in range(configuration.count): 1113 result = unittest.TextTestRunner( 1114 stream=sys.stderr, 1115 verbosity=configuration.verbose, 1116 resultclass=test_result.LLDBTestResult, 1117 ).run(configuration.suite) 1118 1119 configuration.failed = not result.wasSuccessful() 1120 1121 if configuration.sdir_has_content and configuration.verbose: 1122 sys.stderr.write( 1123 "Session logs for test failures/errors/unexpected successes" 1124 " can be found in the test build directory\n" 1125 ) 1126 1127 if configuration.use_categories and len(configuration.failures_per_category) > 0: 1128 sys.stderr.write("Failures per category:\n") 1129 for category in configuration.failures_per_category: 1130 sys.stderr.write( 1131 "%s - %d\n" % (category, configuration.failures_per_category[category]) 1132 ) 1133 1134 # Exiting. 1135 exitTestSuite(configuration.failed) 1136 1137 1138if __name__ == "__main__": 1139 print( 1140 __file__ 1141 + " is for use as a module only. It should not be run as a standalone script." 1142 ) 1143 sys.exit(-1) 1144