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