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