2754 lines · python
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 the5common 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 the8entire 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 types12...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 types16compilers=['clang']17 18Configuration: arch=x86_64 compiler=clang19----------------------------------------------------------------------20Collected 72 tests21 22........................................................................23----------------------------------------------------------------------24Ran 72 tests in 135.468s25 26OK27$28"""29 30# System modules31import abc32from functools import wraps33import gc34import io35import json36import os.path37import re38import shutil39import shlex40import signal41from subprocess import *42import sys43import time44import traceback45 46# Third-party modules47import unittest48 49# LLDB modules50import lldb51from . import configuration52from . import cpu_feature53from . import decorators54from . import lldbplatformutil55from . import lldbtest_config56from . import lldbutil57from . import test_categories58from lldbsuite.support import encoded_file59from lldbsuite.support import funcutils60from lldbsuite.test_event import build_exception61 62# See also dotest.parseOptionsAndInitTestdirs(), where the environment variables63# 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 = True68else:69 traceAlways = False70 71# By default, doCleanup is True.72if "LLDB_DO_CLEANUP" in os.environ and os.environ["LLDB_DO_CLEANUP"] == "NO":73 doCleanup = False74else:75 doCleanup = True76 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 147STOPPED_DUE_TO_HISTORY_BOUNDARY = "Process should be stopped due to history boundary"148 149DATA_TYPES_DISPLAYED_CORRECTLY = "Data type(s) displayed correctly"150 151VALID_BREAKPOINT = "Got a valid breakpoint"152 153VALID_BREAKPOINT_LOCATION = "Got a valid breakpoint location"154 155VALID_COMMAND_INTERPRETER = "Got a valid command interpreter"156 157VALID_FILESPEC = "Got a valid filespec"158 159VALID_MODULE = "Got a valid module"160 161VALID_PROCESS = "Got a valid process"162 163VALID_SYMBOL = "Got a valid symbol"164 165VALID_TARGET = "Got a valid target"166 167VALID_PLATFORM = "Got a valid platform"168 169VALID_TYPE = "Got a valid type"170 171VALID_VARIABLE = "Got a valid variable"172 173VARIABLES_DISPLAYED_CORRECTLY = "Variable(s) displayed correctly"174 175WATCHPOINT_CREATED = "Watchpoint created successfully"176 177 178def CMD_MSG(command):179 """A generic "Command '%s' did not return successfully" message generator."""180 return f"Command '{command}' did not return successfully"181 182 183def COMPLETION_MSG(str_before, str_after, completions):184 """A generic assertion failed message generator for the completion mechanism."""185 return "'%s' successfully completes to '%s', but completions were:\n%s" % (186 str_before,187 str_after,188 "\n".join(completions),189 )190 191 192def EXP_MSG(str, actual, exe):193 """A generic "'%s' returned unexpected result" message generator if exe.194 Otherwise, it generates "'%s' does not match expected result" message."""195 196 return "'%s' %s result, got '%s'" % (197 str,198 "returned unexpected" if exe else "does not match expected",199 actual.strip(),200 )201 202 203def SETTING_MSG(setting):204 """A generic "Value of setting '%s' is not correct" message generator."""205 return "Value of setting '%s' is not correct" % setting206 207 208def line_number(filename, string_to_match):209 """Helper function to return the line number of the first matched string."""210 with io.open(filename, mode="r", encoding="utf-8") as f:211 for i, line in enumerate(f):212 if line.find(string_to_match) != -1:213 # Found our match.214 return i + 1215 raise Exception("Unable to find '%s' within file %s" % (string_to_match, filename))216 217 218def get_line(filename, line_number):219 """Return the text of the line at the 1-based line number."""220 with io.open(filename, mode="r", encoding="utf-8") as f:221 return f.readlines()[line_number - 1]222 223 224def pointer_size():225 """Return the pointer size of the host system."""226 import ctypes227 228 a_pointer = ctypes.c_void_p(0xFFFF)229 return 8 * ctypes.sizeof(a_pointer)230 231 232def is_exe(fpath):233 """Returns true if fpath is an executable."""234 if fpath is None:235 return False236 if sys.platform == "win32":237 if not fpath.endswith(".exe"):238 fpath += ".exe"239 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)240 241 242def which(program):243 """Returns the full path to a program; None otherwise."""244 fpath, fname = os.path.split(program)245 if fpath:246 if is_exe(program):247 return program248 else:249 for path in os.environ["PATH"].split(os.pathsep):250 exe_file = os.path.join(path, program)251 if is_exe(exe_file):252 return exe_file253 return None254 255 256class ValueCheck:257 def __init__(258 self,259 name=None,260 value=None,261 type=None,262 summary=None,263 children=None,264 dereference=None,265 ):266 """267 :param name: The name that the SBValue should have. None if the summary268 should not be checked.269 :param summary: The summary that the SBValue should have. None if the270 summary should not be checked.271 :param value: The value that the SBValue should have. None if the value272 should not be checked.273 :param type: The type that the SBValue result should have. None if the274 type should not be checked.275 :param children: A list of ValueChecks that need to match the children276 of this SBValue. None if children shouldn't be checked.277 The order of checks is the order of the checks in the278 list. The number of checks has to match the number of279 children.280 :param dereference: A ValueCheck for the SBValue returned by the281 `Dereference` function.282 """283 self.expect_name = name284 self.expect_value = value285 self.expect_type = type286 self.expect_summary = summary287 self.children = children288 self.dereference = dereference289 290 def check_value(self, test_base, val, error_msg=None):291 """292 Checks that the given value matches the currently set properties293 of this ValueCheck. If a match failed, the given TestBase will294 be used to emit an error. A custom error message can be specified295 that will be used to describe failed check for this SBValue (but296 not errors in the child values).297 """298 299 this_error_msg = error_msg if error_msg else ""300 this_error_msg += "\nChecking SBValue: " + str(val)301 302 test_base.assertSuccess(val.GetError())303 304 # Python 3.6 doesn't declare a `re.Pattern` type, get the dynamic type.305 pattern_type = type(re.compile(""))306 307 if self.expect_name:308 test_base.assertEqual(self.expect_name, val.GetName(), this_error_msg)309 if self.expect_value:310 if isinstance(self.expect_value, pattern_type):311 test_base.assertRegex(val.GetValue(), self.expect_value, this_error_msg)312 else:313 test_base.assertEqual(self.expect_value, val.GetValue(), this_error_msg)314 if self.expect_type:315 test_base.assertEqual(316 self.expect_type, val.GetDisplayTypeName(), this_error_msg317 )318 if self.expect_summary:319 if isinstance(self.expect_summary, pattern_type):320 test_base.assertRegex(321 val.GetSummary(), self.expect_summary, this_error_msg322 )323 else:324 test_base.assertEqual(325 self.expect_summary, val.GetSummary(), this_error_msg326 )327 if self.children is not None:328 self.check_value_children(test_base, val, error_msg)329 330 if self.dereference is not None:331 self.dereference.check_value(test_base, val.Dereference(), error_msg)332 333 def check_value_children(self, test_base, val, error_msg=None):334 """335 Checks that the children of a SBValue match a certain structure and336 have certain properties.337 338 :param test_base: The current test's TestBase object.339 :param val: The SBValue to check.340 """341 342 this_error_msg = error_msg if error_msg else ""343 this_error_msg += "\nChecking SBValue: " + str(val)344 345 test_base.assertEqual(len(self.children), val.GetNumChildren(), this_error_msg)346 347 for i in range(0, val.GetNumChildren()):348 expected_child = self.children[i]349 actual_child = val.GetChildAtIndex(i)350 child_error = "Checking child with index " + str(i) + ":\n" + error_msg351 expected_child.check_value(test_base, actual_child, child_error)352 353 354class recording(io.StringIO):355 """356 A nice little context manager for recording the debugger interactions into357 our session object. If trace flag is ON, it also emits the interactions358 into the stderr.359 """360 361 def __init__(self, test, trace):362 """Create a io.StringIO instance; record the session obj and trace flag."""363 io.StringIO.__init__(self)364 # The test might not have undergone the 'setUp(self)' phase yet, so that365 # the attribute 'session' might not even exist yet.366 self.session = getattr(test, "session", None) if test else None367 self.trace = trace368 369 def __enter__(self):370 """371 Context management protocol on entry to the body of the with statement.372 Just return the io.StringIO object.373 """374 return self375 376 def __exit__(self, type, value, tb):377 """378 Context management protocol on exit from the body of the with statement.379 If trace is ON, it emits the recordings into stderr. Always add the380 recordings to our session object. And close the io.StringIO object, too.381 """382 if self.trace:383 print(self.getvalue(), file=sys.stderr)384 if self.session:385 print(self.getvalue(), file=self.session)386 self.close()387 388 389class _BaseProcess(object, metaclass=abc.ABCMeta):390 @abc.abstractproperty391 def pid(self):392 """Returns process PID if has been launched already."""393 394 @abc.abstractmethod395 def launch(self, executable, args, extra_env):396 """Launches new process with given executable and args."""397 398 @abc.abstractmethod399 def terminate(self):400 """Terminates previously launched process.."""401 402 403class _LocalProcess(_BaseProcess):404 def __init__(self, trace_on):405 self._proc = None406 self._trace_on = trace_on407 self._delayafterterminate = 0.1408 409 @property410 def pid(self):411 return self._proc.pid412 413 def launch(self, executable, args, extra_env):414 env = None415 if extra_env:416 env = dict(os.environ)417 env.update([kv.split("=", 1) for kv in extra_env])418 419 self._proc = Popen(420 [executable] + args,421 stdout=DEVNULL if not self._trace_on else None,422 stdin=PIPE,423 env=env,424 )425 426 def terminate(self):427 if self._proc.poll() is None:428 # Terminate _proc like it does the pexpect429 signals_to_try = [430 sig for sig in ["SIGHUP", "SIGCONT", "SIGINT"] if sig in dir(signal)431 ]432 for sig in signals_to_try:433 try:434 self._proc.send_signal(getattr(signal, sig))435 time.sleep(self._delayafterterminate)436 if self._proc.poll() is not None:437 return438 except ValueError:439 pass # Windows says SIGINT is not a valid signal to send440 self._proc.terminate()441 time.sleep(self._delayafterterminate)442 if self._proc.poll() is not None:443 return444 self._proc.kill()445 time.sleep(self._delayafterterminate)446 447 def poll(self):448 return self._proc.poll()449 450 def wait(self, timeout=None):451 return self._proc.wait(timeout)452 453 454class _RemoteProcess(_BaseProcess):455 def __init__(self, install_remote):456 self._pid = None457 self._install_remote = install_remote458 459 @property460 def pid(self):461 return self._pid462 463 def launch(self, executable, args, extra_env):464 if self._install_remote:465 src_path = executable466 dst_path = lldbutil.join_remote_paths(467 lldb.remote_platform.GetWorkingDirectory(), os.path.basename(executable)468 )469 470 dst_file_spec = lldb.SBFileSpec(dst_path, False)471 err = lldb.remote_platform.Install(472 lldb.SBFileSpec(src_path, True), dst_file_spec473 )474 if err.Fail():475 raise Exception(476 "remote_platform.Install('%s', '%s') failed: %s"477 % (src_path, dst_path, err)478 )479 else:480 dst_path = executable481 dst_file_spec = lldb.SBFileSpec(executable, False)482 483 launch_info = lldb.SBLaunchInfo(args)484 launch_info.SetExecutableFile(dst_file_spec, True)485 launch_info.SetWorkingDirectory(lldb.remote_platform.GetWorkingDirectory())486 487 # Redirect stdout and stderr to /dev/null488 launch_info.AddSuppressFileAction(1, False, True)489 launch_info.AddSuppressFileAction(2, False, True)490 491 if extra_env:492 launch_info.SetEnvironmentEntries(extra_env, True)493 494 err = lldb.remote_platform.Launch(launch_info)495 if err.Fail():496 raise Exception(497 "remote_platform.Launch('%s', '%s') failed: %s" % (dst_path, args, err)498 )499 self._pid = launch_info.GetProcessID()500 501 def terminate(self):502 lldb.remote_platform.Kill(self._pid)503 504 505def getsource_if_available(obj):506 """507 Return the text of the source code for an object if available. Otherwise,508 a print representation is returned.509 """510 import inspect511 512 try:513 return inspect.getsource(obj)514 except:515 return repr(obj)516 517 518def builder_module():519 return lldbplatformutil.builder_module()520 521 522class Base(unittest.TestCase):523 """524 Abstract base for performing lldb (see TestBase) or other generic tests (see525 BenchBase for one example). lldbtest.Base works with the test driver to526 accomplish things.527 528 """529 530 # The concrete subclass should override this attribute.531 mydir = None532 533 # Keep track of the old current working directory.534 oldcwd = None535 536 # Maximum allowed attempts when launching the inferior process.537 # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable.538 maxLaunchCount = 1539 540 # Time to wait before the next launching attempt in second(s).541 # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable.542 timeWaitNextLaunch = 1.0543 544 @staticmethod545 def compute_mydir(test_file):546 """Subclasses should call this function to correctly calculate the547 required "mydir" attribute as follows:548 549 mydir = TestBase.compute_mydir(__file__)550 """551 # /abs/path/to/packages/group/subdir/mytest.py -> group/subdir552 lldb_test_src = configuration.test_src_root553 if not test_file.startswith(lldb_test_src):554 raise Exception(555 "Test file '%s' must reside within lldb_test_src "556 "(which is '%s')." % (test_file, lldb_test_src)557 )558 return os.path.dirname(os.path.relpath(test_file, start=lldb_test_src))559 560 def TraceOn(self):561 """Returns True if we are in trace mode (tracing detailed test execution)."""562 return traceAlways563 564 def trace(self, *args, **kwargs):565 with recording(self, self.TraceOn()) as sbuf:566 print(*args, file=sbuf, **kwargs)567 568 @classmethod569 def setUpClass(cls):570 """571 Python unittest framework class setup fixture.572 Do current directory manipulation.573 """574 # Fail fast if 'mydir' attribute is not overridden.575 if not cls.mydir:576 cls.mydir = Base.compute_mydir(sys.modules[cls.__module__].__file__)577 if not cls.mydir:578 raise Exception("Subclasses must override the 'mydir' attribute.")579 580 # Save old working directory.581 cls.oldcwd = os.getcwd()582 583 full_dir = os.path.join(configuration.test_src_root, cls.mydir)584 if traceAlways:585 print("Change dir to:", full_dir, file=sys.stderr)586 os.chdir(full_dir)587 588 # Set platform context.589 cls.platformContext = lldbplatformutil.createPlatformContext()590 591 @classmethod592 def tearDownClass(cls):593 """594 Python unittest framework class teardown fixture.595 Do class-wide cleanup.596 """597 598 if doCleanup:599 # First, let's do the platform-specific cleanup.600 module = builder_module()601 module.cleanup()602 603 # Subclass might have specific cleanup function defined.604 if getattr(cls, "classCleanup", None):605 if traceAlways:606 print(607 "Call class-specific cleanup function for class:",608 cls,609 file=sys.stderr,610 )611 try:612 cls.classCleanup()613 except:614 exc_type, exc_value, exc_tb = sys.exc_info()615 traceback.print_exception(exc_type, exc_value, exc_tb)616 617 # Restore old working directory.618 if traceAlways:619 print("Restore dir to:", cls.oldcwd, file=sys.stderr)620 os.chdir(cls.oldcwd)621 622 def enableLogChannelsForCurrentTest(self):623 if len(lldbtest_config.channels) == 0:624 return625 626 # if debug channels are specified in lldbtest_config.channels,627 # create a new set of log files for every test628 log_basename = self.getLogBasenameForCurrentTest()629 630 # confirm that the file is writeable631 host_log_path = "{}-host.log".format(log_basename)632 open(host_log_path, "w").close()633 self.log_files.append(host_log_path)634 635 log_enable = "log enable -Tpn -f {} ".format(host_log_path)636 for channel_with_categories in lldbtest_config.channels:637 channel_then_categories = channel_with_categories.split(" ", 1)638 channel = channel_then_categories[0]639 if len(channel_then_categories) > 1:640 categories = channel_then_categories[1]641 else:642 categories = "default"643 644 if channel == "gdb-remote" and lldb.remote_platform is None:645 # communicate gdb-remote categories to debugserver646 os.environ["LLDB_DEBUGSERVER_LOG_FLAGS"] = categories647 648 self.ci.HandleCommand(log_enable + channel_with_categories, self.res)649 if not self.res.Succeeded():650 raise Exception(651 "log enable failed (check LLDB_LOG_OPTION env variable)"652 )653 654 # Communicate log path name to debugserver & lldb-server655 # For remote debugging, these variables need to be set when starting the platform656 # instance.657 if lldb.remote_platform is None:658 server_log_path = "{}-server.log".format(log_basename)659 open(server_log_path, "w").close()660 self.log_files.append(server_log_path)661 os.environ["LLDB_DEBUGSERVER_LOG_FILE"] = server_log_path662 663 # Communicate channels to lldb-server664 os.environ["LLDB_SERVER_LOG_CHANNELS"] = ":".join(lldbtest_config.channels)665 666 self.addTearDownHook(self.disableLogChannelsForCurrentTest)667 668 def disableLogChannelsForCurrentTest(self):669 # close all log files that we opened670 for channel_and_categories in lldbtest_config.channels:671 # channel format - <channel-name> [<category0> [<category1> ...]]672 channel = channel_and_categories.split(" ", 1)[0]673 self.ci.HandleCommand("log disable " + channel, self.res)674 if not self.res.Succeeded():675 raise Exception(676 "log disable failed (check LLDB_LOG_OPTION env variable)"677 )678 679 # Retrieve the server log (if any) from the remote system. It is assumed the server log680 # is writing to the "server.log" file in the current test directory. This can be681 # achieved by setting LLDB_DEBUGSERVER_LOG_FILE="server.log" when starting remote682 # platform.683 if lldb.remote_platform:684 server_log_path = self.getLogBasenameForCurrentTest() + "-server.log"685 if lldb.remote_platform.Get(686 lldb.SBFileSpec("server.log"), lldb.SBFileSpec(server_log_path)687 ).Success():688 self.log_files.append(server_log_path)689 690 def setPlatformWorkingDir(self):691 if not lldb.remote_platform or not configuration.lldb_platform_working_dir:692 return693 694 components = self.mydir.split(os.path.sep) + [695 str(self.test_number),696 self.getBuildDirBasename(),697 ]698 remote_test_dir = configuration.lldb_platform_working_dir699 for c in components:700 remote_test_dir = lldbutil.join_remote_paths(remote_test_dir, c)701 error = lldb.remote_platform.MakeDirectory(702 remote_test_dir, 448703 ) # 448 = 0o700704 if error.Fail():705 raise Exception(706 "making remote directory '%s': %s" % (remote_test_dir, error)707 )708 709 lldb.remote_platform.SetWorkingDirectory(remote_test_dir)710 711 # This function removes all files from the current working directory while leaving712 # the directories in place. The cleanup is required to reduce the disk space required713 # by the test suite while leaving the directories untouched is neccessary because714 # sub-directories might belong to an other test715 def clean_working_directory():716 # TODO: Make it working on Windows when we need it for remote debugging support717 # TODO: Replace the heuristic to remove the files with a logic what collects the718 # list of files we have to remove during test runs.719 shell_cmd = lldb.SBPlatformShellCommand("rm %s/*" % remote_test_dir)720 lldb.remote_platform.Run(shell_cmd)721 722 self.addTearDownHook(clean_working_directory)723 724 def getSourceDir(self):725 """Return the full path to the current test."""726 return os.path.join(configuration.test_src_root, self.mydir)727 728 def getBuildDirBasename(self):729 return self.__class__.__module__ + "." + self.testMethodName730 731 def getBuildDir(self):732 """Return the full path to the current test."""733 return os.path.join(734 configuration.test_build_dir, self.mydir, self.getBuildDirBasename()735 )736 737 def makeBuildDir(self):738 """Create the test-specific working directory, deleting any previous739 contents."""740 bdir = self.getBuildDir()741 if os.path.isdir(bdir):742 shutil.rmtree(bdir)743 lldbutil.mkdir_p(bdir)744 745 def getBuildArtifact(self, name="a.out"):746 """Return absolute path to an artifact in the test's build directory."""747 return os.path.join(self.getBuildDir(), name)748 749 def getSourcePath(self, name):750 """Return absolute path to a file in the test's source directory."""751 return os.path.join(self.getSourceDir(), name)752 753 def getPlatformAvailablePorts(self):754 """Return ports available for connection to a lldb server on the remote platform."""755 return configuration.lldb_platform_available_ports756 757 @classmethod758 def setUpCommands(cls):759 commands = [760 # First of all, clear all settings to have clean state of global properties.761 "settings clear --all",762 # Disable Spotlight lookup. The testsuite creates763 # different binaries with the same UUID, because they only764 # differ in the debug info, which is not being hashed.765 "settings set symbols.enable-external-lookup false",766 # Inherit the TCC permissions from the inferior's parent.767 "settings set target.inherit-tcc true",768 # Based on https://discourse.llvm.org/t/running-lldb-in-a-container/76801/4769 "settings set target.disable-aslr false",770 # Kill rather than detach from the inferior if something goes wrong.771 "settings set target.detach-on-error false",772 # Disable fix-its by default so that incorrect expressions in tests don't773 # pass just because Clang thinks it has a fix-it.774 "settings set target.auto-apply-fixits false",775 # Testsuite runs in parallel and the host can have also other load.776 "settings set plugin.process.gdb-remote.packet-timeout 60",777 'settings set symbols.clang-modules-cache-path "{}"'.format(778 configuration.lldb_module_cache_dir779 ),780 # Disable colors by default.781 "settings set use-color false",782 # Disable the statusline by default.783 "settings set show-statusline false",784 ]785 786 # Set any user-overridden settings.787 for setting, value in configuration.settings:788 commands.append("setting set %s %s" % (setting, value))789 790 # Make sure that a sanitizer LLDB's environment doesn't get passed on.791 if (792 cls.platformContext793 and cls.platformContext.shlib_environment_var in os.environ794 ):795 commands.append(796 "settings set target.env-vars {}=".format(797 cls.platformContext.shlib_environment_var798 )799 )800 801 # Set environment variables for the inferior.802 if lldbtest_config.inferior_env:803 commands.append(804 "settings set target.env-vars {}".format(lldbtest_config.inferior_env)805 )806 return commands807 808 def setUp(self):809 """Fixture for unittest test case setup.810 811 It works with the test driver to conditionally skip tests and does other812 initializations."""813 # import traceback814 # traceback.print_stack()815 816 if "LLDB_MAX_LAUNCH_COUNT" in os.environ:817 self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"])818 819 if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ:820 self.timeWaitNextLaunch = float(os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"])821 822 if "LIBCXX_PATH" in os.environ:823 self.libcxxPath = os.environ["LIBCXX_PATH"]824 else:825 self.libcxxPath = None826 827 if "LLDBDAP_EXEC" in os.environ:828 self.lldbDAPExec = os.environ["LLDBDAP_EXEC"]829 else:830 self.lldbDAPExec = None831 832 self.lldbOption = " ".join("-o '" + s + "'" for s in self.setUpCommands())833 834 # If we spawn an lldb process for test (via pexpect), do not load the835 # init file unless told otherwise.836 if os.environ.get("NO_LLDBINIT") != "NO":837 self.lldbOption += " --no-lldbinit"838 839 # Assign the test method name to self.testMethodName.840 #841 # For an example of the use of this attribute, look at test/types dir.842 # There are a bunch of test cases under test/types and we don't want the843 # module cacheing subsystem to be confused with executable name "a.out"844 # used for all the test cases.845 self.testMethodName = self._testMethodName846 847 # This is for the case of directly spawning 'lldb'/'gdb' and interacting848 # with it using pexpect.849 self.child = None850 self.child_prompt = "(lldb) "851 # If the child is interacting with the embedded script interpreter,852 # there are two exits required during tear down, first to quit the853 # embedded script interpreter and second to quit the lldb command854 # interpreter.855 self.child_in_script_interpreter = False856 857 # These are for customized teardown cleanup.858 self.dict = None859 self.doTearDownCleanup = False860 # And in rare cases where there are multiple teardown cleanups.861 self.dicts = []862 self.doTearDownCleanups = False863 864 # List of spawned subproces.Popen objects865 self.subprocesses = []866 867 # List of log files produced by the current test.868 self.log_files = []869 870 # Create the build directory.871 # The logs are stored in the build directory, so we have to create it872 # before creating the first log file.873 self.makeBuildDir()874 875 session_file = self.getLogBasenameForCurrentTest() + ".log"876 self.log_files.append(session_file)877 878 # Optimistically set __errored__, __failed__, __expected__ to False879 # initially. If the test errored/failed, the session info880 # is then dumped into a session specific file for diagnosis.881 self.__cleanup_errored__ = False882 self.__errored__ = False883 self.__failed__ = False884 self.__expected__ = False885 # We are also interested in unexpected success.886 self.__unexpected__ = False887 # And skipped tests.888 self.__skipped__ = False889 890 # See addTearDownHook(self, hook) which allows the client to add a hook891 # function to be run during tearDown() time.892 self.hooks = []893 894 # See HideStdout(self).895 self.sys_stdout_hidden = False896 897 if self.platformContext:898 # set environment variable names for finding shared libraries899 self.dylibPath = self.platformContext.shlib_environment_var900 901 # Create the debugger instance.902 self.dbg = lldb.SBDebugger.Create()903 # Copy selected platform from a global instance if it exists.904 if lldb.selected_platform is not None:905 self.dbg.SetSelectedPlatform(lldb.selected_platform)906 907 if not self.dbg:908 raise Exception("Invalid debugger instance")909 910 # Retrieve the associated command interpreter instance.911 self.ci = self.dbg.GetCommandInterpreter()912 if not self.ci:913 raise Exception("Could not get the command interpreter")914 915 # And the result object.916 self.res = lldb.SBCommandReturnObject()917 918 self.setPlatformWorkingDir()919 self.enableLogChannelsForCurrentTest()920 921 self.lib_lldb = None922 self.framework_dir = None923 self.darwinWithFramework = False924 925 if sys.platform.startswith("darwin") and configuration.lldb_framework_path:926 framework = configuration.lldb_framework_path927 lib = os.path.join(framework, "LLDB")928 if os.path.exists(lib):929 self.framework_dir = os.path.dirname(framework)930 self.lib_lldb = lib931 self.darwinWithFramework = self.platformIsDarwin()932 933 def setAsync(self, value):934 """Sets async mode to True/False and ensures it is reset after the testcase completes."""935 old_async = self.dbg.GetAsync()936 self.dbg.SetAsync(value)937 self.addTearDownHook(lambda: self.dbg.SetAsync(old_async))938 939 def cleanupSubprocesses(self):940 # Terminate subprocesses in reverse order from how they were created.941 for p in reversed(self.subprocesses):942 p.terminate()943 del p944 del self.subprocesses[:]945 946 def spawnSubprocess(self, executable, args=[], extra_env=None, install_remote=True):947 """Creates a subprocess.Popen object with the specified executable and arguments,948 saves it in self.subprocesses, and returns the object.949 """950 proc = (951 _RemoteProcess(install_remote)952 if lldb.remote_platform953 else _LocalProcess(self.TraceOn())954 )955 proc.launch(executable, args, extra_env=extra_env)956 self.subprocesses.append(proc)957 return proc958 959 def runCmd(self, cmd, msg=None, check=True, trace=False, inHistory=False):960 """961 Ask the command interpreter to handle the command and then check its962 return status.963 """964 # Fail fast if 'cmd' is not meaningful.965 if cmd is None:966 raise Exception("Bad 'cmd' parameter encountered")967 968 trace = True if traceAlways else trace969 970 if cmd.startswith("target create "):971 cmd = cmd.replace("target create ", "file ")972 973 running = cmd.startswith("run") or cmd.startswith("process launch")974 975 for i in range(self.maxLaunchCount if running else 1):976 with recording(self, trace) as sbuf:977 print("runCmd:", cmd, file=sbuf)978 if not check:979 print("check of return status not required", file=sbuf)980 981 self.ci.HandleCommand(cmd, self.res, inHistory)982 983 with recording(self, trace) as sbuf:984 if self.res.Succeeded():985 print("output:", self.res.GetOutput(), file=sbuf)986 else:987 print("runCmd failed!", file=sbuf)988 print(self.res.GetError(), file=sbuf)989 990 if self.res.Succeeded():991 break992 elif running:993 # For process launch, wait some time before possible next try.994 time.sleep(self.timeWaitNextLaunch)995 with recording(self, trace) as sbuf:996 print("Command '" + cmd + "' failed!", file=sbuf)997 998 if check:999 if not msg:1000 msg = CMD_MSG(cmd)1001 output = ""1002 if self.res.GetOutput():1003 output += "\nCommand output:\n" + self.res.GetOutput()1004 if self.res.GetError():1005 output += "\nError output:\n" + self.res.GetError()1006 self.assertTrue(self.res.Succeeded(), msg + output)1007 1008 def HideStdout(self):1009 """Hide output to stdout from the user.1010 1011 During test execution, there might be cases where we don't want to show the1012 standard output to the user. For example,1013 1014 self.runCmd(r'''sc print("\n\n\tHello!\n")''')1015 1016 tests whether command abbreviation for 'script' works or not. There is no1017 need to show the 'Hello' output to the user as long as the 'script' command1018 succeeds and we are not in TraceOn() mode (see the '-t' option).1019 1020 In this case, the test method calls self.HideStdout(self) to redirect the1021 sys.stdout to a null device, and restores the sys.stdout upon teardown.1022 1023 Note that you should only call this method at most once during a test case1024 execution. Any subsequent call has no effect at all."""1025 if self.sys_stdout_hidden:1026 return1027 1028 self.sys_stdout_hidden = True1029 old_stdout = sys.stdout1030 sys.stdout = open(os.devnull, "w")1031 1032 def restore_stdout():1033 sys.stdout = old_stdout1034 1035 self.addTearDownHook(restore_stdout)1036 1037 # =======================================================================1038 # Methods for customized teardown cleanups as well as execution of hooks.1039 # =======================================================================1040 1041 def setTearDownCleanup(self, dictionary=None):1042 """Register a cleanup action at tearDown() time with a dictionary"""1043 self.dict = dictionary1044 self.doTearDownCleanup = True1045 1046 def addTearDownCleanup(self, dictionary):1047 """Add a cleanup action at tearDown() time with a dictionary"""1048 self.dicts.append(dictionary)1049 self.doTearDownCleanups = True1050 1051 def addTearDownHook(self, hook):1052 """1053 Add a function to be run during tearDown() time.1054 1055 Hooks are executed in a first come first serve manner.1056 """1057 if callable(hook):1058 with recording(self, traceAlways) as sbuf:1059 print("Adding tearDown hook:", getsource_if_available(hook), file=sbuf)1060 self.hooks.append(hook)1061 1062 return self1063 1064 def deletePexpectChild(self):1065 # This is for the case of directly spawning 'lldb' and interacting with it1066 # using pexpect.1067 if self.child and self.child.isalive():1068 import pexpect1069 1070 with recording(self, traceAlways) as sbuf:1071 print("tearing down the child process....", file=sbuf)1072 try:1073 if self.child_in_script_interpreter:1074 self.child.sendline("quit()")1075 self.child.expect_exact(self.child_prompt)1076 self.child.sendline("settings set interpreter.prompt-on-quit false")1077 self.child.sendline("quit")1078 self.child.expect(pexpect.EOF)1079 except (ValueError, pexpect.ExceptionPexpect):1080 # child is already terminated1081 pass1082 except OSError as exception:1083 import errno1084 1085 if exception.errno != errno.EIO:1086 # unexpected error1087 raise1088 # child is already terminated1089 finally:1090 # Give it one final blow to make sure the child is terminated.1091 self.child.close()1092 1093 def tearDown(self):1094 """Fixture for unittest test case teardown."""1095 self.deletePexpectChild()1096 1097 # Check and run any hook functions.1098 for hook in reversed(self.hooks):1099 with recording(self, traceAlways) as sbuf:1100 print(1101 "Executing tearDown hook:", getsource_if_available(hook), file=sbuf1102 )1103 if funcutils.requires_self(hook):1104 hook(self)1105 else:1106 hook() # try the plain call and hope it works1107 1108 del self.hooks1109 1110 # Perform registered teardown cleanup.1111 if doCleanup and self.doTearDownCleanup:1112 self.cleanup(dictionary=self.dict)1113 1114 # In rare cases where there are multiple teardown cleanups added.1115 if doCleanup and self.doTearDownCleanups:1116 if self.dicts:1117 for dict in reversed(self.dicts):1118 self.cleanup(dictionary=dict)1119 1120 # Remove subprocesses created by the test.1121 self.cleanupSubprocesses()1122 1123 # This must be the last statement, otherwise teardown hooks or other1124 # lines might depend on this still being active.1125 lldb.SBDebugger.Destroy(self.dbg)1126 del self.dbg1127 1128 # All modules should be orphaned now so that they can be cleared from1129 # the shared module cache.1130 lldb.SBModule.GarbageCollectAllocatedModules()1131 1132 # Assert that the global module cache is empty.1133 # FIXME: This assert fails on Windows.1134 if self.getPlatform() != "windows":1135 self.assertEqual(lldb.SBModule.GetNumberAllocatedModules(), 0)1136 1137 # =========================================================1138 # Various callbacks to allow introspection of test progress1139 # =========================================================1140 1141 def markError(self):1142 """Callback invoked when an error (unexpected exception) errored."""1143 self.__errored__ = True1144 with recording(self, False) as sbuf:1145 # False because there's no need to write "ERROR" to the stderr twice.1146 # Once by the Python unittest framework, and a second time by us.1147 print("ERROR", file=sbuf)1148 1149 def markCleanupError(self):1150 """Callback invoked when an error occurs while a test is cleaning up."""1151 self.__cleanup_errored__ = True1152 with recording(self, False) as sbuf:1153 # False because there's no need to write "CLEANUP_ERROR" to the stderr twice.1154 # Once by the Python unittest framework, and a second time by us.1155 print("CLEANUP_ERROR", file=sbuf)1156 1157 def markFailure(self):1158 """Callback invoked when a failure (test assertion failure) occurred."""1159 self.__failed__ = True1160 with recording(self, False) as sbuf:1161 # False because there's no need to write "FAIL" to the stderr twice.1162 # Once by the Python unittest framework, and a second time by us.1163 print("FAIL", file=sbuf)1164 1165 def markExpectedFailure(self, err):1166 """Callback invoked when an expected failure/error occurred."""1167 self.__expected__ = True1168 with recording(self, False) as sbuf:1169 # False because there's no need to write "expected failure" to the1170 # stderr twice.1171 # Once by the Python unittest framework, and a second time by us.1172 print("expected failure", file=sbuf)1173 1174 def markSkippedTest(self):1175 """Callback invoked when a test is skipped."""1176 self.__skipped__ = True1177 with recording(self, False) as sbuf:1178 # False because there's no need to write "skipped test" to the1179 # stderr twice.1180 # Once by the Python unittest framework, and a second time by us.1181 print("skipped test", file=sbuf)1182 1183 def markUnexpectedSuccess(self):1184 """Callback invoked when an unexpected success occurred."""1185 self.__unexpected__ = True1186 with recording(self, False) as sbuf:1187 # False because there's no need to write "unexpected success" to the1188 # stderr twice.1189 # Once by the Python unittest framework, and a second time by us.1190 print("unexpected success", file=sbuf)1191 1192 def getRerunArgs(self):1193 return " -f %s.%s" % (self.__class__.__name__, self._testMethodName)1194 1195 def getLogBasenameForCurrentTest(self, prefix="Incomplete"):1196 """1197 returns a partial path that can be used as the beginning of the name of multiple1198 log files pertaining to this test1199 """1200 return os.path.join(self.getBuildDir(), prefix)1201 1202 def dumpSessionInfo(self):1203 """1204 Dump the debugger interactions leading to a test error/failure. This1205 allows for more convenient postmortem analysis.1206 1207 See also LLDBTestResult (dotest.py) which is a singlton class derived1208 from TextTestResult and overwrites addError, addFailure, and1209 addExpectedFailure methods to allow us to to mark the test instance as1210 such.1211 """1212 1213 # We are here because self.tearDown() detected that this test instance1214 # either errored or failed. The lldb.test_result singleton contains1215 # two lists (errors and failures) which get populated by the unittest1216 # framework. Look over there for stack trace information.1217 #1218 # The lists contain 2-tuples of TestCase instances and strings holding1219 # formatted tracebacks.1220 #1221 # See http://docs.python.org/library/unittest.html#unittest.TestResult.1222 1223 # output tracebacks into session1224 pairs = []1225 if self.__errored__:1226 pairs = configuration.test_result.errors1227 prefix = "Error"1228 elif self.__cleanup_errored__:1229 pairs = configuration.test_result.cleanup_errors1230 prefix = "CleanupError"1231 elif self.__failed__:1232 pairs = configuration.test_result.failures1233 prefix = "Failure"1234 elif self.__expected__:1235 pairs = configuration.test_result.expectedFailures1236 prefix = "ExpectedFailure"1237 elif self.__skipped__:1238 prefix = "SkippedTest"1239 elif self.__unexpected__:1240 prefix = "UnexpectedSuccess"1241 else:1242 prefix = "Success"1243 1244 session_file = self.getLogBasenameForCurrentTest() + ".log"1245 1246 # Python 3 doesn't support unbuffered I/O in text mode. Open buffered.1247 session = encoded_file.open(session_file, "utf-8", mode="w")1248 1249 if not self.__unexpected__ and not self.__skipped__:1250 for test, traceback in pairs:1251 if test is self:1252 print(traceback, file=session)1253 1254 import datetime1255 1256 print(1257 "Session info generated @",1258 datetime.datetime.now().ctime(),1259 file=session,1260 )1261 session.close()1262 del session1263 1264 # process the log files1265 if prefix != "Success" or lldbtest_config.log_success:1266 # keep all log files, rename them to include prefix1267 src_log_basename = self.getLogBasenameForCurrentTest()1268 dst_log_basename = self.getLogBasenameForCurrentTest(prefix)1269 for src in self.log_files:1270 if os.path.isfile(src):1271 dst = src.replace(src_log_basename, dst_log_basename)1272 if os.name == "nt" and os.path.isfile(dst):1273 # On Windows, renaming a -> b will throw an exception if1274 # b exists. On non-Windows platforms it silently1275 # replaces the destination. Ultimately this means that1276 # atomic renames are not guaranteed to be possible on1277 # Windows, but we need this to work anyway, so just1278 # remove the destination first if it already exists.1279 remove_file(dst)1280 1281 lldbutil.mkdir_p(os.path.dirname(dst))1282 os.rename(src, dst)1283 else:1284 # success! (and we don't want log files) delete log files1285 for log_file in self.log_files:1286 if os.path.isfile(log_file):1287 remove_file(log_file)1288 1289 # ====================================================1290 # Config. methods supported through a plugin interface1291 # (enables reading of the current test configuration)1292 # ====================================================1293 1294 def hasXMLSupport(self):1295 """Returns True if lldb was built with XML support. Use this check to1296 enable parts of tests, if you want to skip a whole test use skipIfXmlSupportMissing1297 instead."""1298 return (1299 lldb.SBDebugger.GetBuildConfiguration()1300 .GetValueForKey("xml")1301 .GetValueForKey("value")1302 .GetBooleanValue(False)1303 )1304 1305 def isMIPS(self):1306 """Returns true if the architecture is MIPS."""1307 arch = self.getArchitecture()1308 if re.match("mips", arch):1309 return True1310 return False1311 1312 def isPPC64le(self):1313 """Returns true if the architecture is PPC64LE."""1314 arch = self.getArchitecture()1315 if re.match("powerpc64le", arch):1316 return True1317 return False1318 1319 def isAArch64(self):1320 """Returns true if the architecture is AArch64."""1321 arch = self.getArchitecture().lower()1322 return arch in ["aarch64", "arm64", "arm64e"]1323 1324 def isARM(self):1325 """Returns true if the architecture is ARM, meaning 32-bit ARM. Which could1326 be M profile, A profile Armv7-a, or the AArch32 mode of Armv8-a."""1327 return not self.isAArch64() and (1328 self.getArchitecture().lower().startswith("arm")1329 )1330 1331 def isSupported(self, cpu_feature: cpu_feature.CPUFeature):1332 triple = self.dbg.GetSelectedPlatform().GetTriple()1333 cmd_runner = self.run_platform_command1334 return cpu_feature.is_supported(triple, cmd_runner)1335 1336 def isAArch64SVE(self):1337 return self.isAArch64() and self.isSupported(cpu_feature.AArch64.SVE)1338 1339 def isAArch64SME(self):1340 return self.isAArch64() and self.isSupported(cpu_feature.AArch64.SME)1341 1342 def isAArch64SME2(self):1343 # If you have sme2, you also have sme.1344 return self.isAArch64() and self.isSupported(cpu_feature.AArch64.SME2)1345 1346 def isAArch64SMEFA64(self):1347 # smefa64 allows the use of the full A64 instruction set in streaming1348 # mode. This is required by certain test programs to setup register1349 # state.1350 return (1351 self.isAArch64()1352 and self.isSupported(cpu_feature.AArch64.SME)1353 and self.isSupported(cpu_feature.AArch64.SME_FA64)1354 )1355 1356 def isAArch64MTE(self):1357 return self.isAArch64() and self.isSupported(cpu_feature.AArch64.MTE)1358 1359 def isAArch64MTEStoreOnly(self):1360 return self.isAArch64() and self.isSupported(cpu_feature.AArch64.MTE_STORE_ONLY)1361 1362 def isAArch64GCS(self):1363 return self.isAArch64() and self.isSupported(cpu_feature.AArch64.GCS)1364 1365 def isAArch64PAuth(self):1366 if self.getArchitecture() == "arm64e":1367 return True1368 return self.isAArch64() and self.isSupported(cpu_feature.AArch64.PTR_AUTH)1369 1370 def isAArch64FPMR(self):1371 return self.isAArch64() and self.isSupported(cpu_feature.AArch64.FPMR)1372 1373 def isAArch64Windows(self):1374 """Returns true if the architecture is AArch64 and platform windows."""1375 if self.getPlatform() == "windows":1376 arch = self.getArchitecture().lower()1377 return arch in ["aarch64", "arm64", "arm64e"]1378 return False1379 1380 def isLoongArch(self):1381 """Returns true if the architecture is LoongArch."""1382 arch = self.getArchitecture().lower()1383 return arch in ["loongarch64", "loongarch32"]1384 1385 def isLoongArchLSX(self):1386 return self.isLoongArch() and self.isSupported(cpu_feature.Loong.LSX)1387 1388 def isLoongArchLASX(self):1389 return self.isLoongArch() and self.isSupported(cpu_feature.Loong.LASX)1390 1391 def isRISCV(self):1392 """Returns true if the architecture is RISCV64 or RISCV32."""1393 return self.getArchitecture() in ["riscv64", "riscv32"]1394 1395 def getArchitecture(self):1396 """Returns the architecture in effect the test suite is running with."""1397 return lldbplatformutil.getArchitecture()1398 1399 def getLldbArchitecture(self):1400 """Returns the architecture of the lldb binary."""1401 return lldbplatformutil.getLLDBArchitecture()1402 1403 def getCompiler(self):1404 """Returns the compiler in effect the test suite is running with."""1405 return lldbplatformutil.getCompiler()1406 1407 def getCompilerVersion(self):1408 """Returns a string that represents the compiler version.1409 Supports: llvm, clang.1410 """1411 return lldbplatformutil.getCompilerVersion()1412 1413 def getDwarfVersion(self):1414 """Returns the dwarf version generated by clang or '0'."""1415 return lldbplatformutil.getDwarfVersion()1416 1417 def platformIsDarwin(self):1418 """Returns true if the OS triple for the selected platform is any valid apple OS"""1419 return lldbplatformutil.platformIsDarwin()1420 1421 def hasDarwinFramework(self):1422 return self.darwinWithFramework1423 1424 def getPlatform(self):1425 """Returns the target platform the test suite is running on."""1426 return lldbplatformutil.getPlatform()1427 1428 def isIntelCompiler(self):1429 """Returns true if using an Intel (ICC) compiler, false otherwise."""1430 return any([x in self.getCompiler() for x in ["icc", "icpc", "icl"]])1431 1432 def expectedCompilerVersion(self, compiler_version):1433 """Returns True iff compiler_version[1] matches the current compiler version.1434 Use compiler_version[0] to specify the operator used to determine if a match has occurred.1435 Any operator other than the following defaults to an equality test:1436 '>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not'1437 1438 If the current compiler version cannot be determined, we assume it is close to the top1439 of trunk, so any less-than or equal-to comparisons will return False, and any1440 greater-than or not-equal-to comparisons will return True.1441 """1442 return lldbplatformutil.expectedCompilerVersion(compiler_version)1443 1444 def expectedCompiler(self, compilers):1445 """Returns True iff any element of compilers is a sub-string of the current compiler."""1446 return lldbplatformutil.expectedCompiler(compilers)1447 1448 def expectedArch(self, archs):1449 """Returns True iff any element of archs is a sub-string of the current architecture."""1450 if archs is None:1451 return True1452 1453 for arch in archs:1454 if arch in self.getArchitecture():1455 return True1456 1457 return False1458 1459 def getRunOptions(self):1460 """Command line option for -A and -C to run this test again, called from1461 self.dumpSessionInfo()."""1462 arch = self.getArchitecture()1463 comp = self.getCompiler()1464 option_str = ""1465 if arch:1466 option_str = "-A " + arch1467 if comp:1468 option_str += " -C " + comp1469 return option_str1470 1471 def getDebugInfo(self):1472 method = getattr(self, self.testMethodName)1473 return getattr(method, "debug_info", None)1474 1475 def build(1476 self,1477 debug_info=None,1478 architecture=None,1479 compiler=None,1480 dictionary=None,1481 make_targets=None,1482 ):1483 """Platform specific way to build binaries."""1484 if not architecture and configuration.arch:1485 architecture = configuration.arch1486 1487 if debug_info is None:1488 debug_info = self.getDebugInfo()1489 1490 dictionary = lldbplatformutil.finalize_build_dictionary(dictionary)1491 1492 testdir = self.mydir1493 testname = self.getBuildDirBasename()1494 1495 module = builder_module()1496 command = module.getBuildCommand(1497 debug_info,1498 architecture,1499 compiler,1500 dictionary,1501 testdir,1502 testname,1503 make_targets,1504 )1505 if command is None:1506 raise Exception("Don't know how to build binary")1507 1508 self.runBuildCommand(command)1509 1510 def runBuildCommand(self, command):1511 self.trace(shlex.join(command))1512 try:1513 output = check_output(command, stderr=STDOUT, errors="replace")1514 except CalledProcessError as cpe:1515 raise build_exception.BuildError(cpe)1516 self.trace(output)1517 1518 # ==================================================1519 # Build methods supported through a plugin interface1520 # ==================================================1521 1522 def getstdlibFlag(self):1523 """Returns the proper -stdlib flag, or empty if not required."""1524 if (1525 self.platformIsDarwin()1526 or self.getPlatform() == "freebsd"1527 or self.getPlatform() == "openbsd"1528 ):1529 stdlibflag = "-stdlib=libc++"1530 else: # this includes NetBSD1531 stdlibflag = ""1532 return stdlibflag1533 1534 def getstdFlag(self):1535 """Returns the proper stdflag."""1536 if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion():1537 stdflag = "-std=c++0x"1538 else:1539 stdflag = "-std=c++11"1540 return stdflag1541 1542 def buildDriver(self, sources, exe_name, defines=None):1543 """Platform-specific way to build a program that links with LLDB (via the liblldb.so1544 or LLDB.framework).1545 """1546 if defines is None:1547 defines = []1548 1549 stdflag = self.getstdFlag()1550 stdlibflag = self.getstdlibFlag()1551 defines = " ".join(["-D{}={}".format(name, value) for name, value in defines])1552 1553 lib_dir = configuration.lldb_libs_dir1554 if self.hasDarwinFramework():1555 d = {1556 "CXX_SOURCES": sources,1557 "EXE": exe_name,1558 "CFLAGS_EXTRAS": "%s %s %s" % (stdflag, stdlibflag, defines),1559 "FRAMEWORK_INCLUDES": "-F%s" % self.framework_dir,1560 "LD_EXTRAS": "%s -Wl,-rpath,%s" % (self.lib_lldb, self.framework_dir),1561 }1562 elif sys.platform.startswith("win"):1563 d = {1564 "CXX_SOURCES": sources,1565 "EXE": exe_name,1566 "CFLAGS_EXTRAS": "%s %s -I%s -I%s %s"1567 % (1568 stdflag,1569 stdlibflag,1570 os.path.join(os.environ["LLDB_SRC"], "include"),1571 os.path.join(configuration.lldb_obj_root, "include"),1572 defines,1573 ),1574 "LD_EXTRAS": "-L%s -lliblldb" % lib_dir,1575 }1576 else:1577 d = {1578 "CXX_SOURCES": sources,1579 "EXE": exe_name,1580 "CFLAGS_EXTRAS": "%s %s -I%s -I%s %s"1581 % (1582 stdflag,1583 stdlibflag,1584 os.path.join(os.environ["LLDB_SRC"], "include"),1585 os.path.join(configuration.lldb_obj_root, "include"),1586 defines,1587 ),1588 "LD_EXTRAS": "-L%s -llldb -Wl,-rpath,%s" % (lib_dir, lib_dir),1589 }1590 if self.TraceOn():1591 print("Building LLDB Driver (%s) from sources %s" % (exe_name, sources))1592 1593 self.build(dictionary=d)1594 1595 def buildLibrary(self, sources, lib_name):1596 """Platform specific way to build a default library."""1597 1598 stdflag = self.getstdFlag()1599 1600 lib_dir = configuration.lldb_libs_dir1601 if self.hasDarwinFramework():1602 d = {1603 "DYLIB_CXX_SOURCES": sources,1604 "DYLIB_NAME": lib_name,1605 "CFLAGS_EXTRAS": "%s -stdlib=libc++ -I%s"1606 % (stdflag, os.path.join(configuration.lldb_obj_root, "include")),1607 "FRAMEWORK_INCLUDES": "-F%s" % self.framework_dir,1608 "LD_EXTRAS": "%s -Wl,-rpath,%s -dynamiclib"1609 % (self.lib_lldb, self.framework_dir),1610 }1611 elif self.getPlatform() == "windows":1612 d = {1613 "DYLIB_CXX_SOURCES": sources,1614 "DYLIB_NAME": lib_name,1615 "CFLAGS_EXTRAS": "%s -I%s -I%s"1616 % (1617 stdflag,1618 os.path.join(os.environ["LLDB_SRC"], "include"),1619 os.path.join(configuration.lldb_obj_root, "include"),1620 ),1621 "LD_EXTRAS": "-shared -l%s\\liblldb.lib" % lib_dir,1622 }1623 else:1624 d = {1625 "DYLIB_CXX_SOURCES": sources,1626 "DYLIB_NAME": lib_name,1627 "CFLAGS_EXTRAS": "%s -I%s -I%s -fPIC"1628 % (1629 stdflag,1630 os.path.join(os.environ["LLDB_SRC"], "include"),1631 os.path.join(configuration.lldb_obj_root, "include"),1632 ),1633 "LD_EXTRAS": "-shared -L%s -llldb -Wl,-rpath,%s" % (lib_dir, lib_dir),1634 }1635 if self.TraceOn():1636 print("Building LLDB Library (%s) from sources %s" % (lib_name, sources))1637 1638 self.build(dictionary=d)1639 1640 def buildProgram(self, sources, exe_name):1641 """Platform specific way to build an executable from C/C++ sources."""1642 d = {"CXX_SOURCES": sources, "EXE": exe_name}1643 self.build(dictionary=d)1644 1645 def findBuiltClang(self):1646 """Tries to find and use Clang from the build directory as the compiler (instead of the system compiler)."""1647 paths_to_try = [1648 "llvm-build/Release+Asserts/x86_64/bin/clang",1649 "llvm-build/Debug+Asserts/x86_64/bin/clang",1650 "llvm-build/Release/x86_64/bin/clang",1651 "llvm-build/Debug/x86_64/bin/clang",1652 ]1653 lldb_root_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")1654 for p in paths_to_try:1655 path = os.path.join(lldb_root_path, p)1656 if os.path.exists(path):1657 return path1658 1659 # Tries to find clang at the same folder as the lldb1660 lldb_dir = os.path.dirname(lldbtest_config.lldbExec)1661 path = shutil.which("clang", path=lldb_dir)1662 if path is not None:1663 return path1664 1665 return os.environ["CC"]1666 1667 def yaml2obj(self, yaml_path, obj_path, max_size=None):1668 """1669 Create an object file at the given path from a yaml file.1670 1671 Throws subprocess.CalledProcessError if the object could not be created.1672 """1673 yaml2obj_bin = configuration.get_yaml2obj_path()1674 if not yaml2obj_bin:1675 self.assertTrue(False, "No valid yaml2obj executable specified")1676 command = [yaml2obj_bin, "-o=%s" % obj_path, yaml_path]1677 if max_size is not None:1678 command += ["--max-size=%d" % max_size]1679 self.runBuildCommand(command)1680 1681 def yaml2macho_core(self, yaml_path, obj_path, uuids=None):1682 """1683 Create a Mach-O corefile at the given path from a yaml file.1684 1685 Throws subprocess.CalledProcessError if the object could not be created.1686 """1687 yaml2macho_core_bin = configuration.get_yaml2macho_core_path()1688 if not yaml2macho_core_bin:1689 self.assertTrue(False, "No valid yaml2macho-core executable specified")1690 if uuids != None:1691 command = [1692 yaml2macho_core_bin,1693 "-i",1694 yaml_path,1695 "-o",1696 obj_path,1697 "-u",1698 uuids,1699 ]1700 else:1701 command = [yaml2macho_core_bin, "-i", yaml_path, "-o", obj_path]1702 self.runBuildCommand(command)1703 1704 def cleanup(self, dictionary=None):1705 """Platform specific way to do cleanup after build."""1706 module = builder_module()1707 if not module.cleanup(dictionary):1708 raise Exception(1709 "Don't know how to do cleanup with dictionary: " + dictionary1710 )1711 1712 def invoke(self, obj, name, trace=False):1713 """Use reflection to call a method dynamically with no argument."""1714 trace = True if traceAlways else trace1715 1716 method = getattr(obj, name)1717 import inspect1718 1719 self.assertTrue(1720 inspect.ismethod(method), name + "is a method name of object: " + str(obj)1721 )1722 result = method()1723 with recording(self, trace) as sbuf:1724 print(str(method) + ":", result, file=sbuf)1725 return result1726 1727 def getLibcPlusPlusLibs(self):1728 if self.getPlatform() in ("freebsd", "linux", "netbsd", "openbsd"):1729 return ["libc++.so.1"]1730 else:1731 return ["libc++.1.dylib", "libc++abi."]1732 1733 def run_platform_command(self, cmd):1734 platform = self.dbg.GetSelectedPlatform()1735 shell_command = lldb.SBPlatformShellCommand(cmd)1736 err = platform.Run(shell_command)1737 return (err, shell_command.GetStatus(), shell_command.GetOutput())1738 1739 def get_stats(self, options=None):1740 """1741 Get the output of the "statistics dump" with optional extra options1742 and return the JSON as a python dictionary.1743 """1744 return_obj = lldb.SBCommandReturnObject()1745 command = "statistics dump "1746 if options is not None:1747 command += options1748 self.ci.HandleCommand(command, return_obj, False)1749 metrics_json = return_obj.GetOutput()1750 return json.loads(metrics_json)1751 1752 1753# Metaclass for TestBase to change the list of test metods when a new TestCase is loaded.1754# We change the test methods to create a new test method for each test for each debug info we are1755# testing. The name of the new test method will be '<original-name>_<debug-info>' and with adding1756# the new test method we remove the old method at the same time. This functionality can be1757# supressed by at test case level setting the class attribute NO_DEBUG_INFO_TESTCASE or at test1758# level by using the decorator @no_debug_info_test.1759 1760 1761class LLDBTestCaseFactory(type):1762 def __new__(cls, name, bases, attrs):1763 original_testcase = super(LLDBTestCaseFactory, cls).__new__(1764 cls, name, bases, attrs1765 )1766 if original_testcase.NO_DEBUG_INFO_TESTCASE:1767 return original_testcase1768 1769 # Default implementation for skip/xfail reason based on the debug category,1770 # where "None" means to run the test as usual.1771 def no_reason(_):1772 return None1773 1774 newattrs = {}1775 for attrname, attrvalue in attrs.items():1776 if attrname.startswith("test") and not getattr(1777 attrvalue, "__no_debug_info_test__", False1778 ):1779 # If any debug info categories were explicitly tagged, assume that list to be1780 # authoritative. If none were specified, try with all debug info formats.1781 test_method_categories = set(getattr(attrvalue, "categories", []))1782 all_dbginfo_categories = set(1783 test_categories.debug_info_categories.keys()1784 )1785 dbginfo_categories = test_method_categories & all_dbginfo_categories1786 other_categories = list(test_method_categories - all_dbginfo_categories)1787 if not dbginfo_categories:1788 dbginfo_categories = [1789 category1790 for category, can_replicate in test_categories.debug_info_categories.items()1791 if can_replicate1792 ]1793 1794 # PDB is off by default, because it has a lot of failures right now.1795 # See llvm.org/pr1494981796 if original_testcase.TEST_WITH_PDB_DEBUG_INFO:1797 dbginfo_categories.append("pdb")1798 1799 xfail_for_debug_info_cat_fn = getattr(1800 attrvalue, "__xfail_for_debug_info_cat_fn__", no_reason1801 )1802 skip_for_debug_info_cat_fn = getattr(1803 attrvalue, "__skip_for_debug_info_cat_fn__", no_reason1804 )1805 for cat in dbginfo_categories:1806 1807 @wraps(attrvalue)1808 def test_method(self, attrvalue=attrvalue):1809 return attrvalue(self)1810 1811 method_name = attrname + "_" + cat1812 test_method.__name__ = method_name1813 test_method.debug_info = cat1814 test_method.categories = other_categories + [cat]1815 1816 xfail_reason = xfail_for_debug_info_cat_fn(cat)1817 if xfail_reason:1818 test_method = unittest.expectedFailure(test_method)1819 1820 skip_reason = skip_for_debug_info_cat_fn(cat)1821 if skip_reason:1822 test_method = unittest.skip(skip_reason)(test_method)1823 1824 newattrs[method_name] = test_method1825 1826 else:1827 newattrs[attrname] = attrvalue1828 return super(LLDBTestCaseFactory, cls).__new__(cls, name, bases, newattrs)1829 1830 1831# Setup the metaclass for this class to change the list of the test1832# methods when a new class is loaded1833 1834 1835class TestBase(Base, metaclass=LLDBTestCaseFactory):1836 """1837 This abstract base class is meant to be subclassed. It provides default1838 implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(),1839 among other things.1840 1841 Important things for test class writers:1842 1843 - The setUp method sets up things to facilitate subsequent interactions1844 with the debugger as part of the test. These include:1845 - populate the test method name1846 - create/get a debugger set with synchronous mode (self.dbg)1847 - get the command interpreter from with the debugger (self.ci)1848 - create a result object for use with the command interpreter1849 (self.res)1850 - plus other stuffs1851 1852 - The tearDown method tries to perform some necessary cleanup on behalf1853 of the test to return the debugger to a good state for the next test.1854 These include:1855 - execute any tearDown hooks registered by the test method with1856 TestBase.addTearDownHook(); examples can be found in1857 settings/TestSettings.py1858 - kill the inferior process associated with each target, if any,1859 and, then delete the target from the debugger's target list1860 - perform build cleanup before running the next test method in the1861 same test class; examples of registering for this service can be1862 found in types/TestIntegerTypes.py with the call:1863 - self.setTearDownCleanup(dictionary=d)1864 1865 - Similarly setUpClass and tearDownClass perform classwise setup and1866 teardown fixtures. The tearDownClass method invokes a default build1867 cleanup for the entire test class; also, subclasses can implement the1868 classmethod classCleanup(cls) to perform special class cleanup action.1869 1870 - The instance methods runCmd and expect are used heavily by existing1871 test cases to send a command to the command interpreter and to perform1872 string/pattern matching on the output of such command execution. The1873 expect method also provides a mode to peform string/pattern matching1874 without running a command.1875 1876 - The build method is used to build the binaries used during a1877 particular test scenario. A plugin should be provided for the1878 sys.platform running the test suite. The Mac OS X implementation is1879 located in builders/darwin.py.1880 """1881 1882 # Subclasses can set this to true (if they don't depend on debug info) to avoid running the1883 # test multiple times with various debug info types.1884 NO_DEBUG_INFO_TESTCASE = False1885 1886 TEST_WITH_PDB_DEBUG_INFO = False1887 """1888 Subclasses can set this to True to test with PDB in addition to the other debug info1889 types. This id off by default because many tests will fail due to missing functionality in PDB.1890 See llvm.org/pr149498.1891 """1892 1893 def generateSource(self, source):1894 template = source + ".template"1895 temp = os.path.join(self.getSourceDir(), template)1896 with open(temp, "r") as f:1897 content = f.read()1898 1899 public_api_dir = os.path.join(os.environ["LLDB_SRC"], "include", "lldb", "API")1900 1901 # Look under the include/lldb/API directory and add #include statements1902 # for all the SB API headers.1903 public_headers = os.listdir(public_api_dir)1904 # For different platforms, the include statement can vary.1905 if self.hasDarwinFramework():1906 include_stmt = "'#include <%s>' % os.path.join('LLDB', header)"1907 else:1908 include_stmt = (1909 "'#include <%s>' % os.path.join(r'" + public_api_dir + "', header)"1910 )1911 list = [1912 eval(include_stmt)1913 for header in public_headers1914 if (header.startswith("SB") and header.endswith(".h"))1915 ]1916 includes = "\n".join(list)1917 new_content = content.replace("%include_SB_APIs%", includes)1918 new_content = new_content.replace("%SOURCE_DIR%", self.getSourceDir())1919 src = os.path.join(self.getBuildDir(), source)1920 with open(src, "w") as f:1921 f.write(new_content)1922 1923 self.addTearDownHook(lambda: os.remove(src))1924 1925 def setUp(self):1926 # Works with the test driver to conditionally skip tests via1927 # decorators.1928 Base.setUp(self)1929 1930 for s in self.setUpCommands():1931 self.runCmd(s)1932 1933 # We want our debugger to be synchronous.1934 self.dbg.SetAsync(False)1935 1936 # Retrieve the associated command interpreter instance.1937 self.ci = self.dbg.GetCommandInterpreter()1938 if not self.ci:1939 raise Exception("Could not get the command interpreter")1940 1941 # And the result object.1942 self.res = lldb.SBCommandReturnObject()1943 1944 def registerSharedLibrariesWithTarget(self, target, shlibs):1945 """If we are remotely running the test suite, register the shared libraries with the target so they get uploaded, otherwise do nothing1946 1947 Any modules in the target that have their remote install file specification set will1948 get uploaded to the remote host. This function registers the local copies of the1949 shared libraries with the target and sets their remote install locations so they will1950 be uploaded when the target is run.1951 """1952 if not shlibs or not self.platformContext:1953 return None1954 1955 shlib_environment_var = self.platformContext.shlib_environment_var1956 shlib_prefix = self.platformContext.shlib_prefix1957 shlib_extension = "." + self.platformContext.shlib_extension1958 1959 dirs = []1960 # Add any shared libraries to our target if remote so they get1961 # uploaded into the working directory on the remote side1962 for name in shlibs:1963 # The path can be a full path to a shared library, or a make file name like "Foo" for1964 # "libFoo.dylib" or "libFoo.so", or "Foo.so" for "Foo.so" or "libFoo.so", or just a1965 # basename like "libFoo.so". So figure out which one it is and resolve the local copy1966 # of the shared library accordingly1967 if os.path.isfile(name):1968 local_shlib_path = (1969 name # name is the full path to the local shared library1970 )1971 else:1972 # Check relative names1973 local_shlib_path = os.path.join(1974 self.getBuildDir(), shlib_prefix + name + shlib_extension1975 )1976 if not os.path.exists(local_shlib_path):1977 local_shlib_path = os.path.join(1978 self.getBuildDir(), name + shlib_extension1979 )1980 if not os.path.exists(local_shlib_path):1981 local_shlib_path = os.path.join(self.getBuildDir(), name)1982 1983 # Make sure we found the local shared library in the above code1984 self.assertTrue(os.path.exists(local_shlib_path))1985 1986 # Add the shared library to our target1987 shlib_module = target.AddModule(local_shlib_path, None, None, None)1988 if lldb.remote_platform:1989 # We must set the remote install location if we want the shared library1990 # to get uploaded to the remote target1991 remote_shlib_path = lldbutil.append_to_process_working_directory(1992 self, os.path.basename(local_shlib_path)1993 )1994 shlib_module.SetRemoteInstallFileSpec(1995 lldb.SBFileSpec(remote_shlib_path, False)1996 )1997 dir_to_add = self.get_process_working_directory()1998 else:1999 dir_to_add = os.path.dirname(local_shlib_path)2000 2001 if dir_to_add not in dirs:2002 dirs.append(dir_to_add)2003 2004 env_value = self.platformContext.shlib_path_separator.join(dirs)2005 return ["%s=%s" % (shlib_environment_var, env_value)]2006 2007 def registerSanitizerLibrariesWithTarget(self, target):2008 runtimes = []2009 for m in target.module_iter():2010 libspec = m.GetFileSpec()2011 if "clang_rt" in libspec.GetFilename():2012 runtimes.append(2013 os.path.join(libspec.GetDirectory(), libspec.GetFilename())2014 )2015 return self.registerSharedLibrariesWithTarget(target, runtimes)2016 2017 # utility methods that tests can use to access the current objects2018 def target(self):2019 if not self.dbg:2020 raise Exception("Invalid debugger instance")2021 return self.dbg.GetSelectedTarget()2022 2023 def process(self):2024 if not self.dbg:2025 raise Exception("Invalid debugger instance")2026 return self.dbg.GetSelectedTarget().GetProcess()2027 2028 def thread(self):2029 if not self.dbg:2030 raise Exception("Invalid debugger instance")2031 return self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread()2032 2033 def frame(self):2034 if not self.dbg:2035 raise Exception("Invalid debugger instance")2036 return (2037 self.dbg.GetSelectedTarget()2038 .GetProcess()2039 .GetSelectedThread()2040 .GetSelectedFrame()2041 )2042 2043 def get_process_working_directory(self):2044 """Get the working directory that should be used when launching processes for local or remote processes."""2045 if lldb.remote_platform:2046 # Remote tests set the platform working directory up in2047 # Base.setUp()2048 return lldb.remote_platform.GetWorkingDirectory()2049 else:2050 # local tests change directory into each test subdirectory2051 return self.getBuildDir()2052 2053 def tearDown(self):2054 # Ensure all the references to SB objects have gone away so that we can2055 # be sure that all test-specific resources have been freed before we2056 # attempt to delete the targets.2057 gc.collect()2058 2059 # Delete the target(s) from the debugger as a general cleanup step.2060 # This includes terminating the process for each target, if any.2061 # We'd like to reuse the debugger for our next test without incurring2062 # the initialization overhead.2063 targets = []2064 for target in self.dbg:2065 if target:2066 targets.append(target)2067 process = target.GetProcess()2068 if process:2069 rc = self.invoke(process, "Kill")2070 assert rc.Success()2071 for target in targets:2072 self.dbg.DeleteTarget(target)2073 2074 # Assert that all targets are deleted.2075 self.assertEqual(self.dbg.GetNumTargets(), 0)2076 2077 # Do this last, to make sure it's in reverse order from how we setup.2078 Base.tearDown(self)2079 2080 def switch_to_thread_with_stop_reason(self, stop_reason):2081 """2082 Run the 'thread list' command, and select the thread with stop reason as2083 'stop_reason'. If no such thread exists, no select action is done.2084 """2085 from .lldbutil import stop_reason_to_str2086 2087 self.runCmd("thread list")2088 output = self.res.GetOutput()2089 thread_line_pattern = re.compile(2090 "^[ *] thread #([0-9]+):.*stop reason = %s"2091 % stop_reason_to_str(stop_reason)2092 )2093 for line in output.splitlines():2094 matched = thread_line_pattern.match(line)2095 if matched:2096 self.runCmd("thread select %s" % matched.group(1))2097 2098 def match(2099 self, str, patterns, msg=None, trace=False, error=False, matching=True, exe=True2100 ):2101 """run command in str, and match the result against regexp in patterns returning the match object for the first matching pattern2102 2103 Otherwise, all the arguments have the same meanings as for the expect function2104 """2105 2106 trace = True if traceAlways else trace2107 2108 if exe:2109 # First run the command. If we are expecting error, set check=False.2110 # Pass the assert message along since it provides more semantic2111 # info.2112 self.runCmd(str, msg=msg, trace=(True if trace else False), check=not error)2113 2114 # Then compare the output against expected strings.2115 output = self.res.GetError() if error else self.res.GetOutput()2116 2117 # If error is True, the API client expects the command to fail!2118 if error:2119 self.assertFalse(2120 self.res.Succeeded(), "Command '" + str + "' is expected to fail!"2121 )2122 else:2123 # No execution required, just compare str against the golden input.2124 output = str2125 with recording(self, trace) as sbuf:2126 print("looking at:", output, file=sbuf)2127 2128 # The heading says either "Expecting" or "Not expecting".2129 heading = "Expecting" if matching else "Not expecting"2130 2131 for pattern in patterns:2132 # Match Objects always have a boolean value of True.2133 match_object = re.search(pattern, output)2134 matched = bool(match_object)2135 with recording(self, trace) as sbuf:2136 print("%s pattern: %s" % (heading, pattern), file=sbuf)2137 print("Matched" if matched else "Not matched", file=sbuf)2138 if matched:2139 break2140 2141 self.assertTrue(2142 matched if matching else not matched,2143 msg if msg else EXP_MSG(str, output, exe),2144 )2145 2146 return match_object2147 2148 def check_completion_with_desc(2149 self, str_input, match_desc_pairs, enforce_order=False2150 ):2151 """2152 Checks that when the given input is completed at the given list of2153 completions and descriptions is returned.2154 :param str_input: The input that should be completed. The completion happens at the end of the string.2155 :param match_desc_pairs: A list of pairs that indicate what completions have to be in the list of2156 completions returned by LLDB. The first element of the pair is the completion2157 string that LLDB should generate and the second element the description.2158 :param enforce_order: True iff the order in which the completions are returned by LLDB2159 should match the order of the match_desc_pairs pairs.2160 """2161 interp = self.dbg.GetCommandInterpreter()2162 match_strings = lldb.SBStringList()2163 description_strings = lldb.SBStringList()2164 num_matches = interp.HandleCompletionWithDescriptions(2165 str_input, len(str_input), 0, -1, match_strings, description_strings2166 )2167 self.assertEqual(len(description_strings), len(match_strings))2168 2169 # The index of the last matched description in description_strings or2170 # -1 if no description has been matched yet.2171 last_found_index = -12172 out_of_order_errors = ""2173 missing_pairs = []2174 for pair in match_desc_pairs:2175 found_pair = False2176 for i in range(num_matches + 1):2177 match_candidate = match_strings.GetStringAtIndex(i)2178 description_candidate = description_strings.GetStringAtIndex(i)2179 if match_candidate == pair[0] and description_candidate == pair[1]:2180 found_pair = True2181 if enforce_order and last_found_index > i:2182 new_err = (2183 "Found completion "2184 + pair[0]2185 + " at index "2186 + str(i)2187 + " in returned completion list but "2188 + "should have been after completion "2189 + match_strings.GetStringAtIndex(last_found_index)2190 + " (index:"2191 + str(last_found_index)2192 + ")\n"2193 )2194 out_of_order_errors += new_err2195 last_found_index = i2196 break2197 if not found_pair:2198 missing_pairs.append(pair)2199 2200 error_msg = ""2201 got_failure = False2202 if len(missing_pairs):2203 got_failure = True2204 error_msg += "Missing pairs:\n"2205 for pair in missing_pairs:2206 error_msg += " [" + pair[0] + ":" + pair[1] + "]\n"2207 if len(out_of_order_errors):2208 got_failure = True2209 error_msg += out_of_order_errors2210 if got_failure:2211 error_msg += (2212 "Got the following " + str(num_matches) + " completions back:\n"2213 )2214 for i in range(num_matches + 1):2215 match_candidate = match_strings.GetStringAtIndex(i)2216 description_candidate = description_strings.GetStringAtIndex(i)2217 error_msg += (2218 "["2219 + match_candidate2220 + ":"2221 + description_candidate2222 + "] index "2223 + str(i)2224 + "\n"2225 )2226 self.assertFalse(got_failure, error_msg)2227 2228 def complete_from_to(self, str_input, patterns):2229 """Test that the completion mechanism completes str_input to patterns,2230 where patterns could be a single pattern-string or a list of2231 pattern-strings.2232 2233 If there is only one pattern and it is exactly equal to str_input, this2234 assumes that there should be no completions provided and that the result2235 should be the same as the input."""2236 2237 # Patterns should not be None in order to proceed.2238 self.assertFalse(patterns is None)2239 # And should be either a string or list of strings. Check for list type2240 # below, if not, make a list out of the singleton string. If patterns2241 # is not a string or not a list of strings, there'll be runtime errors2242 # later on.2243 if not isinstance(patterns, list):2244 patterns = [patterns]2245 2246 interp = self.dbg.GetCommandInterpreter()2247 match_strings = lldb.SBStringList()2248 num_matches = interp.HandleCompletion(2249 str_input, len(str_input), 0, -1, match_strings2250 )2251 common_match = match_strings.GetStringAtIndex(0)2252 if num_matches == 0:2253 compare_string = str_input2254 else:2255 if common_match is not None and len(common_match) > 0:2256 compare_string = str_input + common_match2257 else:2258 compare_string = ""2259 for idx in range(1, num_matches + 1):2260 compare_string += match_strings.GetStringAtIndex(idx) + "\n"2261 2262 if len(patterns) == 1 and str_input == patterns[0] and num_matches:2263 self.fail("Expected no completions but got:\n" + compare_string)2264 2265 for p in patterns:2266 self.expect(2267 compare_string,2268 msg=COMPLETION_MSG(str_input, p, match_strings),2269 exe=False,2270 substrs=[p],2271 )2272 2273 def completions_match(self, command, completions, max_completions=-1):2274 """Checks that the completions for the given command are equal to the2275 given list of completions"""2276 interp = self.dbg.GetCommandInterpreter()2277 match_strings = lldb.SBStringList()2278 interp.HandleCompletion(2279 command, len(command), 0, max_completions, match_strings2280 )2281 # match_strings is a 1-indexed list, so we have to slice...2282 self.assertCountEqual(2283 completions, list(match_strings)[1:], "List of returned completion is wrong"2284 )2285 2286 def completions_contain(self, command, completions, match=True):2287 """Checks that the completions for the given command contain the given2288 list of completions."""2289 interp = self.dbg.GetCommandInterpreter()2290 match_strings = lldb.SBStringList()2291 interp.HandleCompletion(command, len(command), 0, -1, match_strings)2292 for completion in completions:2293 # match_strings is a 1-indexed list, so we have to slice...2294 if match:2295 self.assertIn(2296 completion,2297 list(match_strings)[1:],2298 "Couldn't find expected completion",2299 )2300 else:2301 self.assertNotIn(2302 completion, list(match_strings)[1:], "Found unexpected completion"2303 )2304 2305 def filecheck(2306 self, command, check_file, filecheck_options="", expect_cmd_failure=False2307 ):2308 # Run the command.2309 self.runCmd(2310 command,2311 check=(not expect_cmd_failure),2312 msg="FileCheck'ing result of `{0}`".format(command),2313 )2314 2315 self.assertTrue((not expect_cmd_failure) == self.res.Succeeded())2316 2317 # Get the error text if there was an error, and the regular text if not.2318 output = self.res.GetOutput() if self.res.Succeeded() else self.res.GetError()2319 2320 # Assemble the absolute path to the check file. As a convenience for2321 # LLDB inline tests, assume that the check file is a relative path to2322 # a file within the inline test directory.2323 if check_file.endswith(".pyc"):2324 check_file = check_file[:-1]2325 check_file_abs = os.path.abspath(check_file)2326 2327 # Run FileCheck.2328 filecheck_bin = configuration.get_filecheck_path()2329 if not filecheck_bin:2330 self.assertTrue(False, "No valid FileCheck executable specified")2331 filecheck_args = [filecheck_bin, check_file_abs]2332 if filecheck_options:2333 filecheck_args.append(filecheck_options)2334 subproc = Popen(2335 filecheck_args,2336 stdin=PIPE,2337 stdout=PIPE,2338 stderr=PIPE,2339 universal_newlines=True,2340 )2341 cmd_stdout, cmd_stderr = subproc.communicate(input=output)2342 cmd_status = subproc.returncode2343 2344 filecheck_cmd = " ".join(filecheck_args)2345 filecheck_trace = """2346--- FileCheck trace (code={0}) ---2347{1}2348 2349FileCheck input:2350{2}2351 2352FileCheck output:2353{3}2354{4}2355""".format(2356 cmd_status, filecheck_cmd, output, cmd_stdout, cmd_stderr2357 )2358 2359 trace = cmd_status != 0 or traceAlways2360 with recording(self, trace) as sbuf:2361 print(filecheck_trace, file=sbuf)2362 2363 self.assertTrue(cmd_status == 0)2364 2365 def expect(2366 self,2367 string,2368 msg=None,2369 patterns=None,2370 startstr=None,2371 endstr=None,2372 substrs=None,2373 trace=False,2374 error=False,2375 ordered=True,2376 matching=True,2377 exe=True,2378 inHistory=False,2379 ):2380 """2381 Similar to runCmd; with additional expect style output matching ability.2382 2383 Ask the command interpreter to handle the command and then check its2384 return status. The 'msg' parameter specifies an informational assert2385 message. We expect the output from running the command to start with2386 'startstr', matches the substrings contained in 'substrs', and regexp2387 matches the patterns contained in 'patterns'.2388 2389 When matching is true and ordered is true, which are both the default,2390 the strings in the substrs array, and regex in the patterns array, have2391 to appear in the command output in the order in which they appear in2392 their respective array.2393 2394 If the keyword argument error is set to True, it signifies that the API2395 client is expecting the command to fail. In this case, the error stream2396 from running the command is retrieved and compared against the golden2397 input, instead.2398 2399 If the keyword argument matching is set to False, it signifies that the API2400 client is expecting the output of the command not to match the golden2401 input.2402 2403 Finally, the required argument 'string' represents the lldb command to be2404 sent to the command interpreter. In case the keyword argument 'exe' is2405 set to False, the 'string' is treated as a string to be matched/not-matched2406 against the golden input.2407 """2408 # Catch cases where `expect` has been miscalled. Specifically, prevent2409 # this easy to make mistake:2410 # self.expect("lldb command", "some substr")2411 # The `msg` parameter is used only when a failed match occurs. A failed2412 # match can only occur when one of `patterns`, `startstr`, `endstr`, or2413 # `substrs` has been given. Thus, if a `msg` is given, it's an error to2414 # not also provide one of the matcher parameters.2415 if msg and not (patterns or startstr or endstr or substrs or error):2416 assert False, "expect() missing a matcher argument"2417 2418 # Check `patterns` and `substrs` are not accidentally given as strings.2419 assert not isinstance(patterns, str), "patterns must be a collection of strings"2420 assert not isinstance(substrs, str), "substrs must be a collection of strings"2421 2422 trace = True if traceAlways else trace2423 2424 if exe:2425 # First run the command. If we are expecting error, set check=False.2426 # Pass the assert message along since it provides more semantic2427 # info.2428 self.runCmd(2429 string,2430 msg=msg,2431 trace=(True if trace else False),2432 check=not error,2433 inHistory=inHistory,2434 )2435 2436 # Then compare the output against expected strings.2437 output = self.res.GetError() if error else self.res.GetOutput()2438 2439 # If error is True, the API client expects the command to fail!2440 if error:2441 self.assertFalse(2442 self.res.Succeeded(),2443 "Command '" + string + "' is expected to fail!",2444 )2445 else:2446 # No execution required, just compare string against the golden input.2447 if isinstance(string, lldb.SBCommandReturnObject):2448 output = string.GetOutput()2449 else:2450 output = string2451 with recording(self, trace) as sbuf:2452 print("looking at:", output, file=sbuf)2453 2454 expecting_str = "Expecting" if matching else "Not expecting"2455 2456 def found_str(matched):2457 return "was found" if matched else "was not found"2458 2459 # To be used as assert fail message and/or trace content2460 log_lines = [2461 "{}:".format("Ran command" if exe else "Checking string"),2462 '"{}"'.format(string),2463 # Space out command and output2464 "",2465 ]2466 if exe:2467 # Newline before output to make large strings more readable2468 log_lines.append("Got output:\n{}".format(output))2469 2470 # Assume that we start matched if we want a match2471 # Meaning if you have no conditions, matching or2472 # not matching will always pass2473 matched = matching2474 2475 # We will stop checking on first failure2476 if startstr:2477 matched = output.startswith(startstr)2478 log_lines.append(2479 '{} start string: "{}" ({})'.format(2480 expecting_str, startstr, found_str(matched)2481 )2482 )2483 2484 if endstr and matched == matching:2485 matched = output.endswith(endstr)2486 log_lines.append(2487 '{} end string: "{}" ({})'.format(2488 expecting_str, endstr, found_str(matched)2489 )2490 )2491 2492 if substrs and matched == matching:2493 start = 02494 for substr in substrs:2495 index = output.find(substr, start)2496 matched = index != -12497 start = index + len(substr) if ordered and matched else 02498 log_lines.append(2499 '{} sub string: "{}" ({})'.format(2500 expecting_str, substr, found_str(matched)2501 )2502 )2503 2504 if matched != matching:2505 break2506 2507 if patterns and matched == matching:2508 start = 02509 for pattern in patterns:2510 pat = re.compile(pattern)2511 match = pat.search(output, start)2512 matched = bool(match)2513 start = match.end() if ordered and matched else 02514 2515 pattern_line = '{} regex pattern: "{}" ({}'.format(2516 expecting_str, pattern, found_str(matched)2517 )2518 if match:2519 pattern_line += ', matched "{}"'.format(match.group(0))2520 pattern_line += ")"2521 log_lines.append(pattern_line)2522 2523 if matched != matching:2524 break2525 2526 # If a check failed, add any extra assert message2527 if msg is not None and matched != matching:2528 log_lines.append(msg)2529 2530 log_msg = "\n".join(log_lines)2531 with recording(self, trace) as sbuf:2532 print(log_msg, file=sbuf)2533 if matched != matching:2534 self.fail(log_msg)2535 2536 def expect_expr(2537 self,2538 expr,2539 result_summary=None,2540 result_value=None,2541 result_type=None,2542 result_children=None,2543 ):2544 """2545 Evaluates the given expression and verifies the result.2546 :param expr: The expression as a string.2547 :param result_summary: The summary that the expression should have. None if the summary should not be checked.2548 :param result_value: The value that the expression should have. None if the value should not be checked.2549 :param result_type: The type that the expression result should have. None if the type should not be checked.2550 :param result_children: The expected children of the expression result2551 as a list of ValueChecks. None if the children shouldn't be checked.2552 """2553 self.assertTrue(2554 expr.strip() == expr,2555 "Expression contains trailing/leading whitespace: '" + expr + "'",2556 )2557 2558 frame = self.frame()2559 options = lldb.SBExpressionOptions()2560 2561 # Disable fix-its that tests don't pass by accident.2562 options.SetAutoApplyFixIts(False)2563 2564 # Set the usual default options for normal expressions.2565 options.SetIgnoreBreakpoints(True)2566 2567 if self.frame().IsValid():2568 options.SetLanguage(frame.GuessLanguage())2569 eval_result = self.frame().EvaluateExpression(expr, options)2570 else:2571 target = self.target()2572 # If there is no selected target, run the expression in the dummy2573 # target.2574 if not target.IsValid():2575 target = self.dbg.GetDummyTarget()2576 eval_result = target.EvaluateExpression(expr, options)2577 2578 value_check = ValueCheck(2579 type=result_type,2580 value=result_value,2581 summary=result_summary,2582 children=result_children,2583 )2584 value_check.check_value(self, eval_result, str(eval_result))2585 return eval_result2586 2587 def expect_var_path(2588 self, var_path, summary=None, value=None, type=None, children=None2589 ):2590 """2591 Evaluates the given variable path and verifies the result.2592 See also 'frame variable' and SBFrame.GetValueForVariablePath.2593 :param var_path: The variable path as a string.2594 :param summary: The summary that the variable should have. None if the summary should not be checked.2595 :param value: The value that the variable should have. None if the value should not be checked.2596 :param type: The type that the variable result should have. None if the type should not be checked.2597 :param children: The expected children of the variable as a list of ValueChecks.2598 None if the children shouldn't be checked.2599 """2600 self.assertTrue(2601 var_path.strip() == var_path,2602 "Expression contains trailing/leading whitespace: '" + var_path + "'",2603 )2604 2605 frame = self.frame()2606 eval_result = frame.GetValueForVariablePath(var_path)2607 2608 value_check = ValueCheck(2609 type=type, value=value, summary=summary, children=children2610 )2611 value_check.check_value(self, eval_result, str(eval_result))2612 return eval_result2613 2614 """Assert that an lldb.SBError is in the "success" state."""2615 2616 def assertSuccess(self, obj, msg=None):2617 if not obj.Success():2618 error = obj.GetCString()2619 self.fail(self._formatMessage(msg, "'{}' is not success".format(error)))2620 2621 """Assert that an lldb.SBError is in the "failure" state."""2622 2623 def assertFailure(self, obj, error_str=None, msg=None):2624 if obj.Success():2625 self.fail(self._formatMessage(msg, "Error not in a fail state"))2626 2627 if error_str is None:2628 return2629 2630 error = obj.GetCString()2631 self.assertEqual(error, error_str, msg)2632 2633 """Assert that a command return object is successful"""2634 2635 def assertCommandReturn(self, obj, msg=None):2636 if not obj.Succeeded():2637 error = obj.GetError()2638 self.fail(self._formatMessage(msg, "'{}' is not success".format(error)))2639 2640 """Assert two states are equal"""2641 2642 def assertState(self, first, second, msg=None):2643 if first != second:2644 error = "{} ({}) != {} ({})".format(2645 lldbutil.state_type_to_str(first),2646 first,2647 lldbutil.state_type_to_str(second),2648 second,2649 )2650 self.fail(self._formatMessage(msg, error))2651 2652 """Assert two stop reasons are equal"""2653 2654 def assertStopReason(self, first, second, msg=None):2655 if first != second:2656 error = "{} ({}) != {} ({})".format(2657 lldbutil.stop_reason_to_str(first),2658 first,2659 lldbutil.stop_reason_to_str(second),2660 second,2661 )2662 self.fail(self._formatMessage(msg, error))2663 2664 def createTestTarget(self, file_path=None, msg=None, load_dependent_modules=True):2665 """2666 Creates a target from the file found at the given file path.2667 Asserts that the resulting target is valid.2668 :param file_path: The file path that should be used to create the target.2669 The default argument opens the current default test2670 executable in the current test directory.2671 :param msg: A custom error message.2672 """2673 if file_path is None:2674 file_path = self.getBuildArtifact("a.out")2675 error = lldb.SBError()2676 triple = ""2677 platform = ""2678 target = self.dbg.CreateTarget(2679 file_path, triple, platform, load_dependent_modules, error2680 )2681 if error.Fail():2682 err = "Couldn't create target for path '{}': {}".format(2683 file_path, str(error)2684 )2685 self.fail(self._formatMessage(msg, err))2686 2687 self.assertTrue(target.IsValid(), "Got invalid target without error")2688 return target2689 2690 # =================================================2691 # Misc. helper methods for debugging test execution2692 # =================================================2693 2694 def DebugSBValue(self, val):2695 """Debug print a SBValue object, if traceAlways is True."""2696 from .lldbutil import value_type_to_str2697 2698 if not traceAlways:2699 return2700 2701 err = sys.stderr2702 err.write(val.GetName() + ":\n")2703 err.write("\t" + "TypeName -> " + val.GetTypeName() + "\n")2704 err.write("\t" + "ByteSize -> " + str(val.GetByteSize()) + "\n")2705 err.write("\t" + "NumChildren -> " + str(val.GetNumChildren()) + "\n")2706 err.write("\t" + "Value -> " + str(val.GetValue()) + "\n")2707 err.write("\t" + "ValueAsUnsigned -> " + str(val.GetValueAsUnsigned()) + "\n")2708 err.write(2709 "\t" + "ValueType -> " + value_type_to_str(val.GetValueType()) + "\n"2710 )2711 err.write("\t" + "Summary -> " + str(val.GetSummary()) + "\n")2712 err.write("\t" + "IsPointerType -> " + str(val.TypeIsPointerType()) + "\n")2713 err.write("\t" + "Location -> " + val.GetLocation() + "\n")2714 2715 def DebugSBType(self, type):2716 """Debug print a SBType object, if traceAlways is True."""2717 if not traceAlways:2718 return2719 2720 err = sys.stderr2721 err.write(type.GetName() + ":\n")2722 err.write("\t" + "ByteSize -> " + str(type.GetByteSize()) + "\n")2723 err.write("\t" + "IsAggregateType -> " + str(type.IsAggregateType()) + "\n")2724 err.write("\t" + "IsPointerType -> " + str(type.IsPointerType()) + "\n")2725 err.write("\t" + "IsReferenceType -> " + str(type.IsReferenceType()) + "\n")2726 2727 def DebugPExpect(self, child):2728 """Debug the spwaned pexpect object."""2729 if not traceAlways:2730 return2731 2732 print(child)2733 2734 @classmethod2735 def RemoveTempFile(cls, file):2736 if os.path.exists(file):2737 remove_file(file)2738 2739 2740# On Windows, the first attempt to delete a recently-touched file can fail2741# because of a race with antimalware scanners. This function will detect a2742# failure and retry.2743 2744 2745def remove_file(file, num_retries=1, sleep_duration=0.5):2746 for i in range(num_retries + 1):2747 try:2748 os.remove(file)2749 return True2750 except:2751 time.sleep(sleep_duration)2752 continue2753 return False2754