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