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