1#!/usr/bin/python 2 3#---------------------------------------------------------------------- 4# Be sure to add the python path that points to the LLDB shared library. 5# On MacOSX csh, tcsh: 6# setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python 7# On MacOSX sh, bash: 8# export PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python 9#---------------------------------------------------------------------- 10 11import lldb 12import optparse 13import os 14import sys 15 16def print_threads(process, options): 17 if options.show_threads: 18 for thread in process: 19 print '%s %s' % (thread, thread.GetFrameAtIndex(0)) 20 21def run_commands(command_interpreter, commands): 22 return_obj = lldb.SBCommandReturnObject() 23 for command in commands: 24 command_interpreter.HandleCommand( command, return_obj ) 25 if return_obj.Succeeded(): 26 print return_obj.GetOutput() 27 else: 28 print return_obj 29 if options.stop_on_error: 30 break 31 32def main(argv): 33 description='''Debugs a program using the LLDB python API and uses asynchronous broadcast events to watch for process state changes.''' 34 parser = optparse.OptionParser(description=description, prog='process_events',usage='usage: process_events [options] program [arg1 arg2]') 35 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help="Enable verbose logging.", default=False) 36 parser.add_option('-b', '--breakpoint', action='append', type='string', dest='breakpoints', help='Breakpoint commands to create after the target has been created, the values will be sent to the "_regexp-break" command.') 37 parser.add_option('-a', '--arch', type='string', dest='arch', help='The architecture to use when creating the debug target.', default=lldb.LLDB_ARCH_DEFAULT) 38 parser.add_option('-s', '--stop-command', action='append', type='string', dest='stop_commands', help='Commands to run each time the process stops.', default=[]) 39 parser.add_option('-S', '--crash-command', action='append', type='string', dest='crash_commands', help='Commands to run in case the process crashes.', default=[]) 40 parser.add_option('-T', '--no-threads', action='store_false', dest='show_threads', help="Don't show threads when process stops.", default=True) 41 parser.add_option('-e', '--no_stop-on-error', action='store_false', dest='stop_on_error', help="Stop executing stop or crash commands if the command returns an error.", default=True) 42 parser.add_option('-c', '--run-count', type='int', dest='run_count', help='How many times to run the process in case the process exits.', default=1) 43 parser.add_option('-t', '--event-timeout', type='int', dest='event_timeout', help='Specify the timeout in seconds to wait for process state change events.', default=5) 44 try: 45 (options, args) = parser.parse_args(argv) 46 except: 47 return 48 if not args: 49 print 'error: a program path for a program to debug and its arguments are required' 50 sys.exit(1) 51 52 exe = args.pop(0) 53 54 # Create a new debugger instance 55 debugger = lldb.SBDebugger.Create() 56 command_interpreter = debugger.GetCommandInterpreter() 57 return_obj = lldb.SBCommandReturnObject() 58 # Create a target from a file and arch 59 print "Creating a target for '%s'" % exe 60 61 target = debugger.CreateTargetWithFileAndArch (exe, options.arch) 62 63 if target: 64 65 # Set any breakpoints that were specified in the args 66 for bp in options.breakpoints: 67 command_interpreter.HandleCommand( "_regexp-break %s" % (bp), return_obj ) 68 print return_obj 69 70 for run_idx in range(options.run_count): 71 # Launch the process. Since we specified synchronous mode, we won't return 72 # from this function until we hit the breakpoint at main 73 if options.run_count == 1: 74 print 'Launching "%s"...' % (exe) 75 else: 76 print 'Launching "%s"... (launch %u of %u)' % (exe, run_idx + 1, options.run_count) 77 78 process = target.LaunchSimple (args, None, os.getcwd()) 79 80 # Make sure the launch went ok 81 if process: 82 pid = process.GetProcessID() 83 listener = lldb.SBListener("event_listener") 84 # sign up for process state change events 85 process.GetBroadcaster().AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged) 86 stop_idx = 0 87 done = False 88 while not done: 89 event = lldb.SBEvent() 90 if listener.WaitForEvent (options.event_timeout, event): 91 state = lldb.SBProcess.GetStateFromEvent (event) 92 if state == lldb.eStateStopped: 93 if stop_idx == 0: 94 print "process %u launched" % (pid) 95 else: 96 if options.verbose: 97 print "process %u stopped" % (pid) 98 stop_idx += 1 99 print_threads (process, options) 100 run_commands (command_interpreter, options.stop_commands) 101 process.Continue() 102 elif state == lldb.eStateExited: 103 exit_desc = process.GetExitDescription() 104 if exit_desc: 105 print "process %u exited with status %u: %s" % (pid, process.GetExitStatus (), exit_desc) 106 else: 107 print "process %u exited with status %u" % (pid, process.GetExitStatus ()) 108 done = True 109 elif state == lldb.eStateCrashed: 110 print "process %u crashed" % (pid) 111 print_threads (process, options) 112 run_commands (command_interpreter, options.crash_commands) 113 done = True 114 elif state == lldb.eStateDetached: 115 print "process %u detached" % (pid) 116 done = True 117 elif state == lldb.eStateRunning: 118 # process is running, don't say anything, we will always get one of these after resuming 119 if options.verbose: 120 print "process %u resumed" % (pid) 121 elif state == lldb.eStateUnloaded: 122 print "process %u unloaded, this shouldn't happen" % (pid) 123 done = True 124 elif state == lldb.eStateConnected: 125 print "process connected" 126 elif state == lldb.eStateAttaching: 127 print "process attaching" 128 elif state == lldb.eStateLaunching: 129 print "process launching" 130 else: 131 # timeout waiting for an event 132 print "no process event for %u seconds, killing the process..." % (options.event_timeout) 133 done = True 134 process.Kill() # kill the process 135 136 lldb.SBDebugger.Terminate() 137 138if __name__ == '__main__': 139 main(sys.argv[1:])