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