blob: b869c071a99de861787a1705094adb28d0d9e70e [file] [log] [blame]
Greg Claytonc7697222012-08-31 01:11:17 +00001#!/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:])