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