1import lldb 2import binascii 3import os.path 4from lldbsuite.test.lldbtest import * 5from lldbsuite.test.decorators import * 6from gdbclientutils import * 7 8 9class TestGDBRemoteClient(GDBRemoteTestBase): 10 11 class gPacketResponder(MockGDBServerResponder): 12 registers = [ 13 "name:rax;bitsize:64;offset:0;encoding:uint;format:hex;set:General Purpose Registers;ehframe:0;dwarf:0;", 14 "name:rbx;bitsize:64;offset:8;encoding:uint;format:hex;set:General Purpose Registers;ehframe:3;dwarf:3;", 15 "name:rcx;bitsize:64;offset:16;encoding:uint;format:hex;set:General Purpose Registers;ehframe:2;dwarf:2;generic:arg4;", 16 "name:rdx;bitsize:64;offset:24;encoding:uint;format:hex;set:General Purpose Registers;ehframe:1;dwarf:1;generic:arg3;", 17 "name:rdi;bitsize:64;offset:32;encoding:uint;format:hex;set:General Purpose Registers;ehframe:5;dwarf:5;generic:arg1;", 18 "name:rsi;bitsize:64;offset:40;encoding:uint;format:hex;set:General Purpose Registers;ehframe:4;dwarf:4;generic:arg2;", 19 "name:rbp;bitsize:64;offset:48;encoding:uint;format:hex;set:General Purpose Registers;ehframe:6;dwarf:6;generic:fp;", 20 "name:rsp;bitsize:64;offset:56;encoding:uint;format:hex;set:General Purpose Registers;ehframe:7;dwarf:7;generic:sp;", 21 ] 22 23 def qRegisterInfo(self, num): 24 try: 25 return self.registers[num] 26 except IndexError: 27 return "E45" 28 29 def readRegisters(self): 30 return len(self.registers) * 16 * '0' 31 32 def readRegister(self, register): 33 return "0000000000000000" 34 35 def test_connect(self): 36 """Test connecting to a remote gdb server""" 37 target = self.createTarget("a.yaml") 38 process = self.connect(target) 39 self.assertPacketLogContains(["qProcessInfo", "qfThreadInfo"]) 40 41 def test_attach_fail(self): 42 error_msg = "mock-error-msg" 43 44 class MyResponder(MockGDBServerResponder): 45 # Pretend we don't have any process during the initial queries. 46 def qC(self): 47 return "E42" 48 49 def qfThreadInfo(self): 50 return "OK" # No threads. 51 52 # Then, when we are asked to attach, error out. 53 def vAttach(self, pid): 54 return "E42;" + binascii.hexlify(error_msg.encode()).decode() 55 56 self.server.responder = MyResponder() 57 58 target = self.dbg.CreateTarget("") 59 process = self.connect(target) 60 lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateConnected]) 61 62 error = lldb.SBError() 63 target.AttachToProcessWithID(lldb.SBListener(), 47, error) 64 self.assertEquals(error_msg, error.GetCString()) 65 66 def test_launch_fail(self): 67 class MyResponder(MockGDBServerResponder): 68 # Pretend we don't have any process during the initial queries. 69 def qC(self): 70 return "E42" 71 72 def qfThreadInfo(self): 73 return "OK" # No threads. 74 75 # Then, when we are asked to attach, error out. 76 def A(self, packet): 77 return "E47" 78 79 self.server.responder = MyResponder() 80 81 target = self.createTarget("a.yaml") 82 process = self.connect(target) 83 lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateConnected]) 84 85 error = lldb.SBError() 86 target.Launch(lldb.SBListener(), None, None, None, None, None, 87 None, 0, True, error) 88 self.assertEquals("'A' packet returned an error: 71", error.GetCString()) 89 90 def test_read_registers_using_g_packets(self): 91 """Test reading registers using 'g' packets (default behavior)""" 92 self.dbg.HandleCommand( 93 "settings set plugin.process.gdb-remote.use-g-packet-for-reading true") 94 self.addTearDownHook(lambda: 95 self.runCmd("settings set plugin.process.gdb-remote.use-g-packet-for-reading false")) 96 self.server.responder = self.gPacketResponder() 97 target = self.createTarget("a.yaml") 98 process = self.connect(target) 99 100 self.assertEquals(1, self.server.responder.packetLog.count("g")) 101 self.server.responder.packetLog = [] 102 self.read_registers(process) 103 # Reading registers should not cause any 'p' packets to be exchanged. 104 self.assertEquals( 105 0, len([p for p in self.server.responder.packetLog if p.startswith("p")])) 106 107 def test_read_registers_using_p_packets(self): 108 """Test reading registers using 'p' packets""" 109 self.dbg.HandleCommand( 110 "settings set plugin.process.gdb-remote.use-g-packet-for-reading false") 111 self.server.responder = self.gPacketResponder() 112 target = self.createTarget("a.yaml") 113 process = self.connect(target) 114 115 self.read_registers(process) 116 self.assertNotIn("g", self.server.responder.packetLog) 117 self.assertGreater( 118 len([p for p in self.server.responder.packetLog if p.startswith("p")]), 0) 119 120 def test_write_registers_using_P_packets(self): 121 """Test writing registers using 'P' packets (default behavior)""" 122 self.server.responder = self.gPacketResponder() 123 target = self.createTarget("a.yaml") 124 process = self.connect(target) 125 126 self.write_registers(process) 127 self.assertEquals(0, len( 128 [p for p in self.server.responder.packetLog if p.startswith("G")])) 129 self.assertGreater( 130 len([p for p in self.server.responder.packetLog if p.startswith("P")]), 0) 131 132 def test_write_registers_using_G_packets(self): 133 """Test writing registers using 'G' packets""" 134 135 class MyResponder(self.gPacketResponder): 136 def readRegister(self, register): 137 # empty string means unsupported 138 return "" 139 140 self.server.responder = MyResponder() 141 target = self.createTarget("a.yaml") 142 process = self.connect(target) 143 144 self.write_registers(process) 145 self.assertEquals(0, len( 146 [p for p in self.server.responder.packetLog if p.startswith("P")])) 147 self.assertGreater(len( 148 [p for p in self.server.responder.packetLog if p.startswith("G")]), 0) 149 150 def read_registers(self, process): 151 self.for_each_gpr( 152 process, lambda r: self.assertEquals("0x0000000000000000", r.GetValue())) 153 154 def write_registers(self, process): 155 self.for_each_gpr( 156 process, lambda r: r.SetValueFromCString("0x0000000000000000")) 157 158 def for_each_gpr(self, process, operation): 159 registers = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters() 160 self.assertGreater(registers.GetSize(), 0) 161 regSet = registers[0] 162 numChildren = regSet.GetNumChildren() 163 self.assertGreater(numChildren, 0) 164 for i in range(numChildren): 165 operation(regSet.GetChildAtIndex(i)) 166 167 def test_launch_A(self): 168 class MyResponder(MockGDBServerResponder): 169 def __init__(self, *args, **kwargs): 170 self.started = False 171 return super().__init__(*args, **kwargs) 172 173 def qC(self): 174 if self.started: 175 return "QCp10.10" 176 else: 177 return "E42" 178 179 def qfThreadInfo(self): 180 if self.started: 181 return "mp10.10" 182 else: 183 return "E42" 184 185 def qsThreadInfo(self): 186 return "l" 187 188 def A(self, packet): 189 self.started = True 190 return "OK" 191 192 def qLaunchSuccess(self): 193 if self.started: 194 return "OK" 195 return "E42" 196 197 self.server.responder = MyResponder() 198 199 target = self.createTarget("a.yaml") 200 # NB: apparently GDB packets are using "/" on Windows too 201 exe_path = self.getBuildArtifact("a").replace(os.path.sep, '/') 202 exe_hex = binascii.b2a_hex(exe_path.encode()).decode() 203 process = self.connect(target) 204 lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, 205 [lldb.eStateConnected]) 206 207 target.Launch(lldb.SBListener(), 208 ["arg1", "arg2", "arg3"], # argv 209 [], # envp 210 None, # stdin_path 211 None, # stdout_path 212 None, # stderr_path 213 None, # working_directory 214 0, # launch_flags 215 True, # stop_at_entry 216 lldb.SBError()) # error 217 self.assertTrue(process, PROCESS_IS_VALID) 218 self.assertEqual(process.GetProcessID(), 16) 219 220 self.assertPacketLogContains([ 221 "A%d,0,%s,8,1,61726731,8,2,61726732,8,3,61726733" % ( 222 len(exe_hex), exe_hex), 223 ]) 224 225 def test_launch_vRun(self): 226 class MyResponder(MockGDBServerResponder): 227 def __init__(self, *args, **kwargs): 228 self.started = False 229 return super().__init__(*args, **kwargs) 230 231 def qC(self): 232 if self.started: 233 return "QCp10.10" 234 else: 235 return "E42" 236 237 def qfThreadInfo(self): 238 if self.started: 239 return "mp10.10" 240 else: 241 return "E42" 242 243 def qsThreadInfo(self): 244 return "l" 245 246 def vRun(self, packet): 247 self.started = True 248 return "T13" 249 250 def A(self, packet): 251 return "E28" 252 253 self.server.responder = MyResponder() 254 255 target = self.createTarget("a.yaml") 256 # NB: apparently GDB packets are using "/" on Windows too 257 exe_path = self.getBuildArtifact("a").replace(os.path.sep, '/') 258 exe_hex = binascii.b2a_hex(exe_path.encode()).decode() 259 process = self.connect(target) 260 lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, 261 [lldb.eStateConnected]) 262 263 process = target.Launch(lldb.SBListener(), 264 ["arg1", "arg2", "arg3"], # argv 265 [], # envp 266 None, # stdin_path 267 None, # stdout_path 268 None, # stderr_path 269 None, # working_directory 270 0, # launch_flags 271 True, # stop_at_entry 272 lldb.SBError()) # error 273 self.assertTrue(process, PROCESS_IS_VALID) 274 self.assertEqual(process.GetProcessID(), 16) 275 276 self.assertPacketLogContains([ 277 "vRun;%s;61726731;61726732;61726733" % (exe_hex,) 278 ]) 279 280 def test_launch_QEnvironment(self): 281 class MyResponder(MockGDBServerResponder): 282 def qC(self): 283 return "E42" 284 285 def qfThreadInfo(self): 286 return "E42" 287 288 def vRun(self, packet): 289 self.started = True 290 return "E28" 291 292 self.server.responder = MyResponder() 293 294 target = self.createTarget("a.yaml") 295 process = self.connect(target) 296 lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, 297 [lldb.eStateConnected]) 298 299 target.Launch(lldb.SBListener(), 300 [], # argv 301 ["PLAIN=foo", 302 "NEEDSENC=frob$", 303 "NEEDSENC2=fr*ob", 304 "NEEDSENC3=fro}b", 305 "NEEDSENC4=f#rob", 306 "EQUALS=foo=bar", 307 ], # envp 308 None, # stdin_path 309 None, # stdout_path 310 None, # stderr_path 311 None, # working_directory 312 0, # launch_flags 313 True, # stop_at_entry 314 lldb.SBError()) # error 315 316 self.assertPacketLogContains([ 317 "QEnvironment:PLAIN=foo", 318 "QEnvironmentHexEncoded:4e45454453454e433d66726f6224", 319 "QEnvironmentHexEncoded:4e45454453454e43323d66722a6f62", 320 "QEnvironmentHexEncoded:4e45454453454e43333d66726f7d62", 321 "QEnvironmentHexEncoded:4e45454453454e43343d6623726f62", 322 "QEnvironment:EQUALS=foo=bar", 323 ]) 324 325 def test_launch_QEnvironmentHexEncoded_only(self): 326 class MyResponder(MockGDBServerResponder): 327 def qC(self): 328 return "E42" 329 330 def qfThreadInfo(self): 331 return "E42" 332 333 def vRun(self, packet): 334 self.started = True 335 return "E28" 336 337 def QEnvironment(self, packet): 338 return "" 339 340 self.server.responder = MyResponder() 341 342 target = self.createTarget("a.yaml") 343 process = self.connect(target) 344 lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, 345 [lldb.eStateConnected]) 346 347 target.Launch(lldb.SBListener(), 348 [], # argv 349 ["PLAIN=foo", 350 "NEEDSENC=frob$", 351 "NEEDSENC2=fr*ob", 352 "NEEDSENC3=fro}b", 353 "NEEDSENC4=f#rob", 354 "EQUALS=foo=bar", 355 ], # envp 356 None, # stdin_path 357 None, # stdout_path 358 None, # stderr_path 359 None, # working_directory 360 0, # launch_flags 361 True, # stop_at_entry 362 lldb.SBError()) # error 363 364 self.assertPacketLogContains([ 365 "QEnvironmentHexEncoded:504c41494e3d666f6f", 366 "QEnvironmentHexEncoded:4e45454453454e433d66726f6224", 367 "QEnvironmentHexEncoded:4e45454453454e43323d66722a6f62", 368 "QEnvironmentHexEncoded:4e45454453454e43333d66726f7d62", 369 "QEnvironmentHexEncoded:4e45454453454e43343d6623726f62", 370 "QEnvironmentHexEncoded:455155414c533d666f6f3d626172", 371 ]) 372 373 def test_detach_no_multiprocess(self): 374 class MyResponder(MockGDBServerResponder): 375 def __init__(self): 376 super().__init__() 377 self.detached = None 378 379 def qfThreadInfo(self): 380 return "10200" 381 382 def D(self, packet): 383 self.detached = packet 384 return "OK" 385 386 self.server.responder = MyResponder() 387 target = self.dbg.CreateTarget('') 388 process = self.connect(target) 389 process.Detach() 390 self.assertEqual(self.server.responder.detached, "D") 391 392 def test_detach_pid(self): 393 class MyResponder(MockGDBServerResponder): 394 def __init__(self, test_case): 395 super().__init__() 396 self.test_case = test_case 397 self.detached = None 398 399 def qSupported(self, client_supported): 400 self.test_case.assertIn("multiprocess+", client_supported) 401 return "multiprocess+;" + super().qSupported(client_supported) 402 403 def qfThreadInfo(self): 404 return "mp400.10200" 405 406 def D(self, packet): 407 self.detached = packet 408 return "OK" 409 410 self.server.responder = MyResponder(self) 411 target = self.dbg.CreateTarget('') 412 process = self.connect(target) 413 process.Detach() 414 self.assertRegex(self.server.responder.detached, r"D;0*400") 415 416 def test_signal_gdb(self): 417 class MyResponder(MockGDBServerResponder): 418 def qSupported(self, client_supported): 419 return "PacketSize=3fff;QStartNoAckMode+" 420 421 def haltReason(self): 422 return "S0a" 423 424 def cont(self): 425 return self.haltReason() 426 427 self.server.responder = MyResponder() 428 429 target = self.createTarget("a.yaml") 430 process = self.connect(target) 431 432 self.assertEqual(process.threads[0].GetStopReason(), 433 lldb.eStopReasonSignal) 434 self.assertEqual(process.threads[0].GetStopDescription(100), 435 'signal SIGBUS') 436 437 def test_signal_lldb_old(self): 438 class MyResponder(MockGDBServerResponder): 439 def qSupported(self, client_supported): 440 return "PacketSize=3fff;QStartNoAckMode+" 441 442 def qHostInfo(self): 443 return "triple:61726d76372d756e6b6e6f776e2d6c696e75782d676e75;" 444 445 def QThreadSuffixSupported(self): 446 return "OK" 447 448 def haltReason(self): 449 return "S0a" 450 451 def cont(self): 452 return self.haltReason() 453 454 self.server.responder = MyResponder() 455 456 target = self.createTarget("a.yaml") 457 process = self.connect(target) 458 459 self.assertEqual(process.threads[0].GetStopReason(), 460 lldb.eStopReasonSignal) 461 self.assertEqual(process.threads[0].GetStopDescription(100), 462 'signal SIGUSR1') 463 464 def test_signal_lldb(self): 465 class MyResponder(MockGDBServerResponder): 466 def qSupported(self, client_supported): 467 return "PacketSize=3fff;QStartNoAckMode+;native-signals+" 468 469 def qHostInfo(self): 470 return "triple:61726d76372d756e6b6e6f776e2d6c696e75782d676e75;" 471 472 def haltReason(self): 473 return "S0a" 474 475 def cont(self): 476 return self.haltReason() 477 478 self.server.responder = MyResponder() 479 480 target = self.createTarget("a.yaml") 481 process = self.connect(target) 482 483 self.assertEqual(process.threads[0].GetStopReason(), 484 lldb.eStopReasonSignal) 485 self.assertEqual(process.threads[0].GetStopDescription(100), 486 'signal SIGUSR1') 487