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