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