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