#!/usr/bin/env python

"""
Run a program via lldb until it fails.
The lldb executable is located via your PATH env variable, if not specified.
"""

import os
import sys
from optparse import OptionParser

def is_exe(fpath):
    """Check whether fpath is an executable."""
    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)

def which(program):
    """Find the full path to a program, or return None."""
    fpath, fname = os.path.split(program)
    if fpath:
        if is_exe(program):
            return program
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                return exe_file
    return None

def do_lldb_launch_loop(lldb_command, exe, exe_options):
    from cStringIO import StringIO 
    import pexpect, time

    prompt = "\(lldb\) "
    lldb = pexpect.spawn(lldb_command)
    # Turn on logging for what lldb sends back.
    lldb.logfile_read = sys.stdout
    lldb.expect(prompt)

    # Now issue the file command.
    #print "sending 'file %s' command..." % exe
    lldb.sendline('file %s' % exe)
    lldb.expect(prompt)

    # Loop until it faults....
    count = 0
    #while True:
    #    count = count + 1
    for i in range(100):
        count = i
        #print "sending 'process launch -- %s' command... (iteration: %d)" % (exe_options, count)
        lldb.sendline('process launch -- %s' % exe_options)
        index = lldb.expect(['Process .* exited with status',
                             'Process .* stopped',
                             pexpect.TIMEOUT])
        if index == 0:
            # We'll try again later.
            time.sleep(3)
        elif index == 1:
            # Perfect, our process had stopped; break out of the loop.
            break;
        elif index == 2:
            # Something went wrong.
            print "TIMEOUT occurred:", str(lldb)        

    # Give control of lldb shell to the user.
    lldb.interact()

def main():
    # This is to set up the Python path to include the pexpect-2.4 dir.
    # Remember to update this when/if things change.
    scriptPath = sys.path[0]
    sys.path.append(os.path.join(scriptPath, os.pardir, os.pardir, 'test', 'pexpect-2.4'))

    parser = OptionParser(usage="""\
%prog [options]
Run a program via lldb until it fails.
The lldb executable is located via your PATH env variable, if not specified.\
""")
    parser.add_option('-l', '--lldb-command',
                      type='string', action='store', metavar='LLDB_COMMAND',
                      default='lldb', dest='lldb_command',
                      help='Full path to your lldb command')
    parser.add_option('-e', '--executable',
                      type='string', action='store',
                      dest='exe',
                      help="""(Mandatory) The executable to launch via lldb.""")
    parser.add_option('-o', '--options',
                      type='string', action='store',
                      default = '', dest='exe_options',
                      help="""The args/options passed to the launched program, if specified.""")

    opts, args = parser.parse_args()

    lldb_command = which(opts.lldb_command)

    if not opts.exe:
        parser.print_help()
        sys.exit(1)
    exe = opts.exe

    exe_options = opts.exe_options

    # We have parsed the options.
    print "lldb command:", lldb_command
    print "executable:", exe
    print "executable options:", exe_options

    do_lldb_launch_loop(lldb_command, exe, exe_options)

if __name__ == '__main__':
    main()
