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