process_events.py revision 565add09831504997f8e4297ec44a479e321fcec
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 commands 12import optparse 13import os 14import platform 15import sys 16 17#---------------------------------------------------------------------- 18# Code that auto imports LLDB 19#---------------------------------------------------------------------- 20try: 21 # Just try for LLDB in case PYTHONPATH is already correctly setup 22 import lldb 23except ImportError: 24 lldb_python_dirs = list() 25 # lldb is not in the PYTHONPATH, try some defaults for the current platform 26 platform_system = platform.system() 27 if platform_system == 'Darwin': 28 # On Darwin, try the currently selected Xcode directory 29 xcode_dir = commands.getoutput("xcode-select --print-path") 30 if xcode_dir: 31 lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) 32 lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 33 lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 34 success = False 35 for lldb_python_dir in lldb_python_dirs: 36 if os.path.exists(lldb_python_dir): 37 if not (sys.path.__contains__(lldb_python_dir)): 38 sys.path.append(lldb_python_dir) 39 try: 40 import lldb 41 except ImportError: 42 pass 43 else: 44 print 'imported lldb from: "%s"' % (lldb_python_dir) 45 success = True 46 break 47 if not success: 48 print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" 49 sys.exit(1) 50 51 52 53 54 55 56 57 58def print_threads(process, options): 59 if options.show_threads: 60 for thread in process: 61 print '%s %s' % (thread, thread.GetFrameAtIndex(0)) 62 63def run_commands(command_interpreter, commands): 64 return_obj = lldb.SBCommandReturnObject() 65 for command in commands: 66 command_interpreter.HandleCommand( command, return_obj ) 67 if return_obj.Succeeded(): 68 print return_obj.GetOutput() 69 else: 70 print return_obj 71 if options.stop_on_error: 72 break 73 74def main(argv): 75 description='''Debugs a program using the LLDB python API and uses asynchronous broadcast events to watch for process state changes.''' 76 epilog='''Examples: 77 78#---------------------------------------------------------------------- 79# Run "/bin/ls" with the arguments "-lAF /tmp/", and set a breakpoint 80# at "malloc" and backtrace and read all registers each time we stop 81#---------------------------------------------------------------------- 82% ./process_events.py --breakpoint malloc --stop-command bt --stop-command 'register read' -- /bin/ls -lAF /tmp/ 83 84''' 85 optparse.OptionParser.format_epilog = lambda self, formatter: self.epilog 86 parser = optparse.OptionParser(description=description, prog='process_events',usage='usage: process_events [options] program [arg1 arg2]', epilog=epilog) 87 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help="Enable verbose logging.", default=False) 88 parser.add_option('-b', '--breakpoint', action='append', type='string', metavar='BPEXPR', dest='breakpoints', help='Breakpoint commands to create after the target has been created, the values will be sent to the "_regexp-break" command which supports breakpoints by name, file:line, and address.') 89 parser.add_option('-a', '--arch', type='string', dest='arch', help='The architecture to use when creating the debug target.', default=None) 90 parser.add_option('-l', '--launch-command', action='append', type='string', metavar='CMD', dest='launch_commands', help='LLDB command interpreter commands to run once after the process has launched. This option can be specified more than once.', default=[]) 91 parser.add_option('-s', '--stop-command', action='append', type='string', metavar='CMD', dest='stop_commands', help='LLDB command interpreter commands to run each time the process stops. This option can be specified more than once.', default=[]) 92 parser.add_option('-c', '--crash-command', action='append', type='string', metavar='CMD', dest='crash_commands', help='LLDB command interpreter commands to run in case the process crashes. This option can be specified more than once.', default=[]) 93 parser.add_option('-x', '--exit-command', action='append', type='string', metavar='CMD', dest='exit_commands', help='LLDB command interpreter commands to run once after the process has exited. This option can be specified more than once.', default=[]) 94 parser.add_option('-T', '--no-threads', action='store_false', dest='show_threads', help="Don't show threads when process stops.", default=True) 95 parser.add_option('-e', '--ignore-errors', action='store_false', dest='stop_on_error', help="Don't stop executing LLDB commands if the command returns an error. This applies to all of the LLDB command interpreter commands that get run for launch, stop, crash and exit.", default=True) 96 parser.add_option('-n', '--run-count', type='int', dest='run_count', metavar='N', help='How many times to run the process in case the process exits.', default=1) 97 parser.add_option('-t', '--event-timeout', type='int', dest='event_timeout', metavar='SEC', help='Specify the timeout in seconds to wait for process state change events.', default=5) 98 try: 99 (options, args) = parser.parse_args(argv) 100 except: 101 return 102 if not args: 103 print 'error: a program path for a program to debug and its arguments are required' 104 sys.exit(1) 105 106 exe = args.pop(0) 107 108 # Create a new debugger instance 109 debugger = lldb.SBDebugger.Create() 110 command_interpreter = debugger.GetCommandInterpreter() 111 # Create a target from a file and arch 112 print "Creating a target for '%s'" % exe 113 114 target = debugger.CreateTargetWithFileAndArch (exe, options.arch) 115 116 if target: 117 118 # Set any breakpoints that were specified in the args 119 if options.breakpoints: 120 for bp in options.breakpoints: 121 debugger.HandleCommand( "_regexp-break %s" % (bp)) 122 run_commands(command_interpreter, ['breakpoint list']) 123 124 for run_idx in range(options.run_count): 125 # Launch the process. Since we specified synchronous mode, we won't return 126 # from this function until we hit the breakpoint at main 127 if options.run_count == 1: 128 print 'Launching "%s"...' % (exe) 129 else: 130 print 'Launching "%s"... (launch %u of %u)' % (exe, run_idx + 1, options.run_count) 131 132 process = target.LaunchSimple (args, None, os.getcwd()) 133 134 # Make sure the launch went ok 135 if process: 136 pid = process.GetProcessID() 137 listener = lldb.SBListener("event_listener") 138 # sign up for process state change events 139 process.GetBroadcaster().AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged) 140 stop_idx = 0 141 done = False 142 while not done: 143 event = lldb.SBEvent() 144 if listener.WaitForEvent (options.event_timeout, event): 145 state = lldb.SBProcess.GetStateFromEvent (event) 146 if state == lldb.eStateStopped: 147 if stop_idx == 0: 148 print "process %u launched" % (pid) 149 run_commands (command_interpreter, options.launch_commands) 150 else: 151 if options.verbose: 152 print "process %u stopped" % (pid) 153 run_commands (command_interpreter, options.stop_commands) 154 stop_idx += 1 155 print_threads (process, options) 156 process.Continue() 157 elif state == lldb.eStateExited: 158 exit_desc = process.GetExitDescription() 159 if exit_desc: 160 print "process %u exited with status %u: %s" % (pid, process.GetExitStatus (), exit_desc) 161 else: 162 print "process %u exited with status %u" % (pid, process.GetExitStatus ()) 163 run_commands (command_interpreter, options.exit_commands) 164 done = True 165 elif state == lldb.eStateCrashed: 166 print "process %u crashed" % (pid) 167 print_threads (process, options) 168 run_commands (command_interpreter, options.crash_commands) 169 done = True 170 elif state == lldb.eStateDetached: 171 print "process %u detached" % (pid) 172 done = True 173 elif state == lldb.eStateRunning: 174 # process is running, don't say anything, we will always get one of these after resuming 175 if options.verbose: 176 print "process %u resumed" % (pid) 177 elif state == lldb.eStateUnloaded: 178 print "process %u unloaded, this shouldn't happen" % (pid) 179 done = True 180 elif state == lldb.eStateConnected: 181 print "process connected" 182 elif state == lldb.eStateAttaching: 183 print "process attaching" 184 elif state == lldb.eStateLaunching: 185 print "process launching" 186 else: 187 # timeout waiting for an event 188 print "no process event for %u seconds, killing the process..." % (options.event_timeout) 189 done = True 190 process.Kill() # kill the process 191 192 lldb.SBDebugger.Terminate() 193 194if __name__ == '__main__': 195 main(sys.argv[1:])