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