xref: /llvm-project/lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py (revision 8d1de7b34af46a089eb5433c700419ad9b2923ee)
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    class gPacketResponder(MockGDBServerResponder):
13        registers = [
14            "name:rax;bitsize:64;offset:0;encoding:uint;format:hex;set:General Purpose Registers;ehframe:0;dwarf:0;",
15            "name:rbx;bitsize:64;offset:8;encoding:uint;format:hex;set:General Purpose Registers;ehframe:3;dwarf:3;",
16            "name:rcx;bitsize:64;offset:16;encoding:uint;format:hex;set:General Purpose Registers;ehframe:2;dwarf:2;generic:arg4;",
17            "name:rdx;bitsize:64;offset:24;encoding:uint;format:hex;set:General Purpose Registers;ehframe:1;dwarf:1;generic:arg3;",
18            "name:rdi;bitsize:64;offset:32;encoding:uint;format:hex;set:General Purpose Registers;ehframe:5;dwarf:5;generic:arg1;",
19            "name:rsi;bitsize:64;offset:40;encoding:uint;format:hex;set:General Purpose Registers;ehframe:4;dwarf:4;generic:arg2;",
20            "name:rbp;bitsize:64;offset:48;encoding:uint;format:hex;set:General Purpose Registers;ehframe:6;dwarf:6;generic:fp;",
21            "name:rsp;bitsize:64;offset:56;encoding:uint;format:hex;set:General Purpose Registers;ehframe:7;dwarf:7;generic:sp;",
22        ]
23
24        def qRegisterInfo(self, num):
25            try:
26                return self.registers[num]
27            except IndexError:
28                return "E45"
29
30        def readRegisters(self):
31            return len(self.registers) * 16 * '0'
32
33        def readRegister(self, register):
34            return "0000000000000000"
35
36    def test_connect(self):
37        """Test connecting to a remote gdb server"""
38        target = self.createTarget("a.yaml")
39        process = self.connect(target)
40        self.assertPacketLogContains(["qProcessInfo", "qfThreadInfo"])
41
42    def test_attach_fail(self):
43        error_msg = "mock-error-msg"
44
45        class MyResponder(MockGDBServerResponder):
46            # Pretend we don't have any process during the initial queries.
47            def qC(self):
48                return "E42"
49
50            def qfThreadInfo(self):
51                return "OK" # No threads.
52
53            # Then, when we are asked to attach, error out.
54            def vAttach(self, pid):
55                return "E42;" + binascii.hexlify(error_msg.encode()).decode()
56
57        self.server.responder = MyResponder()
58
59        target = self.dbg.CreateTarget("")
60        process = self.connect(target)
61        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateConnected])
62
63        error = lldb.SBError()
64        target.AttachToProcessWithID(lldb.SBListener(), 47, error)
65        self.assertEquals(error_msg, error.GetCString())
66
67    def test_launch_fail(self):
68        class MyResponder(MockGDBServerResponder):
69            # Pretend we don't have any process during the initial queries.
70            def qC(self):
71                return "E42"
72
73            def qfThreadInfo(self):
74                return "OK" # No threads.
75
76            # Then, when we are asked to attach, error out.
77            def A(self, packet):
78                return "E47"
79
80        self.server.responder = MyResponder()
81
82        target = self.createTarget("a.yaml")
83        process = self.connect(target)
84        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateConnected])
85
86        error = lldb.SBError()
87        target.Launch(lldb.SBListener(), None, None, None, None, None,
88                None, 0, True, error)
89        self.assertRegex(error.GetCString(), "Cannot launch '.*a': Error 71")
90
91    def test_launch_rich_error(self):
92        class MyResponder(MockGDBServerResponder):
93            def qC(self):
94                return "E42"
95
96            def qfThreadInfo(self):
97                return "OK" # No threads.
98
99            # Then, when we are asked to attach, error out.
100            def vRun(self, packet):
101                return "Eff;" + seven.hexlify("I'm a teapot")
102
103        self.server.responder = MyResponder()
104
105        target = self.createTarget("a.yaml")
106        process = self.connect(target)
107        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateConnected])
108
109        error = lldb.SBError()
110        target.Launch(lldb.SBListener(), None, None, None, None, None,
111                None, 0, True, error)
112        self.assertRegex(error.GetCString(), "Cannot launch '.*a': I'm a teapot")
113
114    def test_read_registers_using_g_packets(self):
115        """Test reading registers using 'g' packets (default behavior)"""
116        self.dbg.HandleCommand(
117                "settings set plugin.process.gdb-remote.use-g-packet-for-reading true")
118        self.addTearDownHook(lambda:
119                self.runCmd("settings set plugin.process.gdb-remote.use-g-packet-for-reading false"))
120        self.server.responder = self.gPacketResponder()
121        target = self.createTarget("a.yaml")
122        process = self.connect(target)
123
124        self.assertEquals(1, self.server.responder.packetLog.count("g"))
125        self.server.responder.packetLog = []
126        self.read_registers(process)
127        # Reading registers should not cause any 'p' packets to be exchanged.
128        self.assertEquals(
129                0, len([p for p in self.server.responder.packetLog if p.startswith("p")]))
130
131    def test_read_registers_using_p_packets(self):
132        """Test reading registers using 'p' packets"""
133        self.dbg.HandleCommand(
134                "settings set plugin.process.gdb-remote.use-g-packet-for-reading false")
135        self.server.responder = self.gPacketResponder()
136        target = self.createTarget("a.yaml")
137        process = self.connect(target)
138
139        self.read_registers(process)
140        self.assertNotIn("g", self.server.responder.packetLog)
141        self.assertGreater(
142                len([p for p in self.server.responder.packetLog if p.startswith("p")]), 0)
143
144    def test_write_registers_using_P_packets(self):
145        """Test writing registers using 'P' packets (default behavior)"""
146        self.server.responder = self.gPacketResponder()
147        target = self.createTarget("a.yaml")
148        process = self.connect(target)
149
150        self.write_registers(process)
151        self.assertEquals(0, len(
152                [p for p in self.server.responder.packetLog if p.startswith("G")]))
153        self.assertGreater(
154                len([p for p in self.server.responder.packetLog if p.startswith("P")]), 0)
155
156    def test_write_registers_using_G_packets(self):
157        """Test writing registers using 'G' packets"""
158
159        class MyResponder(self.gPacketResponder):
160            def readRegister(self, register):
161                # empty string means unsupported
162                return ""
163
164        self.server.responder = MyResponder()
165        target = self.createTarget("a.yaml")
166        process = self.connect(target)
167
168        self.write_registers(process)
169        self.assertEquals(0, len(
170                [p for p in self.server.responder.packetLog if p.startswith("P")]))
171        self.assertGreater(len(
172                [p for p in self.server.responder.packetLog if p.startswith("G")]), 0)
173
174    def read_registers(self, process):
175        self.for_each_gpr(
176                process, lambda r: self.assertEquals("0x0000000000000000", r.GetValue()))
177
178    def write_registers(self, process):
179        self.for_each_gpr(
180                process, lambda r: r.SetValueFromCString("0x0000000000000000"))
181
182    def for_each_gpr(self, process, operation):
183        registers = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters()
184        self.assertGreater(registers.GetSize(), 0)
185        regSet = registers[0]
186        numChildren = regSet.GetNumChildren()
187        self.assertGreater(numChildren, 0)
188        for i in range(numChildren):
189            operation(regSet.GetChildAtIndex(i))
190
191    def test_launch_A(self):
192        class MyResponder(MockGDBServerResponder):
193            def __init__(self, *args, **kwargs):
194                self.started = False
195                return super().__init__(*args, **kwargs)
196
197            def qC(self):
198                if self.started:
199                    return "QCp10.10"
200                else:
201                    return "E42"
202
203            def qfThreadInfo(self):
204                if self.started:
205                    return "mp10.10"
206                else:
207                   return "E42"
208
209            def qsThreadInfo(self):
210                return "l"
211
212            def A(self, packet):
213                self.started = True
214                return "OK"
215
216            def qLaunchSuccess(self):
217                if self.started:
218                    return "OK"
219                return "E42"
220
221        self.server.responder = MyResponder()
222
223        target = self.createTarget("a.yaml")
224        # NB: apparently GDB packets are using "/" on Windows too
225        exe_path = self.getBuildArtifact("a").replace(os.path.sep, '/')
226        exe_hex = binascii.b2a_hex(exe_path.encode()).decode()
227        process = self.connect(target)
228        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
229                                      [lldb.eStateConnected])
230
231        target.Launch(lldb.SBListener(),
232                      ["arg1", "arg2", "arg3"],  # argv
233                      [],  # envp
234                      None,  # stdin_path
235                      None,  # stdout_path
236                      None,  # stderr_path
237                      None,  # working_directory
238                      0,  # launch_flags
239                      True,  # stop_at_entry
240                      lldb.SBError())  # error
241        self.assertTrue(process, PROCESS_IS_VALID)
242        self.assertEqual(process.GetProcessID(), 16)
243
244        self.assertPacketLogContains([
245          "A%d,0,%s,8,1,61726731,8,2,61726732,8,3,61726733" % (
246              len(exe_hex), exe_hex),
247        ])
248
249    def test_launch_vRun(self):
250        class MyResponder(MockGDBServerResponder):
251            def __init__(self, *args, **kwargs):
252                self.started = False
253                return super().__init__(*args, **kwargs)
254
255            def qC(self):
256                if self.started:
257                    return "QCp10.10"
258                else:
259                    return "E42"
260
261            def qfThreadInfo(self):
262                if self.started:
263                    return "mp10.10"
264                else:
265                   return "E42"
266
267            def qsThreadInfo(self):
268                return "l"
269
270            def vRun(self, packet):
271                self.started = True
272                return "T13"
273
274            def A(self, packet):
275                return "E28"
276
277        self.server.responder = MyResponder()
278
279        target = self.createTarget("a.yaml")
280        # NB: apparently GDB packets are using "/" on Windows too
281        exe_path = self.getBuildArtifact("a").replace(os.path.sep, '/')
282        exe_hex = binascii.b2a_hex(exe_path.encode()).decode()
283        process = self.connect(target)
284        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
285                                      [lldb.eStateConnected])
286
287        process = target.Launch(lldb.SBListener(),
288                                ["arg1", "arg2", "arg3"],  # argv
289                                [],  # envp
290                                None,  # stdin_path
291                                None,  # stdout_path
292                                None,  # stderr_path
293                                None,  # working_directory
294                                0,  # launch_flags
295                                True,  # stop_at_entry
296                                lldb.SBError())  # error
297        self.assertTrue(process, PROCESS_IS_VALID)
298        self.assertEqual(process.GetProcessID(), 16)
299
300        self.assertPacketLogContains([
301          "vRun;%s;61726731;61726732;61726733" % (exe_hex,)
302        ])
303
304    def test_launch_QEnvironment(self):
305        class MyResponder(MockGDBServerResponder):
306            def qC(self):
307                return "E42"
308
309            def qfThreadInfo(self):
310               return "E42"
311
312            def vRun(self, packet):
313                self.started = True
314                return "E28"
315
316        self.server.responder = MyResponder()
317
318        target = self.createTarget("a.yaml")
319        process = self.connect(target)
320        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
321                                      [lldb.eStateConnected])
322
323        target.Launch(lldb.SBListener(),
324                      [],  # argv
325                      ["PLAIN=foo",
326                       "NEEDSENC=frob$",
327                       "NEEDSENC2=fr*ob",
328                       "NEEDSENC3=fro}b",
329                       "NEEDSENC4=f#rob",
330                       "EQUALS=foo=bar",
331                       ],  # envp
332                      None,  # stdin_path
333                      None,  # stdout_path
334                      None,  # stderr_path
335                      None,  # working_directory
336                      0,  # launch_flags
337                      True,  # stop_at_entry
338                      lldb.SBError())  # error
339
340        self.assertPacketLogContains([
341          "QEnvironment:PLAIN=foo",
342          "QEnvironmentHexEncoded:4e45454453454e433d66726f6224",
343          "QEnvironmentHexEncoded:4e45454453454e43323d66722a6f62",
344          "QEnvironmentHexEncoded:4e45454453454e43333d66726f7d62",
345          "QEnvironmentHexEncoded:4e45454453454e43343d6623726f62",
346          "QEnvironment:EQUALS=foo=bar",
347        ])
348
349    def test_launch_QEnvironmentHexEncoded_only(self):
350        class MyResponder(MockGDBServerResponder):
351            def qC(self):
352                return "E42"
353
354            def qfThreadInfo(self):
355               return "E42"
356
357            def vRun(self, packet):
358                self.started = True
359                return "E28"
360
361            def QEnvironment(self, packet):
362                return ""
363
364        self.server.responder = MyResponder()
365
366        target = self.createTarget("a.yaml")
367        process = self.connect(target)
368        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
369                                      [lldb.eStateConnected])
370
371        target.Launch(lldb.SBListener(),
372                      [],  # argv
373                      ["PLAIN=foo",
374                       "NEEDSENC=frob$",
375                       "NEEDSENC2=fr*ob",
376                       "NEEDSENC3=fro}b",
377                       "NEEDSENC4=f#rob",
378                       "EQUALS=foo=bar",
379                       ],  # envp
380                      None,  # stdin_path
381                      None,  # stdout_path
382                      None,  # stderr_path
383                      None,  # working_directory
384                      0,  # launch_flags
385                      True,  # stop_at_entry
386                      lldb.SBError())  # error
387
388        self.assertPacketLogContains([
389          "QEnvironmentHexEncoded:504c41494e3d666f6f",
390          "QEnvironmentHexEncoded:4e45454453454e433d66726f6224",
391          "QEnvironmentHexEncoded:4e45454453454e43323d66722a6f62",
392          "QEnvironmentHexEncoded:4e45454453454e43333d66726f7d62",
393          "QEnvironmentHexEncoded:4e45454453454e43343d6623726f62",
394          "QEnvironmentHexEncoded:455155414c533d666f6f3d626172",
395        ])
396
397    def test_detach_no_multiprocess(self):
398        class MyResponder(MockGDBServerResponder):
399            def __init__(self):
400                super().__init__()
401                self.detached = None
402
403            def qfThreadInfo(self):
404                return "10200"
405
406            def D(self, packet):
407                self.detached = packet
408                return "OK"
409
410        self.server.responder = MyResponder()
411        target = self.dbg.CreateTarget('')
412        process = self.connect(target)
413        process.Detach()
414        self.assertEqual(self.server.responder.detached, "D")
415
416    def test_detach_pid(self):
417        class MyResponder(MockGDBServerResponder):
418            def __init__(self, test_case):
419                super().__init__()
420                self.test_case = test_case
421                self.detached = None
422
423            def qSupported(self, client_supported):
424                self.test_case.assertIn("multiprocess+", client_supported)
425                return "multiprocess+;" + super().qSupported(client_supported)
426
427            def qfThreadInfo(self):
428                return "mp400.10200"
429
430            def D(self, packet):
431                self.detached = packet
432                return "OK"
433
434        self.server.responder = MyResponder(self)
435        target = self.dbg.CreateTarget('')
436        process = self.connect(target)
437        process.Detach()
438        self.assertRegex(self.server.responder.detached, r"D;0*400")
439
440    def test_signal_gdb(self):
441        class MyResponder(MockGDBServerResponder):
442            def qSupported(self, client_supported):
443                return "PacketSize=3fff;QStartNoAckMode+"
444
445            def haltReason(self):
446                return "S0a"
447
448            def cont(self):
449                return self.haltReason()
450
451        self.server.responder = MyResponder()
452
453        self.runCmd("platform select remote-linux")
454        target = self.createTarget("a.yaml")
455        process = self.connect(target)
456
457        self.assertEqual(process.threads[0].GetStopReason(),
458                         lldb.eStopReasonSignal)
459        self.assertEqual(process.threads[0].GetStopDescription(100),
460                         'signal SIGBUS')
461
462    def test_signal_lldb_old(self):
463        class MyResponder(MockGDBServerResponder):
464            def qSupported(self, client_supported):
465                return "PacketSize=3fff;QStartNoAckMode+"
466
467            def qHostInfo(self):
468                return "triple:61726d76372d756e6b6e6f776e2d6c696e75782d676e75;"
469
470            def QThreadSuffixSupported(self):
471                return "OK"
472
473            def haltReason(self):
474                return "S0a"
475
476            def cont(self):
477                return self.haltReason()
478
479        self.server.responder = MyResponder()
480
481        self.runCmd("platform select remote-linux")
482        target = self.createTarget("a.yaml")
483        process = self.connect(target)
484
485        self.assertEqual(process.threads[0].GetStopReason(),
486                         lldb.eStopReasonSignal)
487        self.assertEqual(process.threads[0].GetStopDescription(100),
488                         'signal SIGUSR1')
489
490    def test_signal_lldb(self):
491        class MyResponder(MockGDBServerResponder):
492            def qSupported(self, client_supported):
493                return "PacketSize=3fff;QStartNoAckMode+;native-signals+"
494
495            def qHostInfo(self):
496                return "triple:61726d76372d756e6b6e6f776e2d6c696e75782d676e75;"
497
498            def haltReason(self):
499                return "S0a"
500
501            def cont(self):
502                return self.haltReason()
503
504        self.server.responder = MyResponder()
505
506        self.runCmd("platform select remote-linux")
507        target = self.createTarget("a.yaml")
508        process = self.connect(target)
509
510        self.assertEqual(process.threads[0].GetStopReason(),
511                         lldb.eStopReasonSignal)
512        self.assertEqual(process.threads[0].GetStopDescription(100),
513                         'signal SIGUSR1')
514
515    def do_siginfo_test(self, platform, target_yaml, raw_data, expected):
516        class MyResponder(MockGDBServerResponder):
517            def qSupported(self, client_supported):
518                return "PacketSize=3fff;QStartNoAckMode+;qXfer:siginfo:read+"
519
520            def qXferRead(self, obj, annex, offset, length):
521                if obj == "siginfo":
522                    return raw_data, False
523                else:
524                    return None, False
525
526            def haltReason(self):
527                return "T02"
528
529            def cont(self):
530                return self.haltReason()
531
532        self.server.responder = MyResponder()
533
534        self.runCmd("platform select " + platform)
535        target = self.createTarget(target_yaml)
536        process = self.connect(target)
537
538        siginfo = process.threads[0].GetSiginfo()
539        self.assertSuccess(siginfo.GetError())
540
541        for key, value in expected.items():
542            self.assertEqual(siginfo.GetValueForExpressionPath("." + key)
543                             .GetValueAsUnsigned(),
544                             value)
545
546
547    def test_siginfo_linux_amd64(self):
548        data = (
549          # si_signo         si_errno        si_code
550            "\x11\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00"
551          # __pad0           si_pid          si_uid
552            "\x00\x00\x00\x00\xbf\xf7\x0b\x00\xe8\x03\x00\x00"
553          # si_status
554            "\x0c\x00\x00\x00" + "\x00" * 100)
555        expected = {
556            "si_signo": 17,  # SIGCHLD
557            "si_errno": 0,
558            "si_code": 1,  # CLD_EXITED
559            "_sifields._sigchld.si_pid": 784319,
560            "_sifields._sigchld.si_uid": 1000,
561            "_sifields._sigchld.si_status": 12,
562            "_sifields._sigchld.si_utime": 0,
563            "_sifields._sigchld.si_stime": 0,
564        }
565        self.do_siginfo_test("remote-linux", "basic_eh_frame.yaml",
566                             data, expected)
567
568    def test_siginfo_linux_i386(self):
569        data = (
570          # si_signo         si_errno        si_code
571            "\x11\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00"
572          # si_pid           si_uid          si_status
573            "\x49\x43\x07\x00\xe8\x03\x00\x00\x0c\x00\x00\x00"
574            + "\x00" * 104)
575        expected = {
576            "si_signo": 17,  # SIGCHLD
577            "si_errno": 0,
578            "si_code": 1,  # CLD_EXITED
579            "_sifields._sigchld.si_pid": 475977,
580            "_sifields._sigchld.si_uid": 1000,
581            "_sifields._sigchld.si_status": 12,
582            "_sifields._sigchld.si_utime": 0,
583            "_sifields._sigchld.si_stime": 0,
584        }
585        self.do_siginfo_test("remote-linux", "basic_eh_frame-i386.yaml",
586                             data, expected)
587
588    def test_siginfo_freebsd_amd64(self):
589        data = (
590          # si_signo         si_errno        si_code
591            "\x0b\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00"
592          # si_pid           si_uid          si_status
593            "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
594          # si_addr
595            "\x76\x98\xba\xdc\xfe\x00\x00\x00"
596          # si_status                        si_trapno
597            "\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00"
598            + "\x00" * 36)
599
600        expected = {
601            "si_signo": 11,  # SIGSEGV
602            "si_errno": 0,
603            "si_code": 1,  # SEGV_MAPERR
604            "si_addr": 0xfedcba9876,
605            "_reason._fault._trapno": 12,
606        }
607        self.do_siginfo_test("remote-freebsd", "basic_eh_frame.yaml",
608                             data, expected)
609