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