annotate lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py @ 227:21e6aa2e49ef

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 19 Jul 2021 06:57:16 +0900
parents 5f17cb93ff66
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
1 import errno
150
anatofuz
parents:
diff changeset
2 import os
anatofuz
parents:
diff changeset
3 import os.path
anatofuz
parents:
diff changeset
4 import threading
anatofuz
parents:
diff changeset
5 import socket
anatofuz
parents:
diff changeset
6 import lldb
anatofuz
parents:
diff changeset
7 import binascii
anatofuz
parents:
diff changeset
8 import traceback
anatofuz
parents:
diff changeset
9 from lldbsuite.support import seven
anatofuz
parents:
diff changeset
10 from lldbsuite.test.lldbtest import *
anatofuz
parents:
diff changeset
11 from lldbsuite.test import lldbtest_config
anatofuz
parents:
diff changeset
12
anatofuz
parents:
diff changeset
13
anatofuz
parents:
diff changeset
14 def checksum(message):
anatofuz
parents:
diff changeset
15 """
anatofuz
parents:
diff changeset
16 Calculate the GDB server protocol checksum of the message.
anatofuz
parents:
diff changeset
17
anatofuz
parents:
diff changeset
18 The GDB server protocol uses a simple modulo 256 sum.
anatofuz
parents:
diff changeset
19 """
anatofuz
parents:
diff changeset
20 check = 0
anatofuz
parents:
diff changeset
21 for c in message:
anatofuz
parents:
diff changeset
22 check += ord(c)
anatofuz
parents:
diff changeset
23 return check % 256
anatofuz
parents:
diff changeset
24
anatofuz
parents:
diff changeset
25
anatofuz
parents:
diff changeset
26 def frame_packet(message):
anatofuz
parents:
diff changeset
27 """
anatofuz
parents:
diff changeset
28 Create a framed packet that's ready to send over the GDB connection
anatofuz
parents:
diff changeset
29 channel.
anatofuz
parents:
diff changeset
30
anatofuz
parents:
diff changeset
31 Framing includes surrounding the message between $ and #, and appending
anatofuz
parents:
diff changeset
32 a two character hex checksum.
anatofuz
parents:
diff changeset
33 """
anatofuz
parents:
diff changeset
34 return "$%s#%02x" % (message, checksum(message))
anatofuz
parents:
diff changeset
35
anatofuz
parents:
diff changeset
36
anatofuz
parents:
diff changeset
37 def escape_binary(message):
anatofuz
parents:
diff changeset
38 """
anatofuz
parents:
diff changeset
39 Escape the binary message using the process described in the GDB server
anatofuz
parents:
diff changeset
40 protocol documentation.
anatofuz
parents:
diff changeset
41
anatofuz
parents:
diff changeset
42 Most bytes are sent through as-is, but $, #, and { are escaped by writing
anatofuz
parents:
diff changeset
43 a { followed by the original byte mod 0x20.
anatofuz
parents:
diff changeset
44 """
anatofuz
parents:
diff changeset
45 out = ""
anatofuz
parents:
diff changeset
46 for c in message:
anatofuz
parents:
diff changeset
47 d = ord(c)
anatofuz
parents:
diff changeset
48 if d in (0x23, 0x24, 0x7d):
anatofuz
parents:
diff changeset
49 out += chr(0x7d)
anatofuz
parents:
diff changeset
50 out += chr(d ^ 0x20)
anatofuz
parents:
diff changeset
51 else:
anatofuz
parents:
diff changeset
52 out += c
anatofuz
parents:
diff changeset
53 return out
anatofuz
parents:
diff changeset
54
anatofuz
parents:
diff changeset
55
anatofuz
parents:
diff changeset
56 def hex_encode_bytes(message):
anatofuz
parents:
diff changeset
57 """
anatofuz
parents:
diff changeset
58 Encode the binary message by converting each byte into a two-character
anatofuz
parents:
diff changeset
59 hex string.
anatofuz
parents:
diff changeset
60 """
anatofuz
parents:
diff changeset
61 out = ""
anatofuz
parents:
diff changeset
62 for c in message:
anatofuz
parents:
diff changeset
63 out += "%02x" % ord(c)
anatofuz
parents:
diff changeset
64 return out
anatofuz
parents:
diff changeset
65
anatofuz
parents:
diff changeset
66
anatofuz
parents:
diff changeset
67 def hex_decode_bytes(hex_bytes):
anatofuz
parents:
diff changeset
68 """
anatofuz
parents:
diff changeset
69 Decode the hex string into a binary message by converting each two-character
anatofuz
parents:
diff changeset
70 hex string into a single output byte.
anatofuz
parents:
diff changeset
71 """
anatofuz
parents:
diff changeset
72 out = ""
anatofuz
parents:
diff changeset
73 hex_len = len(hex_bytes)
anatofuz
parents:
diff changeset
74 while i < hex_len - 1:
anatofuz
parents:
diff changeset
75 out += chr(int(hex_bytes[i:i + 2]), 16)
anatofuz
parents:
diff changeset
76 i += 2
anatofuz
parents:
diff changeset
77 return out
anatofuz
parents:
diff changeset
78
anatofuz
parents:
diff changeset
79
anatofuz
parents:
diff changeset
80 class MockGDBServerResponder:
anatofuz
parents:
diff changeset
81 """
anatofuz
parents:
diff changeset
82 A base class for handling client packets and issuing server responses for
anatofuz
parents:
diff changeset
83 GDB tests.
anatofuz
parents:
diff changeset
84
anatofuz
parents:
diff changeset
85 This handles many typical situations, while still allowing subclasses to
anatofuz
parents:
diff changeset
86 completely customize their responses.
anatofuz
parents:
diff changeset
87
anatofuz
parents:
diff changeset
88 Most subclasses will be interested in overriding the other() method, which
anatofuz
parents:
diff changeset
89 handles any packet not recognized in the common packet handling code.
anatofuz
parents:
diff changeset
90 """
anatofuz
parents:
diff changeset
91
anatofuz
parents:
diff changeset
92 registerCount = 40
anatofuz
parents:
diff changeset
93 packetLog = None
anatofuz
parents:
diff changeset
94
anatofuz
parents:
diff changeset
95 def __init__(self):
anatofuz
parents:
diff changeset
96 self.packetLog = []
anatofuz
parents:
diff changeset
97
anatofuz
parents:
diff changeset
98 def respond(self, packet):
anatofuz
parents:
diff changeset
99 """
anatofuz
parents:
diff changeset
100 Return the unframed packet data that the server should issue in response
anatofuz
parents:
diff changeset
101 to the given packet received from the client.
anatofuz
parents:
diff changeset
102 """
anatofuz
parents:
diff changeset
103 self.packetLog.append(packet)
anatofuz
parents:
diff changeset
104 if packet is MockGDBServer.PACKET_INTERRUPT:
anatofuz
parents:
diff changeset
105 return self.interrupt()
anatofuz
parents:
diff changeset
106 if packet == "c":
anatofuz
parents:
diff changeset
107 return self.cont()
anatofuz
parents:
diff changeset
108 if packet.startswith("vCont;c"):
anatofuz
parents:
diff changeset
109 return self.vCont(packet)
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
110 if packet[0] == "A":
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
111 return self.A(packet)
150
anatofuz
parents:
diff changeset
112 if packet[0] == "g":
anatofuz
parents:
diff changeset
113 return self.readRegisters()
anatofuz
parents:
diff changeset
114 if packet[0] == "G":
anatofuz
parents:
diff changeset
115 # Gxxxxxxxxxxx
anatofuz
parents:
diff changeset
116 # Gxxxxxxxxxxx;thread:1234;
anatofuz
parents:
diff changeset
117 return self.writeRegisters(packet[1:].split(';')[0])
anatofuz
parents:
diff changeset
118 if packet[0] == "p":
anatofuz
parents:
diff changeset
119 regnum = packet[1:].split(';')[0]
anatofuz
parents:
diff changeset
120 return self.readRegister(int(regnum, 16))
anatofuz
parents:
diff changeset
121 if packet[0] == "P":
anatofuz
parents:
diff changeset
122 register, value = packet[1:].split("=")
anatofuz
parents:
diff changeset
123 return self.writeRegister(int(register, 16), value)
anatofuz
parents:
diff changeset
124 if packet[0] == "m":
anatofuz
parents:
diff changeset
125 addr, length = [int(x, 16) for x in packet[1:].split(',')]
anatofuz
parents:
diff changeset
126 return self.readMemory(addr, length)
anatofuz
parents:
diff changeset
127 if packet[0] == "M":
anatofuz
parents:
diff changeset
128 location, encoded_data = packet[1:].split(":")
anatofuz
parents:
diff changeset
129 addr, length = [int(x, 16) for x in location.split(',')]
anatofuz
parents:
diff changeset
130 return self.writeMemory(addr, encoded_data)
anatofuz
parents:
diff changeset
131 if packet[0:7] == "qSymbol":
anatofuz
parents:
diff changeset
132 return self.qSymbol(packet[8:])
anatofuz
parents:
diff changeset
133 if packet[0:10] == "qSupported":
anatofuz
parents:
diff changeset
134 return self.qSupported(packet[11:].split(";"))
anatofuz
parents:
diff changeset
135 if packet == "qfThreadInfo":
anatofuz
parents:
diff changeset
136 return self.qfThreadInfo()
anatofuz
parents:
diff changeset
137 if packet == "qsThreadInfo":
anatofuz
parents:
diff changeset
138 return self.qsThreadInfo()
anatofuz
parents:
diff changeset
139 if packet == "qC":
anatofuz
parents:
diff changeset
140 return self.qC()
anatofuz
parents:
diff changeset
141 if packet == "QEnableErrorStrings":
anatofuz
parents:
diff changeset
142 return self.QEnableErrorStrings()
anatofuz
parents:
diff changeset
143 if packet == "?":
anatofuz
parents:
diff changeset
144 return self.haltReason()
anatofuz
parents:
diff changeset
145 if packet == "s":
anatofuz
parents:
diff changeset
146 return self.haltReason()
anatofuz
parents:
diff changeset
147 if packet[0] == "H":
anatofuz
parents:
diff changeset
148 return self.selectThread(packet[1], int(packet[2:], 16))
anatofuz
parents:
diff changeset
149 if packet[0:6] == "qXfer:":
anatofuz
parents:
diff changeset
150 obj, read, annex, location = packet[6:].split(":")
anatofuz
parents:
diff changeset
151 offset, length = [int(x, 16) for x in location.split(',')]
anatofuz
parents:
diff changeset
152 data, has_more = self.qXferRead(obj, annex, offset, length)
anatofuz
parents:
diff changeset
153 if data is not None:
anatofuz
parents:
diff changeset
154 return self._qXferResponse(data, has_more)
anatofuz
parents:
diff changeset
155 return ""
anatofuz
parents:
diff changeset
156 if packet.startswith("vAttach;"):
anatofuz
parents:
diff changeset
157 pid = packet.partition(';')[2]
anatofuz
parents:
diff changeset
158 return self.vAttach(int(pid, 16))
anatofuz
parents:
diff changeset
159 if packet[0] == "Z":
anatofuz
parents:
diff changeset
160 return self.setBreakpoint(packet)
anatofuz
parents:
diff changeset
161 if packet.startswith("qThreadStopInfo"):
anatofuz
parents:
diff changeset
162 threadnum = int (packet[15:], 16)
anatofuz
parents:
diff changeset
163 return self.threadStopInfo(threadnum)
anatofuz
parents:
diff changeset
164 if packet == "QThreadSuffixSupported":
anatofuz
parents:
diff changeset
165 return self.QThreadSuffixSupported()
anatofuz
parents:
diff changeset
166 if packet == "QListThreadsInStopReply":
anatofuz
parents:
diff changeset
167 return self.QListThreadsInStopReply()
anatofuz
parents:
diff changeset
168 if packet.startswith("qMemoryRegionInfo:"):
223
5f17cb93ff66 LLVM13 (2021/7/18)
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 221
diff changeset
169 return self.qMemoryRegionInfo(int(packet.split(':')[1], 16))
150
anatofuz
parents:
diff changeset
170 if packet == "qQueryGDBServer":
anatofuz
parents:
diff changeset
171 return self.qQueryGDBServer()
anatofuz
parents:
diff changeset
172 if packet == "qHostInfo":
anatofuz
parents:
diff changeset
173 return self.qHostInfo()
anatofuz
parents:
diff changeset
174 if packet == "qGetWorkingDir":
anatofuz
parents:
diff changeset
175 return self.qGetWorkingDir()
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
176 if packet == "qOffsets":
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
177 return self.qOffsets();
150
anatofuz
parents:
diff changeset
178 if packet == "qsProcessInfo":
anatofuz
parents:
diff changeset
179 return self.qsProcessInfo()
anatofuz
parents:
diff changeset
180 if packet.startswith("qfProcessInfo"):
anatofuz
parents:
diff changeset
181 return self.qfProcessInfo(packet)
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
182 if packet.startswith("qPathComplete:"):
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
183 return self.qPathComplete()
150
anatofuz
parents:
diff changeset
184
anatofuz
parents:
diff changeset
185 return self.other(packet)
anatofuz
parents:
diff changeset
186
anatofuz
parents:
diff changeset
187 def qsProcessInfo(self):
anatofuz
parents:
diff changeset
188 return "E04"
anatofuz
parents:
diff changeset
189
anatofuz
parents:
diff changeset
190 def qfProcessInfo(self, packet):
anatofuz
parents:
diff changeset
191 return "E04"
anatofuz
parents:
diff changeset
192
anatofuz
parents:
diff changeset
193 def qGetWorkingDir(self):
anatofuz
parents:
diff changeset
194 return "2f"
anatofuz
parents:
diff changeset
195
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
196 def qOffsets(self):
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
197 return ""
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
198
150
anatofuz
parents:
diff changeset
199 def qHostInfo(self):
anatofuz
parents:
diff changeset
200 return "ptrsize:8;endian:little;"
anatofuz
parents:
diff changeset
201
anatofuz
parents:
diff changeset
202 def qQueryGDBServer(self):
anatofuz
parents:
diff changeset
203 return "E04"
anatofuz
parents:
diff changeset
204
anatofuz
parents:
diff changeset
205 def interrupt(self):
anatofuz
parents:
diff changeset
206 raise self.UnexpectedPacketException()
anatofuz
parents:
diff changeset
207
anatofuz
parents:
diff changeset
208 def cont(self):
anatofuz
parents:
diff changeset
209 raise self.UnexpectedPacketException()
anatofuz
parents:
diff changeset
210
anatofuz
parents:
diff changeset
211 def vCont(self, packet):
anatofuz
parents:
diff changeset
212 raise self.UnexpectedPacketException()
anatofuz
parents:
diff changeset
213
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
214 def A(self, packet):
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
215 return ""
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
216
150
anatofuz
parents:
diff changeset
217 def readRegisters(self):
anatofuz
parents:
diff changeset
218 return "00000000" * self.registerCount
anatofuz
parents:
diff changeset
219
anatofuz
parents:
diff changeset
220 def readRegister(self, register):
anatofuz
parents:
diff changeset
221 return "00000000"
anatofuz
parents:
diff changeset
222
anatofuz
parents:
diff changeset
223 def writeRegisters(self, registers_hex):
anatofuz
parents:
diff changeset
224 return "OK"
anatofuz
parents:
diff changeset
225
anatofuz
parents:
diff changeset
226 def writeRegister(self, register, value_hex):
anatofuz
parents:
diff changeset
227 return "OK"
anatofuz
parents:
diff changeset
228
anatofuz
parents:
diff changeset
229 def readMemory(self, addr, length):
anatofuz
parents:
diff changeset
230 return "00" * length
anatofuz
parents:
diff changeset
231
anatofuz
parents:
diff changeset
232 def writeMemory(self, addr, data_hex):
anatofuz
parents:
diff changeset
233 return "OK"
anatofuz
parents:
diff changeset
234
anatofuz
parents:
diff changeset
235 def qSymbol(self, symbol_args):
anatofuz
parents:
diff changeset
236 return "OK"
anatofuz
parents:
diff changeset
237
anatofuz
parents:
diff changeset
238 def qSupported(self, client_supported):
anatofuz
parents:
diff changeset
239 return "qXfer:features:read+;PacketSize=3fff;QStartNoAckMode+"
anatofuz
parents:
diff changeset
240
anatofuz
parents:
diff changeset
241 def qfThreadInfo(self):
anatofuz
parents:
diff changeset
242 return "l"
anatofuz
parents:
diff changeset
243
anatofuz
parents:
diff changeset
244 def qsThreadInfo(self):
anatofuz
parents:
diff changeset
245 return "l"
anatofuz
parents:
diff changeset
246
anatofuz
parents:
diff changeset
247 def qC(self):
anatofuz
parents:
diff changeset
248 return "QC0"
anatofuz
parents:
diff changeset
249
anatofuz
parents:
diff changeset
250 def QEnableErrorStrings(self):
anatofuz
parents:
diff changeset
251 return "OK"
anatofuz
parents:
diff changeset
252
anatofuz
parents:
diff changeset
253 def haltReason(self):
anatofuz
parents:
diff changeset
254 # SIGINT is 2, return type is 2 digit hex string
anatofuz
parents:
diff changeset
255 return "S02"
anatofuz
parents:
diff changeset
256
anatofuz
parents:
diff changeset
257 def qXferRead(self, obj, annex, offset, length):
anatofuz
parents:
diff changeset
258 return None, False
anatofuz
parents:
diff changeset
259
anatofuz
parents:
diff changeset
260 def _qXferResponse(self, data, has_more):
anatofuz
parents:
diff changeset
261 return "%s%s" % ("m" if has_more else "l", escape_binary(data))
anatofuz
parents:
diff changeset
262
anatofuz
parents:
diff changeset
263 def vAttach(self, pid):
anatofuz
parents:
diff changeset
264 raise self.UnexpectedPacketException()
anatofuz
parents:
diff changeset
265
anatofuz
parents:
diff changeset
266 def selectThread(self, op, thread_id):
anatofuz
parents:
diff changeset
267 return "OK"
anatofuz
parents:
diff changeset
268
anatofuz
parents:
diff changeset
269 def setBreakpoint(self, packet):
anatofuz
parents:
diff changeset
270 raise self.UnexpectedPacketException()
anatofuz
parents:
diff changeset
271
anatofuz
parents:
diff changeset
272 def threadStopInfo(self, threadnum):
anatofuz
parents:
diff changeset
273 return ""
anatofuz
parents:
diff changeset
274
anatofuz
parents:
diff changeset
275 def other(self, packet):
anatofuz
parents:
diff changeset
276 # empty string means unsupported
anatofuz
parents:
diff changeset
277 return ""
anatofuz
parents:
diff changeset
278
anatofuz
parents:
diff changeset
279 def QThreadSuffixSupported(self):
anatofuz
parents:
diff changeset
280 return ""
anatofuz
parents:
diff changeset
281
anatofuz
parents:
diff changeset
282 def QListThreadsInStopReply(self):
anatofuz
parents:
diff changeset
283 return ""
anatofuz
parents:
diff changeset
284
223
5f17cb93ff66 LLVM13 (2021/7/18)
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 221
diff changeset
285 def qMemoryRegionInfo(self, addr):
150
anatofuz
parents:
diff changeset
286 return ""
anatofuz
parents:
diff changeset
287
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
288 def qPathComplete(self):
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
289 return ""
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
290
150
anatofuz
parents:
diff changeset
291 """
anatofuz
parents:
diff changeset
292 Raised when we receive a packet for which there is no default action.
anatofuz
parents:
diff changeset
293 Override the responder class to implement behavior suitable for the test at
anatofuz
parents:
diff changeset
294 hand.
anatofuz
parents:
diff changeset
295 """
anatofuz
parents:
diff changeset
296 class UnexpectedPacketException(Exception):
anatofuz
parents:
diff changeset
297 pass
anatofuz
parents:
diff changeset
298
anatofuz
parents:
diff changeset
299
anatofuz
parents:
diff changeset
300 class MockGDBServer:
anatofuz
parents:
diff changeset
301 """
anatofuz
parents:
diff changeset
302 A simple TCP-based GDB server that can test client behavior by receiving
anatofuz
parents:
diff changeset
303 commands and issuing custom-tailored responses.
anatofuz
parents:
diff changeset
304
anatofuz
parents:
diff changeset
305 Responses are generated via the .responder property, which should be an
anatofuz
parents:
diff changeset
306 instance of a class based on MockGDBServerResponder.
anatofuz
parents:
diff changeset
307 """
anatofuz
parents:
diff changeset
308
anatofuz
parents:
diff changeset
309 responder = None
anatofuz
parents:
diff changeset
310 _socket = None
anatofuz
parents:
diff changeset
311 _client = None
anatofuz
parents:
diff changeset
312 _thread = None
anatofuz
parents:
diff changeset
313 _receivedData = None
anatofuz
parents:
diff changeset
314 _receivedDataOffset = None
anatofuz
parents:
diff changeset
315 _shouldSendAck = True
anatofuz
parents:
diff changeset
316
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
317 def __init__(self):
150
anatofuz
parents:
diff changeset
318 self.responder = MockGDBServerResponder()
anatofuz
parents:
diff changeset
319
anatofuz
parents:
diff changeset
320 def start(self):
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
321 family, type, proto, _, addr = socket.getaddrinfo("localhost", 0,
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
322 proto=socket.IPPROTO_TCP)[0]
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
323 self._socket = socket.socket(family, type, proto)
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
324
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
325
150
anatofuz
parents:
diff changeset
326 self._socket.bind(addr)
anatofuz
parents:
diff changeset
327 self._socket.listen(1)
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
328
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
329 # Start a thread that waits for a client connection.
150
anatofuz
parents:
diff changeset
330 self._thread = threading.Thread(target=self._run)
anatofuz
parents:
diff changeset
331 self._thread.start()
anatofuz
parents:
diff changeset
332
anatofuz
parents:
diff changeset
333 def stop(self):
anatofuz
parents:
diff changeset
334 self._socket.close()
anatofuz
parents:
diff changeset
335 self._thread.join()
anatofuz
parents:
diff changeset
336 self._thread = None
anatofuz
parents:
diff changeset
337
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
338 def get_connect_address(self):
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
339 return "[{}]:{}".format(*self._socket.getsockname())
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
340
150
anatofuz
parents:
diff changeset
341 def _run(self):
anatofuz
parents:
diff changeset
342 # For testing purposes, we only need to worry about one client
anatofuz
parents:
diff changeset
343 # connecting just one time.
anatofuz
parents:
diff changeset
344 try:
anatofuz
parents:
diff changeset
345 # accept() is stubborn and won't fail even when the socket is
anatofuz
parents:
diff changeset
346 # shutdown, so we'll use a timeout
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
347 self._socket.settimeout(30.0)
150
anatofuz
parents:
diff changeset
348 client, client_addr = self._socket.accept()
anatofuz
parents:
diff changeset
349 self._client = client
anatofuz
parents:
diff changeset
350 # The connected client inherits its timeout from self._socket,
anatofuz
parents:
diff changeset
351 # but we'll use a blocking socket for the client
anatofuz
parents:
diff changeset
352 self._client.settimeout(None)
anatofuz
parents:
diff changeset
353 except:
anatofuz
parents:
diff changeset
354 return
anatofuz
parents:
diff changeset
355 self._shouldSendAck = True
anatofuz
parents:
diff changeset
356 self._receivedData = ""
anatofuz
parents:
diff changeset
357 self._receivedDataOffset = 0
anatofuz
parents:
diff changeset
358 data = None
anatofuz
parents:
diff changeset
359 while True:
anatofuz
parents:
diff changeset
360 try:
anatofuz
parents:
diff changeset
361 data = seven.bitcast_to_string(self._client.recv(4096))
anatofuz
parents:
diff changeset
362 if data is None or len(data) == 0:
anatofuz
parents:
diff changeset
363 break
anatofuz
parents:
diff changeset
364 self._receive(data)
anatofuz
parents:
diff changeset
365 except Exception as e:
anatofuz
parents:
diff changeset
366 print("An exception happened when receiving the response from the gdb server. Closing the client...")
anatofuz
parents:
diff changeset
367 traceback.print_exc()
anatofuz
parents:
diff changeset
368 self._client.close()
anatofuz
parents:
diff changeset
369 break
anatofuz
parents:
diff changeset
370
anatofuz
parents:
diff changeset
371 def _receive(self, data):
anatofuz
parents:
diff changeset
372 """
anatofuz
parents:
diff changeset
373 Collects data, parses and responds to as many packets as exist.
anatofuz
parents:
diff changeset
374 Any leftover data is kept for parsing the next time around.
anatofuz
parents:
diff changeset
375 """
anatofuz
parents:
diff changeset
376 self._receivedData += data
anatofuz
parents:
diff changeset
377 try:
anatofuz
parents:
diff changeset
378 packet = self._parsePacket()
anatofuz
parents:
diff changeset
379 while packet is not None:
anatofuz
parents:
diff changeset
380 self._handlePacket(packet)
anatofuz
parents:
diff changeset
381 packet = self._parsePacket()
anatofuz
parents:
diff changeset
382 except self.InvalidPacketException:
anatofuz
parents:
diff changeset
383 self._client.close()
anatofuz
parents:
diff changeset
384
anatofuz
parents:
diff changeset
385 def _parsePacket(self):
anatofuz
parents:
diff changeset
386 """
anatofuz
parents:
diff changeset
387 Reads bytes from self._receivedData, returning:
anatofuz
parents:
diff changeset
388 - a packet's contents if a valid packet is found
anatofuz
parents:
diff changeset
389 - the PACKET_ACK unique object if we got an ack
anatofuz
parents:
diff changeset
390 - None if we only have a partial packet
anatofuz
parents:
diff changeset
391
anatofuz
parents:
diff changeset
392 Raises an InvalidPacketException if unexpected data is received
anatofuz
parents:
diff changeset
393 or if checksums fail.
anatofuz
parents:
diff changeset
394
anatofuz
parents:
diff changeset
395 Once a complete packet is found at the front of self._receivedData,
anatofuz
parents:
diff changeset
396 its data is removed form self._receivedData.
anatofuz
parents:
diff changeset
397 """
anatofuz
parents:
diff changeset
398 data = self._receivedData
anatofuz
parents:
diff changeset
399 i = self._receivedDataOffset
anatofuz
parents:
diff changeset
400 data_len = len(data)
anatofuz
parents:
diff changeset
401 if data_len == 0:
anatofuz
parents:
diff changeset
402 return None
anatofuz
parents:
diff changeset
403 if i == 0:
anatofuz
parents:
diff changeset
404 # If we're looking at the start of the received data, that means
anatofuz
parents:
diff changeset
405 # we're looking for the start of a new packet, denoted by a $.
anatofuz
parents:
diff changeset
406 # It's also possible we'll see an ACK here, denoted by a +
anatofuz
parents:
diff changeset
407 if data[0] == '+':
anatofuz
parents:
diff changeset
408 self._receivedData = data[1:]
anatofuz
parents:
diff changeset
409 return self.PACKET_ACK
anatofuz
parents:
diff changeset
410 if ord(data[0]) == 3:
anatofuz
parents:
diff changeset
411 self._receivedData = data[1:]
anatofuz
parents:
diff changeset
412 return self.PACKET_INTERRUPT
anatofuz
parents:
diff changeset
413 if data[0] == '$':
anatofuz
parents:
diff changeset
414 i += 1
anatofuz
parents:
diff changeset
415 else:
anatofuz
parents:
diff changeset
416 raise self.InvalidPacketException(
anatofuz
parents:
diff changeset
417 "Unexpected leading byte: %s" % data[0])
anatofuz
parents:
diff changeset
418
anatofuz
parents:
diff changeset
419 # If we're looking beyond the start of the received data, then we're
anatofuz
parents:
diff changeset
420 # looking for the end of the packet content, denoted by a #.
anatofuz
parents:
diff changeset
421 # Note that we pick up searching from where we left off last time
anatofuz
parents:
diff changeset
422 while i < data_len and data[i] != '#':
anatofuz
parents:
diff changeset
423 i += 1
anatofuz
parents:
diff changeset
424
anatofuz
parents:
diff changeset
425 # If there isn't enough data left for a checksum, just remember where
anatofuz
parents:
diff changeset
426 # we left off so we can pick up there the next time around
anatofuz
parents:
diff changeset
427 if i > data_len - 3:
anatofuz
parents:
diff changeset
428 self._receivedDataOffset = i
anatofuz
parents:
diff changeset
429 return None
anatofuz
parents:
diff changeset
430
anatofuz
parents:
diff changeset
431 # If we have enough data remaining for the checksum, extract it and
anatofuz
parents:
diff changeset
432 # compare to the packet contents
anatofuz
parents:
diff changeset
433 packet = data[1:i]
anatofuz
parents:
diff changeset
434 i += 1
anatofuz
parents:
diff changeset
435 try:
anatofuz
parents:
diff changeset
436 check = int(data[i:i + 2], 16)
anatofuz
parents:
diff changeset
437 except ValueError:
anatofuz
parents:
diff changeset
438 raise self.InvalidPacketException("Checksum is not valid hex")
anatofuz
parents:
diff changeset
439 i += 2
anatofuz
parents:
diff changeset
440 if check != checksum(packet):
anatofuz
parents:
diff changeset
441 raise self.InvalidPacketException(
anatofuz
parents:
diff changeset
442 "Checksum %02x does not match content %02x" %
anatofuz
parents:
diff changeset
443 (check, checksum(packet)))
anatofuz
parents:
diff changeset
444 # remove parsed bytes from _receivedData and reset offset so parsing
anatofuz
parents:
diff changeset
445 # can start on the next packet the next time around
anatofuz
parents:
diff changeset
446 self._receivedData = data[i:]
anatofuz
parents:
diff changeset
447 self._receivedDataOffset = 0
anatofuz
parents:
diff changeset
448 return packet
anatofuz
parents:
diff changeset
449
anatofuz
parents:
diff changeset
450 def _handlePacket(self, packet):
anatofuz
parents:
diff changeset
451 if packet is self.PACKET_ACK:
anatofuz
parents:
diff changeset
452 # Ignore ACKs from the client. For the future, we can consider
anatofuz
parents:
diff changeset
453 # adding validation code to make sure the client only sends ACKs
anatofuz
parents:
diff changeset
454 # when it's supposed to.
anatofuz
parents:
diff changeset
455 return
anatofuz
parents:
diff changeset
456 response = ""
anatofuz
parents:
diff changeset
457 # We'll handle the ack stuff here since it's not something any of the
anatofuz
parents:
diff changeset
458 # tests will be concerned about, and it'll get turned off quickly anyway.
anatofuz
parents:
diff changeset
459 if self._shouldSendAck:
anatofuz
parents:
diff changeset
460 self._client.sendall(seven.bitcast_to_bytes('+'))
anatofuz
parents:
diff changeset
461 if packet == "QStartNoAckMode":
anatofuz
parents:
diff changeset
462 self._shouldSendAck = False
anatofuz
parents:
diff changeset
463 response = "OK"
anatofuz
parents:
diff changeset
464 elif self.responder is not None:
anatofuz
parents:
diff changeset
465 # Delegate everything else to our responder
anatofuz
parents:
diff changeset
466 response = self.responder.respond(packet)
anatofuz
parents:
diff changeset
467 # Handle packet framing since we don't want to bother tests with it.
anatofuz
parents:
diff changeset
468 if response is not None:
anatofuz
parents:
diff changeset
469 framed = frame_packet(response)
anatofuz
parents:
diff changeset
470 self._client.sendall(seven.bitcast_to_bytes(framed))
anatofuz
parents:
diff changeset
471
anatofuz
parents:
diff changeset
472 PACKET_ACK = object()
anatofuz
parents:
diff changeset
473 PACKET_INTERRUPT = object()
anatofuz
parents:
diff changeset
474
anatofuz
parents:
diff changeset
475 class InvalidPacketException(Exception):
anatofuz
parents:
diff changeset
476 pass
anatofuz
parents:
diff changeset
477
anatofuz
parents:
diff changeset
478 class GDBRemoteTestBase(TestBase):
anatofuz
parents:
diff changeset
479 """
anatofuz
parents:
diff changeset
480 Base class for GDB client tests.
anatofuz
parents:
diff changeset
481
anatofuz
parents:
diff changeset
482 This class will setup and start a mock GDB server for the test to use.
anatofuz
parents:
diff changeset
483 It also provides assertPacketLogContains, which simplifies the checking
anatofuz
parents:
diff changeset
484 of packets sent by the client.
anatofuz
parents:
diff changeset
485 """
anatofuz
parents:
diff changeset
486
anatofuz
parents:
diff changeset
487 NO_DEBUG_INFO_TESTCASE = True
anatofuz
parents:
diff changeset
488 mydir = TestBase.compute_mydir(__file__)
anatofuz
parents:
diff changeset
489 server = None
anatofuz
parents:
diff changeset
490
anatofuz
parents:
diff changeset
491 def setUp(self):
anatofuz
parents:
diff changeset
492 TestBase.setUp(self)
anatofuz
parents:
diff changeset
493 self.server = MockGDBServer()
anatofuz
parents:
diff changeset
494 self.server.start()
anatofuz
parents:
diff changeset
495
anatofuz
parents:
diff changeset
496 def tearDown(self):
anatofuz
parents:
diff changeset
497 # TestBase.tearDown will kill the process, but we need to kill it early
anatofuz
parents:
diff changeset
498 # so its client connection closes and we can stop the server before
anatofuz
parents:
diff changeset
499 # finally calling the base tearDown.
anatofuz
parents:
diff changeset
500 if self.process() is not None:
anatofuz
parents:
diff changeset
501 self.process().Kill()
anatofuz
parents:
diff changeset
502 self.server.stop()
anatofuz
parents:
diff changeset
503 TestBase.tearDown(self)
anatofuz
parents:
diff changeset
504
anatofuz
parents:
diff changeset
505 def createTarget(self, yaml_path):
anatofuz
parents:
diff changeset
506 """
anatofuz
parents:
diff changeset
507 Create a target by auto-generating the object based on the given yaml
anatofuz
parents:
diff changeset
508 instructions.
anatofuz
parents:
diff changeset
509
anatofuz
parents:
diff changeset
510 This will track the generated object so it can be automatically removed
anatofuz
parents:
diff changeset
511 during tearDown.
anatofuz
parents:
diff changeset
512 """
anatofuz
parents:
diff changeset
513 yaml_base, ext = os.path.splitext(yaml_path)
anatofuz
parents:
diff changeset
514 obj_path = self.getBuildArtifact(yaml_base)
anatofuz
parents:
diff changeset
515 self.yaml2obj(yaml_path, obj_path)
anatofuz
parents:
diff changeset
516 return self.dbg.CreateTarget(obj_path)
anatofuz
parents:
diff changeset
517
anatofuz
parents:
diff changeset
518 def connect(self, target):
anatofuz
parents:
diff changeset
519 """
anatofuz
parents:
diff changeset
520 Create a process by connecting to the mock GDB server.
anatofuz
parents:
diff changeset
521
anatofuz
parents:
diff changeset
522 Includes assertions that the process was successfully created.
anatofuz
parents:
diff changeset
523 """
anatofuz
parents:
diff changeset
524 listener = self.dbg.GetListener()
anatofuz
parents:
diff changeset
525 error = lldb.SBError()
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
526 process = target.ConnectRemote(listener,
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
527 "connect://" + self.server.get_connect_address(), "gdb-remote", error)
150
anatofuz
parents:
diff changeset
528 self.assertTrue(error.Success(), error.description)
anatofuz
parents:
diff changeset
529 self.assertTrue(process, PROCESS_IS_VALID)
anatofuz
parents:
diff changeset
530 return process
anatofuz
parents:
diff changeset
531
anatofuz
parents:
diff changeset
532 def assertPacketLogContains(self, packets):
anatofuz
parents:
diff changeset
533 """
anatofuz
parents:
diff changeset
534 Assert that the mock server's packet log contains the given packets.
anatofuz
parents:
diff changeset
535
anatofuz
parents:
diff changeset
536 The packet log includes all packets sent by the client and received
anatofuz
parents:
diff changeset
537 by the server. This fuction makes it easy to verify that the client
anatofuz
parents:
diff changeset
538 sent the expected packets to the server.
anatofuz
parents:
diff changeset
539
anatofuz
parents:
diff changeset
540 The check does not require that the packets be consecutive, but does
anatofuz
parents:
diff changeset
541 require that they are ordered in the log as they ordered in the arg.
anatofuz
parents:
diff changeset
542 """
anatofuz
parents:
diff changeset
543 i = 0
anatofuz
parents:
diff changeset
544 j = 0
anatofuz
parents:
diff changeset
545 log = self.server.responder.packetLog
anatofuz
parents:
diff changeset
546
anatofuz
parents:
diff changeset
547 while i < len(packets) and j < len(log):
anatofuz
parents:
diff changeset
548 if log[j] == packets[i]:
anatofuz
parents:
diff changeset
549 i += 1
anatofuz
parents:
diff changeset
550 j += 1
anatofuz
parents:
diff changeset
551 if i < len(packets):
anatofuz
parents:
diff changeset
552 self.fail(u"Did not receive: %s\nLast 10 packets:\n\t%s" %
anatofuz
parents:
diff changeset
553 (packets[i], u'\n\t'.join(log)))