2012-08-31 09:11:17 +08:00
#!/usr/bin/python
#----------------------------------------------------------------------
# Be sure to add the python path that points to the LLDB shared library.
# On MacOSX csh, tcsh:
# setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python
# On MacOSX sh, bash:
# export PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python
#----------------------------------------------------------------------
2019-03-22 02:27:40 +08:00
from __future__ import print_function
2012-08-31 09:11:17 +08:00
import optparse
import os
2012-08-31 10:55:56 +08:00
import platform
2012-08-31 09:11:17 +08:00
import sys
2019-03-26 22:46:15 +08:00
if sys . version_info . major == 2 :
import commands as subprocess
else :
import subprocess
2012-08-31 10:55:56 +08:00
#----------------------------------------------------------------------
# Code that auto imports LLDB
#----------------------------------------------------------------------
2016-09-07 04:57:50 +08:00
try :
2012-08-31 10:55:56 +08:00
# Just try for LLDB in case PYTHONPATH is already correctly setup
import lldb
except ImportError :
lldb_python_dirs = list ( )
# lldb is not in the PYTHONPATH, try some defaults for the current platform
platform_system = platform . system ( )
if platform_system == ' Darwin ' :
# On Darwin, try the currently selected Xcode directory
2019-03-26 22:46:15 +08:00
xcode_dir = subprocess . getoutput ( " xcode-select --print-path " )
2012-08-31 10:55:56 +08:00
if xcode_dir :
2016-09-07 04:57:50 +08:00
lldb_python_dirs . append (
os . path . realpath (
xcode_dir +
' /../SharedFrameworks/LLDB.framework/Resources/Python ' ) )
lldb_python_dirs . append (
xcode_dir + ' /Library/PrivateFrameworks/LLDB.framework/Resources/Python ' )
lldb_python_dirs . append (
' /System/Library/PrivateFrameworks/LLDB.framework/Resources/Python ' )
2012-08-31 10:55:56 +08:00
success = False
for lldb_python_dir in lldb_python_dirs :
if os . path . exists ( lldb_python_dir ) :
if not ( sys . path . __contains__ ( lldb_python_dir ) ) :
sys . path . append ( lldb_python_dir )
2016-09-07 04:57:50 +08:00
try :
2012-08-31 10:55:56 +08:00
import lldb
except ImportError :
pass
else :
2019-03-22 02:27:40 +08:00
print ( ' imported lldb from: " %s " ' % ( lldb_python_dir ) )
2012-08-31 10:55:56 +08:00
success = True
break
if not success :
2019-03-22 02:27:40 +08:00
print ( " error: couldn ' t locate the ' lldb ' module, please set PYTHONPATH correctly " )
2012-08-31 10:55:56 +08:00
sys . exit ( 1 )
2016-09-07 04:57:50 +08:00
2012-08-31 09:11:17 +08:00
def print_threads ( process , options ) :
if options . show_threads :
for thread in process :
2019-03-22 02:27:40 +08:00
print ( ' %s %s ' % ( thread , thread . GetFrameAtIndex ( 0 ) ) )
2012-08-31 09:11:17 +08:00
2016-09-07 04:57:50 +08:00
2012-08-31 09:11:17 +08:00
def run_commands ( command_interpreter , commands ) :
return_obj = lldb . SBCommandReturnObject ( )
for command in commands :
2016-09-07 04:57:50 +08:00
command_interpreter . HandleCommand ( command , return_obj )
2012-08-31 09:11:17 +08:00
if return_obj . Succeeded ( ) :
2019-03-22 02:27:40 +08:00
print ( return_obj . GetOutput ( ) )
2012-08-31 09:11:17 +08:00
else :
2019-03-22 02:27:40 +08:00
print ( return_obj )
2012-08-31 09:11:17 +08:00
if options . stop_on_error :
break
2016-09-07 04:57:50 +08:00
2012-08-31 09:11:17 +08:00
def main ( argv ) :
2016-09-07 04:57:50 +08:00
description = ''' Debugs a program using the LLDB python API and uses asynchronous broadcast events to watch for process state changes. '''
epilog = ''' Examples:
2012-08-31 10:55:56 +08:00
#----------------------------------------------------------------------
2016-09-07 04:57:50 +08:00
# Run "/bin/ls" with the arguments "-lAF /tmp/", and set a breakpoint
2012-08-31 10:55:56 +08:00
# at "malloc" and backtrace and read all registers each time we stop
#----------------------------------------------------------------------
% . / process_events . py - - breakpoint malloc - - stop - command bt - - stop - command ' register read ' - - / bin / ls - lAF / tmp /
'''
optparse . OptionParser . format_epilog = lambda self , formatter : self . epilog
2016-09-07 04:57:50 +08:00
parser = optparse . OptionParser (
description = description ,
prog = ' process_events ' ,
usage = ' usage: process_events [options] program [arg1 arg2] ' ,
epilog = epilog )
parser . add_option (
' -v ' ,
' --verbose ' ,
action = ' store_true ' ,
dest = ' verbose ' ,
help = " Enable verbose logging. " ,
default = False )
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. ' )
parser . add_option (
' -a ' ,
' --arch ' ,
type = ' string ' ,
dest = ' arch ' ,
help = ' The architecture to use when creating the debug target. ' ,
default = None )
parser . add_option (
' --platform ' ,
type = ' string ' ,
metavar = ' platform ' ,
dest = ' platform ' ,
help = ' Specify the platform to use when creating the debug target. Valid values include " localhost " , " darwin-kernel " , " ios-simulator " , " remote-freebsd " , " remote-macosx " , " remote-ios " , " remote-linux " . ' ,
default = None )
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 = [ ] )
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 = [ ] )
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 = [ ] )
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 = [ ] )
parser . add_option (
' -T ' ,
' --no-threads ' ,
action = ' store_false ' ,
dest = ' show_threads ' ,
help = " Don ' t show threads when process stops. " ,
default = True )
parser . add_option (
' --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 )
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 )
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 = lldb . UINT32_MAX )
parser . add_option (
' -e ' ,
' --environment ' ,
action = ' append ' ,
type = ' string ' ,
metavar = ' ENV ' ,
dest = ' env_vars ' ,
help = ' Environment variables to set in the inferior process when launching a process. ' )
parser . add_option (
' -d ' ,
' --working-dir ' ,
type = ' string ' ,
metavar = ' DIR ' ,
dest = ' working_dir ' ,
help = ' The the current working directory when launching a process. ' ,
default = None )
parser . add_option (
' -p ' ,
' --attach-pid ' ,
type = ' int ' ,
dest = ' attach_pid ' ,
metavar = ' PID ' ,
help = ' Specify a process to attach to by process ID. ' ,
default = - 1 )
parser . add_option (
' -P ' ,
' --attach-name ' ,
type = ' string ' ,
dest = ' attach_name ' ,
metavar = ' PROCESSNAME ' ,
help = ' Specify a process to attach to by name. ' ,
default = None )
parser . add_option (
' -w ' ,
' --attach-wait ' ,
action = ' store_true ' ,
dest = ' attach_wait ' ,
help = ' Wait for the next process to launch when attaching to a process by name. ' ,
default = False )
2012-08-31 09:11:17 +08:00
try :
( options , args ) = parser . parse_args ( argv )
except :
return
2016-09-07 04:57:50 +08:00
2012-09-26 02:27:12 +08:00
attach_info = None
launch_info = None
exe = None
if args :
exe = args . pop ( 0 )
2016-09-07 04:57:50 +08:00
launch_info = lldb . SBLaunchInfo ( args )
2012-09-26 02:27:12 +08:00
if options . env_vars :
launch_info . SetEnvironmentEntries ( options . env_vars , True )
if options . working_dir :
launch_info . SetWorkingDirectory ( options . working_dir )
elif options . attach_pid != - 1 :
if options . run_count == 1 :
2016-09-07 04:57:50 +08:00
attach_info = lldb . SBAttachInfo ( options . attach_pid )
2012-09-26 02:27:12 +08:00
else :
2019-03-22 02:27:40 +08:00
print ( " error: --run-count can ' t be used with the --attach-pid option " )
2012-09-26 02:27:12 +08:00
sys . exit ( 1 )
elif not options . attach_name is None :
if options . run_count == 1 :
2016-09-07 04:57:50 +08:00
attach_info = lldb . SBAttachInfo (
options . attach_name , options . attach_wait )
2012-09-26 02:27:12 +08:00
else :
2019-03-22 02:27:40 +08:00
print ( " error: --run-count can ' t be used with the --attach-name option " )
2012-09-26 02:27:12 +08:00
sys . exit ( 1 )
else :
2019-03-22 02:27:40 +08:00
print ( ' error: a program path for a program to debug and its arguments are required ' )
2012-08-31 09:11:17 +08:00
sys . exit ( 1 )
2012-08-31 10:55:56 +08:00
2012-08-31 09:11:17 +08:00
# Create a new debugger instance
debugger = lldb . SBDebugger . Create ( )
2016-09-07 04:57:50 +08:00
debugger . SetAsync ( True )
2012-08-31 09:11:17 +08:00
command_interpreter = debugger . GetCommandInterpreter ( )
# Create a target from a file and arch
2016-09-07 04:57:50 +08:00
2012-09-26 02:27:12 +08:00
if exe :
2019-03-22 02:27:40 +08:00
print ( " Creating a target for ' %s ' " % exe )
2013-06-27 06:23:45 +08:00
error = lldb . SBError ( )
2016-09-07 04:57:50 +08:00
target = debugger . CreateTarget (
exe , options . arch , options . platform , True , error )
2012-08-31 09:11:17 +08:00
if target :
2016-09-07 04:57:50 +08:00
2013-06-27 06:23:45 +08:00
# Set any breakpoints that were specified in the args if we are launching. We use the
2016-09-07 04:57:50 +08:00
# command line command to take advantage of the shorthand breakpoint
# creation
2012-09-26 02:27:12 +08:00
if launch_info and options . breakpoints :
2012-08-31 10:55:56 +08:00
for bp in options . breakpoints :
2016-09-07 04:57:50 +08:00
debugger . HandleCommand ( " _regexp-break %s " % ( bp ) )
2012-08-31 10:55:56 +08:00
run_commands ( command_interpreter , [ ' breakpoint list ' ] )
2016-09-07 04:57:50 +08:00
2012-08-31 09:11:17 +08:00
for run_idx in range ( options . run_count ) :
# Launch the process. Since we specified synchronous mode, we won't return
# from this function until we hit the breakpoint at main
2012-09-26 02:27:12 +08:00
error = lldb . SBError ( )
2016-09-07 04:57:50 +08:00
2012-09-26 02:27:12 +08:00
if launch_info :
if options . run_count == 1 :
2019-03-22 02:27:40 +08:00
print ( ' Launching " %s " ... ' % ( exe ) )
2012-09-26 02:27:12 +08:00
else :
2019-03-22 02:27:40 +08:00
print ( ' Launching " %s " ... (launch %u of %u ) ' % ( exe , run_idx + 1 , options . run_count ) )
2016-09-07 04:57:50 +08:00
process = target . Launch ( launch_info , error )
2012-09-26 02:27:12 +08:00
else :
if options . attach_pid != - 1 :
2019-03-22 02:27:40 +08:00
print ( ' Attaching to process %i ... ' % ( options . attach_pid ) )
2012-09-26 02:27:12 +08:00
else :
if options . attach_wait :
2019-03-22 02:27:40 +08:00
print ( ' Waiting for next to process named " %s " to launch... ' % ( options . attach_name ) )
2012-09-26 02:27:12 +08:00
else :
2019-03-22 02:27:40 +08:00
print ( ' Attaching to existing process named " %s " ... ' % ( options . attach_name ) )
2016-09-07 04:57:50 +08:00
process = target . Attach ( attach_info , error )
2012-08-31 09:11:17 +08:00
# Make sure the launch went ok
2012-09-26 02:27:12 +08:00
if process and process . GetProcessID ( ) != lldb . LLDB_INVALID_PROCESS_ID :
2016-09-07 04:57:50 +08:00
2012-08-31 09:11:17 +08:00
pid = process . GetProcessID ( )
2019-03-22 02:27:40 +08:00
print ( ' Process is %i ' % ( pid ) )
2015-08-13 04:04:01 +08:00
if attach_info :
2016-09-07 04:57:50 +08:00
# continue process if we attached as we won't get an
# initial event
2015-08-13 04:04:01 +08:00
process . Continue ( )
2013-06-27 06:23:45 +08:00
listener = debugger . GetListener ( )
2012-08-31 09:11:17 +08:00
# sign up for process state change events
stop_idx = 0
done = False
while not done :
event = lldb . SBEvent ( )
2016-09-07 04:57:50 +08:00
if listener . WaitForEvent ( options . event_timeout , event ) :
2013-06-27 06:23:45 +08:00
if lldb . SBProcess . EventIsProcessEvent ( event ) :
2016-09-07 04:57:50 +08:00
state = lldb . SBProcess . GetStateFromEvent ( event )
2013-06-28 02:08:32 +08:00
if state == lldb . eStateInvalid :
# Not a state event
2019-03-22 02:27:40 +08:00
print ( ' process event = %s ' % ( event ) )
2013-06-28 02:08:32 +08:00
else :
2019-03-22 02:27:40 +08:00
print ( " process state changed event: %s " % ( lldb . SBDebugger . StateAsCString ( state ) ) )
2013-06-28 02:08:32 +08:00
if state == lldb . eStateStopped :
if stop_idx == 0 :
if launch_info :
2019-03-22 02:27:40 +08:00
print ( " process %u launched " % ( pid ) )
2016-09-07 04:57:50 +08:00
run_commands (
command_interpreter , [ ' breakpoint list ' ] )
2013-06-28 02:08:32 +08:00
else :
2019-03-22 02:27:40 +08:00
print ( " attached to process %u " % ( pid ) )
2013-06-28 02:08:32 +08:00
for m in target . modules :
2019-03-22 02:27:40 +08:00
print ( m )
2013-06-28 02:08:32 +08:00
if options . breakpoints :
for bp in options . breakpoints :
2016-09-07 04:57:50 +08:00
debugger . HandleCommand (
" _regexp-break %s " % ( bp ) )
run_commands (
command_interpreter , [ ' breakpoint list ' ] )
run_commands (
command_interpreter , options . launch_commands )
2013-06-28 02:08:32 +08:00
else :
if options . verbose :
2019-03-22 02:27:40 +08:00
print ( " process %u stopped " % ( pid ) )
2016-09-07 04:57:50 +08:00
run_commands (
command_interpreter , options . stop_commands )
2013-06-28 02:08:32 +08:00
stop_idx + = 1
2016-09-07 04:57:50 +08:00
print_threads ( process , options )
2019-03-22 02:27:40 +08:00
print ( " continuing process %u " % ( pid ) )
2013-06-28 02:08:32 +08:00
process . Continue ( )
elif state == lldb . eStateExited :
exit_desc = process . GetExitDescription ( )
if exit_desc :
2019-03-22 02:27:40 +08:00
print ( " process %u exited with status %u : %s " % ( pid , process . GetExitStatus ( ) , exit_desc ) )
2013-06-28 02:08:32 +08:00
else :
2019-03-22 02:27:40 +08:00
print ( " process %u exited with status %u " % ( pid , process . GetExitStatus ( ) ) )
2016-09-07 04:57:50 +08:00
run_commands (
command_interpreter , options . exit_commands )
2013-06-28 02:08:32 +08:00
done = True
elif state == lldb . eStateCrashed :
2019-03-22 02:27:40 +08:00
print ( " process %u crashed " % ( pid ) )
2016-09-07 04:57:50 +08:00
print_threads ( process , options )
run_commands (
command_interpreter , options . crash_commands )
2013-06-28 02:08:32 +08:00
done = True
elif state == lldb . eStateDetached :
2019-03-22 02:27:40 +08:00
print ( " process %u detached " % ( pid ) )
2013-06-28 02:08:32 +08:00
done = True
elif state == lldb . eStateRunning :
2016-09-07 04:57:50 +08:00
# process is running, don't say anything,
# we will always get one of these after
# resuming
2013-06-27 06:23:45 +08:00
if options . verbose :
2019-03-22 02:27:40 +08:00
print ( " process %u resumed " % ( pid ) )
2013-06-28 02:08:32 +08:00
elif state == lldb . eStateUnloaded :
2019-03-22 02:27:40 +08:00
print ( " process %u unloaded, this shouldn ' t happen " % ( pid ) )
2013-06-28 02:08:32 +08:00
done = True
elif state == lldb . eStateConnected :
2019-03-22 02:27:40 +08:00
print ( " process connected " )
2013-06-28 02:08:32 +08:00
elif state == lldb . eStateAttaching :
2019-03-22 02:27:40 +08:00
print ( " process attaching " )
2013-06-28 02:08:32 +08:00
elif state == lldb . eStateLaunching :
2019-03-22 02:27:40 +08:00
print ( " process launching " )
2013-06-27 06:23:45 +08:00
else :
2019-03-22 02:27:40 +08:00
print ( ' event = %s ' % ( event ) )
2013-06-28 02:08:32 +08:00
else :
# timeout waiting for an event
2019-03-22 02:27:40 +08:00
print ( " no process event for %u seconds, killing the process... " % ( options . event_timeout ) )
2013-06-28 02:08:32 +08:00
done = True
# Now that we are done dump the stdout and stderr
process_stdout = process . GetSTDOUT ( 1024 )
if process_stdout :
2019-03-22 02:27:40 +08:00
print ( " Process STDOUT: \n %s " % ( process_stdout ) )
2013-06-28 02:08:32 +08:00
while process_stdout :
process_stdout = process . GetSTDOUT ( 1024 )
2019-03-22 02:27:40 +08:00
print ( process_stdout )
2013-06-28 02:08:32 +08:00
process_stderr = process . GetSTDERR ( 1024 )
if process_stderr :
2019-03-22 02:27:40 +08:00
print ( " Process STDERR: \n %s " % ( process_stderr ) )
2013-06-28 02:08:32 +08:00
while process_stderr :
process_stderr = process . GetSTDERR ( 1024 )
2019-03-22 02:27:40 +08:00
print ( process_stderr )
2016-09-07 04:57:50 +08:00
process . Kill ( ) # kill the process
2012-09-26 02:27:12 +08:00
else :
if error :
2019-03-22 02:27:40 +08:00
print ( error )
2012-09-26 02:27:12 +08:00
else :
if launch_info :
2019-03-22 02:27:40 +08:00
print ( ' error: launch failed ' )
2012-09-26 02:27:12 +08:00
else :
2019-03-22 02:27:40 +08:00
print ( ' error: attach failed ' )
2016-09-07 04:57:50 +08:00
2012-08-31 09:11:17 +08:00
lldb . SBDebugger . Terminate ( )
if __name__ == ' __main__ ' :
2016-09-07 04:57:50 +08:00
main ( sys . argv [ 1 : ] )