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