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