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