lldbtest.py revision 2c8d1596ec40c321d6d39c2b32c33fd662b66c11
1""" 2LLDB module which provides the abstract base class of lldb test case. 3 4The concrete subclass can override lldbtest.TesBase in order to inherit the 5common behavior for unitest.TestCase.setUp/tearDown implemented in this file. 6 7The subclass should override the attribute mydir in order for the python runtime 8to locate the individual test cases when running as part of a large test suite 9or when running each test case as a separate python invocation. 10 11./dotest.py provides a test driver which sets up the environment to run the 12entire test suite. Users who want to run a test case on its own can specify the 13LLDB_TEST and PYTHONPATH environment variables, for example: 14 15$ export LLDB_TEST=$PWD 16$ export PYTHONPATH=/Volumes/data/lldb/svn/trunk/build/Debug/LLDB.framework/Resources/Python:$LLDB_TEST:$LLDB_TEST/plugins 17$ echo $LLDB_TEST 18/Volumes/data/lldb/svn/trunk/test 19$ echo $PYTHONPATH 20/Volumes/data/lldb/svn/trunk/build/Debug/LLDB.framework/Resources/Python:/Volumes/data/lldb/svn/trunk/test:/Volumes/data/lldb/svn/trunk/test/plugins 21$ python function_types/TestFunctionTypes.py 22. 23---------------------------------------------------------------------- 24Ran 1 test in 0.363s 25 26OK 27$ LLDB_COMMAND_TRACE=YES python array_types/TestArrayTypes.py 28 29... 30 31runCmd: breakpoint set -f main.c -l 42 32output: Breakpoint created: 1: file ='main.c', line = 42, locations = 1 33 34runCmd: run 35output: Launching '/Volumes/data/lldb/svn/trunk/test/array_types/a.out' (x86_64) 36 37... 38 39runCmd: frame variable strings 40output: (char *[4]) strings = { 41 (char *) strings[0] = 0x0000000100000f0c "Hello", 42 (char *) strings[1] = 0x0000000100000f12 "Hola", 43 (char *) strings[2] = 0x0000000100000f17 "Bonjour", 44 (char *) strings[3] = 0x0000000100000f1f "Guten Tag" 45} 46 47runCmd: frame variable char_16 48output: (char [16]) char_16 = { 49 (char) char_16[0] = 'H', 50 (char) char_16[1] = 'e', 51 (char) char_16[2] = 'l', 52 (char) char_16[3] = 'l', 53 (char) char_16[4] = 'o', 54 (char) char_16[5] = ' ', 55 (char) char_16[6] = 'W', 56 (char) char_16[7] = 'o', 57 (char) char_16[8] = 'r', 58 (char) char_16[9] = 'l', 59 (char) char_16[10] = 'd', 60 (char) char_16[11] = '\n', 61 (char) char_16[12] = '\0', 62 (char) char_16[13] = '\0', 63 (char) char_16[14] = '\0', 64 (char) char_16[15] = '\0' 65} 66 67runCmd: frame variable ushort_matrix 68output: (unsigned short [2][3]) ushort_matrix = { 69 (unsigned short [3]) ushort_matrix[0] = { 70 (unsigned short) ushort_matrix[0][0] = 0x0001, 71 (unsigned short) ushort_matrix[0][1] = 0x0002, 72 (unsigned short) ushort_matrix[0][2] = 0x0003 73 }, 74 (unsigned short [3]) ushort_matrix[1] = { 75 (unsigned short) ushort_matrix[1][0] = 0x000b, 76 (unsigned short) ushort_matrix[1][1] = 0x0016, 77 (unsigned short) ushort_matrix[1][2] = 0x0021 78 } 79} 80 81runCmd: frame variable long_6 82output: (long [6]) long_6 = { 83 (long) long_6[0] = 1, 84 (long) long_6[1] = 2, 85 (long) long_6[2] = 3, 86 (long) long_6[3] = 4, 87 (long) long_6[4] = 5, 88 (long) long_6[5] = 6 89} 90 91. 92---------------------------------------------------------------------- 93Ran 1 test in 0.349s 94 95OK 96$ 97""" 98 99import os, sys, traceback 100import re 101from subprocess import * 102import StringIO 103import time 104import types 105import unittest2 106import lldb 107 108# See also dotest.parseOptionsAndInitTestdirs(), where the environment variables 109# LLDB_COMMAND_TRACE and LLDB_NO_CLEANUP are set from '-t' and '-r dir' options. 110 111# By default, traceAlways is False. 112if "LLDB_COMMAND_TRACE" in os.environ and os.environ["LLDB_COMMAND_TRACE"]=="YES": 113 traceAlways = True 114else: 115 traceAlways = False 116 117# By default, doCleanup is True. 118if "LLDB_DO_CLEANUP" in os.environ and os.environ["LLDB_DO_CLEANUP"]=="NO": 119 doCleanup = False 120else: 121 doCleanup = True 122 123 124# 125# Some commonly used assert messages. 126# 127 128COMMAND_FAILED_AS_EXPECTED = "Command has failed as expected" 129 130CURRENT_EXECUTABLE_SET = "Current executable set successfully" 131 132PROCESS_IS_VALID = "Process is valid" 133 134PROCESS_KILLED = "Process is killed successfully" 135 136RUN_SUCCEEDED = "Process is launched successfully" 137 138RUN_COMPLETED = "Process exited successfully" 139 140BACKTRACE_DISPLAYED_CORRECTLY = "Backtrace displayed correctly" 141 142BREAKPOINT_CREATED = "Breakpoint created successfully" 143 144BREAKPOINT_PENDING_CREATED = "Pending breakpoint created successfully" 145 146BREAKPOINT_HIT_ONCE = "Breakpoint resolved with hit cout = 1" 147 148BREAKPOINT_HIT_TWICE = "Breakpoint resolved with hit cout = 2" 149 150BREAKPOINT_HIT_THRICE = "Breakpoint resolved with hit cout = 3" 151 152STEP_OUT_SUCCEEDED = "Thread step-out succeeded" 153 154STOPPED_DUE_TO_BREAKPOINT = "Process state is stopped due to breakpoint" 155 156STOPPED_DUE_TO_BREAKPOINT_CONDITION = "Stopped due to breakpoint condition" 157 158STOPPED_DUE_TO_SIGNAL = "Process state is stopped due to signal" 159 160STOPPED_DUE_TO_STEP_IN = "Process state is stopped due to step in" 161 162DATA_TYPES_DISPLAYED_CORRECTLY = "Data type(s) displayed correctly" 163 164VALID_BREAKPOINT = "Got a valid breakpoint" 165 166VALID_BREAKPOINT_LOCATION = "Got a valid breakpoint location" 167 168VALID_FILESPEC = "Got a valid filespec" 169 170VALID_PROCESS = "Got a valid process" 171 172VALID_TARGET = "Got a valid target" 173 174VARIABLES_DISPLAYED_CORRECTLY = "Variable(s) displayed correctly" 175 176 177# 178# And a generic "Command '%s' returns successfully" message generator. 179# 180def CMD_MSG(str, exe): 181 if exe: 182 return "Command '%s' returns successfully" % str 183 else: 184 return "'%s' compares successfully" % str 185 186# 187# And a generic "Value of setting '%s' is correct" message generator. 188# 189def SETTING_MSG(setting): 190 return "Value of setting '%s' is correct" % setting 191 192# 193# Returns an env variable array from the os.environ map object. 194# 195def EnvArray(): 196 return map(lambda k,v: k+"="+v, os.environ.keys(), os.environ.values()) 197 198def line_number(filename, string_to_match): 199 """Helper function to return the line number of the first matched string.""" 200 with open(filename, 'r') as f: 201 for i, line in enumerate(f): 202 if line.find(string_to_match) != -1: 203 # Found our match. 204 return i+1 205 return -1 206 207def pointer_size(): 208 """Return the pointer size of the host system.""" 209 import ctypes 210 a_pointer = ctypes.c_void_p(0xffff) 211 return 8 * ctypes.sizeof(a_pointer) 212 213 214class recording(StringIO.StringIO): 215 """ 216 A nice little context manager for recording the debugger interactions into 217 our session object. If trace flag is ON, it also emits the interactions 218 into the stderr. 219 """ 220 def __init__(self, test, trace): 221 """Create a StringIO instance; record the session obj and trace flag.""" 222 StringIO.StringIO.__init__(self) 223 self.session = test.session if test else None 224 self.trace = trace 225 226 def __enter__(self): 227 """ 228 Context management protocol on entry to the body of the with statement. 229 Just return the StringIO object. 230 """ 231 return self 232 233 def __exit__(self, type, value, tb): 234 """ 235 Context management protocol on exit from the body of the with statement. 236 If trace is ON, it emits the recordings into stderr. Always add the 237 recordings to our session object. And close the StringIO object, too. 238 """ 239 if self.trace: 240 print >> sys.stderr, self.getvalue() 241 if self.session: 242 print >> self.session, self.getvalue() 243 self.close() 244 245# From 2.7's subprocess.check_output() convenience function. 246def system(*popenargs, **kwargs): 247 r"""Run command with arguments and return its output as a byte string. 248 249 If the exit code was non-zero it raises a CalledProcessError. The 250 CalledProcessError object will have the return code in the returncode 251 attribute and output in the output attribute. 252 253 The arguments are the same as for the Popen constructor. Example: 254 255 >>> check_output(["ls", "-l", "/dev/null"]) 256 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' 257 258 The stdout argument is not allowed as it is used internally. 259 To capture standard error in the result, use stderr=STDOUT. 260 261 >>> check_output(["/bin/sh", "-c", 262 ... "ls -l non_existent_file ; exit 0"], 263 ... stderr=STDOUT) 264 'ls: non_existent_file: No such file or directory\n' 265 """ 266 267 # Assign the sender object to variable 'test' and remove it from kwargs. 268 test = kwargs.pop('sender', None) 269 270 if 'stdout' in kwargs: 271 raise ValueError('stdout argument not allowed, it will be overridden.') 272 process = Popen(stdout=PIPE, *popenargs, **kwargs) 273 output, error = process.communicate() 274 retcode = process.poll() 275 276 with recording(test, traceAlways) as sbuf: 277 if isinstance(popenargs, types.StringTypes): 278 args = [popenargs] 279 else: 280 args = list(popenargs) 281 print >> sbuf 282 print >> sbuf, "os command:", args 283 print >> sbuf, "stdout:", output 284 print >> sbuf, "stderr:", error 285 print >> sbuf, "retcode:", retcode 286 print >> sbuf 287 288 if retcode: 289 cmd = kwargs.get("args") 290 if cmd is None: 291 cmd = popenargs[0] 292 raise CalledProcessError(retcode, cmd) 293 return output 294 295def getsource_if_available(obj): 296 """ 297 Return the text of the source code for an object if available. Otherwise, 298 a print representation is returned. 299 """ 300 import inspect 301 try: 302 return inspect.getsource(obj) 303 except: 304 return repr(obj) 305 306class TestBase(unittest2.TestCase): 307 """ 308 This abstract base class is meant to be subclassed. It provides default 309 implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(), 310 among other things. 311 312 Important things for test class writers: 313 314 - Overwrite the mydir class attribute, otherwise your test class won't 315 run. It specifies the relative directory to the top level 'test' so 316 the test harness can change to the correct working directory before 317 running your test. 318 319 - The setUp method sets up things to facilitate subsequent interactions 320 with the debugger as part of the test. These include: 321 - create/get a debugger set with synchronous mode (self.dbg) 322 - get the command interpreter from with the debugger (self.ci) 323 - create a result object for use with the command interpreter 324 (self.result) 325 - plus other stuffs 326 327 - The tearDown method tries to perform some necessary cleanup on behalf 328 of the test to return the debugger to a good state for the next test. 329 These include: 330 - execute any tearDown hooks registered by the test method with 331 TestBase.addTearDownHook(); examples can be found in 332 settings/TestSettings.py 333 - kill the inferior process launched during the test method 334 - if by 'run' or 'process launch' command, 'process kill' 335 command is used 336 - if the test method uses LLDB Python API to launch process, 337 it should assign the process object to self.process; that 338 way, tearDown will use self.process.Kill() on the object 339 - perform build cleanup before running the next test method in the 340 same test class; examples of registering for this service can be 341 found in types/TestIntegerTypes.py with the call: 342 - self.setTearDownCleanup(dictionary=d) 343 344 - Similarly setUpClass and tearDownClass perform classwise setup and 345 teardown fixtures. The tearDownClass method invokes a default build 346 cleanup for the entire test class; also, subclasses can implement the 347 classmethod classCleanup(cls) to perform special class cleanup action. 348 349 - The instance methods runCmd and expect are used heavily by existing 350 test cases to send a command to the command interpreter and to perform 351 string/pattern matching on the output of such command execution. The 352 expect method also provides a mode to peform string/pattern matching 353 without running a command. 354 355 - The build methods buildDefault, buildDsym, and buildDwarf are used to 356 build the binaries used during a particular test scenario. A plugin 357 should be provided for the sys.platform running the test suite. The 358 Mac OS X implementation is located in plugins/darwin.py. 359 360 """ 361 362 @classmethod 363 def skipLongRunningTest(cls): 364 """ 365 By default, we skip long running test case. 366 This can be overridden by passing '-l' to the test driver (dotest.py). 367 """ 368 if "LLDB_SKIP_LONG_RUNNING_TEST" in os.environ and "NO" == os.environ["LLDB_SKIP_LONG_RUNNING_TEST"]: 369 return False 370 else: 371 return True 372 373 # The concrete subclass should override this attribute. 374 mydir = None 375 376 # State pertaining to the inferior process, if any. 377 # This reflects inferior process started through the command interface with 378 # either the lldb "run" or "process launch" command. 379 # See also self.runCmd(). 380 runStarted = False 381 382 # Maximum allowed attempts when launching the inferior process. 383 # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable. 384 maxLaunchCount = 3; 385 386 # Time to wait before the next launching attempt in second(s). 387 # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable. 388 timeWait = 1.0; 389 390 # Keep track of the old current working directory. 391 oldcwd = None 392 393 @classmethod 394 def setUpClass(cls): 395 """ 396 Python unittest framework class setup fixture. 397 Do current directory manipulation. 398 """ 399 400 # Fail fast if 'mydir' attribute is not overridden. 401 if not cls.mydir or len(cls.mydir) == 0: 402 raise Exception("Subclasses must override the 'mydir' attribute.") 403 # Save old working directory. 404 cls.oldcwd = os.getcwd() 405 406 # Change current working directory if ${LLDB_TEST} is defined. 407 # See also dotest.py which sets up ${LLDB_TEST}. 408 if ("LLDB_TEST" in os.environ): 409 if traceAlways: 410 print >> sys.stderr, "Change dir to:", os.path.join(os.environ["LLDB_TEST"], cls.mydir) 411 os.chdir(os.path.join(os.environ["LLDB_TEST"], cls.mydir)) 412 413 @classmethod 414 def tearDownClass(cls): 415 """ 416 Python unittest framework class teardown fixture. 417 Do class-wide cleanup. 418 """ 419 420 if doCleanup: 421 # First, let's do the platform-specific cleanup. 422 module = __import__(sys.platform) 423 if not module.cleanup(): 424 raise Exception("Don't know how to do cleanup") 425 426 # Subclass might have specific cleanup function defined. 427 if getattr(cls, "classCleanup", None): 428 if traceAlways: 429 print >> sys.stderr, "Call class-specific cleanup function for class:", cls 430 try: 431 cls.classCleanup() 432 except: 433 exc_type, exc_value, exc_tb = sys.exc_info() 434 traceback.print_exception(exc_type, exc_value, exc_tb) 435 436 # Restore old working directory. 437 if traceAlways: 438 print >> sys.stderr, "Restore dir to:", cls.oldcwd 439 os.chdir(cls.oldcwd) 440 441 def setUp(self): 442 #import traceback 443 #traceback.print_stack() 444 445 if ("LLDB_WAIT_BETWEEN_TEST_CASES" in os.environ and 446 os.environ["LLDB_WAIT_BETWEEN_TEST_CASES"] == 'YES'): 447 time.sleep(0.5) 448 449 if "LLDB_MAX_LAUNCH_COUNT" in os.environ: 450 self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"]) 451 452 if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ: 453 self.timeWait = float(os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"]) 454 455 # Create the debugger instance if necessary. 456 try: 457 self.dbg = lldb.DBG 458 except AttributeError: 459 self.dbg = lldb.SBDebugger.Create() 460 461 if not self.dbg.IsValid(): 462 raise Exception('Invalid debugger instance') 463 464 # We want our debugger to be synchronous. 465 self.dbg.SetAsync(False) 466 467 # There is no process associated with the debugger as yet. 468 # See also self.tearDown() where it checks whether self.process has a 469 # valid reference and calls self.process.Kill() to kill the process. 470 self.process = None 471 472 # Retrieve the associated command interpreter instance. 473 self.ci = self.dbg.GetCommandInterpreter() 474 if not self.ci: 475 raise Exception('Could not get the command interpreter') 476 477 # And the result object. 478 self.res = lldb.SBCommandReturnObject() 479 480 # These are for customized teardown cleanup. 481 self.dict = None 482 self.doTearDownCleanup = False 483 484 # Create a string buffer to record the session info, to be dumped into a 485 # test case specific file if test failure is encountered. 486 self.session = StringIO.StringIO() 487 488 # Optimistically set __errored__, __failed__, __expected__ to False 489 # initially. If the test errored/failed, the session info 490 # (self.session) is then dumped into a session specific file for 491 # diagnosis. 492 self.__errored__ = False 493 self.__failed__ = False 494 self.__expected__ = False 495 496 # See addTearDownHook(self, hook) which allows the client to add a hook 497 # function to be run during tearDown() time. 498 self.hooks = [] 499 500 def markError(self): 501 """Callback invoked when we (the test case instance) errored.""" 502 self.__errored__ = True 503 with recording(self, False) as sbuf: 504 # False because there's no need to write "ERROR" to the stderr twice. 505 # Once by the Python unittest framework, and a second time by us. 506 print >> sbuf, "ERROR" 507 508 def markFailure(self): 509 """Callback invoked when we (the test case instance) failed.""" 510 self.__failed__ = True 511 with recording(self, False) as sbuf: 512 # False because there's no need to write "FAIL" to the stderr twice. 513 # Once by the Python unittest framework, and a second time by us. 514 print >> sbuf, "FAIL" 515 516 def markExpectedFailure(self): 517 """Callback invoked when an expected failure/error occurred.""" 518 self.__expected__ = True 519 with recording(self, False) as sbuf: 520 # False because there's no need to write "expected failure" to the 521 # stderr twice. 522 # Once by the Python unittest framework, and a second time by us. 523 print >> sbuf, "expected failure" 524 525 def dumpSessionInfo(self): 526 """ 527 Dump the debugger interactions leading to a test error/failure. This 528 allows for more convenient postmortem analysis. 529 530 See also LLDBTestResult (dotest.py) which is a singlton class derived 531 from TextTestResult and overwrites addError, addFailure, and 532 addExpectedFailure methods to allow us to to mark the test instance as 533 such. 534 """ 535 536 # We are here because self.tearDown() detected that this test instance 537 # either errored or failed. The lldb.test_result singleton contains 538 # two lists (erros and failures) which get populated by the unittest 539 # framework. Look over there for stack trace information. 540 # 541 # The lists contain 2-tuples of TestCase instances and strings holding 542 # formatted tracebacks. 543 # 544 # See http://docs.python.org/library/unittest.html#unittest.TestResult. 545 if self.__errored__: 546 pairs = lldb.test_result.errors 547 elif self.__failed__: 548 pairs = lldb.test_result.failures 549 elif self.__expected__: 550 pairs = lldb.test_result.expectedFailures 551 else: 552 # Simply return, there's no session info to dump! 553 return 554 555 for test, traceback in pairs: 556 if test is self: 557 print >> self.session, traceback 558 559 dname = os.path.join(os.environ["LLDB_TEST"], 560 os.environ["LLDB_SESSION_DIRNAME"]) 561 if not os.path.isdir(dname): 562 os.mkdir(dname) 563 fname = os.path.join(dname, "%s.log" % self.id()) 564 with open(fname, "w") as f: 565 import datetime 566 print >> f, "Session info generated @", datetime.datetime.now().ctime() 567 print >> f, self.session.getvalue() 568 569 def setTearDownCleanup(self, dictionary=None): 570 """Register a cleanup action at tearDown() time with a dictinary""" 571 self.dict = dictionary 572 self.doTearDownCleanup = True 573 574 def addTearDownHook(self, hook): 575 """ 576 Add a function to be run during tearDown() time. 577 578 Hooks are executed in a first come first serve manner. 579 """ 580 if callable(hook): 581 with recording(self, traceAlways) as sbuf: 582 print >> sbuf, "Adding tearDown hook:", getsource_if_available(hook) 583 self.hooks.append(hook) 584 585 def tearDown(self): 586 #import traceback 587 #traceback.print_stack() 588 589 # Check and run any hook functions. 590 for hook in self.hooks: 591 with recording(self, traceAlways) as sbuf: 592 print >> sbuf, "Executing tearDown hook:", getsource_if_available(hook) 593 hook() 594 595 # Terminate the current process being debugged, if any. 596 if self.runStarted: 597 self.runCmd("process kill", PROCESS_KILLED, check=False) 598 elif self.process and self.process.IsValid(): 599 rc = self.invoke(self.process, "Kill") 600 self.assertTrue(rc.Success(), PROCESS_KILLED) 601 del self.process 602 603 del self.dbg 604 del self.hooks 605 606 # Perform registered teardown cleanup. 607 if doCleanup and self.doTearDownCleanup: 608 module = __import__(sys.platform) 609 if not module.cleanup(self, dictionary=self.dict): 610 raise Exception("Don't know how to do cleanup") 611 612 # We always dump the session infos for failures/errors. 613 self.dumpSessionInfo() 614 615 def runCmd(self, cmd, msg=None, check=True, trace=False, setCookie=True): 616 """ 617 Ask the command interpreter to handle the command and then check its 618 return status. 619 """ 620 # Fail fast if 'cmd' is not meaningful. 621 if not cmd or len(cmd) == 0: 622 raise Exception("Bad 'cmd' parameter encountered") 623 624 trace = (True if traceAlways else trace) 625 626 running = (cmd.startswith("run") or cmd.startswith("process launch")) 627 628 for i in range(self.maxLaunchCount if running else 1): 629 self.ci.HandleCommand(cmd, self.res) 630 631 with recording(self, trace) as sbuf: 632 print >> sbuf, "runCmd:", cmd 633 if not check: 634 print >> sbuf, "check of return status not required" 635 if self.res.Succeeded(): 636 print >> sbuf, "output:", self.res.GetOutput() 637 else: 638 print >> sbuf, "runCmd failed!" 639 print >> sbuf, self.res.GetError() 640 641 if running: 642 # For process launch, wait some time before possible next try. 643 time.sleep(self.timeWait) 644 645 if self.res.Succeeded(): 646 break 647 elif running: 648 with recording(self, True) as sbuf: 649 print >> sbuf, "Command '" + cmd + "' failed!" 650 651 # Modify runStarted only if "run" or "process launch" was encountered. 652 if running: 653 self.runStarted = running and setCookie 654 655 if check: 656 self.assertTrue(self.res.Succeeded(), 657 msg if msg else CMD_MSG(cmd, True)) 658 659 def expect(self, str, msg=None, patterns=None, startstr=None, substrs=None, trace=False, error=False, matching=True, exe=True): 660 """ 661 Similar to runCmd; with additional expect style output matching ability. 662 663 Ask the command interpreter to handle the command and then check its 664 return status. The 'msg' parameter specifies an informational assert 665 message. We expect the output from running the command to start with 666 'startstr', matches the substrings contained in 'substrs', and regexp 667 matches the patterns contained in 'patterns'. 668 669 If the keyword argument error is set to True, it signifies that the API 670 client is expecting the command to fail. In this case, the error stream 671 from running the command is retrieved and compared against the golden 672 input, instead. 673 674 If the keyword argument matching is set to False, it signifies that the API 675 client is expecting the output of the command not to match the golden 676 input. 677 678 Finally, the required argument 'str' represents the lldb command to be 679 sent to the command interpreter. In case the keyword argument 'exe' is 680 set to False, the 'str' is treated as a string to be matched/not-matched 681 against the golden input. 682 """ 683 trace = (True if traceAlways else trace) 684 685 if exe: 686 # First run the command. If we are expecting error, set check=False. 687 # Pass the assert message along since it provides more semantic info. 688 self.runCmd(str, msg=msg, trace = (True if trace else False), check = not error) 689 690 # Then compare the output against expected strings. 691 output = self.res.GetError() if error else self.res.GetOutput() 692 693 # If error is True, the API client expects the command to fail! 694 if error: 695 self.assertFalse(self.res.Succeeded(), 696 "Command '" + str + "' is expected to fail!") 697 else: 698 # No execution required, just compare str against the golden input. 699 output = str 700 with recording(self, trace) as sbuf: 701 print >> sbuf, "looking at:", output 702 703 # The heading says either "Expecting" or "Not expecting". 704 heading = "Expecting" if matching else "Not expecting" 705 706 # Start from the startstr, if specified. 707 # If there's no startstr, set the initial state appropriately. 708 matched = output.startswith(startstr) if startstr else (True if matching else False) 709 710 if startstr: 711 with recording(self, trace) as sbuf: 712 print >> sbuf, "%s start string: %s" % (heading, startstr) 713 print >> sbuf, "Matched" if matched else "Not matched" 714 715 # Look for sub strings, if specified. 716 keepgoing = matched if matching else not matched 717 if substrs and keepgoing: 718 for str in substrs: 719 matched = output.find(str) != -1 720 with recording(self, trace) as sbuf: 721 print >> sbuf, "%s sub string: %s" % (heading, str) 722 print >> sbuf, "Matched" if matched else "Not matched" 723 keepgoing = matched if matching else not matched 724 if not keepgoing: 725 break 726 727 # Search for regular expression patterns, if specified. 728 keepgoing = matched if matching else not matched 729 if patterns and keepgoing: 730 for pattern in patterns: 731 # Match Objects always have a boolean value of True. 732 matched = bool(re.search(pattern, output)) 733 with recording(self, trace) as sbuf: 734 print >> sbuf, "%s pattern: %s" % (heading, pattern) 735 print >> sbuf, "Matched" if matched else "Not matched" 736 keepgoing = matched if matching else not matched 737 if not keepgoing: 738 break 739 740 self.assertTrue(matched if matching else not matched, 741 msg if msg else CMD_MSG(str, exe)) 742 743 def invoke(self, obj, name, trace=False): 744 """Use reflection to call a method dynamically with no argument.""" 745 trace = (True if traceAlways else trace) 746 747 method = getattr(obj, name) 748 import inspect 749 self.assertTrue(inspect.ismethod(method), 750 name + "is a method name of object: " + str(obj)) 751 result = method() 752 with recording(self, trace) as sbuf: 753 print >> sbuf, str(method) + ":", result 754 return result 755 756 def breakAfterLaunch(self, process, func, trace=False): 757 """ 758 Perform some dancees after LaunchProcess() to break at func name. 759 760 Return True if we can successfully break at the func name in due time. 761 """ 762 trace = (True if traceAlways else trace) 763 764 count = 0 765 while True: 766 # The stop reason of the thread should be breakpoint. 767 thread = process.GetThreadAtIndex(0) 768 SR = thread.GetStopReason() 769 with recording(self, trace) as sbuf: 770 print >> sbuf, "StopReason =", StopReasonString(SR) 771 772 if SR == StopReasonEnum("Breakpoint"): 773 frame = thread.GetFrameAtIndex(0) 774 name = frame.GetFunction().GetName() 775 with recording(self, trace) as sbuf: 776 print >> sbuf, "function =", name 777 if (name == func): 778 # We got what we want; now break out of the loop. 779 return True 780 781 # The inferior is in a transient state; continue the process. 782 time.sleep(1.0) 783 with recording(self, trace) as sbuf: 784 print >> sbuf, "Continuing the process:", process 785 process.Continue() 786 787 count = count + 1 788 if count == 15: 789 with recording(self, trace) as sbuf: 790 print >> sbuf, "Reached 15 iterations, giving up..." 791 # Enough iterations already, break out of the loop. 792 return False 793 794 # End of while loop. 795 796 797 def buildDefault(self, architecture=None, compiler=None, dictionary=None): 798 """Platform specific way to build the default binaries.""" 799 module = __import__(sys.platform) 800 if not module.buildDefault(self, architecture, compiler, dictionary): 801 raise Exception("Don't know how to build default binary") 802 803 def buildDsym(self, architecture=None, compiler=None, dictionary=None): 804 """Platform specific way to build binaries with dsym info.""" 805 module = __import__(sys.platform) 806 if not module.buildDsym(self, architecture, compiler, dictionary): 807 raise Exception("Don't know how to build binary with dsym") 808 809 def buildDwarf(self, architecture=None, compiler=None, dictionary=None): 810 """Platform specific way to build binaries with dwarf maps.""" 811 module = __import__(sys.platform) 812 if not module.buildDwarf(self, architecture, compiler, dictionary): 813 raise Exception("Don't know how to build binary with dwarf") 814 815 def DebugSBValue(self, frame, val): 816 """Debug print a SBValue object, if traceAlways is True.""" 817 from lldbutil import ValueTypeString 818 819 if not traceAlways: 820 return 821 822 err = sys.stderr 823 err.write(val.GetName() + ":\n") 824 err.write('\t' + "TypeName -> " + val.GetTypeName() + '\n') 825 err.write('\t' + "ByteSize -> " + str(val.GetByteSize()) + '\n') 826 err.write('\t' + "NumChildren -> " + str(val.GetNumChildren()) + '\n') 827 err.write('\t' + "Value -> " + str(val.GetValue(frame)) + '\n') 828 err.write('\t' + "ValueType -> " + ValueTypeString(val.GetValueType()) + '\n') 829 err.write('\t' + "Summary -> " + str(val.GetSummary(frame)) + '\n') 830 err.write('\t' + "IsPointerType -> " + str(val.TypeIsPointerType()) + '\n') 831 err.write('\t' + "Location -> " + val.GetLocation(frame) + '\n') 832 833