xref: /llvm-project/lldb/test/API/python_api/thread/TestThreadAPI.py (revision 193259cbcec77add8e189c4dedeefb15fef50d5e)
1"""
2Test SBThread APIs.
3"""
4
5import lldb
6from lldbsuite.test.decorators import *
7from lldbsuite.test.lldbtest import *
8from lldbsuite.test import lldbutil
9from lldbsuite.test.lldbutil import get_stopped_thread, get_caller_symbol
10
11
12class ThreadAPITestCase(TestBase):
13
14    def test_get_process(self):
15        """Test Python SBThread.GetProcess() API."""
16        self.build()
17        self.get_process()
18
19    def test_get_stop_description(self):
20        """Test Python SBThread.GetStopDescription() API."""
21        self.build()
22        self.get_stop_description()
23
24    def test_run_to_address(self):
25        """Test Python SBThread.RunToAddress() API."""
26        # We build a different executable than the default build() does.
27        d = {'CXX_SOURCES': 'main2.cpp', 'EXE': self.exe_name}
28        self.build(dictionary=d)
29        self.setTearDownCleanup(dictionary=d)
30        self.run_to_address(self.exe_name)
31
32    @skipIfAsan # The output looks different under ASAN.
33    @expectedFailureAll(oslist=["linux"], archs=['arm'], bugnumber="llvm.org/pr45892")
34    @expectedFailureAll(oslist=["windows"])
35    def test_step_out_of_malloc_into_function_b(self):
36        """Test Python SBThread.StepOut() API to step out of a malloc call where the call site is at function b()."""
37        # We build a different executable than the default build() does.
38        d = {'CXX_SOURCES': 'main2.cpp', 'EXE': self.exe_name}
39        self.build(dictionary=d)
40        self.setTearDownCleanup(dictionary=d)
41        self.step_out_of_malloc_into_function_b(self.exe_name)
42
43    def test_step_over_3_times(self):
44        """Test Python SBThread.StepOver() API."""
45        # We build a different executable than the default build() does.
46        d = {'CXX_SOURCES': 'main2.cpp', 'EXE': self.exe_name}
47        self.build(dictionary=d)
48        self.setTearDownCleanup(dictionary=d)
49        self.step_over_3_times(self.exe_name)
50
51    def setUp(self):
52        # Call super's setUp().
53        TestBase.setUp(self)
54        # Find the line number within main.cpp to break inside main().
55        self.break_line = line_number(
56            "main.cpp", "// Set break point at this line and check variable 'my_char'.")
57        # Find the line numbers within main2.cpp for
58        # step_out_of_malloc_into_function_b() and step_over_3_times().
59        self.step_out_of_malloc = line_number(
60            "main2.cpp", "// thread step-out of malloc into function b.")
61        self.after_3_step_overs = line_number(
62            "main2.cpp", "// we should reach here after 3 step-over's.")
63
64        # We'll use the test method name as the exe_name for executable
65        # compiled from main2.cpp.
66        self.exe_name = self.testMethodName
67
68    def get_process(self):
69        """Test Python SBThread.GetProcess() API."""
70        exe = self.getBuildArtifact("a.out")
71
72        target = self.dbg.CreateTarget(exe)
73        self.assertTrue(target, VALID_TARGET)
74
75        breakpoint = target.BreakpointCreateByLocation(
76            "main.cpp", self.break_line)
77        self.assertTrue(breakpoint, VALID_BREAKPOINT)
78        self.runCmd("breakpoint list")
79
80        # Launch the process, and do not stop at the entry point.
81        process = target.LaunchSimple(
82            None, None, self.get_process_working_directory())
83
84        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
85        self.assertTrue(
86            thread.IsValid(),
87            "There should be a thread stopped due to breakpoint")
88        self.runCmd("process status")
89
90        proc_of_thread = thread.GetProcess()
91        self.trace("proc_of_thread:", proc_of_thread)
92        self.assertEqual(proc_of_thread.GetProcessID(), process.GetProcessID())
93
94    def get_stop_description(self):
95        """Test Python SBThread.GetStopDescription() API."""
96        exe = self.getBuildArtifact("a.out")
97
98        target = self.dbg.CreateTarget(exe)
99        self.assertTrue(target, VALID_TARGET)
100
101        breakpoint = target.BreakpointCreateByLocation(
102            "main.cpp", self.break_line)
103        self.assertTrue(breakpoint, VALID_BREAKPOINT)
104        #self.runCmd("breakpoint list")
105
106        # Launch the process, and do not stop at the entry point.
107        process = target.LaunchSimple(
108            None, None, self.get_process_working_directory())
109
110        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
111        self.assertTrue(
112            thread.IsValid(),
113            "There should be a thread stopped due to breakpoint")
114
115        # Get the stop reason. GetStopDescription expects that we pass in the size of the description
116        # we expect plus an additional byte for the null terminator.
117
118        # Test with a buffer that is exactly as large as the expected stop reason.
119        self.assertEqual("breakpoint 1.1", thread.GetStopDescription(len('breakpoint 1.1') + 1))
120
121        # Test some smaller buffer sizes.
122        self.assertEqual("breakpoint", thread.GetStopDescription(len('breakpoint') + 1))
123        self.assertEqual("break", thread.GetStopDescription(len('break') + 1))
124        self.assertEqual("b", thread.GetStopDescription(len('b') + 1))
125
126        # Test that we can pass in a much larger size and still get the right output.
127        self.assertEqual("breakpoint 1.1", thread.GetStopDescription(len('breakpoint 1.1') + 100))
128
129    def step_out_of_malloc_into_function_b(self, exe_name):
130        """Test Python SBThread.StepOut() API to step out of a malloc call where the call site is at function b()."""
131        exe = self.getBuildArtifact(exe_name)
132
133        target = self.dbg.CreateTarget(exe)
134        self.assertTrue(target, VALID_TARGET)
135
136        breakpoint = target.BreakpointCreateByName('malloc')
137        self.assertTrue(breakpoint, VALID_BREAKPOINT)
138
139        # Launch the process, and do not stop at the entry point.
140        process = target.LaunchSimple(
141            None, None, self.get_process_working_directory())
142
143        while True:
144            thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
145            self.assertTrue(
146                thread.IsValid(),
147                "There should be a thread stopped due to breakpoint")
148            caller_symbol = get_caller_symbol(thread)
149            if not caller_symbol:
150                self.fail(
151                    "Test failed: could not locate the caller symbol of malloc")
152
153            # Our top frame may be an inlined function in malloc() (e.g., on
154            # FreeBSD).  Apply a simple heuristic of stepping out until we find
155            # a non-malloc caller
156            while caller_symbol.startswith("malloc"):
157                thread.StepOut()
158                self.assertTrue(thread.IsValid(),
159                                "Thread valid after stepping to outer malloc")
160                caller_symbol = get_caller_symbol(thread)
161
162            if caller_symbol == "b(int)":
163                break
164            process.Continue()
165
166        # On Linux malloc calls itself in some case. Remove the breakpoint because we don't want
167        # to hit it during step-out.
168        target.BreakpointDelete(breakpoint.GetID())
169
170        thread.StepOut()
171        self.runCmd("thread backtrace")
172        self.assertEqual(
173            thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), self.step_out_of_malloc,
174            "step out of malloc into function b is successful")
175
176    def step_over_3_times(self, exe_name):
177        """Test Python SBThread.StepOver() API."""
178        exe = self.getBuildArtifact(exe_name)
179
180        target = self.dbg.CreateTarget(exe)
181        self.assertTrue(target, VALID_TARGET)
182
183        breakpoint = target.BreakpointCreateByLocation(
184            'main2.cpp', self.step_out_of_malloc)
185        self.assertTrue(breakpoint, VALID_BREAKPOINT)
186        self.runCmd("breakpoint list")
187
188        # Launch the process, and do not stop at the entry point.
189        process = target.LaunchSimple(
190            None, None, self.get_process_working_directory())
191
192        self.assertTrue(process, PROCESS_IS_VALID)
193
194        # Frame #0 should be on self.step_out_of_malloc.
195        self.assertState(process.GetState(), lldb.eStateStopped)
196        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
197        self.assertTrue(
198            thread.IsValid(),
199            "There should be a thread stopped due to breakpoint condition")
200        self.runCmd("thread backtrace")
201        frame0 = thread.GetFrameAtIndex(0)
202        lineEntry = frame0.GetLineEntry()
203        self.assertEqual(lineEntry.GetLine(), self.step_out_of_malloc)
204
205        thread.StepOver()
206        thread.StepOver()
207        thread.StepOver()
208        self.runCmd("thread backtrace")
209
210        # Verify that we are stopped at the correct source line number in
211        # main2.cpp.
212        frame0 = thread.GetFrameAtIndex(0)
213        lineEntry = frame0.GetLineEntry()
214        self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete)
215        # Expected failure with clang as the compiler.
216        # rdar://problem/9223880
217        #
218        # Which has been fixed on the lldb by compensating for inaccurate line
219        # table information with r140416.
220        self.assertEqual(lineEntry.GetLine(), self.after_3_step_overs)
221
222    def run_to_address(self, exe_name):
223        """Test Python SBThread.RunToAddress() API."""
224        exe = self.getBuildArtifact(exe_name)
225
226        target = self.dbg.CreateTarget(exe)
227        self.assertTrue(target, VALID_TARGET)
228
229        breakpoint = target.BreakpointCreateByLocation(
230            'main2.cpp', self.step_out_of_malloc)
231        self.assertTrue(breakpoint, VALID_BREAKPOINT)
232        self.runCmd("breakpoint list")
233
234        # Launch the process, and do not stop at the entry point.
235        process = target.LaunchSimple(
236            None, None, self.get_process_working_directory())
237
238        self.assertTrue(process, PROCESS_IS_VALID)
239
240        # Frame #0 should be on self.step_out_of_malloc.
241        self.assertState(process.GetState(), lldb.eStateStopped)
242        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
243        self.assertTrue(
244            thread.IsValid(),
245            "There should be a thread stopped due to breakpoint condition")
246        self.runCmd("thread backtrace")
247        frame0 = thread.GetFrameAtIndex(0)
248        lineEntry = frame0.GetLineEntry()
249        self.assertEqual(lineEntry.GetLine(), self.step_out_of_malloc)
250
251        # Get the start/end addresses for this line entry.
252        start_addr = lineEntry.GetStartAddress().GetLoadAddress(target)
253        end_addr = lineEntry.GetEndAddress().GetLoadAddress(target)
254        if self.TraceOn():
255            print("start addr:", hex(start_addr))
256            print("end addr:", hex(end_addr))
257
258        # Disable the breakpoint.
259        self.assertTrue(target.DisableAllBreakpoints())
260        self.runCmd("breakpoint list")
261
262        thread.StepOver()
263        thread.StepOver()
264        thread.StepOver()
265        self.runCmd("thread backtrace")
266
267        # Now ask SBThread to run to the address 'start_addr' we got earlier, which
268        # corresponds to self.step_out_of_malloc line entry's start address.
269        thread.RunToAddress(start_addr)
270        self.runCmd("process status")
271        #self.runCmd("thread backtrace")
272