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 os 27import errno 28import logging 29import platform 30import re 31import signal 32import socket 33import subprocess 34import sys 35 36# Third-party modules 37import six 38import unittest2 39 40# LLDB Modules 41import lldbsuite 42from . import configuration 43from . import dotest_args 44from . import lldbtest_config 45from . import test_categories 46from lldbsuite.test_event import formatter 47from . import test_result 48from lldbsuite.test_event.event_builder import EventBuilder 49from ..support import seven 50 51 52def is_exe(fpath): 53 """Returns true if fpath is an executable.""" 54 return os.path.isfile(fpath) and os.access(fpath, os.X_OK) 55 56 57def which(program): 58 """Returns the full path to a program; None otherwise.""" 59 fpath, fname = os.path.split(program) 60 if fpath: 61 if is_exe(program): 62 return program 63 else: 64 for path in os.environ["PATH"].split(os.pathsep): 65 exe_file = os.path.join(path, program) 66 if is_exe(exe_file): 67 return exe_file 68 return None 69 70 71class _WritelnDecorator(object): 72 """Used to decorate file-like objects with a handy 'writeln' method""" 73 74 def __init__(self, stream): 75 self.stream = stream 76 77 def __getattr__(self, attr): 78 if attr in ('stream', '__getstate__'): 79 raise AttributeError(attr) 80 return getattr(self.stream, attr) 81 82 def writeln(self, arg=None): 83 if arg: 84 self.write(arg) 85 self.write('\n') # text-mode streams translate to \r\n if needed 86 87# 88# Global variables: 89# 90 91 92def usage(parser): 93 parser.print_help() 94 if configuration.verbose > 0: 95 print(""" 96Examples: 97 98This is an example of using the -f option to pinpoint to a specific test class 99and test method to be run: 100 101$ ./dotest.py -f ClassTypesTestCase.test_with_dsym_and_run_command 102---------------------------------------------------------------------- 103Collected 1 test 104 105test_with_dsym_and_run_command (TestClassTypes.ClassTypesTestCase) 106Test 'frame variable this' when stopped on a class constructor. ... ok 107 108---------------------------------------------------------------------- 109Ran 1 test in 1.396s 110 111OK 112 113And this is an example of using the -p option to run a single file (the filename 114matches the pattern 'ObjC' and it happens to be 'TestObjCMethods.py'): 115 116$ ./dotest.py -v -p ObjC 117---------------------------------------------------------------------- 118Collected 4 tests 119 120test_break_with_dsym (TestObjCMethods.FoundationTestCase) 121Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok 122test_break_with_dwarf (TestObjCMethods.FoundationTestCase) 123Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok 124test_data_type_and_expr_with_dsym (TestObjCMethods.FoundationTestCase) 125Lookup objective-c data types and evaluate expressions. ... ok 126test_data_type_and_expr_with_dwarf (TestObjCMethods.FoundationTestCase) 127Lookup objective-c data types and evaluate expressions. ... ok 128 129---------------------------------------------------------------------- 130Ran 4 tests in 16.661s 131 132OK 133 134Running of this script also sets up the LLDB_TEST environment variable so that 135individual test cases can locate their supporting files correctly. The script 136tries to set up Python's search paths for modules by looking at the build tree 137relative to this script. See also the '-i' option in the following example. 138 139Finally, this is an example of using the lldb.py module distributed/installed by 140Xcode4 to run against the tests under the 'forward' directory, and with the '-w' 141option to add some delay between two tests. It uses ARCH=x86_64 to specify that 142as the architecture and CC=clang to specify the compiler used for the test run: 143 144$ PYTHONPATH=/Xcode4/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python ARCH=x86_64 CC=clang ./dotest.py -v -w -i forward 145 146Session logs for test failures/errors will go into directory '2010-11-11-13_56_16' 147---------------------------------------------------------------------- 148Collected 2 tests 149 150test_with_dsym_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase) 151Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok 152test_with_dwarf_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase) 153Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok 154 155---------------------------------------------------------------------- 156Ran 2 tests in 5.659s 157 158OK 159 160The 'Session ...' verbiage is recently introduced (see also the '-s' option) to 161notify the directory containing the session logs for test failures or errors. 162In case there is any test failure/error, a similar message is appended at the 163end of the stderr output for your convenience. 164 165ENABLING LOGS FROM TESTS 166 167Option 1: 168 169Writing logs into different files per test case:: 170 171This option is particularly useful when multiple dotest instances are created 172by dosep.py 173 174$ ./dotest.py --channel "lldb all" 175 176$ ./dotest.py --channel "lldb all" --channel "gdb-remote packets" 177 178These log files are written to: 179 180<session-dir>/<test-id>-host.log (logs from lldb host process) 181<session-dir>/<test-id>-server.log (logs from debugserver/lldb-server) 182<session-dir>/<test-id>-<test-result>.log (console logs) 183 184By default, logs from successful runs are deleted. Use the --log-success flag 185to create reference logs for debugging. 186 187$ ./dotest.py --log-success 188 189Option 2: (DEPRECATED) 190 191The following options can only enable logs from the host lldb process. 192Only categories from the "lldb" or "gdb-remote" channels can be enabled 193They also do not automatically enable logs in locally running debug servers. 194Also, logs from all test case are written into each log file 195 196o LLDB_LOG: if defined, specifies the log file pathname for the 'lldb' subsystem 197 with a default option of 'event process' if LLDB_LOG_OPTION is not defined. 198 199o GDB_REMOTE_LOG: if defined, specifies the log file pathname for the 200 'process.gdb-remote' subsystem with a default option of 'packets' if 201 GDB_REMOTE_LOG_OPTION is not defined. 202 203""") 204 sys.exit(0) 205 206 207def parseExclusion(exclusion_file): 208 """Parse an exclusion file, of the following format, where 209 'skip files', 'skip methods', 'xfail files', and 'xfail methods' 210 are the possible list heading values: 211 212 skip files 213 <file name> 214 <file name> 215 216 xfail methods 217 <method name> 218 """ 219 excl_type = None 220 221 with open(exclusion_file) as f: 222 for line in f: 223 line = line.strip() 224 if not excl_type: 225 excl_type = line 226 continue 227 228 if not line: 229 excl_type = None 230 elif excl_type == 'skip': 231 if not configuration.skip_tests: 232 configuration.skip_tests = [] 233 configuration.skip_tests.append(line) 234 elif excl_type == 'xfail': 235 if not configuration.xfail_tests: 236 configuration.xfail_tests = [] 237 configuration.xfail_tests.append(line) 238 239 240def parseOptionsAndInitTestdirs(): 241 """Initialize the list of directories containing our unittest scripts. 242 243 '-h/--help as the first option prints out usage info and exit the program. 244 """ 245 246 do_help = False 247 248 platform_system = platform.system() 249 platform_machine = platform.machine() 250 251 parser = dotest_args.create_parser() 252 args = dotest_args.parse_args(parser, sys.argv[1:]) 253 254 if args.unset_env_varnames: 255 for env_var in args.unset_env_varnames: 256 if env_var in os.environ: 257 # From Python Doc: When unsetenv() is supported, deletion of items in os.environ 258 # is automatically translated into a corresponding call to 259 # unsetenv(). 260 del os.environ[env_var] 261 # os.unsetenv(env_var) 262 263 if args.set_env_vars: 264 for env_var in args.set_env_vars: 265 parts = env_var.split('=', 1) 266 if len(parts) == 1: 267 os.environ[parts[0]] = "" 268 else: 269 os.environ[parts[0]] = parts[1] 270 271 # only print the args if being verbose (and parsable is off) 272 if args.v and not args.q: 273 print(sys.argv) 274 275 if args.h: 276 do_help = True 277 278 if args.compiler: 279 configuration.compiler = os.path.realpath(args.compiler) 280 if not is_exe(configuration.compiler): 281 configuration.compiler = which(args.compiler) 282 if not is_exe(configuration.compiler): 283 logging.error( 284 '%s is not a valid compiler executable; aborting...', 285 args.compiler) 286 sys.exit(-1) 287 else: 288 # Use a compiler appropriate appropriate for the Apple SDK if one was 289 # specified 290 if platform_system == 'Darwin' and args.apple_sdk: 291 configuration.compiler = seven.get_command_output( 292 'xcrun -sdk "%s" -find clang 2> /dev/null' % 293 (args.apple_sdk)) 294 else: 295 # 'clang' on ubuntu 14.04 is 3.4 so we try clang-3.5 first 296 candidateCompilers = ['clang-3.5', 'clang', 'gcc'] 297 for candidate in candidateCompilers: 298 if which(candidate): 299 configuration.compiler = candidate 300 break 301 302 if args.channels: 303 lldbtest_config.channels = args.channels 304 305 if args.log_success: 306 lldbtest_config.log_success = args.log_success 307 308 # Set SDKROOT if we are using an Apple SDK 309 if platform_system == 'Darwin' and args.apple_sdk: 310 os.environ['SDKROOT'] = seven.get_command_output( 311 'xcrun --sdk "%s" --show-sdk-path 2> /dev/null' % 312 (args.apple_sdk)) 313 314 if args.arch: 315 configuration.arch = args.arch 316 if configuration.arch.startswith( 317 'arm') and platform_system == 'Darwin' and not args.apple_sdk: 318 os.environ['SDKROOT'] = seven.get_command_output( 319 'xcrun --sdk iphoneos.internal --show-sdk-path 2> /dev/null') 320 if not os.path.exists(os.environ['SDKROOT']): 321 os.environ['SDKROOT'] = seven.get_command_output( 322 'xcrun --sdk iphoneos --show-sdk-path 2> /dev/null') 323 else: 324 configuration.arch = platform_machine 325 326 if args.categoriesList: 327 configuration.categoriesList = set( 328 test_categories.validate( 329 args.categoriesList, False)) 330 configuration.useCategories = True 331 else: 332 configuration.categoriesList = [] 333 334 if args.skipCategories: 335 configuration.skipCategories = test_categories.validate( 336 args.skipCategories, False) 337 338 if args.E: 339 cflags_extras = args.E 340 os.environ['CFLAGS_EXTRAS'] = cflags_extras 341 342 if args.d: 343 sys.stdout.write( 344 "Suspending the process %d to wait for debugger to attach...\n" % 345 os.getpid()) 346 sys.stdout.flush() 347 os.kill(os.getpid(), signal.SIGSTOP) 348 349 if args.f: 350 if any([x.startswith('-') for x in args.f]): 351 usage(parser) 352 configuration.filters.extend(args.f) 353 # Shut off multiprocessing mode when additional filters are specified. 354 # The rational is that the user is probably going after a very specific 355 # test and doesn't need a bunch of parallel test runners all looking for 356 # it in a frenzy. Also, '-v' now spits out all test run output even 357 # on success, so the standard recipe for redoing a failing test (with -v 358 # and a -f to filter to the specific test) now causes all test scanning 359 # (in parallel) to print results for do-nothing runs in a very distracting 360 # manner. If we really need filtered parallel runs in the future, consider 361 # adding a --no-output-on-success that prevents -v from setting 362 # output-on-success. 363 configuration.no_multiprocess_test_runner = True 364 365 if args.l: 366 configuration.skip_long_running_test = False 367 368 if args.framework: 369 configuration.lldbFrameworkPath = args.framework 370 371 if args.executable: 372 # lldb executable is passed explicitly 373 lldbtest_config.lldbExec = os.path.realpath(args.executable) 374 if not is_exe(lldbtest_config.lldbExec): 375 lldbtest_config.lldbExec = which(args.executable) 376 if not is_exe(lldbtest_config.lldbExec): 377 logging.error( 378 '%s is not a valid executable to test; aborting...', 379 args.executable) 380 sys.exit(-1) 381 382 if args.server: 383 os.environ['LLDB_DEBUGSERVER_PATH'] = args.server 384 385 if args.excluded: 386 for excl_file in args.excluded: 387 parseExclusion(excl_file) 388 389 if args.p: 390 if args.p.startswith('-'): 391 usage(parser) 392 configuration.regexp = args.p 393 394 if args.q: 395 configuration.parsable = True 396 397 if args.s: 398 if args.s.startswith('-'): 399 usage(parser) 400 configuration.sdir_name = args.s 401 configuration.session_file_format = args.session_file_format 402 403 if args.t: 404 os.environ['LLDB_COMMAND_TRACE'] = 'YES' 405 406 if args.v: 407 configuration.verbose = 2 408 409 # argparse makes sure we have a number 410 if args.sharp: 411 configuration.count = args.sharp 412 413 if sys.platform.startswith('win32'): 414 os.environ['LLDB_DISABLE_CRASH_DIALOG'] = str( 415 args.disable_crash_dialog) 416 os.environ['LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE'] = str(True) 417 418 if do_help: 419 usage(parser) 420 421 if args.no_multiprocess: 422 configuration.no_multiprocess_test_runner = True 423 424 if args.inferior: 425 configuration.is_inferior_test_runner = True 426 427 if args.num_threads: 428 configuration.num_threads = args.num_threads 429 430 if args.test_subdir: 431 configuration.multiprocess_test_subdir = args.test_subdir 432 433 if args.test_runner_name: 434 configuration.test_runner_name = args.test_runner_name 435 436 # Capture test results-related args. 437 if args.curses and not args.inferior: 438 # Act as if the following args were set. 439 args.results_formatter = "lldbsuite.test_event.formatter.curses.Curses" 440 args.results_file = "stdout" 441 442 if args.results_file: 443 configuration.results_filename = args.results_file 444 445 if args.results_port: 446 configuration.results_port = args.results_port 447 448 if args.results_file and args.results_port: 449 sys.stderr.write( 450 "only one of --results-file and --results-port should " 451 "be specified\n") 452 usage(args) 453 454 if args.results_formatter: 455 configuration.results_formatter_name = args.results_formatter 456 if args.results_formatter_options: 457 configuration.results_formatter_options = args.results_formatter_options 458 459 # Default to using the BasicResultsFormatter if no formatter is specified 460 # and we're not a test inferior. 461 if not args.inferior and configuration.results_formatter_name is None: 462 configuration.results_formatter_name = ( 463 "lldbsuite.test_event.formatter.results_formatter.ResultsFormatter") 464 465 # rerun-related arguments 466 configuration.rerun_all_issues = args.rerun_all_issues 467 configuration.rerun_max_file_threshold = args.rerun_max_file_threshold 468 469 if args.lldb_platform_name: 470 configuration.lldb_platform_name = args.lldb_platform_name 471 if args.lldb_platform_url: 472 configuration.lldb_platform_url = args.lldb_platform_url 473 if args.lldb_platform_working_dir: 474 configuration.lldb_platform_working_dir = args.lldb_platform_working_dir 475 476 if args.event_add_entries and len(args.event_add_entries) > 0: 477 entries = {} 478 # Parse out key=val pairs, separated by comma 479 for keyval in args.event_add_entries.split(","): 480 key_val_entry = keyval.split("=") 481 if len(key_val_entry) == 2: 482 (key, val) = key_val_entry 483 val_parts = val.split(':') 484 if len(val_parts) > 1: 485 (val, val_type) = val_parts 486 if val_type == 'int': 487 val = int(val) 488 entries[key] = val 489 # Tell the event builder to create all events with these 490 # key/val pairs in them. 491 if len(entries) > 0: 492 EventBuilder.add_entries_to_all_events(entries) 493 494 # Gather all the dirs passed on the command line. 495 if len(args.args) > 0: 496 configuration.testdirs = list( 497 map(lambda x: os.path.realpath(os.path.abspath(x)), args.args)) 498 # Shut off multiprocessing mode when test directories are specified. 499 configuration.no_multiprocess_test_runner = True 500 501 lldbtest_config.codesign_identity = args.codesign_identity 502 503 #print("testdirs:", testdirs) 504 505 506def getXcodeOutputPaths(lldbRootDirectory): 507 result = [] 508 509 # These are for xcode build directories. 510 xcode3_build_dir = ['build'] 511 xcode4_build_dir = ['build', 'lldb', 'Build', 'Products'] 512 513 configurations = [ 514 ['Debug'], 515 ['DebugClang'], 516 ['Release'], 517 ['BuildAndIntegration']] 518 xcode_build_dirs = [xcode3_build_dir, xcode4_build_dir] 519 for configuration in configurations: 520 for xcode_build_dir in xcode_build_dirs: 521 outputPath = os.path.join( 522 lldbRootDirectory, *(xcode_build_dir + configuration)) 523 result.append(outputPath) 524 525 return result 526 527 528def createSocketToLocalPort(port): 529 def socket_closer(s): 530 """Close down an opened socket properly.""" 531 s.shutdown(socket.SHUT_RDWR) 532 s.close() 533 534 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 535 sock.connect(("localhost", port)) 536 return (sock, lambda: socket_closer(sock)) 537 538 539def setupTestResults(): 540 """Sets up test results-related objects based on arg settings.""" 541 # Setup the results formatter configuration. 542 formatter_config = formatter.FormatterConfig() 543 formatter_config.filename = configuration.results_filename 544 formatter_config.formatter_name = configuration.results_formatter_name 545 formatter_config.formatter_options = ( 546 configuration.results_formatter_options) 547 formatter_config.port = configuration.results_port 548 549 # Create the results formatter. 550 formatter_spec = formatter.create_results_formatter( 551 formatter_config) 552 if formatter_spec is not None and formatter_spec.formatter is not None: 553 configuration.results_formatter_object = formatter_spec.formatter 554 555 # Send an initialize message to the formatter. 556 initialize_event = EventBuilder.bare_event("initialize") 557 if isMultiprocessTestRunner(): 558 if (configuration.test_runner_name is not None and 559 configuration.test_runner_name == "serial"): 560 # Only one worker queue here. 561 worker_count = 1 562 else: 563 # Workers will be the number of threads specified. 564 worker_count = configuration.num_threads 565 else: 566 worker_count = 1 567 initialize_event["worker_count"] = worker_count 568 569 formatter_spec.formatter.handle_event(initialize_event) 570 571 # Make sure we clean up the formatter on shutdown. 572 if formatter_spec.cleanup_func is not None: 573 atexit.register(formatter_spec.cleanup_func) 574 575 576def getOutputPaths(lldbRootDirectory): 577 """ 578 Returns typical build output paths for the lldb executable 579 580 lldbDirectory - path to the root of the lldb svn/git repo 581 """ 582 result = [] 583 584 if sys.platform == 'darwin': 585 result.extend(getXcodeOutputPaths(lldbRootDirectory)) 586 587 # cmake builds? look for build or build/host folder next to llvm directory 588 # lldb is located in llvm/tools/lldb so we need to go up three levels 589 llvmParentDir = os.path.abspath( 590 os.path.join( 591 lldbRootDirectory, 592 os.pardir, 593 os.pardir, 594 os.pardir)) 595 result.append(os.path.join(llvmParentDir, 'build', 'bin')) 596 result.append(os.path.join(llvmParentDir, 'build', 'host', 'bin')) 597 598 # some cmake developers keep their build directory beside their lldb 599 # directory 600 lldbParentDir = os.path.abspath(os.path.join(lldbRootDirectory, os.pardir)) 601 result.append(os.path.join(lldbParentDir, 'build', 'bin')) 602 result.append(os.path.join(lldbParentDir, 'build', 'host', 'bin')) 603 604 return result 605 606 607def setupSysPath(): 608 """ 609 Add LLDB.framework/Resources/Python to the search paths for modules. 610 As a side effect, we also discover the 'lldb' executable and export it here. 611 """ 612 613 # Get the directory containing the current script. 614 if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ: 615 scriptPath = os.environ["DOTEST_SCRIPT_DIR"] 616 else: 617 scriptPath = os.path.dirname(os.path.realpath(__file__)) 618 if not scriptPath.endswith('test'): 619 print("This script expects to reside in lldb's test directory.") 620 sys.exit(-1) 621 622 os.environ["LLDB_TEST"] = scriptPath 623 624 # Set up the LLDB_SRC environment variable, so that the tests can locate 625 # the LLDB source code. 626 os.environ["LLDB_SRC"] = lldbsuite.lldb_root 627 628 pluginPath = os.path.join(scriptPath, 'plugins') 629 toolsLLDBMIPath = os.path.join(scriptPath, 'tools', 'lldb-mi') 630 toolsLLDBServerPath = os.path.join(scriptPath, 'tools', 'lldb-server') 631 632 # Insert script dir, plugin dir, lldb-mi dir and lldb-server dir to the 633 # sys.path. 634 sys.path.insert(0, pluginPath) 635 # Adding test/tools/lldb-mi to the path makes it easy 636 sys.path.insert(0, toolsLLDBMIPath) 637 # to "import lldbmi_testcase" from the MI tests 638 # Adding test/tools/lldb-server to the path makes it easy 639 sys.path.insert(0, toolsLLDBServerPath) 640 # to "import lldbgdbserverutils" from the lldb-server tests 641 642 # This is the root of the lldb git/svn checkout 643 # When this changes over to a package instead of a standalone script, this 644 # will be `lldbsuite.lldb_root` 645 lldbRootDirectory = lldbsuite.lldb_root 646 647 # Some of the tests can invoke the 'lldb' command directly. 648 # We'll try to locate the appropriate executable right here. 649 650 # The lldb executable can be set from the command line 651 # if it's not set, we try to find it now 652 # first, we try the environment 653 if not lldbtest_config.lldbExec: 654 # First, you can define an environment variable LLDB_EXEC specifying the 655 # full pathname of the lldb executable. 656 if "LLDB_EXEC" in os.environ: 657 lldbtest_config.lldbExec = os.environ["LLDB_EXEC"] 658 659 if not lldbtest_config.lldbExec: 660 outputPaths = getOutputPaths(lldbRootDirectory) 661 for outputPath in outputPaths: 662 candidatePath = os.path.join(outputPath, 'lldb') 663 if is_exe(candidatePath): 664 lldbtest_config.lldbExec = candidatePath 665 break 666 667 if not lldbtest_config.lldbExec: 668 # Last, check the path 669 lldbtest_config.lldbExec = which('lldb') 670 671 if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec): 672 print( 673 "'{}' is not a path to a valid executable".format( 674 lldbtest_config.lldbExec)) 675 lldbtest_config.lldbExec = None 676 677 if not lldbtest_config.lldbExec: 678 print("The 'lldb' executable cannot be located. Some of the tests may not be run as a result.") 679 sys.exit(-1) 680 681 # confusingly, this is the "bin" directory 682 lldbLibDir = os.path.dirname(lldbtest_config.lldbExec) 683 os.environ["LLDB_LIB_DIR"] = lldbLibDir 684 lldbImpLibDir = os.path.join( 685 lldbLibDir, 686 '..', 687 'lib') if sys.platform.startswith('win32') else lldbLibDir 688 os.environ["LLDB_IMPLIB_DIR"] = lldbImpLibDir 689 print("LLDB library dir:", os.environ["LLDB_LIB_DIR"]) 690 print("LLDB import library dir:", os.environ["LLDB_IMPLIB_DIR"]) 691 os.system('%s -v' % lldbtest_config.lldbExec) 692 693 # Assume lldb-mi is in same place as lldb 694 # If not found, disable the lldb-mi tests 695 # TODO: Append .exe on Windows 696 # - this will be in a separate commit in case the mi tests fail horribly 697 lldbDir = os.path.dirname(lldbtest_config.lldbExec) 698 lldbMiExec = os.path.join(lldbDir, "lldb-mi") 699 if is_exe(lldbMiExec): 700 os.environ["LLDBMI_EXEC"] = lldbMiExec 701 else: 702 if not configuration.shouldSkipBecauseOfCategories(["lldb-mi"]): 703 print( 704 "The 'lldb-mi' executable cannot be located. The lldb-mi tests can not be run as a result.") 705 configuration.skipCategories.append("lldb-mi") 706 707 lldbPythonDir = None # The directory that contains 'lldb/__init__.py' 708 if not configuration.lldbFrameworkPath and os.path.exists(os.path.join(lldbLibDir, "LLDB.framework")): 709 configuration.lldbFrameworkPath = os.path.join(lldbLibDir, "LLDB.framework") 710 if configuration.lldbFrameworkPath: 711 lldbtest_config.lldbFrameworkPath = configuration.lldbFrameworkPath 712 candidatePath = os.path.join( 713 configuration.lldbFrameworkPath, 'Resources', 'Python') 714 if os.path.isfile(os.path.join(candidatePath, 'lldb/__init__.py')): 715 lldbPythonDir = candidatePath 716 if not lldbPythonDir: 717 print( 718 'Resources/Python/lldb/__init__.py was not found in ' + 719 configuration.lldbFrameworkPath) 720 sys.exit(-1) 721 else: 722 # If our lldb supports the -P option, use it to find the python path: 723 init_in_python_dir = os.path.join('lldb', '__init__.py') 724 725 lldb_dash_p_result = subprocess.check_output( 726 [lldbtest_config.lldbExec, "-P"], stderr=subprocess.STDOUT, universal_newlines=True) 727 728 if lldb_dash_p_result and not lldb_dash_p_result.startswith( 729 ("<", "lldb: invalid option:")) and not lldb_dash_p_result.startswith("Traceback"): 730 lines = lldb_dash_p_result.splitlines() 731 732 # Workaround for readline vs libedit issue on FreeBSD. If stdout 733 # is not a terminal Python executes 734 # rl_variable_bind ("enable-meta-key", "off"); 735 # This produces a warning with FreeBSD's libedit because the 736 # enable-meta-key variable is unknown. Not an issue on Apple 737 # because cpython commit f0ab6f9f0603 added a #ifndef __APPLE__ 738 # around the call. See http://bugs.python.org/issue19884 for more 739 # information. For now we just discard the warning output. 740 if len(lines) >= 1 and lines[0].startswith( 741 "bind: Invalid command"): 742 lines.pop(0) 743 744 # Taking the last line because lldb outputs 745 # 'Cannot read termcap database;\nusing dumb terminal settings.\n' 746 # before the path 747 if len(lines) >= 1 and os.path.isfile( 748 os.path.join(lines[-1], init_in_python_dir)): 749 lldbPythonDir = lines[-1] 750 if "freebsd" in sys.platform or "linux" in sys.platform: 751 os.environ['LLDB_LIB_DIR'] = os.path.join( 752 lldbPythonDir, '..', '..') 753 754 if not lldbPythonDir: 755 if platform.system() == "Darwin": 756 python_resource_dir = ['LLDB.framework', 'Resources', 'Python'] 757 outputPaths = getXcodeOutputPaths(lldbRootDirectory) 758 for outputPath in outputPaths: 759 candidatePath = os.path.join( 760 outputPath, *python_resource_dir) 761 if os.path.isfile( 762 os.path.join( 763 candidatePath, 764 init_in_python_dir)): 765 lldbPythonDir = candidatePath 766 break 767 768 if not lldbPythonDir: 769 print("lldb.py is not found, some tests may fail.") 770 else: 771 print( 772 "Unable to load lldb extension module. Possible reasons for this include:") 773 print(" 1) LLDB was built with LLDB_DISABLE_PYTHON=1") 774 print( 775 " 2) PYTHONPATH and PYTHONHOME are not set correctly. PYTHONHOME should refer to") 776 print( 777 " the version of Python that LLDB built and linked against, and PYTHONPATH") 778 print( 779 " should contain the Lib directory for the same python distro, as well as the") 780 print(" location of LLDB\'s site-packages folder.") 781 print( 782 " 3) A different version of Python than that which was built against is exported in") 783 print(" the system\'s PATH environment variable, causing conflicts.") 784 print( 785 " 4) The executable '%s' could not be found. Please check " % 786 lldbtest_config.lldbExec) 787 print(" that it exists and is executable.") 788 789 if lldbPythonDir: 790 lldbPythonDir = os.path.normpath(lldbPythonDir) 791 # Some of the code that uses this path assumes it hasn't resolved the Versions... link. 792 # If the path we've constructed looks like that, then we'll strip out 793 # the Versions/A part. 794 (before, frameWithVersion, after) = lldbPythonDir.rpartition( 795 "LLDB.framework/Versions/A") 796 if frameWithVersion != "": 797 lldbPythonDir = before + "LLDB.framework" + after 798 799 lldbPythonDir = os.path.abspath(lldbPythonDir) 800 801 # If tests need to find LLDB_FRAMEWORK, now they can do it 802 os.environ["LLDB_FRAMEWORK"] = os.path.dirname( 803 os.path.dirname(lldbPythonDir)) 804 805 # This is to locate the lldb.py module. Insert it right after 806 # sys.path[0]. 807 sys.path[1:1] = [lldbPythonDir] 808 809 810def visit_file(dir, name): 811 # Try to match the regexp pattern, if specified. 812 if configuration.regexp: 813 if not re.search(configuration.regexp, name): 814 # We didn't match the regex, we're done. 815 return 816 817 if configuration.skip_tests: 818 for file_regexp in configuration.skip_tests: 819 if re.search(file_regexp, name): 820 return 821 822 # We found a match for our test. Add it to the suite. 823 824 # Update the sys.path first. 825 if not sys.path.count(dir): 826 sys.path.insert(0, dir) 827 base = os.path.splitext(name)[0] 828 829 # Thoroughly check the filterspec against the base module and admit 830 # the (base, filterspec) combination only when it makes sense. 831 filterspec = None 832 for filterspec in configuration.filters: 833 # Optimistically set the flag to True. 834 filtered = True 835 module = __import__(base) 836 parts = filterspec.split('.') 837 obj = module 838 for part in parts: 839 try: 840 parent, obj = obj, getattr(obj, part) 841 except AttributeError: 842 # The filterspec has failed. 843 filtered = False 844 break 845 846 # If filtered, we have a good filterspec. Add it. 847 if filtered: 848 # print("adding filter spec %s to module %s" % (filterspec, module)) 849 configuration.suite.addTests( 850 unittest2.defaultTestLoader.loadTestsFromName( 851 filterspec, module)) 852 continue 853 854 # Forgo this module if the (base, filterspec) combo is invalid 855 if configuration.filters and not filtered: 856 return 857 858 if not filterspec or not filtered: 859 # Add the entire file's worth of tests since we're not filtered. 860 # Also the fail-over case when the filterspec branch 861 # (base, filterspec) combo doesn't make sense. 862 configuration.suite.addTests( 863 unittest2.defaultTestLoader.loadTestsFromName(base)) 864 865 866def visit(prefix, dir, names): 867 """Visitor function for os.path.walk(path, visit, arg).""" 868 869 dir_components = set(dir.split(os.sep)) 870 excluded_components = set(['.svn', '.git']) 871 if dir_components.intersection(excluded_components): 872 return 873 874 # Gather all the Python test file names that follow the Test*.py pattern. 875 python_test_files = [ 876 name 877 for name in names 878 if name.endswith('.py') and name.startswith(prefix)] 879 880 # Visit all the python test files. 881 for name in python_test_files: 882 try: 883 # Ensure we error out if we have multiple tests with the same 884 # base name. 885 # Future improvement: find all the places where we work with base 886 # names and convert to full paths. We have directory structure 887 # to disambiguate these, so we shouldn't need this constraint. 888 if name in configuration.all_tests: 889 raise Exception("Found multiple tests with the name %s" % name) 890 configuration.all_tests.add(name) 891 892 # Run the relevant tests in the python file. 893 visit_file(dir, name) 894 except Exception as ex: 895 # Convert this exception to a test event error for the file. 896 test_filename = os.path.abspath(os.path.join(dir, name)) 897 if configuration.results_formatter_object is not None: 898 # Grab the backtrace for the exception. 899 import traceback 900 backtrace = traceback.format_exc() 901 902 # Generate the test event. 903 configuration.results_formatter_object.handle_event( 904 EventBuilder.event_for_job_test_add_error( 905 test_filename, ex, backtrace)) 906 raise 907 908 909def disabledynamics(): 910 import lldb 911 ci = lldb.DBG.GetCommandInterpreter() 912 res = lldb.SBCommandReturnObject() 913 ci.HandleCommand( 914 "setting set target.prefer-dynamic-value no-dynamic-values", 915 res, 916 False) 917 if not res.Succeeded(): 918 raise Exception('disabling dynamic type support failed') 919 920 921def lldbLoggings(): 922 import lldb 923 """Check and do lldb loggings if necessary.""" 924 925 # Turn on logging for debugging purposes if ${LLDB_LOG} environment variable is 926 # defined. Use ${LLDB_LOG} to specify the log file. 927 ci = lldb.DBG.GetCommandInterpreter() 928 res = lldb.SBCommandReturnObject() 929 if ("LLDB_LOG" in os.environ): 930 open(os.environ["LLDB_LOG"], 'w').close() 931 if ("LLDB_LOG_OPTION" in os.environ): 932 lldb_log_option = os.environ["LLDB_LOG_OPTION"] 933 else: 934 lldb_log_option = "event process expr state api" 935 ci.HandleCommand( 936 "log enable -n -f " + 937 os.environ["LLDB_LOG"] + 938 " lldb " + 939 lldb_log_option, 940 res) 941 if not res.Succeeded(): 942 raise Exception('log enable failed (check LLDB_LOG env variable)') 943 944 if ("LLDB_LINUX_LOG" in os.environ): 945 open(os.environ["LLDB_LINUX_LOG"], 'w').close() 946 if ("LLDB_LINUX_LOG_OPTION" in os.environ): 947 lldb_log_option = os.environ["LLDB_LINUX_LOG_OPTION"] 948 else: 949 lldb_log_option = "event process expr state api" 950 ci.HandleCommand( 951 "log enable -n -f " + 952 os.environ["LLDB_LINUX_LOG"] + 953 " linux " + 954 lldb_log_option, 955 res) 956 if not res.Succeeded(): 957 raise Exception( 958 'log enable failed (check LLDB_LINUX_LOG env variable)') 959 960 # Ditto for gdb-remote logging if ${GDB_REMOTE_LOG} environment variable is defined. 961 # Use ${GDB_REMOTE_LOG} to specify the log file. 962 if ("GDB_REMOTE_LOG" in os.environ): 963 if ("GDB_REMOTE_LOG_OPTION" in os.environ): 964 gdb_remote_log_option = os.environ["GDB_REMOTE_LOG_OPTION"] 965 else: 966 gdb_remote_log_option = "packets process" 967 ci.HandleCommand( 968 "log enable -n -f " + os.environ["GDB_REMOTE_LOG"] + " gdb-remote " 969 + gdb_remote_log_option, 970 res) 971 if not res.Succeeded(): 972 raise Exception( 973 'log enable failed (check GDB_REMOTE_LOG env variable)') 974 975 976def getMyCommandLine(): 977 return ' '.join(sys.argv) 978 979# ======================================== # 980# # 981# Execution of the test driver starts here # 982# # 983# ======================================== # 984 985 986def checkDsymForUUIDIsNotOn(): 987 cmd = ["defaults", "read", "com.apple.DebugSymbols"] 988 pipe = subprocess.Popen( 989 cmd, 990 stdout=subprocess.PIPE, 991 stderr=subprocess.STDOUT) 992 cmd_output = pipe.stdout.read() 993 if cmd_output and "DBGFileMappedPaths = " in cmd_output: 994 print("%s =>" % ' '.join(cmd)) 995 print(cmd_output) 996 print( 997 "Disable automatic lookup and caching of dSYMs before running the test suite!") 998 print("Exiting...") 999 sys.exit(0) 1000 1001 1002def exitTestSuite(exitCode=None): 1003 import lldb 1004 lldb.SBDebugger.Terminate() 1005 if exitCode: 1006 sys.exit(exitCode) 1007 1008 1009def isMultiprocessTestRunner(): 1010 # We're not multiprocess when we're either explicitly 1011 # the inferior (as specified by the multiprocess test 1012 # runner) OR we've been told to skip using the multiprocess 1013 # test runner 1014 return not ( 1015 configuration.is_inferior_test_runner or configuration.no_multiprocess_test_runner) 1016 1017 1018def getVersionForSDK(sdk): 1019 sdk = str.lower(sdk) 1020 full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk) 1021 basename = os.path.basename(full_path) 1022 basename = os.path.splitext(basename)[0] 1023 basename = str.lower(basename) 1024 ver = basename.replace(sdk, '') 1025 return ver 1026 1027 1028def getPathForSDK(sdk): 1029 sdk = str.lower(sdk) 1030 full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk) 1031 if os.path.exists(full_path): 1032 return full_path 1033 return None 1034 1035 1036def setDefaultTripleForPlatform(): 1037 if configuration.lldb_platform_name == 'ios-simulator': 1038 triple_str = 'x86_64-apple-ios%s' % ( 1039 getVersionForSDK('iphonesimulator')) 1040 os.environ['TRIPLE'] = triple_str 1041 return {'TRIPLE': triple_str} 1042 return {} 1043 1044 1045def checkCompiler(): 1046 # Add some intervention here to sanity check that the compiler requested is sane. 1047 # If found not to be an executable program, we abort. 1048 c = configuration.compiler 1049 if which(c): 1050 return 1051 1052 if not sys.platform.startswith("darwin"): 1053 raise Exception(c + " is not a valid compiler") 1054 1055 pipe = subprocess.Popen( 1056 ['xcrun', '-find', c], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 1057 cmd_output = pipe.stdout.read() 1058 if not cmd_output or "not found" in cmd_output: 1059 raise Exception(c + " is not a valid compiler") 1060 1061 configuration.compiler = cmd_output.split('\n')[0] 1062 print("'xcrun -find %s' returning %s" % (c, configuration.compiler)) 1063 1064def canRunLibcxxTests(): 1065 from lldbsuite.test import lldbplatformutil 1066 1067 platform = lldbplatformutil.getPlatform() 1068 1069 if lldbplatformutil.target_is_android() or lldbplatformutil.platformIsDarwin(): 1070 return True, "libc++ always present" 1071 1072 if platform == "linux": 1073 if not os.path.isdir("/usr/include/c++/v1"): 1074 return False, "Unable to find libc++ installation" 1075 return True, "Headers found, let's hope they work" 1076 1077 return False, "Don't know how to build with libc++ on %s" % platform 1078 1079def checkLibcxxSupport(): 1080 result, reason = canRunLibcxxTests() 1081 if result: 1082 return # libc++ supported 1083 if "libc++" in configuration.categoriesList: 1084 return # libc++ category explicitly requested, let it run. 1085 print("Libc++ tests will not be run because: " + reason) 1086 configuration.skipCategories.append("libc++") 1087 1088def run_suite(): 1089 # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults 1090 # does not exist before proceeding to running the test suite. 1091 if sys.platform.startswith("darwin"): 1092 checkDsymForUUIDIsNotOn() 1093 1094 # 1095 # Start the actions by first parsing the options while setting up the test 1096 # directories, followed by setting up the search paths for lldb utilities; 1097 # then, we walk the directory trees and collect the tests into our test suite. 1098 # 1099 parseOptionsAndInitTestdirs() 1100 1101 # Setup test results (test results formatter and output handling). 1102 setupTestResults() 1103 1104 # If we are running as the multiprocess test runner, kick off the 1105 # multiprocess test runner here. 1106 if isMultiprocessTestRunner(): 1107 from . import dosep 1108 dosep.main( 1109 configuration.num_threads, 1110 configuration.multiprocess_test_subdir, 1111 configuration.test_runner_name, 1112 configuration.results_formatter_object) 1113 raise Exception("should never get here") 1114 elif configuration.is_inferior_test_runner: 1115 # Shut off Ctrl-C processing in inferiors. The parallel 1116 # test runner handles this more holistically. 1117 signal.signal(signal.SIGINT, signal.SIG_IGN) 1118 1119 setupSysPath() 1120 1121 # 1122 # If '-l' is specified, do not skip the long running tests. 1123 if not configuration.skip_long_running_test: 1124 os.environ["LLDB_SKIP_LONG_RUNNING_TEST"] = "NO" 1125 1126 # For the time being, let's bracket the test runner within the 1127 # lldb.SBDebugger.Initialize()/Terminate() pair. 1128 import lldb 1129 1130 # Create a singleton SBDebugger in the lldb namespace. 1131 lldb.DBG = lldb.SBDebugger.Create() 1132 1133 if configuration.lldb_platform_name: 1134 print("Setting up remote platform '%s'" % 1135 (configuration.lldb_platform_name)) 1136 lldb.remote_platform = lldb.SBPlatform( 1137 configuration.lldb_platform_name) 1138 if not lldb.remote_platform.IsValid(): 1139 print( 1140 "error: unable to create the LLDB platform named '%s'." % 1141 (configuration.lldb_platform_name)) 1142 exitTestSuite(1) 1143 if configuration.lldb_platform_url: 1144 # We must connect to a remote platform if a LLDB platform URL was 1145 # specified 1146 print( 1147 "Connecting to remote platform '%s' at '%s'..." % 1148 (configuration.lldb_platform_name, configuration.lldb_platform_url)) 1149 platform_connect_options = lldb.SBPlatformConnectOptions( 1150 configuration.lldb_platform_url) 1151 err = lldb.remote_platform.ConnectRemote(platform_connect_options) 1152 if err.Success(): 1153 print("Connected.") 1154 else: 1155 print("error: failed to connect to remote platform using URL '%s': %s" % ( 1156 configuration.lldb_platform_url, err)) 1157 exitTestSuite(1) 1158 else: 1159 configuration.lldb_platform_url = None 1160 1161 platform_changes = setDefaultTripleForPlatform() 1162 first = True 1163 for key in platform_changes: 1164 if first: 1165 print("Environment variables setup for platform support:") 1166 first = False 1167 print("%s = %s" % (key, platform_changes[key])) 1168 1169 if configuration.lldb_platform_working_dir: 1170 print("Setting remote platform working directory to '%s'..." % 1171 (configuration.lldb_platform_working_dir)) 1172 error = lldb.remote_platform.MakeDirectory( 1173 configuration.lldb_platform_working_dir, 448) # 448 = 0o700 1174 if error.Fail(): 1175 raise Exception("making remote directory '%s': %s" % ( 1176 remote_test_dir, error)) 1177 1178 if not lldb.remote_platform.SetWorkingDirectory( 1179 configuration.lldb_platform_working_dir): 1180 raise Exception("failed to set working directory '%s'" % remote_test_dir) 1181 lldb.DBG.SetSelectedPlatform(lldb.remote_platform) 1182 else: 1183 lldb.remote_platform = None 1184 configuration.lldb_platform_working_dir = None 1185 configuration.lldb_platform_url = None 1186 1187 target_platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2] 1188 1189 checkLibcxxSupport() 1190 1191 # Don't do debugserver tests on everything except OS X. 1192 configuration.dont_do_debugserver_test = "linux" in target_platform or "freebsd" in target_platform or "windows" in target_platform 1193 1194 # Don't do lldb-server (llgs) tests on anything except Linux. 1195 configuration.dont_do_llgs_test = not ("linux" in target_platform) 1196 1197 # 1198 # Walk through the testdirs while collecting tests. 1199 # 1200 for testdir in configuration.testdirs: 1201 for (dirpath, dirnames, filenames) in os.walk(testdir): 1202 visit('Test', dirpath, filenames) 1203 1204 # 1205 # Now that we have loaded all the test cases, run the whole test suite. 1206 # 1207 1208 # Turn on lldb loggings if necessary. 1209 lldbLoggings() 1210 1211 # Disable default dynamic types for testing purposes 1212 disabledynamics() 1213 1214 # Install the control-c handler. 1215 unittest2.signals.installHandler() 1216 1217 # If sdir_name is not specified through the '-s sdir_name' option, get a 1218 # timestamp string and export it as LLDB_SESSION_DIR environment var. This will 1219 # be used when/if we want to dump the session info of individual test cases 1220 # later on. 1221 # 1222 # See also TestBase.dumpSessionInfo() in lldbtest.py. 1223 import datetime 1224 # The windows platforms don't like ':' in the pathname. 1225 timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S") 1226 if not configuration.sdir_name: 1227 configuration.sdir_name = timestamp_started 1228 os.environ["LLDB_SESSION_DIRNAME"] = os.path.join( 1229 os.getcwd(), configuration.sdir_name) 1230 1231 sys.stderr.write( 1232 "\nSession logs for test failures/errors/unexpected successes" 1233 " will go into directory '%s'\n" % 1234 configuration.sdir_name) 1235 sys.stderr.write("Command invoked: %s\n" % getMyCommandLine()) 1236 1237 if not os.path.isdir(configuration.sdir_name): 1238 try: 1239 os.mkdir(configuration.sdir_name) 1240 except OSError as exception: 1241 if exception.errno != errno.EEXIST: 1242 raise 1243 1244 # 1245 # Invoke the default TextTestRunner to run the test suite 1246 # 1247 checkCompiler() 1248 1249 if not configuration.parsable: 1250 print("compiler=%s" % configuration.compiler) 1251 1252 # Iterating over all possible architecture and compiler combinations. 1253 os.environ["ARCH"] = configuration.arch 1254 os.environ["CC"] = configuration.compiler 1255 configString = "arch=%s compiler=%s" % (configuration.arch, 1256 configuration.compiler) 1257 1258 # Translate ' ' to '-' for pathname component. 1259 if six.PY2: 1260 import string 1261 tbl = string.maketrans(' ', '-') 1262 else: 1263 tbl = str.maketrans(' ', '-') 1264 configPostfix = configString.translate(tbl) 1265 1266 # Output the configuration. 1267 if not configuration.parsable: 1268 sys.stderr.write("\nConfiguration: " + configString + "\n") 1269 1270 # First, write out the number of collected test cases. 1271 if not configuration.parsable: 1272 sys.stderr.write(configuration.separator + "\n") 1273 sys.stderr.write( 1274 "Collected %d test%s\n\n" % 1275 (configuration.suite.countTestCases(), 1276 configuration.suite.countTestCases() != 1 and "s" or "")) 1277 1278 if configuration.parsable: 1279 v = 0 1280 else: 1281 v = configuration.verbose 1282 1283 # Invoke the test runner. 1284 if configuration.count == 1: 1285 result = unittest2.TextTestRunner( 1286 stream=sys.stderr, 1287 verbosity=v, 1288 resultclass=test_result.LLDBTestResult).run( 1289 configuration.suite) 1290 else: 1291 # We are invoking the same test suite more than once. In this case, 1292 # mark __ignore_singleton__ flag as True so the signleton pattern is 1293 # not enforced. 1294 test_result.LLDBTestResult.__ignore_singleton__ = True 1295 for i in range(configuration.count): 1296 1297 result = unittest2.TextTestRunner( 1298 stream=sys.stderr, 1299 verbosity=v, 1300 resultclass=test_result.LLDBTestResult).run( 1301 configuration.suite) 1302 1303 configuration.failed = not result.wasSuccessful() 1304 1305 if configuration.sdir_has_content and not configuration.parsable: 1306 sys.stderr.write( 1307 "Session logs for test failures/errors/unexpected successes" 1308 " can be found in directory '%s'\n" % 1309 configuration.sdir_name) 1310 1311 if configuration.useCategories and len( 1312 configuration.failuresPerCategory) > 0: 1313 sys.stderr.write("Failures per category:\n") 1314 for category in configuration.failuresPerCategory: 1315 sys.stderr.write( 1316 "%s - %d\n" % 1317 (category, configuration.failuresPerCategory[category])) 1318 1319 # Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined. 1320 # This should not be necessary now. 1321 if ("LLDB_TESTSUITE_FORCE_FINISH" in os.environ): 1322 print("Terminating Test suite...") 1323 subprocess.Popen(["/bin/sh", "-c", "kill %s; exit 0" % (os.getpid())]) 1324 1325 # Exiting. 1326 exitTestSuite(configuration.failed) 1327 1328if __name__ == "__main__": 1329 print( 1330 __file__ + 1331 " is for use as a module only. It should not be run as a standalone script.") 1332 sys.exit(-1) 1333