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