xref: /llvm-project/lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py (revision 3cc37622921f39e4bdad7a37b7199defa58a213a)
1import lldb
2import binascii
3from lldbsuite.test.lldbtest import *
4from lldbsuite.test.decorators import *
5from gdbclientutils import *
6
7
8class TestGDBRemoteClient(GDBRemoteTestBase):
9
10    class gPacketResponder(MockGDBServerResponder):
11        def readRegisters(self):
12            return '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
13
14    @skipIfReproducer # Packet log is not populated during replay.
15    def test_connect(self):
16        """Test connecting to a remote gdb server"""
17        target = self.createTarget("a.yaml")
18        process = self.connect(target)
19        self.assertPacketLogContains(["qProcessInfo", "qfThreadInfo"])
20
21    @skipIfReproducer # FIXME: Unexpected packet during (active) replay
22    def test_attach_fail(self):
23        error_msg = "mock-error-msg"
24
25        class MyResponder(MockGDBServerResponder):
26            # Pretend we don't have any process during the initial queries.
27            def qC(self):
28                return "E42"
29
30            def qfThreadInfo(self):
31                return "OK" # No threads.
32
33            # Then, when we are asked to attach, error out.
34            def vAttach(self, pid):
35                return "E42;" + binascii.hexlify(error_msg.encode()).decode()
36
37        self.server.responder = MyResponder()
38
39        target = self.dbg.CreateTarget("")
40        process = self.connect(target)
41        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateConnected])
42
43        error = lldb.SBError()
44        target.AttachToProcessWithID(lldb.SBListener(), 47, error)
45        self.assertEquals(error_msg, error.GetCString())
46
47    def test_launch_fail(self):
48        class MyResponder(MockGDBServerResponder):
49            # Pretend we don't have any process during the initial queries.
50            def qC(self):
51                return "E42"
52
53            def qfThreadInfo(self):
54                return "OK" # No threads.
55
56            # Then, when we are asked to attach, error out.
57            def A(self, packet):
58                return "E47"
59
60        self.runCmd("log enable gdb-remote packets")
61        self.server.responder = MyResponder()
62
63        target = self.createTarget("a.yaml")
64        process = self.connect(target)
65        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateConnected])
66
67        error = lldb.SBError()
68        target.Launch(lldb.SBListener(), None, None, None, None, None,
69                None, 0, True, error)
70        self.assertEquals("'A' packet returned an error: 71", error.GetCString())
71
72    @skipIfReproducer # Packet log is not populated during replay.
73    def test_read_registers_using_g_packets(self):
74        """Test reading registers using 'g' packets (default behavior)"""
75        self.dbg.HandleCommand(
76                "settings set plugin.process.gdb-remote.use-g-packet-for-reading true")
77        self.addTearDownHook(lambda:
78                self.runCmd("settings set plugin.process.gdb-remote.use-g-packet-for-reading false"))
79        self.server.responder = self.gPacketResponder()
80        target = self.createTarget("a.yaml")
81        process = self.connect(target)
82
83        self.assertEquals(1, self.server.responder.packetLog.count("g"))
84        self.server.responder.packetLog = []
85        self.read_registers(process)
86        # Reading registers should not cause any 'p' packets to be exchanged.
87        self.assertEquals(
88                0, len([p for p in self.server.responder.packetLog if p.startswith("p")]))
89
90    @skipIfReproducer # Packet log is not populated during replay.
91    def test_read_registers_using_p_packets(self):
92        """Test reading registers using 'p' packets"""
93        self.dbg.HandleCommand(
94                "settings set plugin.process.gdb-remote.use-g-packet-for-reading false")
95        target = self.createTarget("a.yaml")
96        process = self.connect(target)
97
98        self.read_registers(process)
99        self.assertNotIn("g", self.server.responder.packetLog)
100        self.assertGreater(
101                len([p for p in self.server.responder.packetLog if p.startswith("p")]), 0)
102
103    @skipIfReproducer # Packet log is not populated during replay.
104    def test_write_registers_using_P_packets(self):
105        """Test writing registers using 'P' packets (default behavior)"""
106        self.server.responder = self.gPacketResponder()
107        target = self.createTarget("a.yaml")
108        process = self.connect(target)
109
110        self.write_registers(process)
111        self.assertEquals(0, len(
112                [p for p in self.server.responder.packetLog if p.startswith("G")]))
113        self.assertGreater(
114                len([p for p in self.server.responder.packetLog if p.startswith("P")]), 0)
115
116    @skipIfReproducer # Packet log is not populated during replay.
117    def test_write_registers_using_G_packets(self):
118        """Test writing registers using 'G' packets"""
119
120        class MyResponder(self.gPacketResponder):
121            def readRegister(self, register):
122                # empty string means unsupported
123                return ""
124
125        self.server.responder = MyResponder()
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("P")]))
132        self.assertGreater(len(
133                [p for p in self.server.responder.packetLog if p.startswith("G")]), 0)
134
135    def read_registers(self, process):
136        self.for_each_gpr(
137                process, lambda r: self.assertEquals("0x00000000", r.GetValue()))
138
139    def write_registers(self, process):
140        self.for_each_gpr(
141                process, lambda r: r.SetValueFromCString("0x00000000"))
142
143    def for_each_gpr(self, process, operation):
144        registers = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters()
145        self.assertGreater(registers.GetSize(), 0)
146        regSet = registers[0]
147        numChildren = regSet.GetNumChildren()
148        self.assertGreater(numChildren, 0)
149        for i in range(numChildren):
150            operation(regSet.GetChildAtIndex(i))
151