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