forked from 10p-freddo/fruitstrap
-
Notifications
You must be signed in to change notification settings - Fork 468
/
lldb.py
198 lines (167 loc) · 7.12 KB
/
lldb.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import time
import os
import sys
import shlex
import lldb
listener = None
startup_error = lldb.SBError()
def connect_command(debugger, command, result, internal_dict):
# These two are passed in by the script which loads us
connect_url = internal_dict['fruitstrap_connect_url']
error = lldb.SBError()
# We create a new listener here and will use it for both target and the process.
# It allows us to prevent data races when both our code and internal lldb code
# try to process STDOUT/STDERR messages
global listener
listener = lldb.SBListener('iosdeploy_listener')
listener.StartListeningForEventClass(debugger,
lldb.SBProcess.GetBroadcasterClassName(),
lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR)
process = debugger.GetSelectedTarget().ConnectRemote(listener, connect_url, None, error)
# Wait for connection to succeed
events = []
state = (process.GetState() or lldb.eStateInvalid)
while state != lldb.eStateConnected:
event = lldb.SBEvent()
if listener.WaitForEvent(1, event):
state = process.GetStateFromEvent(event)
events.append(event)
else:
state = lldb.eStateInvalid
# Add events back to queue, otherwise lldb freezes
for event in events:
listener.AddEvent(event)
def run_command(debugger, command, result, internal_dict):
device_app = internal_dict['fruitstrap_device_app']
args = command.split('--',1)
debugger.GetSelectedTarget().modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_app))
args_arr = []
if len(args) > 1:
args_arr = shlex.split(args[1])
args_arr = args_arr + shlex.split('{args}')
launchInfo = lldb.SBLaunchInfo(args_arr)
global listener
launchInfo.SetListener(listener)
#This env variable makes NSLog, CFLog and os_log messages get mirrored to stderr
#https://stackoverflow.com/a/39581193
launchInfo.SetEnvironmentEntries(['OS_ACTIVITY_DT_MODE=enable'], True)
envs_arr = []
if len(args) > 1:
envs_arr = shlex.split(args[1])
envs_arr = envs_arr + shlex.split('{envs}')
launchInfo.SetEnvironmentEntries(envs_arr, True)
debugger.GetSelectedTarget().Launch(launchInfo, startup_error)
lockedstr = ': Locked'
if lockedstr in str(startup_error):
print('\\nDevice Locked\\n')
os._exit(254)
else:
print(str(startup_error))
def safequit_command(debugger, command, result, internal_dict):
process = debugger.GetSelectedTarget().process
state = process.GetState()
if state == lldb.eStateRunning:
process.Detach()
os._exit(0)
elif state > lldb.eStateRunning:
os._exit(state)
else:
print('\\nApplication has not been launched\\n')
os._exit(1)
def print_stacktrace(thread):
# Somewhere between Xcode-13.2.1 and Xcode-13.3 lldb starts to throw an error during printing of backtrace.
# Manually write the backtrace out so we don't just get 'invalid thread'.
sys.stdout.write(' ' + str(thread) + '\\n')
for frame in thread:
out = lldb.SBStream()
frame.GetDescription(out)
sys.stdout.write(' ' * 4 + out.GetData())
def autoexit_command(debugger, command, result, internal_dict):
global listener
process = debugger.GetSelectedTarget().process
if not startup_error.Success():
print('\\nPROCESS_NOT_STARTED\\n')
os._exit({exitcode_app_crash})
output_path = internal_dict['fruitstrap_output_path']
out = None
if output_path:
out = open(output_path, 'w')
error_path = internal_dict['fruitstrap_error_path']
err = None
if error_path:
err = open(error_path, 'w')
detectDeadlockTimeout = {detect_deadlock_timeout}
printBacktraceTime = time.time() + detectDeadlockTimeout if detectDeadlockTimeout > 0 else None
# This line prevents internal lldb listener from processing STDOUT/STDERR/StateChanged messages.
# Without it, an order of log writes is incorrect sometimes
debugger.GetListener().StopListeningForEvents(process.GetBroadcaster(),
lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR | lldb.SBProcess.eBroadcastBitStateChanged )
event = lldb.SBEvent()
def ProcessSTDOUT():
stdout = process.GetSTDOUT(1024)
while stdout:
if out:
out.write(stdout)
else:
sys.stdout.write(stdout)
stdout = process.GetSTDOUT(1024)
def ProcessSTDERR():
stderr = process.GetSTDERR(1024)
while stderr:
if err:
err.write(stderr)
else:
sys.stdout.write(stderr)
stderr = process.GetSTDERR(1024)
def CloseOut():
sys.stdout.flush()
if (out):
out.close()
if (err):
err.close()
while True:
if listener.WaitForEvent(1, event) and lldb.SBProcess.EventIsProcessEvent(event):
state = lldb.SBProcess.GetStateFromEvent(event)
type = event.GetType()
if type & lldb.SBProcess.eBroadcastBitSTDOUT:
ProcessSTDOUT()
if type & lldb.SBProcess.eBroadcastBitSTDERR:
ProcessSTDERR()
else:
state = process.GetState()
if state != lldb.eStateRunning:
# Let's make sure that we drained our streams before exit
ProcessSTDOUT()
ProcessSTDERR()
if state == lldb.eStateExited:
sys.stdout.write( '\\nPROCESS_EXITED\\n' )
CloseOut()
os._exit(process.GetExitStatus())
elif printBacktraceTime is None and state == lldb.eStateStopped:
selectedThread = process.GetSelectedThread()
if selectedThread.GetStopReason() == lldb.eStopReasonNone:
# During startup there are some stops for lldb to setup properly.
# On iOS-16 we receive them with stop reason none.
continue
sys.stdout.write( '\\nPROCESS_STOPPED\\n' )
print_stacktrace(process.GetSelectedThread())
CloseOut()
os._exit({exitcode_app_crash})
elif state == lldb.eStateCrashed:
sys.stdout.write( '\\nPROCESS_CRASHED\\n' )
print_stacktrace(process.GetSelectedThread())
CloseOut()
os._exit({exitcode_app_crash})
elif state == lldb.eStateDetached:
sys.stdout.write( '\\nPROCESS_DETACHED\\n' )
CloseOut()
os._exit({exitcode_app_crash})
elif printBacktraceTime is not None and time.time() >= printBacktraceTime:
printBacktraceTime = None
sys.stdout.write( '\\nPRINT_BACKTRACE_TIMEOUT\\n' )
debugger.HandleCommand('process interrupt')
for thread in process:
print_stacktrace(thread)
sys.stdout.write('\\n')
debugger.HandleCommand('continue')
printBacktraceTime = time.time() + 5