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