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