xref: /openbsd-src/gnu/llvm/lldb/examples/python/process_events.py (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
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