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