1""" 2LLDB module which provides the abstract base class of lldb test case. 3 4The concrete subclass can override lldbtest.TestBase in order to inherit the 5common behavior for unitest.TestCase.setUp/tearDown implemented in this file. 6 7./dotest.py provides a test driver which sets up the environment to run the 8entire of part of the test suite . Example: 9 10# Exercises the test suite in the types directory.... 11/Volumes/data/lldb/svn/ToT/test $ ./dotest.py -A x86_64 types 12... 13 14Session logs for test failures/errors/unexpected successes will go into directory '2012-05-16-13_35_42' 15Command invoked: python ./dotest.py -A x86_64 types 16compilers=['clang'] 17 18Configuration: arch=x86_64 compiler=clang 19---------------------------------------------------------------------- 20Collected 72 tests 21 22........................................................................ 23---------------------------------------------------------------------- 24Ran 72 tests in 135.468s 25 26OK 27$ 28""" 29 30# System modules 31import abc 32from functools import wraps 33import gc 34import glob 35import io 36import json 37import os.path 38import re 39import shutil 40import signal 41from subprocess import * 42import sys 43import time 44import traceback 45 46# Third-party modules 47import unittest2 48 49# LLDB modules 50import lldb 51from . import configuration 52from . import decorators 53from . import lldbplatformutil 54from . import lldbtest_config 55from . import lldbutil 56from . import test_categories 57from lldbsuite.support import encoded_file 58from lldbsuite.support import funcutils 59from lldbsuite.support import seven 60from lldbsuite.test_event import build_exception 61 62# See also dotest.parseOptionsAndInitTestdirs(), where the environment variables 63# LLDB_COMMAND_TRACE is set from '-t' option. 64 65# By default, traceAlways is False. 66if "LLDB_COMMAND_TRACE" in os.environ and os.environ["LLDB_COMMAND_TRACE"] == "YES": 67 traceAlways = True 68else: 69 traceAlways = False 70 71# By default, doCleanup is True. 72if "LLDB_DO_CLEANUP" in os.environ and os.environ["LLDB_DO_CLEANUP"] == "NO": 73 doCleanup = False 74else: 75 doCleanup = True 76 77 78# 79# Some commonly used assert messages. 80# 81 82COMMAND_FAILED_AS_EXPECTED = "Command has failed as expected" 83 84CURRENT_EXECUTABLE_SET = "Current executable set successfully" 85 86PROCESS_IS_VALID = "Process is valid" 87 88PROCESS_KILLED = "Process is killed successfully" 89 90PROCESS_EXITED = "Process exited successfully" 91 92PROCESS_STOPPED = "Process status should be stopped" 93 94RUN_SUCCEEDED = "Process is launched successfully" 95 96RUN_COMPLETED = "Process exited successfully" 97 98BACKTRACE_DISPLAYED_CORRECTLY = "Backtrace displayed correctly" 99 100BREAKPOINT_CREATED = "Breakpoint created successfully" 101 102BREAKPOINT_STATE_CORRECT = "Breakpoint state is correct" 103 104BREAKPOINT_PENDING_CREATED = "Pending breakpoint created successfully" 105 106BREAKPOINT_HIT_ONCE = "Breakpoint resolved with hit count = 1" 107 108BREAKPOINT_HIT_TWICE = "Breakpoint resolved with hit count = 2" 109 110BREAKPOINT_HIT_THRICE = "Breakpoint resolved with hit count = 3" 111 112MISSING_EXPECTED_REGISTERS = "At least one expected register is unavailable." 113 114OBJECT_PRINTED_CORRECTLY = "Object printed correctly" 115 116SOURCE_DISPLAYED_CORRECTLY = "Source code displayed correctly" 117 118STEP_IN_SUCCEEDED = "Thread step-in succeeded" 119 120STEP_OUT_SUCCEEDED = "Thread step-out succeeded" 121 122STOPPED_DUE_TO_EXC_BAD_ACCESS = "Process should be stopped due to bad access exception" 123 124STOPPED_DUE_TO_ASSERT = "Process should be stopped due to an assertion" 125 126STOPPED_DUE_TO_BREAKPOINT = "Process should be stopped due to breakpoint" 127 128STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS = "%s, %s" % ( 129 STOPPED_DUE_TO_BREAKPOINT, 130 "instead, the actual stop reason is: '%s'", 131) 132 133STOPPED_DUE_TO_BREAKPOINT_CONDITION = "Stopped due to breakpoint condition" 134 135STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT = "Stopped due to breakpoint and ignore count" 136 137STOPPED_DUE_TO_BREAKPOINT_JITTED_CONDITION = ( 138 "Stopped due to breakpoint jitted condition" 139) 140 141STOPPED_DUE_TO_SIGNAL = "Process state is stopped due to signal" 142 143STOPPED_DUE_TO_STEP_IN = "Process state is stopped due to step in" 144 145STOPPED_DUE_TO_WATCHPOINT = "Process should be stopped due to watchpoint" 146 147DATA_TYPES_DISPLAYED_CORRECTLY = "Data type(s) displayed correctly" 148 149VALID_BREAKPOINT = "Got a valid breakpoint" 150 151VALID_BREAKPOINT_LOCATION = "Got a valid breakpoint location" 152 153VALID_COMMAND_INTERPRETER = "Got a valid command interpreter" 154 155VALID_FILESPEC = "Got a valid filespec" 156 157VALID_MODULE = "Got a valid module" 158 159VALID_PROCESS = "Got a valid process" 160 161VALID_SYMBOL = "Got a valid symbol" 162 163VALID_TARGET = "Got a valid target" 164 165VALID_PLATFORM = "Got a valid platform" 166 167VALID_TYPE = "Got a valid type" 168 169VALID_VARIABLE = "Got a valid variable" 170 171VARIABLES_DISPLAYED_CORRECTLY = "Variable(s) displayed correctly" 172 173WATCHPOINT_CREATED = "Watchpoint created successfully" 174 175 176def CMD_MSG(str): 177 """A generic "Command '%s' did not return successfully" message generator.""" 178 return "Command '%s' did not return successfully" % str 179 180 181def COMPLETION_MSG(str_before, str_after, completions): 182 """A generic assertion failed message generator for the completion mechanism.""" 183 return "'%s' successfully completes to '%s', but completions were:\n%s" % ( 184 str_before, 185 str_after, 186 "\n".join(completions), 187 ) 188 189 190def EXP_MSG(str, actual, exe): 191 """A generic "'%s' returned unexpected result" message generator if exe. 192 Otherwise, it generates "'%s' does not match expected result" message.""" 193 194 return "'%s' %s result, got '%s'" % ( 195 str, 196 "returned unexpected" if exe else "does not match expected", 197 actual.strip(), 198 ) 199 200 201def SETTING_MSG(setting): 202 """A generic "Value of setting '%s' is not correct" message generator.""" 203 return "Value of setting '%s' is not correct" % setting 204 205 206def line_number(filename, string_to_match): 207 """Helper function to return the line number of the first matched string.""" 208 with io.open(filename, mode="r", encoding="utf-8") as f: 209 for i, line in enumerate(f): 210 if line.find(string_to_match) != -1: 211 # Found our match. 212 return i + 1 213 raise Exception("Unable to find '%s' within file %s" % (string_to_match, filename)) 214 215 216def get_line(filename, line_number): 217 """Return the text of the line at the 1-based line number.""" 218 with io.open(filename, mode="r", encoding="utf-8") as f: 219 return f.readlines()[line_number - 1] 220 221 222def pointer_size(): 223 """Return the pointer size of the host system.""" 224 import ctypes 225 226 a_pointer = ctypes.c_void_p(0xFFFF) 227 return 8 * ctypes.sizeof(a_pointer) 228 229 230def is_exe(fpath): 231 """Returns true if fpath is an executable.""" 232 if fpath == None: 233 return False 234 if sys.platform == "win32": 235 if not fpath.endswith(".exe"): 236 fpath += ".exe" 237 return os.path.isfile(fpath) and os.access(fpath, os.X_OK) 238 239 240def which(program): 241 """Returns the full path to a program; None otherwise.""" 242 fpath, fname = os.path.split(program) 243 if fpath: 244 if is_exe(program): 245 return program 246 else: 247 for path in os.environ["PATH"].split(os.pathsep): 248 exe_file = os.path.join(path, program) 249 if is_exe(exe_file): 250 return exe_file 251 return None 252 253 254class ValueCheck: 255 def __init__( 256 self, 257 name=None, 258 value=None, 259 type=None, 260 summary=None, 261 children=None, 262 dereference=None, 263 ): 264 """ 265 :param name: The name that the SBValue should have. None if the summary 266 should not be checked. 267 :param summary: The summary that the SBValue should have. None if the 268 summary should not be checked. 269 :param value: The value that the SBValue should have. None if the value 270 should not be checked. 271 :param type: The type that the SBValue result should have. None if the 272 type should not be checked. 273 :param children: A list of ValueChecks that need to match the children 274 of this SBValue. None if children shouldn't be checked. 275 The order of checks is the order of the checks in the 276 list. The number of checks has to match the number of 277 children. 278 :param dereference: A ValueCheck for the SBValue returned by the 279 `Dereference` function. 280 """ 281 self.expect_name = name 282 self.expect_value = value 283 self.expect_type = type 284 self.expect_summary = summary 285 self.children = children 286 self.dereference = dereference 287 288 def check_value(self, test_base, val, error_msg=None): 289 """ 290 Checks that the given value matches the currently set properties 291 of this ValueCheck. If a match failed, the given TestBase will 292 be used to emit an error. A custom error message can be specified 293 that will be used to describe failed check for this SBValue (but 294 not errors in the child values). 295 """ 296 297 this_error_msg = error_msg if error_msg else "" 298 this_error_msg += "\nChecking SBValue: " + str(val) 299 300 test_base.assertSuccess(val.GetError()) 301 302 # Python 3.6 doesn't declare a `re.Pattern` type, get the dynamic type. 303 pattern_type = type(re.compile("")) 304 305 if self.expect_name: 306 test_base.assertEqual(self.expect_name, val.GetName(), this_error_msg) 307 if self.expect_value: 308 if isinstance(self.expect_value, pattern_type): 309 test_base.assertRegex(val.GetValue(), self.expect_value, this_error_msg) 310 else: 311 test_base.assertEqual(self.expect_value, val.GetValue(), this_error_msg) 312 if self.expect_type: 313 test_base.assertEqual( 314 self.expect_type, val.GetDisplayTypeName(), this_error_msg 315 ) 316 if self.expect_summary: 317 if isinstance(self.expect_summary, pattern_type): 318 test_base.assertRegex( 319 val.GetSummary(), self.expect_summary, this_error_msg 320 ) 321 else: 322 test_base.assertEqual( 323 self.expect_summary, val.GetSummary(), this_error_msg 324 ) 325 if self.children is not None: 326 self.check_value_children(test_base, val, error_msg) 327 328 if self.dereference is not None: 329 self.dereference.check_value(test_base, val.Dereference(), error_msg) 330 331 def check_value_children(self, test_base, val, error_msg=None): 332 """ 333 Checks that the children of a SBValue match a certain structure and 334 have certain properties. 335 336 :param test_base: The current test's TestBase object. 337 :param val: The SBValue to check. 338 """ 339 340 this_error_msg = error_msg if error_msg else "" 341 this_error_msg += "\nChecking SBValue: " + str(val) 342 343 test_base.assertEqual(len(self.children), val.GetNumChildren(), this_error_msg) 344 345 for i in range(0, val.GetNumChildren()): 346 expected_child = self.children[i] 347 actual_child = val.GetChildAtIndex(i) 348 child_error = "Checking child with index " + str(i) + ":\n" + error_msg 349 expected_child.check_value(test_base, actual_child, child_error) 350 351 352class recording(io.StringIO): 353 """ 354 A nice little context manager for recording the debugger interactions into 355 our session object. If trace flag is ON, it also emits the interactions 356 into the stderr. 357 """ 358 359 def __init__(self, test, trace): 360 """Create a io.StringIO instance; record the session obj and trace flag.""" 361 io.StringIO.__init__(self) 362 # The test might not have undergone the 'setUp(self)' phase yet, so that 363 # the attribute 'session' might not even exist yet. 364 self.session = getattr(test, "session", None) if test else None 365 self.trace = trace 366 367 def __enter__(self): 368 """ 369 Context management protocol on entry to the body of the with statement. 370 Just return the io.StringIO object. 371 """ 372 return self 373 374 def __exit__(self, type, value, tb): 375 """ 376 Context management protocol on exit from the body of the with statement. 377 If trace is ON, it emits the recordings into stderr. Always add the 378 recordings to our session object. And close the io.StringIO object, too. 379 """ 380 if self.trace: 381 print(self.getvalue(), file=sys.stderr) 382 if self.session: 383 print(self.getvalue(), file=self.session) 384 self.close() 385 386 387class _BaseProcess(object, metaclass=abc.ABCMeta): 388 @abc.abstractproperty 389 def pid(self): 390 """Returns process PID if has been launched already.""" 391 392 @abc.abstractmethod 393 def launch(self, executable, args, extra_env): 394 """Launches new process with given executable and args.""" 395 396 @abc.abstractmethod 397 def terminate(self): 398 """Terminates previously launched process..""" 399 400 401class _LocalProcess(_BaseProcess): 402 def __init__(self, trace_on): 403 self._proc = None 404 self._trace_on = trace_on 405 self._delayafterterminate = 0.1 406 407 @property 408 def pid(self): 409 return self._proc.pid 410 411 def launch(self, executable, args, extra_env): 412 env = None 413 if extra_env: 414 env = dict(os.environ) 415 env.update([kv.split("=", 1) for kv in extra_env]) 416 417 self._proc = Popen( 418 [executable] + args, 419 stdout=open(os.devnull) if not self._trace_on else None, 420 stdin=PIPE, 421 env=env, 422 ) 423 424 def terminate(self): 425 if self._proc.poll() is None: 426 # Terminate _proc like it does the pexpect 427 signals_to_try = [ 428 sig for sig in ["SIGHUP", "SIGCONT", "SIGINT"] if sig in dir(signal) 429 ] 430 for sig in signals_to_try: 431 try: 432 self._proc.send_signal(getattr(signal, sig)) 433 time.sleep(self._delayafterterminate) 434 if self._proc.poll() is not None: 435 return 436 except ValueError: 437 pass # Windows says SIGINT is not a valid signal to send 438 self._proc.terminate() 439 time.sleep(self._delayafterterminate) 440 if self._proc.poll() is not None: 441 return 442 self._proc.kill() 443 time.sleep(self._delayafterterminate) 444 445 def poll(self): 446 return self._proc.poll() 447 448 def wait(self, timeout=None): 449 return self._proc.wait(timeout) 450 451 452class _RemoteProcess(_BaseProcess): 453 def __init__(self, install_remote): 454 self._pid = None 455 self._install_remote = install_remote 456 457 @property 458 def pid(self): 459 return self._pid 460 461 def launch(self, executable, args, extra_env): 462 if self._install_remote: 463 src_path = executable 464 dst_path = lldbutil.join_remote_paths( 465 lldb.remote_platform.GetWorkingDirectory(), os.path.basename(executable) 466 ) 467 468 dst_file_spec = lldb.SBFileSpec(dst_path, False) 469 err = lldb.remote_platform.Install( 470 lldb.SBFileSpec(src_path, True), dst_file_spec 471 ) 472 if err.Fail(): 473 raise Exception( 474 "remote_platform.Install('%s', '%s') failed: %s" 475 % (src_path, dst_path, err) 476 ) 477 else: 478 dst_path = executable 479 dst_file_spec = lldb.SBFileSpec(executable, False) 480 481 launch_info = lldb.SBLaunchInfo(args) 482 launch_info.SetExecutableFile(dst_file_spec, True) 483 launch_info.SetWorkingDirectory(lldb.remote_platform.GetWorkingDirectory()) 484 485 # Redirect stdout and stderr to /dev/null 486 launch_info.AddSuppressFileAction(1, False, True) 487 launch_info.AddSuppressFileAction(2, False, True) 488 489 if extra_env: 490 launch_info.SetEnvironmentEntries(extra_env, True) 491 492 err = lldb.remote_platform.Launch(launch_info) 493 if err.Fail(): 494 raise Exception( 495 "remote_platform.Launch('%s', '%s') failed: %s" % (dst_path, args, err) 496 ) 497 self._pid = launch_info.GetProcessID() 498 499 def terminate(self): 500 lldb.remote_platform.Kill(self._pid) 501 502 503def getsource_if_available(obj): 504 """ 505 Return the text of the source code for an object if available. Otherwise, 506 a print representation is returned. 507 """ 508 import inspect 509 510 try: 511 return inspect.getsource(obj) 512 except: 513 return repr(obj) 514 515 516def builder_module(): 517 return lldbplatformutil.builder_module() 518 519 520class Base(unittest2.TestCase): 521 """ 522 Abstract base for performing lldb (see TestBase) or other generic tests (see 523 BenchBase for one example). lldbtest.Base works with the test driver to 524 accomplish things. 525 526 """ 527 528 # The concrete subclass should override this attribute. 529 mydir = None 530 531 # Keep track of the old current working directory. 532 oldcwd = None 533 534 @staticmethod 535 def compute_mydir(test_file): 536 """Subclasses should call this function to correctly calculate the 537 required "mydir" attribute as follows: 538 539 mydir = TestBase.compute_mydir(__file__) 540 """ 541 # /abs/path/to/packages/group/subdir/mytest.py -> group/subdir 542 lldb_test_src = configuration.test_src_root 543 if not test_file.startswith(lldb_test_src): 544 raise Exception( 545 "Test file '%s' must reside within lldb_test_src " 546 "(which is '%s')." % (test_file, lldb_test_src) 547 ) 548 return os.path.dirname(os.path.relpath(test_file, start=lldb_test_src)) 549 550 def TraceOn(self): 551 """Returns True if we are in trace mode (tracing detailed test execution).""" 552 return traceAlways 553 554 def trace(self, *args, **kwargs): 555 with recording(self, self.TraceOn()) as sbuf: 556 print(*args, file=sbuf, **kwargs) 557 558 @classmethod 559 def setUpClass(cls): 560 """ 561 Python unittest framework class setup fixture. 562 Do current directory manipulation. 563 """ 564 # Fail fast if 'mydir' attribute is not overridden. 565 if not cls.mydir: 566 cls.mydir = Base.compute_mydir(sys.modules[cls.__module__].__file__) 567 if not cls.mydir: 568 raise Exception("Subclasses must override the 'mydir' attribute.") 569 570 # Save old working directory. 571 cls.oldcwd = os.getcwd() 572 573 full_dir = os.path.join(configuration.test_src_root, cls.mydir) 574 if traceAlways: 575 print("Change dir to:", full_dir, file=sys.stderr) 576 os.chdir(full_dir) 577 578 # Set platform context. 579 cls.platformContext = lldbplatformutil.createPlatformContext() 580 581 @classmethod 582 def tearDownClass(cls): 583 """ 584 Python unittest framework class teardown fixture. 585 Do class-wide cleanup. 586 """ 587 588 if doCleanup: 589 # First, let's do the platform-specific cleanup. 590 module = builder_module() 591 module.cleanup() 592 593 # Subclass might have specific cleanup function defined. 594 if getattr(cls, "classCleanup", None): 595 if traceAlways: 596 print( 597 "Call class-specific cleanup function for class:", 598 cls, 599 file=sys.stderr, 600 ) 601 try: 602 cls.classCleanup() 603 except: 604 exc_type, exc_value, exc_tb = sys.exc_info() 605 traceback.print_exception(exc_type, exc_value, exc_tb) 606 607 # Restore old working directory. 608 if traceAlways: 609 print("Restore dir to:", cls.oldcwd, file=sys.stderr) 610 os.chdir(cls.oldcwd) 611 612 def enableLogChannelsForCurrentTest(self): 613 if len(lldbtest_config.channels) == 0: 614 return 615 616 # if debug channels are specified in lldbtest_config.channels, 617 # create a new set of log files for every test 618 log_basename = self.getLogBasenameForCurrentTest() 619 620 # confirm that the file is writeable 621 host_log_path = "{}-host.log".format(log_basename) 622 open(host_log_path, "w").close() 623 self.log_files.append(host_log_path) 624 625 log_enable = "log enable -Tpn -f {} ".format(host_log_path) 626 for channel_with_categories in lldbtest_config.channels: 627 channel_then_categories = channel_with_categories.split(" ", 1) 628 channel = channel_then_categories[0] 629 if len(channel_then_categories) > 1: 630 categories = channel_then_categories[1] 631 else: 632 categories = "default" 633 634 if channel == "gdb-remote" and lldb.remote_platform is None: 635 # communicate gdb-remote categories to debugserver 636 os.environ["LLDB_DEBUGSERVER_LOG_FLAGS"] = categories 637 638 self.ci.HandleCommand(log_enable + channel_with_categories, self.res) 639 if not self.res.Succeeded(): 640 raise Exception( 641 "log enable failed (check LLDB_LOG_OPTION env variable)" 642 ) 643 644 # Communicate log path name to debugserver & lldb-server 645 # For remote debugging, these variables need to be set when starting the platform 646 # instance. 647 if lldb.remote_platform is None: 648 server_log_path = "{}-server.log".format(log_basename) 649 open(server_log_path, "w").close() 650 self.log_files.append(server_log_path) 651 os.environ["LLDB_DEBUGSERVER_LOG_FILE"] = server_log_path 652 653 # Communicate channels to lldb-server 654 os.environ["LLDB_SERVER_LOG_CHANNELS"] = ":".join(lldbtest_config.channels) 655 656 self.addTearDownHook(self.disableLogChannelsForCurrentTest) 657 658 def disableLogChannelsForCurrentTest(self): 659 # close all log files that we opened 660 for channel_and_categories in lldbtest_config.channels: 661 # channel format - <channel-name> [<category0> [<category1> ...]] 662 channel = channel_and_categories.split(" ", 1)[0] 663 self.ci.HandleCommand("log disable " + channel, self.res) 664 if not self.res.Succeeded(): 665 raise Exception( 666 "log disable failed (check LLDB_LOG_OPTION env variable)" 667 ) 668 669 # Retrieve the server log (if any) from the remote system. It is assumed the server log 670 # is writing to the "server.log" file in the current test directory. This can be 671 # achieved by setting LLDB_DEBUGSERVER_LOG_FILE="server.log" when starting remote 672 # platform. 673 if lldb.remote_platform: 674 server_log_path = self.getLogBasenameForCurrentTest() + "-server.log" 675 if lldb.remote_platform.Get( 676 lldb.SBFileSpec("server.log"), lldb.SBFileSpec(server_log_path) 677 ).Success(): 678 self.log_files.append(server_log_path) 679 680 def setPlatformWorkingDir(self): 681 if not lldb.remote_platform or not configuration.lldb_platform_working_dir: 682 return 683 684 components = self.mydir.split(os.path.sep) + [ 685 str(self.test_number), 686 self.getBuildDirBasename(), 687 ] 688 remote_test_dir = configuration.lldb_platform_working_dir 689 for c in components: 690 remote_test_dir = lldbutil.join_remote_paths(remote_test_dir, c) 691 error = lldb.remote_platform.MakeDirectory( 692 remote_test_dir, 448 693 ) # 448 = 0o700 694 if error.Fail(): 695 raise Exception( 696 "making remote directory '%s': %s" % (remote_test_dir, error) 697 ) 698 699 lldb.remote_platform.SetWorkingDirectory(remote_test_dir) 700 701 # This function removes all files from the current working directory while leaving 702 # the directories in place. The cleanup is required to reduce the disk space required 703 # by the test suite while leaving the directories untouched is neccessary because 704 # sub-directories might belong to an other test 705 def clean_working_directory(): 706 # TODO: Make it working on Windows when we need it for remote debugging support 707 # TODO: Replace the heuristic to remove the files with a logic what collects the 708 # list of files we have to remove during test runs. 709 shell_cmd = lldb.SBPlatformShellCommand("rm %s/*" % remote_test_dir) 710 lldb.remote_platform.Run(shell_cmd) 711 712 self.addTearDownHook(clean_working_directory) 713 714 def getSourceDir(self): 715 """Return the full path to the current test.""" 716 return os.path.join(configuration.test_src_root, self.mydir) 717 718 def getBuildDirBasename(self): 719 return self.__class__.__module__ + "." + self.testMethodName 720 721 def getBuildDir(self): 722 """Return the full path to the current test.""" 723 return os.path.join( 724 configuration.test_build_dir, self.mydir, self.getBuildDirBasename() 725 ) 726 727 def makeBuildDir(self): 728 """Create the test-specific working directory, deleting any previous 729 contents.""" 730 bdir = self.getBuildDir() 731 if os.path.isdir(bdir): 732 shutil.rmtree(bdir) 733 lldbutil.mkdir_p(bdir) 734 735 def getBuildArtifact(self, name="a.out"): 736 """Return absolute path to an artifact in the test's build directory.""" 737 return os.path.join(self.getBuildDir(), name) 738 739 def getSourcePath(self, name): 740 """Return absolute path to a file in the test's source directory.""" 741 return os.path.join(self.getSourceDir(), name) 742 743 @classmethod 744 def setUpCommands(cls): 745 commands = [ 746 # First of all, clear all settings to have clean state of global properties. 747 "settings clear -all", 748 # Disable Spotlight lookup. The testsuite creates 749 # different binaries with the same UUID, because they only 750 # differ in the debug info, which is not being hashed. 751 "settings set symbols.enable-external-lookup false", 752 # Inherit the TCC permissions from the inferior's parent. 753 "settings set target.inherit-tcc true", 754 # Kill rather than detach from the inferior if something goes wrong. 755 "settings set target.detach-on-error false", 756 # Disable fix-its by default so that incorrect expressions in tests don't 757 # pass just because Clang thinks it has a fix-it. 758 "settings set target.auto-apply-fixits false", 759 # Testsuite runs in parallel and the host can have also other load. 760 "settings set plugin.process.gdb-remote.packet-timeout 60", 761 'settings set symbols.clang-modules-cache-path "{}"'.format( 762 configuration.lldb_module_cache_dir 763 ), 764 "settings set use-color false", 765 ] 766 767 # Set any user-overridden settings. 768 for setting, value in configuration.settings: 769 commands.append("setting set %s %s" % (setting, value)) 770 771 # Make sure that a sanitizer LLDB's environment doesn't get passed on. 772 if ( 773 cls.platformContext 774 and cls.platformContext.shlib_environment_var in os.environ 775 ): 776 commands.append( 777 "settings set target.env-vars {}=".format( 778 cls.platformContext.shlib_environment_var 779 ) 780 ) 781 782 # Set environment variables for the inferior. 783 if lldbtest_config.inferior_env: 784 commands.append( 785 "settings set target.env-vars {}".format(lldbtest_config.inferior_env) 786 ) 787 return commands 788 789 def setUp(self): 790 """Fixture for unittest test case setup. 791 792 It works with the test driver to conditionally skip tests and does other 793 initializations.""" 794 # import traceback 795 # traceback.print_stack() 796 797 if "LIBCXX_PATH" in os.environ: 798 self.libcxxPath = os.environ["LIBCXX_PATH"] 799 else: 800 self.libcxxPath = None 801 802 if "LLDBDAP_EXEC" in os.environ: 803 self.lldbDAPExec = os.environ["LLDBDAP_EXEC"] 804 else: 805 self.lldbDAPExec = None 806 807 self.lldbOption = " ".join("-o '" + s + "'" for s in self.setUpCommands()) 808 809 # If we spawn an lldb process for test (via pexpect), do not load the 810 # init file unless told otherwise. 811 if os.environ.get("NO_LLDBINIT") != "NO": 812 self.lldbOption += " --no-lldbinit" 813 814 # Assign the test method name to self.testMethodName. 815 # 816 # For an example of the use of this attribute, look at test/types dir. 817 # There are a bunch of test cases under test/types and we don't want the 818 # module cacheing subsystem to be confused with executable name "a.out" 819 # used for all the test cases. 820 self.testMethodName = self._testMethodName 821 822 # This is for the case of directly spawning 'lldb'/'gdb' and interacting 823 # with it using pexpect. 824 self.child = None 825 self.child_prompt = "(lldb) " 826 # If the child is interacting with the embedded script interpreter, 827 # there are two exits required during tear down, first to quit the 828 # embedded script interpreter and second to quit the lldb command 829 # interpreter. 830 self.child_in_script_interpreter = False 831 832 # These are for customized teardown cleanup. 833 self.dict = None 834 self.doTearDownCleanup = False 835 # And in rare cases where there are multiple teardown cleanups. 836 self.dicts = [] 837 self.doTearDownCleanups = False 838 839 # List of spawned subproces.Popen objects 840 self.subprocesses = [] 841 842 # List of log files produced by the current test. 843 self.log_files = [] 844 845 # Create the build directory. 846 # The logs are stored in the build directory, so we have to create it 847 # before creating the first log file. 848 self.makeBuildDir() 849 850 session_file = self.getLogBasenameForCurrentTest() + ".log" 851 self.log_files.append(session_file) 852 853 # Python 3 doesn't support unbuffered I/O in text mode. Open buffered. 854 self.session = encoded_file.open(session_file, "utf-8", mode="w") 855 856 # Optimistically set __errored__, __failed__, __expected__ to False 857 # initially. If the test errored/failed, the session info 858 # (self.session) is then dumped into a session specific file for 859 # diagnosis. 860 self.__cleanup_errored__ = False 861 self.__errored__ = False 862 self.__failed__ = False 863 self.__expected__ = False 864 # We are also interested in unexpected success. 865 self.__unexpected__ = False 866 # And skipped tests. 867 self.__skipped__ = False 868 869 # See addTearDownHook(self, hook) which allows the client to add a hook 870 # function to be run during tearDown() time. 871 self.hooks = [] 872 873 # See HideStdout(self). 874 self.sys_stdout_hidden = False 875 876 if self.platformContext: 877 # set environment variable names for finding shared libraries 878 self.dylibPath = self.platformContext.shlib_environment_var 879 880 # Create the debugger instance. 881 self.dbg = lldb.SBDebugger.Create() 882 # Copy selected platform from a global instance if it exists. 883 if lldb.selected_platform is not None: 884 self.dbg.SetSelectedPlatform(lldb.selected_platform) 885 886 if not self.dbg: 887 raise Exception("Invalid debugger instance") 888 889 # Retrieve the associated command interpreter instance. 890 self.ci = self.dbg.GetCommandInterpreter() 891 if not self.ci: 892 raise Exception("Could not get the command interpreter") 893 894 # And the result object. 895 self.res = lldb.SBCommandReturnObject() 896 897 self.setPlatformWorkingDir() 898 self.enableLogChannelsForCurrentTest() 899 900 self.lib_lldb = None 901 self.framework_dir = None 902 self.darwinWithFramework = False 903 904 if sys.platform.startswith("darwin") and configuration.lldb_framework_path: 905 framework = configuration.lldb_framework_path 906 lib = os.path.join(framework, "LLDB") 907 if os.path.exists(lib): 908 self.framework_dir = os.path.dirname(framework) 909 self.lib_lldb = lib 910 self.darwinWithFramework = self.platformIsDarwin() 911 912 def setAsync(self, value): 913 """Sets async mode to True/False and ensures it is reset after the testcase completes.""" 914 old_async = self.dbg.GetAsync() 915 self.dbg.SetAsync(value) 916 self.addTearDownHook(lambda: self.dbg.SetAsync(old_async)) 917 918 def cleanupSubprocesses(self): 919 # Terminate subprocesses in reverse order from how they were created. 920 for p in reversed(self.subprocesses): 921 p.terminate() 922 del p 923 del self.subprocesses[:] 924 925 def spawnSubprocess(self, executable, args=[], extra_env=None, install_remote=True): 926 """Creates a subprocess.Popen object with the specified executable and arguments, 927 saves it in self.subprocesses, and returns the object. 928 """ 929 proc = ( 930 _RemoteProcess(install_remote) 931 if lldb.remote_platform 932 else _LocalProcess(self.TraceOn()) 933 ) 934 proc.launch(executable, args, extra_env=extra_env) 935 self.subprocesses.append(proc) 936 return proc 937 938 def HideStdout(self): 939 """Hide output to stdout from the user. 940 941 During test execution, there might be cases where we don't want to show the 942 standard output to the user. For example, 943 944 self.runCmd(r'''sc print("\n\n\tHello!\n")''') 945 946 tests whether command abbreviation for 'script' works or not. There is no 947 need to show the 'Hello' output to the user as long as the 'script' command 948 succeeds and we are not in TraceOn() mode (see the '-t' option). 949 950 In this case, the test method calls self.HideStdout(self) to redirect the 951 sys.stdout to a null device, and restores the sys.stdout upon teardown. 952 953 Note that you should only call this method at most once during a test case 954 execution. Any subsequent call has no effect at all.""" 955 if self.sys_stdout_hidden: 956 return 957 958 self.sys_stdout_hidden = True 959 old_stdout = sys.stdout 960 sys.stdout = open(os.devnull, "w") 961 962 def restore_stdout(): 963 sys.stdout = old_stdout 964 965 self.addTearDownHook(restore_stdout) 966 967 # ======================================================================= 968 # Methods for customized teardown cleanups as well as execution of hooks. 969 # ======================================================================= 970 971 def setTearDownCleanup(self, dictionary=None): 972 """Register a cleanup action at tearDown() time with a dictionary""" 973 self.dict = dictionary 974 self.doTearDownCleanup = True 975 976 def addTearDownCleanup(self, dictionary): 977 """Add a cleanup action at tearDown() time with a dictionary""" 978 self.dicts.append(dictionary) 979 self.doTearDownCleanups = True 980 981 def addTearDownHook(self, hook): 982 """ 983 Add a function to be run during tearDown() time. 984 985 Hooks are executed in a first come first serve manner. 986 """ 987 if callable(hook): 988 with recording(self, traceAlways) as sbuf: 989 print("Adding tearDown hook:", getsource_if_available(hook), file=sbuf) 990 self.hooks.append(hook) 991 992 return self 993 994 def deletePexpectChild(self): 995 # This is for the case of directly spawning 'lldb' and interacting with it 996 # using pexpect. 997 if self.child and self.child.isalive(): 998 import pexpect 999 1000 with recording(self, traceAlways) as sbuf: 1001 print("tearing down the child process....", file=sbuf) 1002 try: 1003 if self.child_in_script_interpreter: 1004 self.child.sendline("quit()") 1005 self.child.expect_exact(self.child_prompt) 1006 self.child.sendline("settings set interpreter.prompt-on-quit false") 1007 self.child.sendline("quit") 1008 self.child.expect(pexpect.EOF) 1009 except (ValueError, pexpect.ExceptionPexpect): 1010 # child is already terminated 1011 pass 1012 except OSError as exception: 1013 import errno 1014 1015 if exception.errno != errno.EIO: 1016 # unexpected error 1017 raise 1018 # child is already terminated 1019 finally: 1020 # Give it one final blow to make sure the child is terminated. 1021 self.child.close() 1022 1023 def tearDown(self): 1024 """Fixture for unittest test case teardown.""" 1025 self.deletePexpectChild() 1026 1027 # Check and run any hook functions. 1028 for hook in reversed(self.hooks): 1029 with recording(self, traceAlways) as sbuf: 1030 print( 1031 "Executing tearDown hook:", getsource_if_available(hook), file=sbuf 1032 ) 1033 if funcutils.requires_self(hook): 1034 hook(self) 1035 else: 1036 hook() # try the plain call and hope it works 1037 1038 del self.hooks 1039 1040 # Perform registered teardown cleanup. 1041 if doCleanup and self.doTearDownCleanup: 1042 self.cleanup(dictionary=self.dict) 1043 1044 # In rare cases where there are multiple teardown cleanups added. 1045 if doCleanup and self.doTearDownCleanups: 1046 if self.dicts: 1047 for dict in reversed(self.dicts): 1048 self.cleanup(dictionary=dict) 1049 1050 # Remove subprocesses created by the test. 1051 self.cleanupSubprocesses() 1052 1053 # This must be the last statement, otherwise teardown hooks or other 1054 # lines might depend on this still being active. 1055 lldb.SBDebugger.Destroy(self.dbg) 1056 del self.dbg 1057 1058 # All modules should be orphaned now so that they can be cleared from 1059 # the shared module cache. 1060 lldb.SBModule.GarbageCollectAllocatedModules() 1061 1062 # Assert that the global module cache is empty. 1063 self.assertEqual(lldb.SBModule.GetNumberAllocatedModules(), 0) 1064 1065 # ========================================================= 1066 # Various callbacks to allow introspection of test progress 1067 # ========================================================= 1068 1069 def markError(self): 1070 """Callback invoked when an error (unexpected exception) errored.""" 1071 self.__errored__ = True 1072 with recording(self, False) as sbuf: 1073 # False because there's no need to write "ERROR" to the stderr twice. 1074 # Once by the Python unittest framework, and a second time by us. 1075 print("ERROR", file=sbuf) 1076 1077 def markCleanupError(self): 1078 """Callback invoked when an error occurs while a test is cleaning up.""" 1079 self.__cleanup_errored__ = True 1080 with recording(self, False) as sbuf: 1081 # False because there's no need to write "CLEANUP_ERROR" to the stderr twice. 1082 # Once by the Python unittest framework, and a second time by us. 1083 print("CLEANUP_ERROR", file=sbuf) 1084 1085 def markFailure(self): 1086 """Callback invoked when a failure (test assertion failure) occurred.""" 1087 self.__failed__ = True 1088 with recording(self, False) as sbuf: 1089 # False because there's no need to write "FAIL" to the stderr twice. 1090 # Once by the Python unittest framework, and a second time by us. 1091 print("FAIL", file=sbuf) 1092 1093 def markExpectedFailure(self, err, bugnumber): 1094 """Callback invoked when an expected failure/error occurred.""" 1095 self.__expected__ = True 1096 with recording(self, False) as sbuf: 1097 # False because there's no need to write "expected failure" to the 1098 # stderr twice. 1099 # Once by the Python unittest framework, and a second time by us. 1100 if bugnumber is None: 1101 print("expected failure", file=sbuf) 1102 else: 1103 print("expected failure (problem id:" + str(bugnumber) + ")", file=sbuf) 1104 1105 def markSkippedTest(self): 1106 """Callback invoked when a test is skipped.""" 1107 self.__skipped__ = True 1108 with recording(self, False) as sbuf: 1109 # False because there's no need to write "skipped test" to the 1110 # stderr twice. 1111 # Once by the Python unittest framework, and a second time by us. 1112 print("skipped test", file=sbuf) 1113 1114 def markUnexpectedSuccess(self, bugnumber): 1115 """Callback invoked when an unexpected success occurred.""" 1116 self.__unexpected__ = True 1117 with recording(self, False) as sbuf: 1118 # False because there's no need to write "unexpected success" to the 1119 # stderr twice. 1120 # Once by the Python unittest framework, and a second time by us. 1121 if bugnumber is None: 1122 print("unexpected success", file=sbuf) 1123 else: 1124 print( 1125 "unexpected success (problem id:" + str(bugnumber) + ")", file=sbuf 1126 ) 1127 1128 def getRerunArgs(self): 1129 return " -f %s.%s" % (self.__class__.__name__, self._testMethodName) 1130 1131 def getLogBasenameForCurrentTest(self, prefix="Incomplete"): 1132 """ 1133 returns a partial path that can be used as the beginning of the name of multiple 1134 log files pertaining to this test 1135 """ 1136 return os.path.join(self.getBuildDir(), prefix) 1137 1138 def dumpSessionInfo(self): 1139 """ 1140 Dump the debugger interactions leading to a test error/failure. This 1141 allows for more convenient postmortem analysis. 1142 1143 See also LLDBTestResult (dotest.py) which is a singlton class derived 1144 from TextTestResult and overwrites addError, addFailure, and 1145 addExpectedFailure methods to allow us to to mark the test instance as 1146 such. 1147 """ 1148 1149 # We are here because self.tearDown() detected that this test instance 1150 # either errored or failed. The lldb.test_result singleton contains 1151 # two lists (errors and failures) which get populated by the unittest 1152 # framework. Look over there for stack trace information. 1153 # 1154 # The lists contain 2-tuples of TestCase instances and strings holding 1155 # formatted tracebacks. 1156 # 1157 # See http://docs.python.org/library/unittest.html#unittest.TestResult. 1158 1159 # output tracebacks into session 1160 pairs = [] 1161 if self.__errored__: 1162 pairs = configuration.test_result.errors 1163 prefix = "Error" 1164 elif self.__cleanup_errored__: 1165 pairs = configuration.test_result.cleanup_errors 1166 prefix = "CleanupError" 1167 elif self.__failed__: 1168 pairs = configuration.test_result.failures 1169 prefix = "Failure" 1170 elif self.__expected__: 1171 pairs = configuration.test_result.expectedFailures 1172 prefix = "ExpectedFailure" 1173 elif self.__skipped__: 1174 prefix = "SkippedTest" 1175 elif self.__unexpected__: 1176 prefix = "UnexpectedSuccess" 1177 else: 1178 prefix = "Success" 1179 1180 if not self.__unexpected__ and not self.__skipped__: 1181 for test, traceback in pairs: 1182 if test is self: 1183 print(traceback, file=self.session) 1184 1185 import datetime 1186 1187 print( 1188 "Session info generated @", 1189 datetime.datetime.now().ctime(), 1190 file=self.session, 1191 ) 1192 self.session.close() 1193 del self.session 1194 1195 # process the log files 1196 if prefix != "Success" or lldbtest_config.log_success: 1197 # keep all log files, rename them to include prefix 1198 src_log_basename = self.getLogBasenameForCurrentTest() 1199 dst_log_basename = self.getLogBasenameForCurrentTest(prefix) 1200 for src in self.log_files: 1201 if os.path.isfile(src): 1202 dst = src.replace(src_log_basename, dst_log_basename) 1203 if os.name == "nt" and os.path.isfile(dst): 1204 # On Windows, renaming a -> b will throw an exception if 1205 # b exists. On non-Windows platforms it silently 1206 # replaces the destination. Ultimately this means that 1207 # atomic renames are not guaranteed to be possible on 1208 # Windows, but we need this to work anyway, so just 1209 # remove the destination first if it already exists. 1210 remove_file(dst) 1211 1212 lldbutil.mkdir_p(os.path.dirname(dst)) 1213 os.rename(src, dst) 1214 else: 1215 # success! (and we don't want log files) delete log files 1216 for log_file in self.log_files: 1217 if os.path.isfile(log_file): 1218 remove_file(log_file) 1219 1220 # ==================================================== 1221 # Config. methods supported through a plugin interface 1222 # (enables reading of the current test configuration) 1223 # ==================================================== 1224 1225 def hasXMLSupport(self): 1226 """Returns True if lldb was built with XML support. Use this check to 1227 enable parts of tests, if you want to skip a whole test use skipIfXmlSupportMissing 1228 instead.""" 1229 return ( 1230 lldb.SBDebugger.GetBuildConfiguration() 1231 .GetValueForKey("xml") 1232 .GetValueForKey("value") 1233 .GetBooleanValue(False) 1234 ) 1235 1236 def isMIPS(self): 1237 """Returns true if the architecture is MIPS.""" 1238 arch = self.getArchitecture() 1239 if re.match("mips", arch): 1240 return True 1241 return False 1242 1243 def isPPC64le(self): 1244 """Returns true if the architecture is PPC64LE.""" 1245 arch = self.getArchitecture() 1246 if re.match("powerpc64le", arch): 1247 return True 1248 return False 1249 1250 def getCPUInfo(self): 1251 triple = self.dbg.GetSelectedPlatform().GetTriple() 1252 1253 # TODO other platforms, please implement this function 1254 if not re.match(".*-.*-linux", triple): 1255 return "" 1256 1257 # Need to do something different for non-Linux/Android targets 1258 cpuinfo_path = self.getBuildArtifact("cpuinfo") 1259 if configuration.lldb_platform_name: 1260 self.runCmd('platform get-file "/proc/cpuinfo" ' + cpuinfo_path) 1261 else: 1262 cpuinfo_path = "/proc/cpuinfo" 1263 1264 try: 1265 with open(cpuinfo_path, "r") as f: 1266 cpuinfo = f.read() 1267 except: 1268 return "" 1269 1270 return cpuinfo 1271 1272 def isAArch64(self): 1273 """Returns true if the architecture is AArch64.""" 1274 arch = self.getArchitecture().lower() 1275 return arch in ["aarch64", "arm64", "arm64e"] 1276 1277 def isAArch64SVE(self): 1278 return self.isAArch64() and "sve" in self.getCPUInfo() 1279 1280 def isAArch64SME(self): 1281 return self.isAArch64() and "sme" in self.getCPUInfo() 1282 1283 def isAArch64SME2(self): 1284 # If you have sme2, you also have sme. 1285 return self.isAArch64() and "sme2" in self.getCPUInfo() 1286 1287 def isAArch64SMEFA64(self): 1288 # smefa64 allows the use of the full A64 instruction set in streaming 1289 # mode. This is required by certain test programs to setup register 1290 # state. 1291 cpuinfo = self.getCPUInfo() 1292 return self.isAArch64() and "sme" in cpuinfo and "smefa64" in cpuinfo 1293 1294 def isAArch64MTE(self): 1295 return self.isAArch64() and "mte" in self.getCPUInfo() 1296 1297 def isAArch64PAuth(self): 1298 if self.getArchitecture() == "arm64e": 1299 return True 1300 return self.isAArch64() and "paca" in self.getCPUInfo() 1301 1302 def isAArch64Windows(self): 1303 """Returns true if the architecture is AArch64 and platform windows.""" 1304 if self.getPlatform() == "windows": 1305 arch = self.getArchitecture().lower() 1306 return arch in ["aarch64", "arm64", "arm64e"] 1307 return False 1308 1309 def getArchitecture(self): 1310 """Returns the architecture in effect the test suite is running with.""" 1311 return lldbplatformutil.getArchitecture() 1312 1313 def getLldbArchitecture(self): 1314 """Returns the architecture of the lldb binary.""" 1315 return lldbplatformutil.getLLDBArchitecture() 1316 1317 def getCompiler(self): 1318 """Returns the compiler in effect the test suite is running with.""" 1319 return lldbplatformutil.getCompiler() 1320 1321 def getCompilerBinary(self): 1322 """Returns the compiler binary the test suite is running with.""" 1323 return lldbplatformutil.getCompilerBinary() 1324 1325 def getCompilerVersion(self): 1326 """Returns a string that represents the compiler version. 1327 Supports: llvm, clang. 1328 """ 1329 return lldbplatformutil.getCompilerVersion() 1330 1331 def getDwarfVersion(self): 1332 """Returns the dwarf version generated by clang or '0'.""" 1333 return lldbplatformutil.getDwarfVersion() 1334 1335 def platformIsDarwin(self): 1336 """Returns true if the OS triple for the selected platform is any valid apple OS""" 1337 return lldbplatformutil.platformIsDarwin() 1338 1339 def hasDarwinFramework(self): 1340 return self.darwinWithFramework 1341 1342 def getPlatform(self): 1343 """Returns the target platform the test suite is running on.""" 1344 return lldbplatformutil.getPlatform() 1345 1346 def isIntelCompiler(self): 1347 """Returns true if using an Intel (ICC) compiler, false otherwise.""" 1348 return any([x in self.getCompiler() for x in ["icc", "icpc", "icl"]]) 1349 1350 def expectedCompilerVersion(self, compiler_version): 1351 """Returns True iff compiler_version[1] matches the current compiler version. 1352 Use compiler_version[0] to specify the operator used to determine if a match has occurred. 1353 Any operator other than the following defaults to an equality test: 1354 '>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not' 1355 1356 If the current compiler version cannot be determined, we assume it is close to the top 1357 of trunk, so any less-than or equal-to comparisons will return False, and any 1358 greater-than or not-equal-to comparisons will return True. 1359 """ 1360 return lldbplatformutil.expectedCompilerVersion(compiler_version) 1361 1362 def expectedCompiler(self, compilers): 1363 """Returns True iff any element of compilers is a sub-string of the current compiler.""" 1364 return lldbplatformutil.expectedCompiler(compilers) 1365 1366 def expectedArch(self, archs): 1367 """Returns True iff any element of archs is a sub-string of the current architecture.""" 1368 if archs is None: 1369 return True 1370 1371 for arch in archs: 1372 if arch in self.getArchitecture(): 1373 return True 1374 1375 return False 1376 1377 def getRunOptions(self): 1378 """Command line option for -A and -C to run this test again, called from 1379 self.dumpSessionInfo().""" 1380 arch = self.getArchitecture() 1381 comp = self.getCompiler() 1382 option_str = "" 1383 if arch: 1384 option_str = "-A " + arch 1385 if comp: 1386 option_str += " -C " + comp 1387 return option_str 1388 1389 def getDebugInfo(self): 1390 method = getattr(self, self.testMethodName) 1391 return getattr(method, "debug_info", None) 1392 1393 def build( 1394 self, 1395 debug_info=None, 1396 architecture=None, 1397 compiler=None, 1398 dictionary=None, 1399 make_targets=None, 1400 ): 1401 """Platform specific way to build binaries.""" 1402 if not architecture and configuration.arch: 1403 architecture = configuration.arch 1404 1405 if debug_info is None: 1406 debug_info = self.getDebugInfo() 1407 1408 dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) 1409 1410 testdir = self.mydir 1411 testname = self.getBuildDirBasename() 1412 1413 module = builder_module() 1414 command = builder_module().getBuildCommand( 1415 debug_info, 1416 architecture, 1417 compiler, 1418 dictionary, 1419 testdir, 1420 testname, 1421 make_targets, 1422 ) 1423 if command is None: 1424 raise Exception("Don't know how to build binary") 1425 1426 self.runBuildCommand(command) 1427 1428 def runBuildCommand(self, command): 1429 self.trace(seven.join_for_shell(command)) 1430 try: 1431 output = check_output(command, stderr=STDOUT, errors="replace") 1432 except CalledProcessError as cpe: 1433 raise build_exception.BuildError(cpe) 1434 self.trace(output) 1435 1436 # ================================================== 1437 # Build methods supported through a plugin interface 1438 # ================================================== 1439 1440 def getstdlibFlag(self): 1441 """Returns the proper -stdlib flag, or empty if not required.""" 1442 if ( 1443 self.platformIsDarwin() 1444 or self.getPlatform() == "freebsd" 1445 or self.getPlatform() == "openbsd" 1446 ): 1447 stdlibflag = "-stdlib=libc++" 1448 else: # this includes NetBSD 1449 stdlibflag = "" 1450 return stdlibflag 1451 1452 def getstdFlag(self): 1453 """Returns the proper stdflag.""" 1454 if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): 1455 stdflag = "-std=c++0x" 1456 else: 1457 stdflag = "-std=c++11" 1458 return stdflag 1459 1460 def buildDriver(self, sources, exe_name): 1461 """Platform-specific way to build a program that links with LLDB (via the liblldb.so 1462 or LLDB.framework). 1463 """ 1464 stdflag = self.getstdFlag() 1465 stdlibflag = self.getstdlibFlag() 1466 1467 lib_dir = configuration.lldb_libs_dir 1468 if self.hasDarwinFramework(): 1469 d = { 1470 "CXX_SOURCES": sources, 1471 "EXE": exe_name, 1472 "CFLAGS_EXTRAS": "%s %s" % (stdflag, stdlibflag), 1473 "FRAMEWORK_INCLUDES": "-F%s" % self.framework_dir, 1474 "LD_EXTRAS": "%s -Wl,-rpath,%s" % (self.lib_lldb, self.framework_dir), 1475 } 1476 elif sys.platform.startswith("win"): 1477 d = { 1478 "CXX_SOURCES": sources, 1479 "EXE": exe_name, 1480 "CFLAGS_EXTRAS": "%s %s -I%s" 1481 % ( 1482 stdflag, 1483 stdlibflag, 1484 os.path.join(os.environ["LLDB_SRC"], "include"), 1485 ), 1486 "LD_EXTRAS": "-L%s -lliblldb" % lib_dir, 1487 } 1488 else: 1489 d = { 1490 "CXX_SOURCES": sources, 1491 "EXE": exe_name, 1492 "CFLAGS_EXTRAS": "%s %s -I%s" 1493 % ( 1494 stdflag, 1495 stdlibflag, 1496 os.path.join(os.environ["LLDB_SRC"], "include"), 1497 ), 1498 "LD_EXTRAS": "-L%s -llldb -Wl,-rpath,%s" % (lib_dir, lib_dir), 1499 } 1500 if self.TraceOn(): 1501 print("Building LLDB Driver (%s) from sources %s" % (exe_name, sources)) 1502 1503 self.build(dictionary=d) 1504 1505 def buildLibrary(self, sources, lib_name): 1506 """Platform specific way to build a default library.""" 1507 1508 stdflag = self.getstdFlag() 1509 1510 lib_dir = configuration.lldb_libs_dir 1511 if self.hasDarwinFramework(): 1512 d = { 1513 "DYLIB_CXX_SOURCES": sources, 1514 "DYLIB_NAME": lib_name, 1515 "CFLAGS_EXTRAS": "%s -stdlib=libc++" % stdflag, 1516 "FRAMEWORK_INCLUDES": "-F%s" % self.framework_dir, 1517 "LD_EXTRAS": "%s -Wl,-rpath,%s -dynamiclib" 1518 % (self.lib_lldb, self.framework_dir), 1519 } 1520 elif self.getPlatform() == "windows": 1521 d = { 1522 "DYLIB_CXX_SOURCES": sources, 1523 "DYLIB_NAME": lib_name, 1524 "CFLAGS_EXTRAS": "%s -I%s " 1525 % (stdflag, os.path.join(os.environ["LLDB_SRC"], "include")), 1526 "LD_EXTRAS": "-shared -l%s\liblldb.lib" % lib_dir, 1527 } 1528 else: 1529 d = { 1530 "DYLIB_CXX_SOURCES": sources, 1531 "DYLIB_NAME": lib_name, 1532 "CFLAGS_EXTRAS": "%s -I%s -fPIC" 1533 % (stdflag, os.path.join(os.environ["LLDB_SRC"], "include")), 1534 "LD_EXTRAS": "-shared -L%s -llldb -Wl,-rpath,%s" % (lib_dir, lib_dir), 1535 } 1536 if self.TraceOn(): 1537 print("Building LLDB Library (%s) from sources %s" % (lib_name, sources)) 1538 1539 self.build(dictionary=d) 1540 1541 def buildProgram(self, sources, exe_name): 1542 """Platform specific way to build an executable from C/C++ sources.""" 1543 d = {"CXX_SOURCES": sources, "EXE": exe_name} 1544 self.build(dictionary=d) 1545 1546 def findBuiltClang(self): 1547 """Tries to find and use Clang from the build directory as the compiler (instead of the system compiler).""" 1548 paths_to_try = [ 1549 "llvm-build/Release+Asserts/x86_64/bin/clang", 1550 "llvm-build/Debug+Asserts/x86_64/bin/clang", 1551 "llvm-build/Release/x86_64/bin/clang", 1552 "llvm-build/Debug/x86_64/bin/clang", 1553 ] 1554 lldb_root_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "..") 1555 for p in paths_to_try: 1556 path = os.path.join(lldb_root_path, p) 1557 if os.path.exists(path): 1558 return path 1559 1560 # Tries to find clang at the same folder as the lldb 1561 lldb_dir = os.path.dirname(lldbtest_config.lldbExec) 1562 path = shutil.which("clang", path=lldb_dir) 1563 if path is not None: 1564 return path 1565 1566 return os.environ["CC"] 1567 1568 def yaml2obj(self, yaml_path, obj_path, max_size=None): 1569 """ 1570 Create an object file at the given path from a yaml file. 1571 1572 Throws subprocess.CalledProcessError if the object could not be created. 1573 """ 1574 yaml2obj_bin = configuration.get_yaml2obj_path() 1575 if not yaml2obj_bin: 1576 self.assertTrue(False, "No valid yaml2obj executable specified") 1577 command = [yaml2obj_bin, "-o=%s" % obj_path, yaml_path] 1578 if max_size is not None: 1579 command += ["--max-size=%d" % max_size] 1580 self.runBuildCommand(command) 1581 1582 def cleanup(self, dictionary=None): 1583 """Platform specific way to do cleanup after build.""" 1584 module = builder_module() 1585 if not module.cleanup(dictionary): 1586 raise Exception( 1587 "Don't know how to do cleanup with dictionary: " + dictionary 1588 ) 1589 1590 def invoke(self, obj, name, trace=False): 1591 """Use reflection to call a method dynamically with no argument.""" 1592 trace = True if traceAlways else trace 1593 1594 method = getattr(obj, name) 1595 import inspect 1596 1597 self.assertTrue( 1598 inspect.ismethod(method), name + "is a method name of object: " + str(obj) 1599 ) 1600 result = method() 1601 with recording(self, trace) as sbuf: 1602 print(str(method) + ":", result, file=sbuf) 1603 return result 1604 1605 def getLLDBLibraryEnvVal(self): 1606 """Returns the path that the OS-specific library search environment variable 1607 (self.dylibPath) should be set to in order for a program to find the LLDB 1608 library. If an environment variable named self.dylibPath is already set, 1609 the new path is appended to it and returned. 1610 """ 1611 existing_library_path = ( 1612 os.environ[self.dylibPath] if self.dylibPath in os.environ else None 1613 ) 1614 if existing_library_path: 1615 return "%s:%s" % (existing_library_path, configuration.lldb_libs_dir) 1616 if sys.platform.startswith("darwin") and configuration.lldb_framework_path: 1617 return configuration.lldb_framework_path 1618 return configuration.lldb_libs_dir 1619 1620 def getLibcPlusPlusLibs(self): 1621 if self.getPlatform() in ("freebsd", "linux", "netbsd", "openbsd"): 1622 return ["libc++.so.1"] 1623 else: 1624 return ["libc++.1.dylib", "libc++abi."] 1625 1626 def run_platform_command(self, cmd): 1627 platform = self.dbg.GetSelectedPlatform() 1628 shell_command = lldb.SBPlatformShellCommand(cmd) 1629 err = platform.Run(shell_command) 1630 return (err, shell_command.GetStatus(), shell_command.GetOutput()) 1631 1632 def get_stats(self, options=None): 1633 """ 1634 Get the output of the "statistics dump" with optional extra options 1635 and return the JSON as a python dictionary. 1636 """ 1637 return_obj = lldb.SBCommandReturnObject() 1638 command = "statistics dump " 1639 if options is not None: 1640 command += options 1641 self.ci.HandleCommand(command, return_obj, False) 1642 metrics_json = return_obj.GetOutput() 1643 return json.loads(metrics_json) 1644 1645 1646# Metaclass for TestBase to change the list of test metods when a new TestCase is loaded. 1647# We change the test methods to create a new test method for each test for each debug info we are 1648# testing. The name of the new test method will be '<original-name>_<debug-info>' and with adding 1649# the new test method we remove the old method at the same time. This functionality can be 1650# supressed by at test case level setting the class attribute NO_DEBUG_INFO_TESTCASE or at test 1651# level by using the decorator @no_debug_info_test. 1652 1653 1654class LLDBTestCaseFactory(type): 1655 def __new__(cls, name, bases, attrs): 1656 original_testcase = super(LLDBTestCaseFactory, cls).__new__( 1657 cls, name, bases, attrs 1658 ) 1659 if original_testcase.NO_DEBUG_INFO_TESTCASE: 1660 return original_testcase 1661 1662 # Default implementation for skip/xfail reason based on the debug category, 1663 # where "None" means to run the test as usual. 1664 def no_reason(_): 1665 return None 1666 1667 newattrs = {} 1668 for attrname, attrvalue in attrs.items(): 1669 if attrname.startswith("test") and not getattr( 1670 attrvalue, "__no_debug_info_test__", False 1671 ): 1672 # If any debug info categories were explicitly tagged, assume that list to be 1673 # authoritative. If none were specified, try with all debug 1674 # info formats. 1675 all_dbginfo_categories = set( 1676 test_categories.debug_info_categories.keys() 1677 ) 1678 categories = ( 1679 set(getattr(attrvalue, "categories", [])) & all_dbginfo_categories 1680 ) 1681 if not categories: 1682 categories = [ 1683 category 1684 for category, can_replicate in test_categories.debug_info_categories.items() 1685 if can_replicate 1686 ] 1687 1688 xfail_for_debug_info_cat_fn = getattr( 1689 attrvalue, "__xfail_for_debug_info_cat_fn__", no_reason 1690 ) 1691 skip_for_debug_info_cat_fn = getattr( 1692 attrvalue, "__skip_for_debug_info_cat_fn__", no_reason 1693 ) 1694 for cat in categories: 1695 1696 @decorators.add_test_categories([cat]) 1697 @wraps(attrvalue) 1698 def test_method(self, attrvalue=attrvalue): 1699 return attrvalue(self) 1700 1701 method_name = attrname + "_" + cat 1702 test_method.__name__ = method_name 1703 test_method.debug_info = cat 1704 1705 xfail_reason = xfail_for_debug_info_cat_fn(cat) 1706 if xfail_reason: 1707 test_method = unittest2.expectedFailure(xfail_reason)( 1708 test_method 1709 ) 1710 1711 skip_reason = skip_for_debug_info_cat_fn(cat) 1712 if skip_reason: 1713 test_method = unittest2.skip(skip_reason)(test_method) 1714 1715 newattrs[method_name] = test_method 1716 1717 else: 1718 newattrs[attrname] = attrvalue 1719 return super(LLDBTestCaseFactory, cls).__new__(cls, name, bases, newattrs) 1720 1721 1722# Setup the metaclass for this class to change the list of the test 1723# methods when a new class is loaded 1724 1725 1726class TestBase(Base, metaclass=LLDBTestCaseFactory): 1727 """ 1728 This abstract base class is meant to be subclassed. It provides default 1729 implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(), 1730 among other things. 1731 1732 Important things for test class writers: 1733 1734 - The setUp method sets up things to facilitate subsequent interactions 1735 with the debugger as part of the test. These include: 1736 - populate the test method name 1737 - create/get a debugger set with synchronous mode (self.dbg) 1738 - get the command interpreter from with the debugger (self.ci) 1739 - create a result object for use with the command interpreter 1740 (self.res) 1741 - plus other stuffs 1742 1743 - The tearDown method tries to perform some necessary cleanup on behalf 1744 of the test to return the debugger to a good state for the next test. 1745 These include: 1746 - execute any tearDown hooks registered by the test method with 1747 TestBase.addTearDownHook(); examples can be found in 1748 settings/TestSettings.py 1749 - kill the inferior process associated with each target, if any, 1750 and, then delete the target from the debugger's target list 1751 - perform build cleanup before running the next test method in the 1752 same test class; examples of registering for this service can be 1753 found in types/TestIntegerTypes.py with the call: 1754 - self.setTearDownCleanup(dictionary=d) 1755 1756 - Similarly setUpClass and tearDownClass perform classwise setup and 1757 teardown fixtures. The tearDownClass method invokes a default build 1758 cleanup for the entire test class; also, subclasses can implement the 1759 classmethod classCleanup(cls) to perform special class cleanup action. 1760 1761 - The instance methods runCmd and expect are used heavily by existing 1762 test cases to send a command to the command interpreter and to perform 1763 string/pattern matching on the output of such command execution. The 1764 expect method also provides a mode to peform string/pattern matching 1765 without running a command. 1766 1767 - The build method is used to build the binaries used during a 1768 particular test scenario. A plugin should be provided for the 1769 sys.platform running the test suite. The Mac OS X implementation is 1770 located in builders/darwin.py. 1771 """ 1772 1773 # Subclasses can set this to true (if they don't depend on debug info) to avoid running the 1774 # test multiple times with various debug info types. 1775 NO_DEBUG_INFO_TESTCASE = False 1776 1777 # Maximum allowed attempts when launching the inferior process. 1778 # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable. 1779 maxLaunchCount = 1 1780 1781 # Time to wait before the next launching attempt in second(s). 1782 # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable. 1783 timeWaitNextLaunch = 1.0 1784 1785 def generateSource(self, source): 1786 template = source + ".template" 1787 temp = os.path.join(self.getSourceDir(), template) 1788 with open(temp, "r") as f: 1789 content = f.read() 1790 1791 public_api_dir = os.path.join(os.environ["LLDB_SRC"], "include", "lldb", "API") 1792 1793 # Look under the include/lldb/API directory and add #include statements 1794 # for all the SB API headers. 1795 public_headers = os.listdir(public_api_dir) 1796 # For different platforms, the include statement can vary. 1797 if self.hasDarwinFramework(): 1798 include_stmt = "'#include <%s>' % os.path.join('LLDB', header)" 1799 else: 1800 include_stmt = ( 1801 "'#include <%s>' % os.path.join(r'" + public_api_dir + "', header)" 1802 ) 1803 list = [ 1804 eval(include_stmt) 1805 for header in public_headers 1806 if (header.startswith("SB") and header.endswith(".h")) 1807 ] 1808 includes = "\n".join(list) 1809 new_content = content.replace("%include_SB_APIs%", includes) 1810 new_content = new_content.replace("%SOURCE_DIR%", self.getSourceDir()) 1811 src = os.path.join(self.getBuildDir(), source) 1812 with open(src, "w") as f: 1813 f.write(new_content) 1814 1815 self.addTearDownHook(lambda: os.remove(src)) 1816 1817 def setUp(self): 1818 # Works with the test driver to conditionally skip tests via 1819 # decorators. 1820 Base.setUp(self) 1821 1822 for s in self.setUpCommands(): 1823 self.runCmd(s) 1824 1825 if "LLDB_MAX_LAUNCH_COUNT" in os.environ: 1826 self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"]) 1827 1828 if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ: 1829 self.timeWaitNextLaunch = float(os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"]) 1830 1831 # We want our debugger to be synchronous. 1832 self.dbg.SetAsync(False) 1833 1834 # Retrieve the associated command interpreter instance. 1835 self.ci = self.dbg.GetCommandInterpreter() 1836 if not self.ci: 1837 raise Exception("Could not get the command interpreter") 1838 1839 # And the result object. 1840 self.res = lldb.SBCommandReturnObject() 1841 1842 def registerSharedLibrariesWithTarget(self, target, shlibs): 1843 """If we are remotely running the test suite, register the shared libraries with the target so they get uploaded, otherwise do nothing 1844 1845 Any modules in the target that have their remote install file specification set will 1846 get uploaded to the remote host. This function registers the local copies of the 1847 shared libraries with the target and sets their remote install locations so they will 1848 be uploaded when the target is run. 1849 """ 1850 if not shlibs or not self.platformContext: 1851 return None 1852 1853 shlib_environment_var = self.platformContext.shlib_environment_var 1854 shlib_prefix = self.platformContext.shlib_prefix 1855 shlib_extension = "." + self.platformContext.shlib_extension 1856 1857 dirs = [] 1858 # Add any shared libraries to our target if remote so they get 1859 # uploaded into the working directory on the remote side 1860 for name in shlibs: 1861 # The path can be a full path to a shared library, or a make file name like "Foo" for 1862 # "libFoo.dylib" or "libFoo.so", or "Foo.so" for "Foo.so" or "libFoo.so", or just a 1863 # basename like "libFoo.so". So figure out which one it is and resolve the local copy 1864 # of the shared library accordingly 1865 if os.path.isfile(name): 1866 local_shlib_path = ( 1867 name # name is the full path to the local shared library 1868 ) 1869 else: 1870 # Check relative names 1871 local_shlib_path = os.path.join( 1872 self.getBuildDir(), shlib_prefix + name + shlib_extension 1873 ) 1874 if not os.path.exists(local_shlib_path): 1875 local_shlib_path = os.path.join( 1876 self.getBuildDir(), name + shlib_extension 1877 ) 1878 if not os.path.exists(local_shlib_path): 1879 local_shlib_path = os.path.join(self.getBuildDir(), name) 1880 1881 # Make sure we found the local shared library in the above code 1882 self.assertTrue(os.path.exists(local_shlib_path)) 1883 1884 # Add the shared library to our target 1885 shlib_module = target.AddModule(local_shlib_path, None, None, None) 1886 if lldb.remote_platform: 1887 # We must set the remote install location if we want the shared library 1888 # to get uploaded to the remote target 1889 remote_shlib_path = lldbutil.append_to_process_working_directory( 1890 self, os.path.basename(local_shlib_path) 1891 ) 1892 shlib_module.SetRemoteInstallFileSpec( 1893 lldb.SBFileSpec(remote_shlib_path, False) 1894 ) 1895 dir_to_add = self.get_process_working_directory() 1896 else: 1897 dir_to_add = os.path.dirname(local_shlib_path) 1898 1899 if dir_to_add not in dirs: 1900 dirs.append(dir_to_add) 1901 1902 env_value = self.platformContext.shlib_path_separator.join(dirs) 1903 return ["%s=%s" % (shlib_environment_var, env_value)] 1904 1905 def registerSanitizerLibrariesWithTarget(self, target): 1906 runtimes = [] 1907 for m in target.module_iter(): 1908 libspec = m.GetFileSpec() 1909 if "clang_rt" in libspec.GetFilename(): 1910 runtimes.append( 1911 os.path.join(libspec.GetDirectory(), libspec.GetFilename()) 1912 ) 1913 return self.registerSharedLibrariesWithTarget(target, runtimes) 1914 1915 # utility methods that tests can use to access the current objects 1916 def target(self): 1917 if not self.dbg: 1918 raise Exception("Invalid debugger instance") 1919 return self.dbg.GetSelectedTarget() 1920 1921 def process(self): 1922 if not self.dbg: 1923 raise Exception("Invalid debugger instance") 1924 return self.dbg.GetSelectedTarget().GetProcess() 1925 1926 def thread(self): 1927 if not self.dbg: 1928 raise Exception("Invalid debugger instance") 1929 return self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread() 1930 1931 def frame(self): 1932 if not self.dbg: 1933 raise Exception("Invalid debugger instance") 1934 return ( 1935 self.dbg.GetSelectedTarget() 1936 .GetProcess() 1937 .GetSelectedThread() 1938 .GetSelectedFrame() 1939 ) 1940 1941 def get_process_working_directory(self): 1942 """Get the working directory that should be used when launching processes for local or remote processes.""" 1943 if lldb.remote_platform: 1944 # Remote tests set the platform working directory up in 1945 # TestBase.setUp() 1946 return lldb.remote_platform.GetWorkingDirectory() 1947 else: 1948 # local tests change directory into each test subdirectory 1949 return self.getBuildDir() 1950 1951 def tearDown(self): 1952 # Ensure all the references to SB objects have gone away so that we can 1953 # be sure that all test-specific resources have been freed before we 1954 # attempt to delete the targets. 1955 gc.collect() 1956 1957 # Delete the target(s) from the debugger as a general cleanup step. 1958 # This includes terminating the process for each target, if any. 1959 # We'd like to reuse the debugger for our next test without incurring 1960 # the initialization overhead. 1961 targets = [] 1962 for target in self.dbg: 1963 if target: 1964 targets.append(target) 1965 process = target.GetProcess() 1966 if process: 1967 rc = self.invoke(process, "Kill") 1968 assert rc.Success() 1969 for target in targets: 1970 self.dbg.DeleteTarget(target) 1971 1972 # Assert that all targets are deleted. 1973 self.assertEqual(self.dbg.GetNumTargets(), 0) 1974 1975 # Do this last, to make sure it's in reverse order from how we setup. 1976 Base.tearDown(self) 1977 1978 def switch_to_thread_with_stop_reason(self, stop_reason): 1979 """ 1980 Run the 'thread list' command, and select the thread with stop reason as 1981 'stop_reason'. If no such thread exists, no select action is done. 1982 """ 1983 from .lldbutil import stop_reason_to_str 1984 1985 self.runCmd("thread list") 1986 output = self.res.GetOutput() 1987 thread_line_pattern = re.compile( 1988 "^[ *] thread #([0-9]+):.*stop reason = %s" 1989 % stop_reason_to_str(stop_reason) 1990 ) 1991 for line in output.splitlines(): 1992 matched = thread_line_pattern.match(line) 1993 if matched: 1994 self.runCmd("thread select %s" % matched.group(1)) 1995 1996 def runCmd(self, cmd, msg=None, check=True, trace=False, inHistory=False): 1997 """ 1998 Ask the command interpreter to handle the command and then check its 1999 return status. 2000 """ 2001 # Fail fast if 'cmd' is not meaningful. 2002 if cmd is None: 2003 raise Exception("Bad 'cmd' parameter encountered") 2004 2005 trace = True if traceAlways else trace 2006 2007 if cmd.startswith("target create "): 2008 cmd = cmd.replace("target create ", "file ") 2009 2010 running = cmd.startswith("run") or cmd.startswith("process launch") 2011 2012 for i in range(self.maxLaunchCount if running else 1): 2013 with recording(self, trace) as sbuf: 2014 print("runCmd:", cmd, file=sbuf) 2015 if not check: 2016 print("check of return status not required", file=sbuf) 2017 2018 self.ci.HandleCommand(cmd, self.res, inHistory) 2019 2020 with recording(self, trace) as sbuf: 2021 if self.res.Succeeded(): 2022 print("output:", self.res.GetOutput(), file=sbuf) 2023 else: 2024 print("runCmd failed!", file=sbuf) 2025 print(self.res.GetError(), file=sbuf) 2026 2027 if self.res.Succeeded(): 2028 break 2029 elif running: 2030 # For process launch, wait some time before possible next try. 2031 time.sleep(self.timeWaitNextLaunch) 2032 with recording(self, trace) as sbuf: 2033 print("Command '" + cmd + "' failed!", file=sbuf) 2034 2035 if check: 2036 output = "" 2037 if self.res.GetOutput(): 2038 output += "\nCommand output:\n" + self.res.GetOutput() 2039 if self.res.GetError(): 2040 output += "\nError output:\n" + self.res.GetError() 2041 if msg: 2042 msg += output 2043 if cmd: 2044 cmd += output 2045 self.assertTrue(self.res.Succeeded(), msg if (msg) else CMD_MSG(cmd)) 2046 2047 def match( 2048 self, str, patterns, msg=None, trace=False, error=False, matching=True, exe=True 2049 ): 2050 """run command in str, and match the result against regexp in patterns returning the match object for the first matching pattern 2051 2052 Otherwise, all the arguments have the same meanings as for the expect function 2053 """ 2054 2055 trace = True if traceAlways else trace 2056 2057 if exe: 2058 # First run the command. If we are expecting error, set check=False. 2059 # Pass the assert message along since it provides more semantic 2060 # info. 2061 self.runCmd(str, msg=msg, trace=(True if trace else False), check=not error) 2062 2063 # Then compare the output against expected strings. 2064 output = self.res.GetError() if error else self.res.GetOutput() 2065 2066 # If error is True, the API client expects the command to fail! 2067 if error: 2068 self.assertFalse( 2069 self.res.Succeeded(), "Command '" + str + "' is expected to fail!" 2070 ) 2071 else: 2072 # No execution required, just compare str against the golden input. 2073 output = str 2074 with recording(self, trace) as sbuf: 2075 print("looking at:", output, file=sbuf) 2076 2077 # The heading says either "Expecting" or "Not expecting". 2078 heading = "Expecting" if matching else "Not expecting" 2079 2080 for pattern in patterns: 2081 # Match Objects always have a boolean value of True. 2082 match_object = re.search(pattern, output) 2083 matched = bool(match_object) 2084 with recording(self, trace) as sbuf: 2085 print("%s pattern: %s" % (heading, pattern), file=sbuf) 2086 print("Matched" if matched else "Not matched", file=sbuf) 2087 if matched: 2088 break 2089 2090 self.assertTrue( 2091 matched if matching else not matched, 2092 msg if msg else EXP_MSG(str, output, exe), 2093 ) 2094 2095 return match_object 2096 2097 def check_completion_with_desc( 2098 self, str_input, match_desc_pairs, enforce_order=False 2099 ): 2100 """ 2101 Checks that when the given input is completed at the given list of 2102 completions and descriptions is returned. 2103 :param str_input: The input that should be completed. The completion happens at the end of the string. 2104 :param match_desc_pairs: A list of pairs that indicate what completions have to be in the list of 2105 completions returned by LLDB. The first element of the pair is the completion 2106 string that LLDB should generate and the second element the description. 2107 :param enforce_order: True iff the order in which the completions are returned by LLDB 2108 should match the order of the match_desc_pairs pairs. 2109 """ 2110 interp = self.dbg.GetCommandInterpreter() 2111 match_strings = lldb.SBStringList() 2112 description_strings = lldb.SBStringList() 2113 num_matches = interp.HandleCompletionWithDescriptions( 2114 str_input, len(str_input), 0, -1, match_strings, description_strings 2115 ) 2116 self.assertEqual(len(description_strings), len(match_strings)) 2117 2118 # The index of the last matched description in description_strings or 2119 # -1 if no description has been matched yet. 2120 last_found_index = -1 2121 out_of_order_errors = "" 2122 missing_pairs = [] 2123 for pair in match_desc_pairs: 2124 found_pair = False 2125 for i in range(num_matches + 1): 2126 match_candidate = match_strings.GetStringAtIndex(i) 2127 description_candidate = description_strings.GetStringAtIndex(i) 2128 if match_candidate == pair[0] and description_candidate == pair[1]: 2129 found_pair = True 2130 if enforce_order and last_found_index > i: 2131 new_err = ( 2132 "Found completion " 2133 + pair[0] 2134 + " at index " 2135 + str(i) 2136 + " in returned completion list but " 2137 + "should have been after completion " 2138 + match_strings.GetStringAtIndex(last_found_index) 2139 + " (index:" 2140 + str(last_found_index) 2141 + ")\n" 2142 ) 2143 out_of_order_errors += new_err 2144 last_found_index = i 2145 break 2146 if not found_pair: 2147 missing_pairs.append(pair) 2148 2149 error_msg = "" 2150 got_failure = False 2151 if len(missing_pairs): 2152 got_failure = True 2153 error_msg += "Missing pairs:\n" 2154 for pair in missing_pairs: 2155 error_msg += " [" + pair[0] + ":" + pair[1] + "]\n" 2156 if len(out_of_order_errors): 2157 got_failure = True 2158 error_msg += out_of_order_errors 2159 if got_failure: 2160 error_msg += ( 2161 "Got the following " + str(num_matches) + " completions back:\n" 2162 ) 2163 for i in range(num_matches + 1): 2164 match_candidate = match_strings.GetStringAtIndex(i) 2165 description_candidate = description_strings.GetStringAtIndex(i) 2166 error_msg += ( 2167 "[" 2168 + match_candidate 2169 + ":" 2170 + description_candidate 2171 + "] index " 2172 + str(i) 2173 + "\n" 2174 ) 2175 self.assertFalse(got_failure, error_msg) 2176 2177 def complete_from_to(self, str_input, patterns): 2178 """Test that the completion mechanism completes str_input to patterns, 2179 where patterns could be a single pattern-string or a list of 2180 pattern-strings. 2181 2182 If there is only one pattern and it is exactly equal to str_input, this 2183 assumes that there should be no completions provided and that the result 2184 should be the same as the input.""" 2185 2186 # Patterns should not be None in order to proceed. 2187 self.assertFalse(patterns is None) 2188 # And should be either a string or list of strings. Check for list type 2189 # below, if not, make a list out of the singleton string. If patterns 2190 # is not a string or not a list of strings, there'll be runtime errors 2191 # later on. 2192 if not isinstance(patterns, list): 2193 patterns = [patterns] 2194 2195 interp = self.dbg.GetCommandInterpreter() 2196 match_strings = lldb.SBStringList() 2197 num_matches = interp.HandleCompletion( 2198 str_input, len(str_input), 0, -1, match_strings 2199 ) 2200 common_match = match_strings.GetStringAtIndex(0) 2201 if num_matches == 0: 2202 compare_string = str_input 2203 else: 2204 if common_match != None and len(common_match) > 0: 2205 compare_string = str_input + common_match 2206 else: 2207 compare_string = "" 2208 for idx in range(1, num_matches + 1): 2209 compare_string += match_strings.GetStringAtIndex(idx) + "\n" 2210 2211 if len(patterns) == 1 and str_input == patterns[0] and num_matches: 2212 self.fail("Expected no completions but got:\n" + compare_string) 2213 2214 for p in patterns: 2215 self.expect( 2216 compare_string, 2217 msg=COMPLETION_MSG(str_input, p, match_strings), 2218 exe=False, 2219 substrs=[p], 2220 ) 2221 2222 def completions_match(self, command, completions): 2223 """Checks that the completions for the given command are equal to the 2224 given list of completions""" 2225 interp = self.dbg.GetCommandInterpreter() 2226 match_strings = lldb.SBStringList() 2227 interp.HandleCompletion(command, len(command), 0, -1, match_strings) 2228 # match_strings is a 1-indexed list, so we have to slice... 2229 self.assertItemsEqual( 2230 completions, list(match_strings)[1:], "List of returned completion is wrong" 2231 ) 2232 2233 def completions_contain(self, command, completions): 2234 """Checks that the completions for the given command contain the given 2235 list of completions.""" 2236 interp = self.dbg.GetCommandInterpreter() 2237 match_strings = lldb.SBStringList() 2238 interp.HandleCompletion(command, len(command), 0, -1, match_strings) 2239 for completion in completions: 2240 # match_strings is a 1-indexed list, so we have to slice... 2241 self.assertIn( 2242 completion, list(match_strings)[1:], "Couldn't find expected completion" 2243 ) 2244 2245 def filecheck( 2246 self, command, check_file, filecheck_options="", expect_cmd_failure=False 2247 ): 2248 # Run the command. 2249 self.runCmd( 2250 command, 2251 check=(not expect_cmd_failure), 2252 msg="FileCheck'ing result of `{0}`".format(command), 2253 ) 2254 2255 self.assertTrue((not expect_cmd_failure) == self.res.Succeeded()) 2256 2257 # Get the error text if there was an error, and the regular text if not. 2258 output = self.res.GetOutput() if self.res.Succeeded() else self.res.GetError() 2259 2260 # Assemble the absolute path to the check file. As a convenience for 2261 # LLDB inline tests, assume that the check file is a relative path to 2262 # a file within the inline test directory. 2263 if check_file.endswith(".pyc"): 2264 check_file = check_file[:-1] 2265 check_file_abs = os.path.abspath(check_file) 2266 2267 # Run FileCheck. 2268 filecheck_bin = configuration.get_filecheck_path() 2269 if not filecheck_bin: 2270 self.assertTrue(False, "No valid FileCheck executable specified") 2271 filecheck_args = [filecheck_bin, check_file_abs] 2272 if filecheck_options: 2273 filecheck_args.append(filecheck_options) 2274 subproc = Popen( 2275 filecheck_args, 2276 stdin=PIPE, 2277 stdout=PIPE, 2278 stderr=PIPE, 2279 universal_newlines=True, 2280 ) 2281 cmd_stdout, cmd_stderr = subproc.communicate(input=output) 2282 cmd_status = subproc.returncode 2283 2284 filecheck_cmd = " ".join(filecheck_args) 2285 filecheck_trace = """ 2286--- FileCheck trace (code={0}) --- 2287{1} 2288 2289FileCheck input: 2290{2} 2291 2292FileCheck output: 2293{3} 2294{4} 2295""".format( 2296 cmd_status, filecheck_cmd, output, cmd_stdout, cmd_stderr 2297 ) 2298 2299 trace = cmd_status != 0 or traceAlways 2300 with recording(self, trace) as sbuf: 2301 print(filecheck_trace, file=sbuf) 2302 2303 self.assertTrue(cmd_status == 0) 2304 2305 def expect( 2306 self, 2307 string, 2308 msg=None, 2309 patterns=None, 2310 startstr=None, 2311 endstr=None, 2312 substrs=None, 2313 trace=False, 2314 error=False, 2315 ordered=True, 2316 matching=True, 2317 exe=True, 2318 inHistory=False, 2319 ): 2320 """ 2321 Similar to runCmd; with additional expect style output matching ability. 2322 2323 Ask the command interpreter to handle the command and then check its 2324 return status. The 'msg' parameter specifies an informational assert 2325 message. We expect the output from running the command to start with 2326 'startstr', matches the substrings contained in 'substrs', and regexp 2327 matches the patterns contained in 'patterns'. 2328 2329 When matching is true and ordered is true, which are both the default, 2330 the strings in the substrs array have to appear in the command output 2331 in the order in which they appear in the array. 2332 2333 If the keyword argument error is set to True, it signifies that the API 2334 client is expecting the command to fail. In this case, the error stream 2335 from running the command is retrieved and compared against the golden 2336 input, instead. 2337 2338 If the keyword argument matching is set to False, it signifies that the API 2339 client is expecting the output of the command not to match the golden 2340 input. 2341 2342 Finally, the required argument 'string' represents the lldb command to be 2343 sent to the command interpreter. In case the keyword argument 'exe' is 2344 set to False, the 'string' is treated as a string to be matched/not-matched 2345 against the golden input. 2346 """ 2347 # Catch cases where `expect` has been miscalled. Specifically, prevent 2348 # this easy to make mistake: 2349 # self.expect("lldb command", "some substr") 2350 # The `msg` parameter is used only when a failed match occurs. A failed 2351 # match can only occur when one of `patterns`, `startstr`, `endstr`, or 2352 # `substrs` has been given. Thus, if a `msg` is given, it's an error to 2353 # not also provide one of the matcher parameters. 2354 if msg and not (patterns or startstr or endstr or substrs or error): 2355 assert False, "expect() missing a matcher argument" 2356 2357 # Check `patterns` and `substrs` are not accidentally given as strings. 2358 assert not isinstance(patterns, str), "patterns must be a collection of strings" 2359 assert not isinstance(substrs, str), "substrs must be a collection of strings" 2360 2361 trace = True if traceAlways else trace 2362 2363 if exe: 2364 # First run the command. If we are expecting error, set check=False. 2365 # Pass the assert message along since it provides more semantic 2366 # info. 2367 self.runCmd( 2368 string, 2369 msg=msg, 2370 trace=(True if trace else False), 2371 check=not error, 2372 inHistory=inHistory, 2373 ) 2374 2375 # Then compare the output against expected strings. 2376 output = self.res.GetError() if error else self.res.GetOutput() 2377 2378 # If error is True, the API client expects the command to fail! 2379 if error: 2380 self.assertFalse( 2381 self.res.Succeeded(), 2382 "Command '" + string + "' is expected to fail!", 2383 ) 2384 else: 2385 # No execution required, just compare string against the golden input. 2386 if isinstance(string, lldb.SBCommandReturnObject): 2387 output = string.GetOutput() 2388 else: 2389 output = string 2390 with recording(self, trace) as sbuf: 2391 print("looking at:", output, file=sbuf) 2392 2393 expecting_str = "Expecting" if matching else "Not expecting" 2394 2395 def found_str(matched): 2396 return "was found" if matched else "was not found" 2397 2398 # To be used as assert fail message and/or trace content 2399 log_lines = [ 2400 "{}:".format("Ran command" if exe else "Checking string"), 2401 '"{}"'.format(string), 2402 # Space out command and output 2403 "", 2404 ] 2405 if exe: 2406 # Newline before output to make large strings more readable 2407 log_lines.append("Got output:\n{}".format(output)) 2408 2409 # Assume that we start matched if we want a match 2410 # Meaning if you have no conditions, matching or 2411 # not matching will always pass 2412 matched = matching 2413 2414 # We will stop checking on first failure 2415 if startstr: 2416 matched = output.startswith(startstr) 2417 log_lines.append( 2418 '{} start string: "{}" ({})'.format( 2419 expecting_str, startstr, found_str(matched) 2420 ) 2421 ) 2422 2423 if endstr and matched == matching: 2424 matched = output.endswith(endstr) 2425 log_lines.append( 2426 '{} end string: "{}" ({})'.format( 2427 expecting_str, endstr, found_str(matched) 2428 ) 2429 ) 2430 2431 if substrs and matched == matching: 2432 start = 0 2433 for substr in substrs: 2434 index = output[start:].find(substr) 2435 start = start + index + len(substr) if ordered and matching else 0 2436 matched = index != -1 2437 log_lines.append( 2438 '{} sub string: "{}" ({})'.format( 2439 expecting_str, substr, found_str(matched) 2440 ) 2441 ) 2442 2443 if matched != matching: 2444 break 2445 2446 if patterns and matched == matching: 2447 for pattern in patterns: 2448 matched = re.search(pattern, output) 2449 2450 pattern_line = '{} regex pattern: "{}" ({}'.format( 2451 expecting_str, pattern, found_str(matched) 2452 ) 2453 if matched: 2454 pattern_line += ', matched "{}"'.format(matched.group(0)) 2455 pattern_line += ")" 2456 log_lines.append(pattern_line) 2457 2458 # Convert to bool because match objects 2459 # are True-ish but != True itself 2460 matched = bool(matched) 2461 if matched != matching: 2462 break 2463 2464 # If a check failed, add any extra assert message 2465 if msg is not None and matched != matching: 2466 log_lines.append(msg) 2467 2468 log_msg = "\n".join(log_lines) 2469 with recording(self, trace) as sbuf: 2470 print(log_msg, file=sbuf) 2471 if matched != matching: 2472 self.fail(log_msg) 2473 2474 def expect_expr( 2475 self, 2476 expr, 2477 result_summary=None, 2478 result_value=None, 2479 result_type=None, 2480 result_children=None, 2481 ): 2482 """ 2483 Evaluates the given expression and verifies the result. 2484 :param expr: The expression as a string. 2485 :param result_summary: The summary that the expression should have. None if the summary should not be checked. 2486 :param result_value: The value that the expression should have. None if the value should not be checked. 2487 :param result_type: The type that the expression result should have. None if the type should not be checked. 2488 :param result_children: The expected children of the expression result 2489 as a list of ValueChecks. None if the children shouldn't be checked. 2490 """ 2491 self.assertTrue( 2492 expr.strip() == expr, 2493 "Expression contains trailing/leading whitespace: '" + expr + "'", 2494 ) 2495 2496 frame = self.frame() 2497 options = lldb.SBExpressionOptions() 2498 2499 # Disable fix-its that tests don't pass by accident. 2500 options.SetAutoApplyFixIts(False) 2501 2502 # Set the usual default options for normal expressions. 2503 options.SetIgnoreBreakpoints(True) 2504 2505 if self.frame().IsValid(): 2506 options.SetLanguage(frame.GuessLanguage()) 2507 eval_result = self.frame().EvaluateExpression(expr, options) 2508 else: 2509 target = self.target() 2510 # If there is no selected target, run the expression in the dummy 2511 # target. 2512 if not target.IsValid(): 2513 target = self.dbg.GetDummyTarget() 2514 eval_result = target.EvaluateExpression(expr, options) 2515 2516 value_check = ValueCheck( 2517 type=result_type, 2518 value=result_value, 2519 summary=result_summary, 2520 children=result_children, 2521 ) 2522 value_check.check_value(self, eval_result, str(eval_result)) 2523 return eval_result 2524 2525 def expect_var_path( 2526 self, var_path, summary=None, value=None, type=None, children=None 2527 ): 2528 """ 2529 Evaluates the given variable path and verifies the result. 2530 See also 'frame variable' and SBFrame.GetValueForVariablePath. 2531 :param var_path: The variable path as a string. 2532 :param summary: The summary that the variable should have. None if the summary should not be checked. 2533 :param value: The value that the variable should have. None if the value should not be checked. 2534 :param type: The type that the variable result should have. None if the type should not be checked. 2535 :param children: The expected children of the variable as a list of ValueChecks. 2536 None if the children shouldn't be checked. 2537 """ 2538 self.assertTrue( 2539 var_path.strip() == var_path, 2540 "Expression contains trailing/leading whitespace: '" + var_path + "'", 2541 ) 2542 2543 frame = self.frame() 2544 eval_result = frame.GetValueForVariablePath(var_path) 2545 2546 value_check = ValueCheck( 2547 type=type, value=value, summary=summary, children=children 2548 ) 2549 value_check.check_value(self, eval_result, str(eval_result)) 2550 return eval_result 2551 2552 """Assert that an lldb.SBError is in the "success" state.""" 2553 2554 def assertSuccess(self, obj, msg=None): 2555 if not obj.Success(): 2556 error = obj.GetCString() 2557 self.fail(self._formatMessage(msg, "'{}' is not success".format(error))) 2558 2559 """Assert that an lldb.SBError is in the "failure" state.""" 2560 2561 def assertFailure(self, obj, error_str=None, msg=None): 2562 if obj.Success(): 2563 self.fail(self._formatMessage(msg, "Error not in a fail state")) 2564 2565 if error_str == None: 2566 return 2567 2568 error = obj.GetCString() 2569 self.assertEqual(error, error_str, msg) 2570 2571 """Assert that a command return object is successful""" 2572 2573 def assertCommandReturn(self, obj, msg=None): 2574 if not obj.Succeeded(): 2575 error = obj.GetError() 2576 self.fail(self._formatMessage(msg, "'{}' is not success".format(error))) 2577 2578 """Assert two states are equal""" 2579 2580 def assertState(self, first, second, msg=None): 2581 if first != second: 2582 error = "{} ({}) != {} ({})".format( 2583 lldbutil.state_type_to_str(first), 2584 first, 2585 lldbutil.state_type_to_str(second), 2586 second, 2587 ) 2588 self.fail(self._formatMessage(msg, error)) 2589 2590 """Assert two stop reasons are equal""" 2591 2592 def assertStopReason(self, first, second, msg=None): 2593 if first != second: 2594 error = "{} ({}) != {} ({})".format( 2595 lldbutil.stop_reason_to_str(first), 2596 first, 2597 lldbutil.stop_reason_to_str(second), 2598 second, 2599 ) 2600 self.fail(self._formatMessage(msg, error)) 2601 2602 def createTestTarget(self, file_path=None, msg=None, load_dependent_modules=True): 2603 """ 2604 Creates a target from the file found at the given file path. 2605 Asserts that the resulting target is valid. 2606 :param file_path: The file path that should be used to create the target. 2607 The default argument opens the current default test 2608 executable in the current test directory. 2609 :param msg: A custom error message. 2610 """ 2611 if file_path is None: 2612 file_path = self.getBuildArtifact("a.out") 2613 error = lldb.SBError() 2614 triple = "" 2615 platform = "" 2616 target = self.dbg.CreateTarget( 2617 file_path, triple, platform, load_dependent_modules, error 2618 ) 2619 if error.Fail(): 2620 err = "Couldn't create target for path '{}': {}".format( 2621 file_path, str(error) 2622 ) 2623 self.fail(self._formatMessage(msg, err)) 2624 2625 self.assertTrue(target.IsValid(), "Got invalid target without error") 2626 return target 2627 2628 # ================================================= 2629 # Misc. helper methods for debugging test execution 2630 # ================================================= 2631 2632 def DebugSBValue(self, val): 2633 """Debug print a SBValue object, if traceAlways is True.""" 2634 from .lldbutil import value_type_to_str 2635 2636 if not traceAlways: 2637 return 2638 2639 err = sys.stderr 2640 err.write(val.GetName() + ":\n") 2641 err.write("\t" + "TypeName -> " + val.GetTypeName() + "\n") 2642 err.write("\t" + "ByteSize -> " + str(val.GetByteSize()) + "\n") 2643 err.write("\t" + "NumChildren -> " + str(val.GetNumChildren()) + "\n") 2644 err.write("\t" + "Value -> " + str(val.GetValue()) + "\n") 2645 err.write("\t" + "ValueAsUnsigned -> " + str(val.GetValueAsUnsigned()) + "\n") 2646 err.write( 2647 "\t" + "ValueType -> " + value_type_to_str(val.GetValueType()) + "\n" 2648 ) 2649 err.write("\t" + "Summary -> " + str(val.GetSummary()) + "\n") 2650 err.write("\t" + "IsPointerType -> " + str(val.TypeIsPointerType()) + "\n") 2651 err.write("\t" + "Location -> " + val.GetLocation() + "\n") 2652 2653 def DebugSBType(self, type): 2654 """Debug print a SBType object, if traceAlways is True.""" 2655 if not traceAlways: 2656 return 2657 2658 err = sys.stderr 2659 err.write(type.GetName() + ":\n") 2660 err.write("\t" + "ByteSize -> " + str(type.GetByteSize()) + "\n") 2661 err.write("\t" + "IsAggregateType -> " + str(type.IsAggregateType()) + "\n") 2662 err.write("\t" + "IsPointerType -> " + str(type.IsPointerType()) + "\n") 2663 err.write("\t" + "IsReferenceType -> " + str(type.IsReferenceType()) + "\n") 2664 2665 def DebugPExpect(self, child): 2666 """Debug the spwaned pexpect object.""" 2667 if not traceAlways: 2668 return 2669 2670 print(child) 2671 2672 @classmethod 2673 def RemoveTempFile(cls, file): 2674 if os.path.exists(file): 2675 remove_file(file) 2676 2677 2678# On Windows, the first attempt to delete a recently-touched file can fail 2679# because of a race with antimalware scanners. This function will detect a 2680# failure and retry. 2681 2682 2683def remove_file(file, num_retries=1, sleep_duration=0.5): 2684 for i in range(num_retries + 1): 2685 try: 2686 os.remove(file) 2687 return True 2688 except: 2689 time.sleep(sleep_duration) 2690 continue 2691 return False 2692