]> git.proxmox.com Git - rustc.git/blame - src/etc/lldb_batchmode.py
New upstream version 1.43.0+dfsg1
[rustc.git] / src / etc / lldb_batchmode.py
CommitLineData
1a4d82fc
JJ
1# This script allows to use LLDB in a way similar to GDB's batch mode. That is, given a text file
2# containing LLDB commands (one command per line), this script will execute the commands one after
3# the other.
4# LLDB also has the -s and -S commandline options which also execute a list of commands from a text
a1dfa0c6 5# file. However, this command are execute `immediately`: the command of a `run` or `continue`
1a4d82fc
JJ
6# command will be executed immediately after the `run` or `continue`, without waiting for the next
7# breakpoint to be hit. This a command sequence like the following will not yield reliable results:
8#
9# break 11
10# run
11# print x
12#
13# Most of the time the `print` command will be executed while the program is still running will thus
14# fail. Using this Python script, the above will work as expected.
15
16from __future__ import print_function
17import lldb
18import os
19import sys
20import threading
1a4d82fc 21import re
1a4d82fc
JJ
22import time
23
0731742a
XL
24try:
25 import thread
26except ModuleNotFoundError:
27 # The `thread` module was renamed to `_thread` in Python 3.
28 import _thread as thread
29
1a4d82fc
JJ
30# Set this to True for additional output
31DEBUG_OUTPUT = False
32
85aaf69f 33
1a4d82fc 34def print_debug(s):
9e0c209e 35 """Print something if DEBUG_OUTPUT is True"""
85aaf69f
SL
36 global DEBUG_OUTPUT
37 if DEBUG_OUTPUT:
38 print("DEBUG: " + str(s))
1a4d82fc
JJ
39
40
41def normalize_whitespace(s):
9e0c209e 42 """Replace newlines, tabs, multiple spaces, etc with exactly one space"""
85aaf69f 43 return re.sub("\s+", " ", s)
1a4d82fc
JJ
44
45
1a4d82fc 46def breakpoint_callback(frame, bp_loc, dict):
85aaf69f 47 """This callback is registered with every breakpoint and makes sure that the
e1599b0c
XL
48 frame containing the breakpoint location is selected """
49
50 # HACK(eddyb) print a newline to avoid continuing an unfinished line.
51 print("")
85aaf69f 52 print("Hit breakpoint " + str(bp_loc))
1a4d82fc 53
85aaf69f
SL
54 # Select the frame and the thread containing it
55 frame.thread.process.SetSelectedThread(frame.thread)
56 frame.thread.SetSelectedFrame(frame.idx)
1a4d82fc 57
85aaf69f
SL
58 # Returning True means that we actually want to stop at this breakpoint
59 return True
1a4d82fc
JJ
60
61
62# This is a list of breakpoints that are not registered with the breakpoint callback. The list is
63# populated by the breakpoint listener and checked/emptied whenever a command has been executed
64new_breakpoints = []
65
66# This set contains all breakpoint ids that have already been registered with a callback, and is
67# used to avoid hooking callbacks into breakpoints more than once
68registered_breakpoints = set()
69
85aaf69f 70
1a4d82fc 71def execute_command(command_interpreter, command):
9e0c209e 72 """Executes a single CLI command"""
85aaf69f
SL
73 global new_breakpoints
74 global registered_breakpoints
75
76 res = lldb.SBCommandReturnObject()
77 print(command)
78 command_interpreter.HandleCommand(command, res)
79
80 if res.Succeeded():
81 if res.HasResult():
abe05a73 82 print(normalize_whitespace(res.GetOutput() or ''), end='\n')
85aaf69f
SL
83
84 # If the command introduced any breakpoints, make sure to register
85 # them with the breakpoint
86 # callback
87 while len(new_breakpoints) > 0:
88 res.Clear()
89 breakpoint_id = new_breakpoints.pop()
90
91 if breakpoint_id in registered_breakpoints:
92 print_debug("breakpoint with id %s is already registered. Ignoring." %
93 str(breakpoint_id))
94 else:
95 print_debug("registering breakpoint callback, id = " + str(breakpoint_id))
96 callback_command = ("breakpoint command add -F breakpoint_callback " +
97 str(breakpoint_id))
98 command_interpreter.HandleCommand(callback_command, res)
99 if res.Succeeded():
100 print_debug("successfully registered breakpoint callback, id = " +
101 str(breakpoint_id))
102 registered_breakpoints.add(breakpoint_id)
103 else:
104 print("Error while trying to register breakpoint callback, id = " +
105 str(breakpoint_id))
106 else:
107 print(res.GetError())
1a4d82fc
JJ
108
109
110def start_breakpoint_listener(target):
85aaf69f
SL
111 """Listens for breakpoints being added and adds new ones to the callback
112 registration list"""
113 listener = lldb.SBListener("breakpoint listener")
114
115 def listen():
116 event = lldb.SBEvent()
117 try:
118 while True:
119 if listener.WaitForEvent(120, event):
120 if lldb.SBBreakpoint.EventIsBreakpointEvent(event) and \
121 lldb.SBBreakpoint.GetBreakpointEventTypeFromEvent(event) == \
122 lldb.eBreakpointEventTypeAdded:
123 global new_breakpoints
124 breakpoint = lldb.SBBreakpoint.GetBreakpointFromEvent(event)
125 print_debug("breakpoint added, id = " + str(breakpoint.id))
126 new_breakpoints.append(breakpoint.id)
127 except:
128 print_debug("breakpoint listener shutting down")
129
130 # Start the listener and let it run as a daemon
131 listener_thread = threading.Thread(target=listen)
132 listener_thread.daemon = True
133 listener_thread.start()
134
135 # Register the listener with the target
136 target.GetBroadcaster().AddListener(listener, lldb.SBTarget.eBroadcastBitBreakpointChanged)
1a4d82fc
JJ
137
138
139def start_watchdog():
85aaf69f
SL
140 """Starts a watchdog thread that will terminate the process after a certain
141 period of time"""
142 watchdog_start_time = time.clock()
143 watchdog_max_time = watchdog_start_time + 30
144
145 def watchdog():
146 while time.clock() < watchdog_max_time:
147 time.sleep(1)
148 print("TIMEOUT: lldb_batchmode.py has been running for too long. Aborting!")
149 thread.interrupt_main()
150
151 # Start the listener and let it run as a daemon
152 watchdog_thread = threading.Thread(target=watchdog)
153 watchdog_thread.daemon = True
154 watchdog_thread.start()
1a4d82fc
JJ
155
156####################################################################################################
157# ~main
158####################################################################################################
159
74b04a01 160
1a4d82fc 161if len(sys.argv) != 3:
85aaf69f
SL
162 print("usage: python lldb_batchmode.py target-path script-path")
163 sys.exit(1)
1a4d82fc
JJ
164
165target_path = sys.argv[1]
166script_path = sys.argv[2]
167
168print("LLDB batch-mode script")
169print("----------------------")
170print("Debugger commands script is '%s'." % script_path)
171print("Target executable is '%s'." % target_path)
172print("Current working directory is '%s'" % os.getcwd())
173
174# Start the timeout watchdog
175start_watchdog()
176
177# Create a new debugger instance
178debugger = lldb.SBDebugger.Create()
179
180# When we step or continue, don't return from the function until the process
181# stops. We do this by setting the async mode to false.
182debugger.SetAsync(False)
183
184# Create a target from a file and arch
185print("Creating a target for '%s'" % target_path)
186target_error = lldb.SBError()
187target = debugger.CreateTarget(target_path, None, None, True, target_error)
188
189if not target:
85aaf69f
SL
190 print("Could not create debugging target '" + target_path + "': " +
191 str(target_error) + ". Aborting.", file=sys.stderr)
192 sys.exit(1)
1a4d82fc
JJ
193
194
195# Register the breakpoint callback for every breakpoint
196start_breakpoint_listener(target)
197
198command_interpreter = debugger.GetCommandInterpreter()
199
200try:
85aaf69f 201 script_file = open(script_path, 'r')
1a4d82fc 202
85aaf69f
SL
203 for line in script_file:
204 command = line.strip()
205 if command == "run" or command == "r" or re.match("^process\s+launch.*", command):
206 # Before starting to run the program, let the thread sleep a bit, so all
207 # breakpoint added events can be processed
208 time.sleep(0.5)
209 if command != '':
210 execute_command(command_interpreter, command)
1a4d82fc
JJ
211
212except IOError as e:
85aaf69f
SL
213 print("Could not read debugging script '%s'." % script_path, file=sys.stderr)
214 print(e, file=sys.stderr)
215 print("Aborting.", file=sys.stderr)
216 sys.exit(1)
1a4d82fc 217finally:
a7813a04 218 debugger.Terminate()
85aaf69f 219 script_file.close()