xref: /llvm-project/lldb/test/API/functionalities/gdb_remote_client/TestRecognizeBreakpoint.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
1import lldb
2from lldbsuite.test.lldbtest import *
3from lldbsuite.test.decorators import *
4from lldbsuite.test.gdbclientutils import *
5from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
6
7
8class TestRecognizeBreakpoint(GDBRemoteTestBase):
9
10    """This tests the case where the gdb-remote server doesn't support any
11    of the thread-info packets, and just tells which thread got the stop
12    signal with:
13          T05thread:01;
14    There was a bug in lldb that we would set the stop reason from this
15    packet too early - before we had updated the thread list.  So when we
16    later updated the thread list, we would throw away this info.  Normally
17    we would be able to reconstruct it from the thread info, but not if the
18    stub doesn't support it"""
19
20    @skipIfXmlSupportMissing
21    def test(self):
22        class MyResponder(MockGDBServerResponder):
23            def __init__(self):
24                MockGDBServerResponder.__init__(self)
25                self.thread_info_count = 0
26                self.after_cont = False
27                self.current_thread = 0
28
29            def cont(self):
30                # Simulate process stopping due to a breakpoint:
31                self.after_cont = True
32                return "T05thread:01;"
33
34            def vCont(self, packet):
35                self.after_cont = True
36                return "T05thread:01;"
37
38            def haltReason(self):
39                return "T02thread:01;"
40
41            def threadStopInfo(self, num):
42                return ""
43
44            def QThreadSuffixSupported(self):
45                return ""
46
47            def QListThreadsInStopReply(self):
48                return ""
49
50            def setBreakpoint(self, packet):
51                return "OK"
52
53            def qfThreadInfo(self):
54                return "m1"
55
56            def qsThreadInfo(self):
57                if (self.thread_info_count % 2) == 0:
58                    str = "m2"
59                else:
60                    str = "l"
61                self.thread_info_count += 1
62                return str
63
64            def readRegisters(self):
65                if self.after_cont and self.current_thread == 1:
66                    return "c01e990080ffffff"
67                else:
68                    return "badcfe10325476980"
69
70            def readRegister(self, regno):
71                return ""
72
73            def qXferRead(self, obj, annex, offset, length):
74                if annex == "target.xml":
75                    return (
76                        """<?xml version="1.0"?>
77                        <target version="1.0">
78                          <architecture>i386:x86-64</architecture>
79                          <feature name="org.gnu.gdb.i386.core">
80                            <reg name="rip" bitsize="64" regnum="0" type="code_ptr" group="general"/>
81                          </feature>
82                        </target>""",
83                        False,
84                    )
85                else:
86                    return None, False
87
88            def selectThread(self, op, thread):
89                if op != "g":
90                    return ""
91
92                self.current_thread = thread
93                return "OK"
94
95            def other(self, packet):
96                if packet == "vCont?":
97                    return "vCont;c;C;s;S"
98                return ""
99
100        python_os_plugin_path = os.path.join(
101            self.getSourceDir(), "operating_system_2.py"
102        )
103        command = "settings set target.process.python-os-plugin-path '{}'".format(
104            python_os_plugin_path
105        )
106        self.runCmd(command)
107
108        self.server.responder = MyResponder()
109        target = self.dbg.CreateTarget("")
110        process = self.connect(target)
111
112        bkpt = target.BreakpointCreateByAddress(0xFFFFFF8000991EC0)
113        self.assertEqual(bkpt.GetNumLocations(), 1, "Fake breakpoint was resolved.")
114
115        # Get the initial stop, and we should have two threads.
116        num_threads = len(process.threads)
117        self.assertEqual(num_threads, 2, "Got two threads")
118
119        thread_0 = process.threads[0]
120        self.assertEqual(thread_0.GetStopReason(), 1, "Thread_0 stopped for no reason")
121        self.assertEqual(thread_0.GetName(), "one", "Thread_0 is called one")
122
123        thread_1 = process.threads[1]
124        self.assertEqual(thread_1.GetStopReason(), 5, "Thread_0 stopped for SIGSTOP")
125        self.assertEqual(thread_1.GetName(), "two", "Thread_0 is called two")
126
127        # Now continue and we will fake hitting a breakpoint.
128        process.Continue()
129
130        self.assertState(process.GetState(), lldb.eStateStopped, "Process is stopped")
131        num_threads = len(process.threads)
132
133        num_threads = len(process.threads)
134        self.assertEqual(num_threads, 2, "Got two threads")
135
136        thread_0 = process.threads[0]
137        self.assertEqual(thread_0.GetStopReason(), 1, "Thread_0 stopped for no reason")
138        self.assertEqual(thread_0.GetName(), "one", "Thread_0 is called one")
139
140        thread_1 = process.threads[1]
141        self.assertEqual(thread_1.GetStopReason(), 3, "Thread_0 stopped for SIGTRAP")
142        self.assertEqual(thread_1.GetName(), "three", "Thread_0 is called three")
143
144        self.assertTrue(thread_1.IsValid(), "Thread_1 is valid")
145        self.assertStopReason(
146            thread_1.GetStopReason(),
147            lldb.eStopReasonBreakpoint,
148            "Stopped at breakpoint",
149        )
150