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 lldbtest_config.codesign_identity = args.codesign_identity 485 486 #print("testdirs:", testdirs) 487 488 489def getXcodeOutputPaths(lldbRootDirectory): 490 result = [] 491 492 # These are for xcode build directories. 493 xcode3_build_dir = ['build'] 494 xcode4_build_dir = ['build', 'lldb', 'Build', 'Products'] 495 496 configurations = [ 497 ['Debug'], 498 ['DebugClang'], 499 ['Release'], 500 ['BuildAndIntegration']] 501 xcode_build_dirs = [xcode3_build_dir, xcode4_build_dir] 502 for configuration in configurations: 503 for xcode_build_dir in xcode_build_dirs: 504 outputPath = os.path.join( 505 lldbRootDirectory, *(xcode_build_dir + configuration)) 506 result.append(outputPath) 507 508 return result 509 510 511def createSocketToLocalPort(port): 512 def socket_closer(s): 513 """Close down an opened socket properly.""" 514 s.shutdown(socket.SHUT_RDWR) 515 s.close() 516 517 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 518 sock.connect(("localhost", port)) 519 return (sock, lambda: socket_closer(sock)) 520 521 522def setupTestResults(): 523 """Sets up test results-related objects based on arg settings.""" 524 # Setup the results formatter configuration. 525 formatter_config = formatter.FormatterConfig() 526 formatter_config.filename = configuration.results_filename 527 formatter_config.formatter_name = configuration.results_formatter_name 528 formatter_config.formatter_options = ( 529 configuration.results_formatter_options) 530 formatter_config.port = configuration.results_port 531 532 # Create the results formatter. 533 formatter_spec = formatter.create_results_formatter( 534 formatter_config) 535 if formatter_spec is not None and formatter_spec.formatter is not None: 536 configuration.results_formatter_object = formatter_spec.formatter 537 538 # Send an initialize message to the formatter. 539 initialize_event = EventBuilder.bare_event("initialize") 540 if isMultiprocessTestRunner(): 541 if (configuration.test_runner_name is not None and 542 configuration.test_runner_name == "serial"): 543 # Only one worker queue here. 544 worker_count = 1 545 else: 546 # Workers will be the number of threads specified. 547 worker_count = configuration.num_threads 548 else: 549 worker_count = 1 550 initialize_event["worker_count"] = worker_count 551 552 formatter_spec.formatter.handle_event(initialize_event) 553 554 # Make sure we clean up the formatter on shutdown. 555 if formatter_spec.cleanup_func is not None: 556 atexit.register(formatter_spec.cleanup_func) 557 558 559def getOutputPaths(lldbRootDirectory): 560 """ 561 Returns typical build output paths for the lldb executable 562 563 lldbDirectory - path to the root of the lldb svn/git repo 564 """ 565 result = [] 566 567 if sys.platform == 'darwin': 568 result.extend(getXcodeOutputPaths(lldbRootDirectory)) 569 570 # cmake builds? look for build or build/host folder next to llvm directory 571 # lldb is located in llvm/tools/lldb so we need to go up three levels 572 llvmParentDir = os.path.abspath( 573 os.path.join( 574 lldbRootDirectory, 575 os.pardir, 576 os.pardir, 577 os.pardir)) 578 result.append(os.path.join(llvmParentDir, 'build', 'bin')) 579 result.append(os.path.join(llvmParentDir, 'build', 'host', 'bin')) 580 581 # some cmake developers keep their build directory beside their lldb 582 # directory 583 lldbParentDir = os.path.abspath(os.path.join(lldbRootDirectory, os.pardir)) 584 result.append(os.path.join(lldbParentDir, 'build', 'bin')) 585 result.append(os.path.join(lldbParentDir, 'build', 'host', 'bin')) 586 587 return result 588 589 590def setupSysPath(): 591 """ 592 Add LLDB.framework/Resources/Python to the search paths for modules. 593 As a side effect, we also discover the 'lldb' executable and export it here. 594 """ 595 596 # Get the directory containing the current script. 597 if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ: 598 scriptPath = os.environ["DOTEST_SCRIPT_DIR"] 599 else: 600 scriptPath = os.path.dirname(os.path.realpath(__file__)) 601 if not scriptPath.endswith('test'): 602 print("This script expects to reside in lldb's test directory.") 603 sys.exit(-1) 604 605 os.environ["LLDB_TEST"] = scriptPath 606 607 # Set up the LLDB_SRC environment variable, so that the tests can locate 608 # the LLDB source code. 609 os.environ["LLDB_SRC"] = lldbsuite.lldb_root 610 611 pluginPath = os.path.join(scriptPath, 'plugins') 612 toolsLLDBMIPath = os.path.join(scriptPath, 'tools', 'lldb-mi') 613 toolsLLDBServerPath = os.path.join(scriptPath, 'tools', 'lldb-server') 614 615 # Insert script dir, plugin dir, lldb-mi dir and lldb-server dir to the 616 # sys.path. 617 sys.path.insert(0, pluginPath) 618 # Adding test/tools/lldb-mi to the path makes it easy 619 sys.path.insert(0, toolsLLDBMIPath) 620 # to "import lldbmi_testcase" from the MI tests 621 # Adding test/tools/lldb-server to the path makes it easy 622 sys.path.insert(0, toolsLLDBServerPath) 623 # to "import lldbgdbserverutils" from the lldb-server tests 624 625 # This is the root of the lldb git/svn checkout 626 # When this changes over to a package instead of a standalone script, this 627 # will be `lldbsuite.lldb_root` 628 lldbRootDirectory = lldbsuite.lldb_root 629 630 # Some of the tests can invoke the 'lldb' command directly. 631 # We'll try to locate the appropriate executable right here. 632 633 # The lldb executable can be set from the command line 634 # if it's not set, we try to find it now 635 # first, we try the environment 636 if not lldbtest_config.lldbExec: 637 # First, you can define an environment variable LLDB_EXEC specifying the 638 # full pathname of the lldb executable. 639 if "LLDB_EXEC" in os.environ: 640 lldbtest_config.lldbExec = os.environ["LLDB_EXEC"] 641 642 if not lldbtest_config.lldbExec: 643 outputPaths = getOutputPaths(lldbRootDirectory) 644 for outputPath in outputPaths: 645 candidatePath = os.path.join(outputPath, 'lldb') 646 if is_exe(candidatePath): 647 lldbtest_config.lldbExec = candidatePath 648 break 649 650 if not lldbtest_config.lldbExec: 651 # Last, check the path 652 lldbtest_config.lldbExec = which('lldb') 653 654 if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec): 655 print( 656 "'{}' is not a path to a valid executable".format( 657 lldbtest_config.lldbExec)) 658 lldbtest_config.lldbExec = None 659 660 if not lldbtest_config.lldbExec: 661 print("The 'lldb' executable cannot be located. Some of the tests may not be run as a result.") 662 sys.exit(-1) 663 664 # confusingly, this is the "bin" directory 665 lldbLibDir = os.path.dirname(lldbtest_config.lldbExec) 666 os.environ["LLDB_LIB_DIR"] = lldbLibDir 667 lldbImpLibDir = os.path.join( 668 lldbLibDir, 669 '..', 670 'lib') if sys.platform.startswith('win32') else lldbLibDir 671 os.environ["LLDB_IMPLIB_DIR"] = lldbImpLibDir 672 print("LLDB library dir:", os.environ["LLDB_LIB_DIR"]) 673 print("LLDB import library dir:", os.environ["LLDB_IMPLIB_DIR"]) 674 os.system('%s -v' % lldbtest_config.lldbExec) 675 676 # Assume lldb-mi is in same place as lldb 677 # If not found, disable the lldb-mi tests 678 # TODO: Append .exe on Windows 679 # - this will be in a separate commit in case the mi tests fail horribly 680 lldbDir = os.path.dirname(lldbtest_config.lldbExec) 681 lldbMiExec = os.path.join(lldbDir, "lldb-mi") 682 if is_exe(lldbMiExec): 683 os.environ["LLDBMI_EXEC"] = lldbMiExec 684 else: 685 if not configuration.shouldSkipBecauseOfCategories(["lldb-mi"]): 686 print( 687 "The 'lldb-mi' executable cannot be located. The lldb-mi tests can not be run as a result.") 688 configuration.skipCategories.append("lldb-mi") 689 690 lldbPythonDir = None # The directory that contains 'lldb/__init__.py' 691 if not configuration.lldbFrameworkPath and os.path.exists(os.path.join(lldbLibDir, "LLDB.framework")): 692 configuration.lldbFrameworkPath = os.path.join(lldbLibDir, "LLDB.framework") 693 if configuration.lldbFrameworkPath: 694 lldbtest_config.lldbFrameworkPath = configuration.lldbFrameworkPath 695 candidatePath = os.path.join( 696 configuration.lldbFrameworkPath, 'Resources', 'Python') 697 if os.path.isfile(os.path.join(candidatePath, 'lldb/__init__.py')): 698 lldbPythonDir = candidatePath 699 if not lldbPythonDir: 700 print( 701 'Resources/Python/lldb/__init__.py was not found in ' + 702 configuration.lldbFrameworkPath) 703 sys.exit(-1) 704 else: 705 # If our lldb supports the -P option, use it to find the python path: 706 init_in_python_dir = os.path.join('lldb', '__init__.py') 707 708 lldb_dash_p_result = subprocess.check_output( 709 [lldbtest_config.lldbExec, "-P"], stderr=subprocess.STDOUT, universal_newlines=True) 710 711 if lldb_dash_p_result and not lldb_dash_p_result.startswith( 712 ("<", "lldb: invalid option:")) and not lldb_dash_p_result.startswith("Traceback"): 713 lines = lldb_dash_p_result.splitlines() 714 715 # Workaround for readline vs libedit issue on FreeBSD. If stdout 716 # is not a terminal Python executes 717 # rl_variable_bind ("enable-meta-key", "off"); 718 # This produces a warning with FreeBSD's libedit because the 719 # enable-meta-key variable is unknown. Not an issue on Apple 720 # because cpython commit f0ab6f9f0603 added a #ifndef __APPLE__ 721 # around the call. See http://bugs.python.org/issue19884 for more 722 # information. For now we just discard the warning output. 723 if len(lines) >= 1 and lines[0].startswith( 724 "bind: Invalid command"): 725 lines.pop(0) 726 727 # Taking the last line because lldb outputs 728 # 'Cannot read termcap database;\nusing dumb terminal settings.\n' 729 # before the path 730 if len(lines) >= 1 and os.path.isfile( 731 os.path.join(lines[-1], init_in_python_dir)): 732 lldbPythonDir = lines[-1] 733 if "freebsd" in sys.platform or "linux" in sys.platform: 734 os.environ['LLDB_LIB_DIR'] = os.path.join( 735 lldbPythonDir, '..', '..') 736 737 if not lldbPythonDir: 738 if platform.system() == "Darwin": 739 python_resource_dir = ['LLDB.framework', 'Resources', 'Python'] 740 outputPaths = getXcodeOutputPaths(lldbRootDirectory) 741 for outputPath in outputPaths: 742 candidatePath = os.path.join( 743 outputPath, *python_resource_dir) 744 if os.path.isfile( 745 os.path.join( 746 candidatePath, 747 init_in_python_dir)): 748 lldbPythonDir = candidatePath 749 break 750 751 if not lldbPythonDir: 752 print("lldb.py is not found, some tests may fail.") 753 else: 754 print( 755 "Unable to load lldb extension module. Possible reasons for this include:") 756 print(" 1) LLDB was built with LLDB_DISABLE_PYTHON=1") 757 print( 758 " 2) PYTHONPATH and PYTHONHOME are not set correctly. PYTHONHOME should refer to") 759 print( 760 " the version of Python that LLDB built and linked against, and PYTHONPATH") 761 print( 762 " should contain the Lib directory for the same python distro, as well as the") 763 print(" location of LLDB\'s site-packages folder.") 764 print( 765 " 3) A different version of Python than that which was built against is exported in") 766 print(" the system\'s PATH environment variable, causing conflicts.") 767 print( 768 " 4) The executable '%s' could not be found. Please check " % 769 lldbtest_config.lldbExec) 770 print(" that it exists and is executable.") 771 772 if lldbPythonDir: 773 lldbPythonDir = os.path.normpath(lldbPythonDir) 774 # Some of the code that uses this path assumes it hasn't resolved the Versions... link. 775 # If the path we've constructed looks like that, then we'll strip out 776 # the Versions/A part. 777 (before, frameWithVersion, after) = lldbPythonDir.rpartition( 778 "LLDB.framework/Versions/A") 779 if frameWithVersion != "": 780 lldbPythonDir = before + "LLDB.framework" + after 781 782 lldbPythonDir = os.path.abspath(lldbPythonDir) 783 784 # If tests need to find LLDB_FRAMEWORK, now they can do it 785 os.environ["LLDB_FRAMEWORK"] = os.path.dirname( 786 os.path.dirname(lldbPythonDir)) 787 788 # This is to locate the lldb.py module. Insert it right after 789 # sys.path[0]. 790 sys.path[1:1] = [lldbPythonDir] 791 792 793def visit_file(dir, name): 794 # Try to match the regexp pattern, if specified. 795 if configuration.regexp: 796 if not re.search(configuration.regexp, name): 797 # We didn't match the regex, we're done. 798 return 799 800 if configuration.skip_tests: 801 for file_regexp in configuration.skip_tests: 802 if re.search(file_regexp, name): 803 return 804 805 # We found a match for our test. Add it to the suite. 806 807 # Update the sys.path first. 808 if not sys.path.count(dir): 809 sys.path.insert(0, dir) 810 base = os.path.splitext(name)[0] 811 812 # Thoroughly check the filterspec against the base module and admit 813 # the (base, filterspec) combination only when it makes sense. 814 filterspec = None 815 for filterspec in configuration.filters: 816 # Optimistically set the flag to True. 817 filtered = True 818 module = __import__(base) 819 parts = filterspec.split('.') 820 obj = module 821 for part in parts: 822 try: 823 parent, obj = obj, getattr(obj, part) 824 except AttributeError: 825 # The filterspec has failed. 826 filtered = False 827 break 828 829 # If filtered, we have a good filterspec. Add it. 830 if filtered: 831 # print("adding filter spec %s to module %s" % (filterspec, module)) 832 configuration.suite.addTests( 833 unittest2.defaultTestLoader.loadTestsFromName( 834 filterspec, module)) 835 continue 836 837 # Forgo this module if the (base, filterspec) combo is invalid 838 if configuration.filters and not filtered: 839 return 840 841 if not filterspec or not filtered: 842 # Add the entire file's worth of tests since we're not filtered. 843 # Also the fail-over case when the filterspec branch 844 # (base, filterspec) combo doesn't make sense. 845 configuration.suite.addTests( 846 unittest2.defaultTestLoader.loadTestsFromName(base)) 847 848 849def visit(prefix, dir, names): 850 """Visitor function for os.path.walk(path, visit, arg).""" 851 852 dir_components = set(dir.split(os.sep)) 853 excluded_components = set(['.svn', '.git']) 854 if dir_components.intersection(excluded_components): 855 return 856 857 # Gather all the Python test file names that follow the Test*.py pattern. 858 python_test_files = [ 859 name 860 for name in names 861 if name.endswith('.py') and name.startswith(prefix)] 862 863 # Visit all the python test files. 864 for name in python_test_files: 865 try: 866 # Ensure we error out if we have multiple tests with the same 867 # base name. 868 # Future improvement: find all the places where we work with base 869 # names and convert to full paths. We have directory structure 870 # to disambiguate these, so we shouldn't need this constraint. 871 if name in configuration.all_tests: 872 raise Exception("Found multiple tests with the name %s" % name) 873 configuration.all_tests.add(name) 874 875 # Run the relevant tests in the python file. 876 visit_file(dir, name) 877 except Exception as ex: 878 # Convert this exception to a test event error for the file. 879 test_filename = os.path.abspath(os.path.join(dir, name)) 880 if configuration.results_formatter_object is not None: 881 # Grab the backtrace for the exception. 882 import traceback 883 backtrace = traceback.format_exc() 884 885 # Generate the test event. 886 configuration.results_formatter_object.handle_event( 887 EventBuilder.event_for_job_test_add_error( 888 test_filename, ex, backtrace)) 889 raise 890 891 892def disabledynamics(): 893 import lldb 894 ci = lldb.DBG.GetCommandInterpreter() 895 res = lldb.SBCommandReturnObject() 896 ci.HandleCommand( 897 "setting set target.prefer-dynamic-value no-dynamic-values", 898 res, 899 False) 900 if not res.Succeeded(): 901 raise Exception('disabling dynamic type support failed') 902 903 904def lldbLoggings(): 905 import lldb 906 """Check and do lldb loggings if necessary.""" 907 908 # Turn on logging for debugging purposes if ${LLDB_LOG} environment variable is 909 # defined. Use ${LLDB_LOG} to specify the log file. 910 ci = lldb.DBG.GetCommandInterpreter() 911 res = lldb.SBCommandReturnObject() 912 if ("LLDB_LOG" in os.environ): 913 open(os.environ["LLDB_LOG"], 'w').close() 914 if ("LLDB_LOG_OPTION" in os.environ): 915 lldb_log_option = os.environ["LLDB_LOG_OPTION"] 916 else: 917 lldb_log_option = "event process expr state api" 918 ci.HandleCommand( 919 "log enable -n -f " + 920 os.environ["LLDB_LOG"] + 921 " lldb " + 922 lldb_log_option, 923 res) 924 if not res.Succeeded(): 925 raise Exception('log enable failed (check LLDB_LOG env variable)') 926 927 if ("LLDB_LINUX_LOG" in os.environ): 928 open(os.environ["LLDB_LINUX_LOG"], 'w').close() 929 if ("LLDB_LINUX_LOG_OPTION" in os.environ): 930 lldb_log_option = os.environ["LLDB_LINUX_LOG_OPTION"] 931 else: 932 lldb_log_option = "event process expr state api" 933 ci.HandleCommand( 934 "log enable -n -f " + 935 os.environ["LLDB_LINUX_LOG"] + 936 " linux " + 937 lldb_log_option, 938 res) 939 if not res.Succeeded(): 940 raise Exception( 941 'log enable failed (check LLDB_LINUX_LOG env variable)') 942 943 # Ditto for gdb-remote logging if ${GDB_REMOTE_LOG} environment variable is defined. 944 # Use ${GDB_REMOTE_LOG} to specify the log file. 945 if ("GDB_REMOTE_LOG" in os.environ): 946 if ("GDB_REMOTE_LOG_OPTION" in os.environ): 947 gdb_remote_log_option = os.environ["GDB_REMOTE_LOG_OPTION"] 948 else: 949 gdb_remote_log_option = "packets process" 950 ci.HandleCommand( 951 "log enable -n -f " + os.environ["GDB_REMOTE_LOG"] + " gdb-remote " 952 + gdb_remote_log_option, 953 res) 954 if not res.Succeeded(): 955 raise Exception( 956 'log enable failed (check GDB_REMOTE_LOG env variable)') 957 958 959def getMyCommandLine(): 960 return ' '.join(sys.argv) 961 962# ======================================== # 963# # 964# Execution of the test driver starts here # 965# # 966# ======================================== # 967 968 969def checkDsymForUUIDIsNotOn(): 970 cmd = ["defaults", "read", "com.apple.DebugSymbols"] 971 pipe = subprocess.Popen( 972 cmd, 973 stdout=subprocess.PIPE, 974 stderr=subprocess.STDOUT) 975 cmd_output = pipe.stdout.read() 976 if cmd_output and "DBGFileMappedPaths = " in cmd_output: 977 print("%s =>" % ' '.join(cmd)) 978 print(cmd_output) 979 print( 980 "Disable automatic lookup and caching of dSYMs before running the test suite!") 981 print("Exiting...") 982 sys.exit(0) 983 984 985def exitTestSuite(exitCode=None): 986 import lldb 987 lldb.SBDebugger.Terminate() 988 if exitCode: 989 sys.exit(exitCode) 990 991 992def isMultiprocessTestRunner(): 993 # We're not multiprocess when we're either explicitly 994 # the inferior (as specified by the multiprocess test 995 # runner) OR we've been told to skip using the multiprocess 996 # test runner 997 return not ( 998 configuration.is_inferior_test_runner or configuration.no_multiprocess_test_runner) 999 1000 1001def getVersionForSDK(sdk): 1002 sdk = str.lower(sdk) 1003 full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk) 1004 basename = os.path.basename(full_path) 1005 basename = os.path.splitext(basename)[0] 1006 basename = str.lower(basename) 1007 ver = basename.replace(sdk, '') 1008 return ver 1009 1010 1011def getPathForSDK(sdk): 1012 sdk = str.lower(sdk) 1013 full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk) 1014 if os.path.exists(full_path): 1015 return full_path 1016 return None 1017 1018 1019def setDefaultTripleForPlatform(): 1020 if configuration.lldb_platform_name == 'ios-simulator': 1021 triple_str = 'x86_64-apple-ios%s' % ( 1022 getVersionForSDK('iphonesimulator')) 1023 os.environ['TRIPLE'] = triple_str 1024 return {'TRIPLE': triple_str} 1025 return {} 1026 1027 1028def run_suite(): 1029 # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults 1030 # does not exist before proceeding to running the test suite. 1031 if sys.platform.startswith("darwin"): 1032 checkDsymForUUIDIsNotOn() 1033 1034 # 1035 # Start the actions by first parsing the options while setting up the test 1036 # directories, followed by setting up the search paths for lldb utilities; 1037 # then, we walk the directory trees and collect the tests into our test suite. 1038 # 1039 parseOptionsAndInitTestdirs() 1040 1041 # Setup test results (test results formatter and output handling). 1042 setupTestResults() 1043 1044 # If we are running as the multiprocess test runner, kick off the 1045 # multiprocess test runner here. 1046 if isMultiprocessTestRunner(): 1047 from . import dosep 1048 dosep.main( 1049 configuration.num_threads, 1050 configuration.multiprocess_test_subdir, 1051 configuration.test_runner_name, 1052 configuration.results_formatter_object) 1053 raise Exception("should never get here") 1054 elif configuration.is_inferior_test_runner: 1055 # Shut off Ctrl-C processing in inferiors. The parallel 1056 # test runner handles this more holistically. 1057 signal.signal(signal.SIGINT, signal.SIG_IGN) 1058 1059 setupSysPath() 1060 configuration.setupCrashInfoHook() 1061 1062 # 1063 # If '-l' is specified, do not skip the long running tests. 1064 if not configuration.skip_long_running_test: 1065 os.environ["LLDB_SKIP_LONG_RUNNING_TEST"] = "NO" 1066 1067 # For the time being, let's bracket the test runner within the 1068 # lldb.SBDebugger.Initialize()/Terminate() pair. 1069 import lldb 1070 1071 # Create a singleton SBDebugger in the lldb namespace. 1072 lldb.DBG = lldb.SBDebugger.Create() 1073 1074 if configuration.lldb_platform_name: 1075 print("Setting up remote platform '%s'" % 1076 (configuration.lldb_platform_name)) 1077 lldb.remote_platform = lldb.SBPlatform( 1078 configuration.lldb_platform_name) 1079 if not lldb.remote_platform.IsValid(): 1080 print( 1081 "error: unable to create the LLDB platform named '%s'." % 1082 (configuration.lldb_platform_name)) 1083 exitTestSuite(1) 1084 if configuration.lldb_platform_url: 1085 # We must connect to a remote platform if a LLDB platform URL was 1086 # specified 1087 print( 1088 "Connecting to remote platform '%s' at '%s'..." % 1089 (configuration.lldb_platform_name, configuration.lldb_platform_url)) 1090 platform_connect_options = lldb.SBPlatformConnectOptions( 1091 configuration.lldb_platform_url) 1092 err = lldb.remote_platform.ConnectRemote(platform_connect_options) 1093 if err.Success(): 1094 print("Connected.") 1095 else: 1096 print("error: failed to connect to remote platform using URL '%s': %s" % ( 1097 configuration.lldb_platform_url, err)) 1098 exitTestSuite(1) 1099 else: 1100 configuration.lldb_platform_url = None 1101 1102 platform_changes = setDefaultTripleForPlatform() 1103 first = True 1104 for key in platform_changes: 1105 if first: 1106 print("Environment variables setup for platform support:") 1107 first = False 1108 print("%s = %s" % (key, platform_changes[key])) 1109 1110 if configuration.lldb_platform_working_dir: 1111 print("Setting remote platform working directory to '%s'..." % 1112 (configuration.lldb_platform_working_dir)) 1113 lldb.remote_platform.SetWorkingDirectory( 1114 configuration.lldb_platform_working_dir) 1115 lldb.DBG.SetSelectedPlatform(lldb.remote_platform) 1116 else: 1117 lldb.remote_platform = None 1118 configuration.lldb_platform_working_dir = None 1119 configuration.lldb_platform_url = None 1120 1121 target_platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2] 1122 1123 # Don't do debugserver tests on everything except OS X. 1124 configuration.dont_do_debugserver_test = "linux" in target_platform or "freebsd" in target_platform or "windows" in target_platform 1125 1126 # Don't do lldb-server (llgs) tests on anything except Linux. 1127 configuration.dont_do_llgs_test = not ("linux" in target_platform) 1128 1129 # 1130 # Walk through the testdirs while collecting tests. 1131 # 1132 for testdir in configuration.testdirs: 1133 for (dirpath, dirnames, filenames) in os.walk(testdir): 1134 visit('Test', dirpath, filenames) 1135 1136 # 1137 # Now that we have loaded all the test cases, run the whole test suite. 1138 # 1139 1140 # Turn on lldb loggings if necessary. 1141 lldbLoggings() 1142 1143 # Disable default dynamic types for testing purposes 1144 disabledynamics() 1145 1146 # Install the control-c handler. 1147 unittest2.signals.installHandler() 1148 1149 # If sdir_name is not specified through the '-s sdir_name' option, get a 1150 # timestamp string and export it as LLDB_SESSION_DIR environment var. This will 1151 # be used when/if we want to dump the session info of individual test cases 1152 # later on. 1153 # 1154 # See also TestBase.dumpSessionInfo() in lldbtest.py. 1155 import datetime 1156 # The windows platforms don't like ':' in the pathname. 1157 timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S") 1158 if not configuration.sdir_name: 1159 configuration.sdir_name = timestamp_started 1160 os.environ["LLDB_SESSION_DIRNAME"] = os.path.join( 1161 os.getcwd(), configuration.sdir_name) 1162 1163 sys.stderr.write( 1164 "\nSession logs for test failures/errors/unexpected successes" 1165 " will go into directory '%s'\n" % 1166 configuration.sdir_name) 1167 sys.stderr.write("Command invoked: %s\n" % getMyCommandLine()) 1168 1169 if not os.path.isdir(configuration.sdir_name): 1170 try: 1171 os.mkdir(configuration.sdir_name) 1172 except OSError as exception: 1173 if exception.errno != errno.EEXIST: 1174 raise 1175 1176 # 1177 # Invoke the default TextTestRunner to run the test suite, possibly iterating 1178 # over different configurations. 1179 # 1180 1181 iterArchs = False 1182 iterCompilers = False 1183 1184 if isinstance(configuration.archs, list) and len(configuration.archs) >= 1: 1185 iterArchs = True 1186 1187 # 1188 # Add some intervention here to sanity check that the compilers requested are sane. 1189 # If found not to be an executable program, the invalid one is dropped 1190 # from the list. 1191 for i in range(len(configuration.compilers)): 1192 c = configuration.compilers[i] 1193 if which(c): 1194 continue 1195 else: 1196 if sys.platform.startswith("darwin"): 1197 pipe = subprocess.Popen( 1198 ['xcrun', '-find', c], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 1199 cmd_output = pipe.stdout.read() 1200 if cmd_output: 1201 if "not found" in cmd_output: 1202 print("dropping %s from the compilers used" % c) 1203 configuration.compilers.remove(i) 1204 else: 1205 configuration.compilers[i] = cmd_output.split('\n')[0] 1206 print( 1207 "'xcrun -find %s' returning %s" % 1208 (c, configuration.compilers[i])) 1209 1210 if not configuration.parsable: 1211 print("compilers=%s" % str(configuration.compilers)) 1212 1213 if not configuration.compilers or len(configuration.compilers) == 0: 1214 print("No eligible compiler found, exiting.") 1215 exitTestSuite(1) 1216 1217 if isinstance( 1218 configuration.compilers, 1219 list) and len( 1220 configuration.compilers) >= 1: 1221 iterCompilers = True 1222 1223 # If we iterate on archs or compilers, there is a chance we want to split 1224 # stderr/stdout. 1225 if iterArchs or iterCompilers: 1226 old_stderr = sys.stderr 1227 old_stdout = sys.stdout 1228 new_stderr = None 1229 new_stdout = None 1230 1231 # Iterating over all possible architecture and compiler combinations. 1232 for ia in range(len(configuration.archs) if iterArchs else 1): 1233 archConfig = "" 1234 if iterArchs: 1235 os.environ["ARCH"] = configuration.archs[ia] 1236 archConfig = "arch=%s" % configuration.archs[ia] 1237 for ic in range(len(configuration.compilers) if iterCompilers else 1): 1238 if iterCompilers: 1239 os.environ["CC"] = configuration.compilers[ic] 1240 configString = "%s compiler=%s" % ( 1241 archConfig, configuration.compilers[ic]) 1242 else: 1243 configString = archConfig 1244 1245 if iterArchs or iterCompilers: 1246 # Translate ' ' to '-' for pathname component. 1247 if six.PY2: 1248 import string 1249 tbl = string.maketrans(' ', '-') 1250 else: 1251 tbl = str.maketrans(' ', '-') 1252 configPostfix = configString.translate(tbl) 1253 1254 # Output the configuration. 1255 if not configuration.parsable: 1256 sys.stderr.write("\nConfiguration: " + configString + "\n") 1257 1258 #print("sys.stderr name is", sys.stderr.name) 1259 #print("sys.stdout name is", sys.stdout.name) 1260 1261 # First, write out the number of collected test cases. 1262 if not configuration.parsable: 1263 sys.stderr.write(configuration.separator + "\n") 1264 sys.stderr.write( 1265 "Collected %d test%s\n\n" % 1266 (configuration.suite.countTestCases(), 1267 configuration.suite.countTestCases() != 1 and "s" or "")) 1268 1269 if configuration.parsable: 1270 v = 0 1271 else: 1272 v = configuration.verbose 1273 1274 # Invoke the test runner. 1275 if configuration.count == 1: 1276 result = unittest2.TextTestRunner( 1277 stream=sys.stderr, 1278 verbosity=v, 1279 resultclass=test_result.LLDBTestResult).run( 1280 configuration.suite) 1281 else: 1282 # We are invoking the same test suite more than once. In this case, 1283 # mark __ignore_singleton__ flag as True so the signleton pattern is 1284 # not enforced. 1285 test_result.LLDBTestResult.__ignore_singleton__ = True 1286 for i in range(configuration.count): 1287 1288 result = unittest2.TextTestRunner( 1289 stream=sys.stderr, 1290 verbosity=v, 1291 resultclass=test_result.LLDBTestResult).run( 1292 configuration.suite) 1293 1294 configuration.failed = configuration.failed or not result.wasSuccessful() 1295 1296 if configuration.sdir_has_content and not configuration.parsable: 1297 sys.stderr.write( 1298 "Session logs for test failures/errors/unexpected successes" 1299 " can be found in directory '%s'\n" % 1300 configuration.sdir_name) 1301 1302 if configuration.useCategories and len( 1303 configuration.failuresPerCategory) > 0: 1304 sys.stderr.write("Failures per category:\n") 1305 for category in configuration.failuresPerCategory: 1306 sys.stderr.write( 1307 "%s - %d\n" % 1308 (category, configuration.failuresPerCategory[category])) 1309 1310 # Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined. 1311 # This should not be necessary now. 1312 if ("LLDB_TESTSUITE_FORCE_FINISH" in os.environ): 1313 print("Terminating Test suite...") 1314 subprocess.Popen(["/bin/sh", "-c", "kill %s; exit 0" % (os.getpid())]) 1315 1316 # Exiting. 1317 exitTestSuite(configuration.failed) 1318 1319if __name__ == "__main__": 1320 print( 1321 __file__ + 1322 " is for use as a module only. It should not be run as a standalone script.") 1323 sys.exit(-1) 1324