1*be691f3bSpatrick#!/usr/bin/env python 2061da546Spatrick 3061da546Spatrick#---------------------------------------------------------------------- 4061da546Spatrick# Be sure to add the python path that points to the LLDB shared library. 5061da546Spatrick# On MacOSX csh, tcsh: 6061da546Spatrick# setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python 7061da546Spatrick# On MacOSX sh, bash: 8061da546Spatrick# export PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python 9061da546Spatrick#---------------------------------------------------------------------- 10061da546Spatrick 11061da546Spatrickimport optparse 12061da546Spatrickimport os 13061da546Spatrickimport platform 14061da546Spatrickimport sys 15061da546Spatrickimport subprocess 16061da546Spatrick 17061da546Spatrick#---------------------------------------------------------------------- 18061da546Spatrick# Code that auto imports LLDB 19061da546Spatrick#---------------------------------------------------------------------- 20061da546Spatricktry: 21061da546Spatrick # Just try for LLDB in case PYTHONPATH is already correctly setup 22061da546Spatrick import lldb 23061da546Spatrickexcept ImportError: 24061da546Spatrick lldb_python_dirs = list() 25061da546Spatrick # lldb is not in the PYTHONPATH, try some defaults for the current platform 26061da546Spatrick platform_system = platform.system() 27061da546Spatrick if platform_system == 'Darwin': 28061da546Spatrick # On Darwin, try the currently selected Xcode directory 29061da546Spatrick xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True) 30061da546Spatrick if xcode_dir: 31061da546Spatrick lldb_python_dirs.append( 32061da546Spatrick os.path.realpath( 33061da546Spatrick xcode_dir + 34061da546Spatrick '/../SharedFrameworks/LLDB.framework/Resources/Python')) 35061da546Spatrick lldb_python_dirs.append( 36061da546Spatrick xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 37061da546Spatrick lldb_python_dirs.append( 38061da546Spatrick '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 39061da546Spatrick success = False 40061da546Spatrick for lldb_python_dir in lldb_python_dirs: 41061da546Spatrick if os.path.exists(lldb_python_dir): 42061da546Spatrick if not (sys.path.__contains__(lldb_python_dir)): 43061da546Spatrick sys.path.append(lldb_python_dir) 44061da546Spatrick try: 45061da546Spatrick import lldb 46061da546Spatrick except ImportError: 47061da546Spatrick pass 48061da546Spatrick else: 49061da546Spatrick print('imported lldb from: "%s"' % (lldb_python_dir)) 50061da546Spatrick success = True 51061da546Spatrick break 52061da546Spatrick if not success: 53061da546Spatrick print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly") 54061da546Spatrick sys.exit(1) 55061da546Spatrick 56061da546Spatrick 57061da546Spatrickdef print_threads(process, options): 58061da546Spatrick if options.show_threads: 59061da546Spatrick for thread in process: 60061da546Spatrick print('%s %s' % (thread, thread.GetFrameAtIndex(0))) 61061da546Spatrick 62061da546Spatrick 63061da546Spatrickdef run_commands(command_interpreter, commands): 64061da546Spatrick return_obj = lldb.SBCommandReturnObject() 65061da546Spatrick for command in commands: 66061da546Spatrick command_interpreter.HandleCommand(command, return_obj) 67061da546Spatrick if return_obj.Succeeded(): 68061da546Spatrick print(return_obj.GetOutput()) 69061da546Spatrick else: 70061da546Spatrick print(return_obj) 71061da546Spatrick if options.stop_on_error: 72061da546Spatrick break 73061da546Spatrick 74061da546Spatrick 75061da546Spatrickdef main(argv): 76061da546Spatrick description = '''Debugs a program using the LLDB python API and uses asynchronous broadcast events to watch for process state changes.''' 77061da546Spatrick epilog = '''Examples: 78061da546Spatrick 79061da546Spatrick#---------------------------------------------------------------------- 80061da546Spatrick# Run "/bin/ls" with the arguments "-lAF /tmp/", and set a breakpoint 81061da546Spatrick# at "malloc" and backtrace and read all registers each time we stop 82061da546Spatrick#---------------------------------------------------------------------- 83061da546Spatrick% ./process_events.py --breakpoint malloc --stop-command bt --stop-command 'register read' -- /bin/ls -lAF /tmp/ 84061da546Spatrick 85061da546Spatrick''' 86061da546Spatrick optparse.OptionParser.format_epilog = lambda self, formatter: self.epilog 87061da546Spatrick parser = optparse.OptionParser( 88061da546Spatrick description=description, 89061da546Spatrick prog='process_events', 90061da546Spatrick usage='usage: process_events [options] program [arg1 arg2]', 91061da546Spatrick epilog=epilog) 92061da546Spatrick parser.add_option( 93061da546Spatrick '-v', 94061da546Spatrick '--verbose', 95061da546Spatrick action='store_true', 96061da546Spatrick dest='verbose', 97061da546Spatrick help="Enable verbose logging.", 98061da546Spatrick default=False) 99061da546Spatrick parser.add_option( 100061da546Spatrick '-b', 101061da546Spatrick '--breakpoint', 102061da546Spatrick action='append', 103061da546Spatrick type='string', 104061da546Spatrick metavar='BPEXPR', 105061da546Spatrick dest='breakpoints', 106061da546Spatrick 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.') 107061da546Spatrick parser.add_option( 108061da546Spatrick '-a', 109061da546Spatrick '--arch', 110061da546Spatrick type='string', 111061da546Spatrick dest='arch', 112061da546Spatrick help='The architecture to use when creating the debug target.', 113061da546Spatrick default=None) 114061da546Spatrick parser.add_option( 115061da546Spatrick '--platform', 116061da546Spatrick type='string', 117061da546Spatrick metavar='platform', 118061da546Spatrick dest='platform', 119061da546Spatrick 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".', 120061da546Spatrick default=None) 121061da546Spatrick parser.add_option( 122061da546Spatrick '-l', 123061da546Spatrick '--launch-command', 124061da546Spatrick action='append', 125061da546Spatrick type='string', 126061da546Spatrick metavar='CMD', 127061da546Spatrick dest='launch_commands', 128061da546Spatrick help='LLDB command interpreter commands to run once after the process has launched. This option can be specified more than once.', 129061da546Spatrick default=[]) 130061da546Spatrick parser.add_option( 131061da546Spatrick '-s', 132061da546Spatrick '--stop-command', 133061da546Spatrick action='append', 134061da546Spatrick type='string', 135061da546Spatrick metavar='CMD', 136061da546Spatrick dest='stop_commands', 137061da546Spatrick help='LLDB command interpreter commands to run each time the process stops. This option can be specified more than once.', 138061da546Spatrick default=[]) 139061da546Spatrick parser.add_option( 140061da546Spatrick '-c', 141061da546Spatrick '--crash-command', 142061da546Spatrick action='append', 143061da546Spatrick type='string', 144061da546Spatrick metavar='CMD', 145061da546Spatrick dest='crash_commands', 146061da546Spatrick help='LLDB command interpreter commands to run in case the process crashes. This option can be specified more than once.', 147061da546Spatrick default=[]) 148061da546Spatrick parser.add_option( 149061da546Spatrick '-x', 150061da546Spatrick '--exit-command', 151061da546Spatrick action='append', 152061da546Spatrick type='string', 153061da546Spatrick metavar='CMD', 154061da546Spatrick dest='exit_commands', 155061da546Spatrick help='LLDB command interpreter commands to run once after the process has exited. This option can be specified more than once.', 156061da546Spatrick default=[]) 157061da546Spatrick parser.add_option( 158061da546Spatrick '-T', 159061da546Spatrick '--no-threads', 160061da546Spatrick action='store_false', 161061da546Spatrick dest='show_threads', 162061da546Spatrick help="Don't show threads when process stops.", 163061da546Spatrick default=True) 164061da546Spatrick parser.add_option( 165061da546Spatrick '--ignore-errors', 166061da546Spatrick action='store_false', 167061da546Spatrick dest='stop_on_error', 168061da546Spatrick 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.", 169061da546Spatrick default=True) 170061da546Spatrick parser.add_option( 171061da546Spatrick '-n', 172061da546Spatrick '--run-count', 173061da546Spatrick type='int', 174061da546Spatrick dest='run_count', 175061da546Spatrick metavar='N', 176061da546Spatrick help='How many times to run the process in case the process exits.', 177061da546Spatrick default=1) 178061da546Spatrick parser.add_option( 179061da546Spatrick '-t', 180061da546Spatrick '--event-timeout', 181061da546Spatrick type='int', 182061da546Spatrick dest='event_timeout', 183061da546Spatrick metavar='SEC', 184061da546Spatrick help='Specify the timeout in seconds to wait for process state change events.', 185061da546Spatrick default=lldb.UINT32_MAX) 186061da546Spatrick parser.add_option( 187061da546Spatrick '-e', 188061da546Spatrick '--environment', 189061da546Spatrick action='append', 190061da546Spatrick type='string', 191061da546Spatrick metavar='ENV', 192061da546Spatrick dest='env_vars', 193061da546Spatrick help='Environment variables to set in the inferior process when launching a process.') 194061da546Spatrick parser.add_option( 195061da546Spatrick '-d', 196061da546Spatrick '--working-dir', 197061da546Spatrick type='string', 198061da546Spatrick metavar='DIR', 199061da546Spatrick dest='working_dir', 200*be691f3bSpatrick help='The current working directory when launching a process.', 201061da546Spatrick default=None) 202061da546Spatrick parser.add_option( 203061da546Spatrick '-p', 204061da546Spatrick '--attach-pid', 205061da546Spatrick type='int', 206061da546Spatrick dest='attach_pid', 207061da546Spatrick metavar='PID', 208061da546Spatrick help='Specify a process to attach to by process ID.', 209061da546Spatrick default=-1) 210061da546Spatrick parser.add_option( 211061da546Spatrick '-P', 212061da546Spatrick '--attach-name', 213061da546Spatrick type='string', 214061da546Spatrick dest='attach_name', 215061da546Spatrick metavar='PROCESSNAME', 216061da546Spatrick help='Specify a process to attach to by name.', 217061da546Spatrick default=None) 218061da546Spatrick parser.add_option( 219061da546Spatrick '-w', 220061da546Spatrick '--attach-wait', 221061da546Spatrick action='store_true', 222061da546Spatrick dest='attach_wait', 223061da546Spatrick help='Wait for the next process to launch when attaching to a process by name.', 224061da546Spatrick default=False) 225061da546Spatrick try: 226061da546Spatrick (options, args) = parser.parse_args(argv) 227061da546Spatrick except: 228061da546Spatrick return 229061da546Spatrick 230061da546Spatrick attach_info = None 231061da546Spatrick launch_info = None 232061da546Spatrick exe = None 233061da546Spatrick if args: 234061da546Spatrick exe = args.pop(0) 235061da546Spatrick launch_info = lldb.SBLaunchInfo(args) 236061da546Spatrick if options.env_vars: 237061da546Spatrick launch_info.SetEnvironmentEntries(options.env_vars, True) 238061da546Spatrick if options.working_dir: 239061da546Spatrick launch_info.SetWorkingDirectory(options.working_dir) 240061da546Spatrick elif options.attach_pid != -1: 241061da546Spatrick if options.run_count == 1: 242061da546Spatrick attach_info = lldb.SBAttachInfo(options.attach_pid) 243061da546Spatrick else: 244061da546Spatrick print("error: --run-count can't be used with the --attach-pid option") 245061da546Spatrick sys.exit(1) 246061da546Spatrick elif not options.attach_name is None: 247061da546Spatrick if options.run_count == 1: 248061da546Spatrick attach_info = lldb.SBAttachInfo( 249061da546Spatrick options.attach_name, options.attach_wait) 250061da546Spatrick else: 251061da546Spatrick print("error: --run-count can't be used with the --attach-name option") 252061da546Spatrick sys.exit(1) 253061da546Spatrick else: 254061da546Spatrick print('error: a program path for a program to debug and its arguments are required') 255061da546Spatrick sys.exit(1) 256061da546Spatrick 257061da546Spatrick # Create a new debugger instance 258061da546Spatrick debugger = lldb.SBDebugger.Create() 259061da546Spatrick debugger.SetAsync(True) 260061da546Spatrick command_interpreter = debugger.GetCommandInterpreter() 261061da546Spatrick # Create a target from a file and arch 262061da546Spatrick 263061da546Spatrick if exe: 264061da546Spatrick print("Creating a target for '%s'" % exe) 265061da546Spatrick error = lldb.SBError() 266061da546Spatrick target = debugger.CreateTarget( 267061da546Spatrick exe, options.arch, options.platform, True, error) 268061da546Spatrick 269061da546Spatrick if target: 270061da546Spatrick 271061da546Spatrick # Set any breakpoints that were specified in the args if we are launching. We use the 272061da546Spatrick # command line command to take advantage of the shorthand breakpoint 273061da546Spatrick # creation 274061da546Spatrick if launch_info and options.breakpoints: 275061da546Spatrick for bp in options.breakpoints: 276061da546Spatrick debugger.HandleCommand("_regexp-break %s" % (bp)) 277061da546Spatrick run_commands(command_interpreter, ['breakpoint list']) 278061da546Spatrick 279061da546Spatrick for run_idx in range(options.run_count): 280061da546Spatrick # Launch the process. Since we specified synchronous mode, we won't return 281061da546Spatrick # from this function until we hit the breakpoint at main 282061da546Spatrick error = lldb.SBError() 283061da546Spatrick 284061da546Spatrick if launch_info: 285061da546Spatrick if options.run_count == 1: 286061da546Spatrick print('Launching "%s"...' % (exe)) 287061da546Spatrick else: 288061da546Spatrick print('Launching "%s"... (launch %u of %u)' % (exe, run_idx + 1, options.run_count)) 289061da546Spatrick 290061da546Spatrick process = target.Launch(launch_info, error) 291061da546Spatrick else: 292061da546Spatrick if options.attach_pid != -1: 293061da546Spatrick print('Attaching to process %i...' % (options.attach_pid)) 294061da546Spatrick else: 295061da546Spatrick if options.attach_wait: 296061da546Spatrick print('Waiting for next to process named "%s" to launch...' % (options.attach_name)) 297061da546Spatrick else: 298061da546Spatrick print('Attaching to existing process named "%s"...' % (options.attach_name)) 299061da546Spatrick process = target.Attach(attach_info, error) 300061da546Spatrick 301061da546Spatrick # Make sure the launch went ok 302061da546Spatrick if process and process.GetProcessID() != lldb.LLDB_INVALID_PROCESS_ID: 303061da546Spatrick 304061da546Spatrick pid = process.GetProcessID() 305061da546Spatrick print('Process is %i' % (pid)) 306061da546Spatrick if attach_info: 307061da546Spatrick # continue process if we attached as we won't get an 308061da546Spatrick # initial event 309061da546Spatrick process.Continue() 310061da546Spatrick 311061da546Spatrick listener = debugger.GetListener() 312061da546Spatrick # sign up for process state change events 313061da546Spatrick stop_idx = 0 314061da546Spatrick done = False 315061da546Spatrick while not done: 316061da546Spatrick event = lldb.SBEvent() 317061da546Spatrick if listener.WaitForEvent(options.event_timeout, event): 318061da546Spatrick if lldb.SBProcess.EventIsProcessEvent(event): 319061da546Spatrick state = lldb.SBProcess.GetStateFromEvent(event) 320061da546Spatrick if state == lldb.eStateInvalid: 321061da546Spatrick # Not a state event 322061da546Spatrick print('process event = %s' % (event)) 323061da546Spatrick else: 324061da546Spatrick print("process state changed event: %s" % (lldb.SBDebugger.StateAsCString(state))) 325061da546Spatrick if state == lldb.eStateStopped: 326061da546Spatrick if stop_idx == 0: 327061da546Spatrick if launch_info: 328061da546Spatrick print("process %u launched" % (pid)) 329061da546Spatrick run_commands( 330061da546Spatrick command_interpreter, ['breakpoint list']) 331061da546Spatrick else: 332061da546Spatrick print("attached to process %u" % (pid)) 333061da546Spatrick for m in target.modules: 334061da546Spatrick print(m) 335061da546Spatrick if options.breakpoints: 336061da546Spatrick for bp in options.breakpoints: 337061da546Spatrick debugger.HandleCommand( 338061da546Spatrick "_regexp-break %s" % (bp)) 339061da546Spatrick run_commands( 340061da546Spatrick command_interpreter, ['breakpoint list']) 341061da546Spatrick run_commands( 342061da546Spatrick command_interpreter, options.launch_commands) 343061da546Spatrick else: 344061da546Spatrick if options.verbose: 345061da546Spatrick print("process %u stopped" % (pid)) 346061da546Spatrick run_commands( 347061da546Spatrick command_interpreter, options.stop_commands) 348061da546Spatrick stop_idx += 1 349061da546Spatrick print_threads(process, options) 350061da546Spatrick print("continuing process %u" % (pid)) 351061da546Spatrick process.Continue() 352061da546Spatrick elif state == lldb.eStateExited: 353061da546Spatrick exit_desc = process.GetExitDescription() 354061da546Spatrick if exit_desc: 355061da546Spatrick print("process %u exited with status %u: %s" % (pid, process.GetExitStatus(), exit_desc)) 356061da546Spatrick else: 357061da546Spatrick print("process %u exited with status %u" % (pid, process.GetExitStatus())) 358061da546Spatrick run_commands( 359061da546Spatrick command_interpreter, options.exit_commands) 360061da546Spatrick done = True 361061da546Spatrick elif state == lldb.eStateCrashed: 362061da546Spatrick print("process %u crashed" % (pid)) 363061da546Spatrick print_threads(process, options) 364061da546Spatrick run_commands( 365061da546Spatrick command_interpreter, options.crash_commands) 366061da546Spatrick done = True 367061da546Spatrick elif state == lldb.eStateDetached: 368061da546Spatrick print("process %u detached" % (pid)) 369061da546Spatrick done = True 370061da546Spatrick elif state == lldb.eStateRunning: 371061da546Spatrick # process is running, don't say anything, 372061da546Spatrick # we will always get one of these after 373061da546Spatrick # resuming 374061da546Spatrick if options.verbose: 375061da546Spatrick print("process %u resumed" % (pid)) 376061da546Spatrick elif state == lldb.eStateUnloaded: 377061da546Spatrick print("process %u unloaded, this shouldn't happen" % (pid)) 378061da546Spatrick done = True 379061da546Spatrick elif state == lldb.eStateConnected: 380061da546Spatrick print("process connected") 381061da546Spatrick elif state == lldb.eStateAttaching: 382061da546Spatrick print("process attaching") 383061da546Spatrick elif state == lldb.eStateLaunching: 384061da546Spatrick print("process launching") 385061da546Spatrick else: 386061da546Spatrick print('event = %s' % (event)) 387061da546Spatrick else: 388061da546Spatrick # timeout waiting for an event 389061da546Spatrick print("no process event for %u seconds, killing the process..." % (options.event_timeout)) 390061da546Spatrick done = True 391061da546Spatrick # Now that we are done dump the stdout and stderr 392061da546Spatrick process_stdout = process.GetSTDOUT(1024) 393061da546Spatrick if process_stdout: 394061da546Spatrick print("Process STDOUT:\n%s" % (process_stdout)) 395061da546Spatrick while process_stdout: 396061da546Spatrick process_stdout = process.GetSTDOUT(1024) 397061da546Spatrick print(process_stdout) 398061da546Spatrick process_stderr = process.GetSTDERR(1024) 399061da546Spatrick if process_stderr: 400061da546Spatrick print("Process STDERR:\n%s" % (process_stderr)) 401061da546Spatrick while process_stderr: 402061da546Spatrick process_stderr = process.GetSTDERR(1024) 403061da546Spatrick print(process_stderr) 404061da546Spatrick process.Kill() # kill the process 405061da546Spatrick else: 406061da546Spatrick if error: 407061da546Spatrick print(error) 408061da546Spatrick else: 409061da546Spatrick if launch_info: 410061da546Spatrick print('error: launch failed') 411061da546Spatrick else: 412061da546Spatrick print('error: attach failed') 413061da546Spatrick 414061da546Spatrick lldb.SBDebugger.Terminate() 415061da546Spatrick 416061da546Spatrickif __name__ == '__main__': 417061da546Spatrick main(sys.argv[1:]) 418