368 lines · python
1"""2Test SBThread APIs.3"""4 5import lldb6from lldbsuite.test.decorators import *7from lldbsuite.test.lldbtest import *8from lldbsuite.test import lldbutil9from lldbsuite.test.lldbutil import get_stopped_thread, get_caller_symbol10 11 12class ThreadAPITestCase(TestBase):13 def test_get_process(self):14 """Test Python SBThread.GetProcess() API."""15 self.build()16 self.get_process()17 18 def test_get_stop_description(self):19 """Test Python SBThread.GetStopDescription() API."""20 self.build()21 self.get_stop_description()22 23 def test_run_to_address(self):24 """Test Python SBThread.RunToAddress() API."""25 # We build a different executable than the default build() does.26 d = {"CXX_SOURCES": "main2.cpp", "EXE": self.exe_name}27 self.build(dictionary=d)28 self.setTearDownCleanup(dictionary=d)29 self.run_to_address(self.exe_name)30 31 @skipIfAsan # The output looks different under ASAN.32 @expectedFailureAll(oslist=["linux"], archs=["arm$"], bugnumber="llvm.org/pr45892")33 @expectedFailureAll(oslist=["windows"])34 def test_step_out_of_malloc_into_function_b(self):35 """Test Python SBThread.StepOut() API to step out of a malloc call where the call site is at function b()."""36 # We build a different executable than the default build() does.37 d = {"CXX_SOURCES": "main2.cpp", "EXE": self.exe_name}38 self.build(dictionary=d)39 self.setTearDownCleanup(dictionary=d)40 self.step_out_of_malloc_into_function_b(self.exe_name)41 42 def test_step_over_3_times(self):43 """Test Python SBThread.StepOver() API."""44 # We build a different executable than the default build() does.45 d = {"CXX_SOURCES": "main2.cpp", "EXE": self.exe_name}46 self.build(dictionary=d)47 self.setTearDownCleanup(dictionary=d)48 self.step_over_3_times(self.exe_name)49 50 def test_negative_indexing(self):51 """Test SBThread.frame with negative indexes."""52 self.build()53 self.validate_negative_indexing()54 55 def test_StepInstruction(self):56 """Test that StepInstruction preserves the plan stack."""57 self.build()58 self.step_instruction_in_called_function()59 60 def setUp(self):61 # Call super's setUp().62 TestBase.setUp(self)63 # Find the line number within main.cpp to break inside main().64 self.break_line = line_number(65 "main.cpp", "// Set break point at this line and check variable 'my_char'."66 )67 # Find the line numbers within main2.cpp for68 # 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 )72 self.after_3_step_overs = line_number(73 "main2.cpp", "// we should reach here after 3 step-over's."74 )75 76 # We'll use the test method name as the exe_name for executable77 # compiled from main2.cpp.78 self.exe_name = self.testMethodName79 80 def get_process(self):81 """Test Python SBThread.GetProcess() API."""82 exe = self.getBuildArtifact("a.out")83 84 target = self.dbg.CreateTarget(exe)85 self.assertTrue(target, VALID_TARGET)86 87 breakpoint = target.BreakpointCreateByLocation("main.cpp", self.break_line)88 self.assertTrue(breakpoint, VALID_BREAKPOINT)89 self.runCmd("breakpoint list")90 91 # Launch the process, and do not stop at the entry point.92 process = target.LaunchSimple(None, None, self.get_process_working_directory())93 94 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)95 self.assertTrue(96 thread.IsValid(), "There should be a thread stopped due to breakpoint"97 )98 self.runCmd("process status")99 100 proc_of_thread = thread.GetProcess()101 self.trace("proc_of_thread:", proc_of_thread)102 self.assertEqual(proc_of_thread.GetProcessID(), process.GetProcessID())103 104 def get_stop_description(self):105 """Test Python SBThread.GetStopDescription() API."""106 exe = self.getBuildArtifact("a.out")107 108 target = self.dbg.CreateTarget(exe)109 self.assertTrue(target, VALID_TARGET)110 111 breakpoint = target.BreakpointCreateByLocation("main.cpp", self.break_line)112 self.assertTrue(breakpoint, VALID_BREAKPOINT)113 # self.runCmd("breakpoint list")114 115 # Launch the process, and do not stop at the entry point.116 process = target.LaunchSimple(None, None, self.get_process_working_directory())117 118 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)119 self.assertTrue(120 thread.IsValid(), "There should be a thread stopped due to breakpoint"121 )122 123 # Get the stop reason. GetStopDescription expects that we pass in the size of the description124 # we expect plus an additional byte for the null terminator.125 126 # Test with a buffer that is exactly as large as the expected stop reason.127 self.assertEqual(128 "breakpoint 1.1", thread.GetStopDescription(len("breakpoint 1.1") + 1)129 )130 131 # Test some smaller buffer sizes.132 self.assertEqual("breakpoint", thread.GetStopDescription(len("breakpoint") + 1))133 self.assertEqual("break", thread.GetStopDescription(len("break") + 1))134 self.assertEqual("b", thread.GetStopDescription(len("b") + 1))135 136 # Test that we can pass in a much larger size and still get the right output.137 self.assertEqual(138 "breakpoint 1.1", thread.GetStopDescription(len("breakpoint 1.1") + 100)139 )140 141 # Test the stream variation142 stream = lldb.SBStream()143 self.assertTrue(thread.GetStopDescription(stream))144 self.assertEqual("breakpoint 1.1", stream.GetData())145 146 def step_out_of_malloc_into_function_b(self, exe_name):147 """Test Python SBThread.StepOut() API to step out of a malloc call where the call site is at function b()."""148 exe = self.getBuildArtifact(exe_name)149 150 target = self.dbg.CreateTarget(exe)151 self.assertTrue(target, VALID_TARGET)152 153 breakpoint = target.BreakpointCreateByName("malloc")154 self.assertTrue(breakpoint, VALID_BREAKPOINT)155 156 # Launch the process, and do not stop at the entry point.157 process = target.LaunchSimple(None, None, self.get_process_working_directory())158 159 while True:160 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)161 self.assertTrue(162 thread.IsValid(), "There should be a thread stopped due to breakpoint"163 )164 caller_symbol = get_caller_symbol(thread)165 if not caller_symbol:166 self.fail("Test failed: could not locate the caller symbol of malloc")167 168 # Our top frame may be an inlined function in malloc() (e.g., on169 # FreeBSD). Apply a simple heuristic of stepping out until we find170 # a non-malloc caller171 while caller_symbol.startswith("malloc"):172 thread.StepOut()173 self.assertTrue(174 thread.IsValid(), "Thread valid after stepping to outer malloc"175 )176 caller_symbol = get_caller_symbol(thread)177 178 if caller_symbol == "b(int)":179 break180 process.Continue()181 182 # On Linux malloc calls itself in some case. Remove the breakpoint because we don't want183 # to hit it during step-out.184 target.BreakpointDelete(breakpoint.GetID())185 186 thread.StepOut()187 self.runCmd("thread backtrace")188 self.assertEqual(189 thread.GetFrameAtIndex(0).GetLineEntry().GetLine(),190 self.step_out_of_malloc,191 "step out of malloc into function b is successful",192 )193 194 def step_over_3_times(self, exe_name):195 """Test Python SBThread.StepOver() API."""196 exe = self.getBuildArtifact(exe_name)197 198 target = self.dbg.CreateTarget(exe)199 self.assertTrue(target, VALID_TARGET)200 201 breakpoint = target.BreakpointCreateByLocation(202 "main2.cpp", self.step_out_of_malloc203 )204 self.assertTrue(breakpoint, VALID_BREAKPOINT)205 self.runCmd("breakpoint list")206 207 # Launch the process, and do not stop at the entry point.208 process = target.LaunchSimple(None, None, self.get_process_working_directory())209 210 self.assertTrue(process, PROCESS_IS_VALID)211 212 # Frame #0 should be on self.step_out_of_malloc.213 self.assertState(process.GetState(), lldb.eStateStopped)214 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)215 self.assertTrue(216 thread.IsValid(),217 "There should be a thread stopped due to breakpoint condition",218 )219 self.runCmd("thread backtrace")220 frame0 = thread.GetFrameAtIndex(0)221 lineEntry = frame0.GetLineEntry()222 self.assertEqual(lineEntry.GetLine(), self.step_out_of_malloc)223 224 thread.StepOver()225 thread.StepOver()226 thread.StepOver()227 self.runCmd("thread backtrace")228 229 # Verify that we are stopped at the correct source line number in230 # main2.cpp.231 frame0 = thread.GetFrameAtIndex(0)232 lineEntry = frame0.GetLineEntry()233 self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete)234 # Expected failure with clang as the compiler.235 # rdar://problem/9223880236 #237 # Which has been fixed on the lldb by compensating for inaccurate line238 # table information with r140416.239 self.assertEqual(lineEntry.GetLine(), self.after_3_step_overs)240 241 def run_to_address(self, exe_name):242 """Test Python SBThread.RunToAddress() API."""243 exe = self.getBuildArtifact(exe_name)244 245 target = self.dbg.CreateTarget(exe)246 self.assertTrue(target, VALID_TARGET)247 248 breakpoint = target.BreakpointCreateByLocation(249 "main2.cpp", self.step_out_of_malloc250 )251 self.assertTrue(breakpoint, VALID_BREAKPOINT)252 self.runCmd("breakpoint list")253 254 # Launch the process, and do not stop at the entry point.255 process = target.LaunchSimple(None, None, self.get_process_working_directory())256 257 self.assertTrue(process, PROCESS_IS_VALID)258 259 # Frame #0 should be on self.step_out_of_malloc.260 self.assertState(process.GetState(), lldb.eStateStopped)261 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)262 self.assertTrue(263 thread.IsValid(),264 "There should be a thread stopped due to breakpoint condition",265 )266 self.runCmd("thread backtrace")267 frame0 = thread.GetFrameAtIndex(0)268 lineEntry = frame0.GetLineEntry()269 self.assertEqual(lineEntry.GetLine(), self.step_out_of_malloc)270 271 # Get the start/end addresses for this line entry.272 start_addr = lineEntry.GetStartAddress().GetLoadAddress(target)273 end_addr = lineEntry.GetEndAddress().GetLoadAddress(target)274 if self.TraceOn():275 print("start addr:", hex(start_addr))276 print("end addr:", hex(end_addr))277 278 # Disable the breakpoint.279 self.assertTrue(target.DisableAllBreakpoints())280 self.runCmd("breakpoint list")281 282 thread.StepOver()283 thread.StepOver()284 thread.StepOver()285 self.runCmd("thread backtrace")286 287 # Now ask SBThread to run to the address 'start_addr' we got earlier, which288 # corresponds to self.step_out_of_malloc line entry's start address.289 thread.RunToAddress(start_addr)290 self.runCmd("process status")291 # self.runCmd("thread backtrace")292 293 def validate_negative_indexing(self):294 exe = self.getBuildArtifact("a.out")295 296 target = self.dbg.CreateTarget(exe)297 self.assertTrue(target, VALID_TARGET)298 299 breakpoint = target.BreakpointCreateByLocation("main.cpp", self.break_line)300 self.assertTrue(breakpoint, VALID_BREAKPOINT)301 self.runCmd("breakpoint list")302 303 # Launch the process, and do not stop at the entry point.304 process = target.LaunchSimple(None, None, self.get_process_working_directory())305 306 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)307 self.assertTrue(308 thread.IsValid(), "There should be a thread stopped due to breakpoint"309 )310 self.runCmd("process status")311 312 pos_range = range(thread.num_frames)313 neg_range = range(thread.num_frames, 0, -1)314 for pos, neg in zip(pos_range, neg_range):315 self.assertEqual(thread.frame[pos].idx, thread.frame[-neg].idx)316 317 def step_instruction_in_called_function(self):318 main_file_spec = lldb.SBFileSpec("main.cpp")319 target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(320 self, "Set break point at this line", main_file_spec321 )322 options = lldb.SBExpressionOptions()323 options.SetIgnoreBreakpoints(False)324 325 call_me_bkpt = target.BreakpointCreateBySourceRegex(326 "Set a breakpoint in call_me", main_file_spec327 )328 self.assertGreater(329 call_me_bkpt.GetNumLocations(), 0, "Got at least one location in call_me"330 )331 332 # On Windows this may be the full name "void __cdecl call_me(bool)",333 # elsewhere it's just "call_me(bool)".334 expected_name = r".*call_me\(bool\)$"335 336 # Now run the expression, this will fail because we stopped at a breakpoint:337 self.runCmd("expr -i 0 -- call_me(true)", check=False)338 # Now we should be stopped in call_me:339 self.assertRegex(340 thread.frames[0].name, expected_name, "Stopped in call_me(bool)"341 )342 343 # Now do a various API steps. These should not cause the expression context to get unshipped:344 thread.StepInstruction(False)345 self.assertRegex(346 thread.frames[0].name,347 expected_name,348 "Still in call_me(bool) after StepInstruction",349 )350 thread.StepInstruction(True)351 self.assertRegex(352 thread.frames[0].name,353 expected_name,354 "Still in call_me(bool) after NextInstruction",355 )356 thread.StepInto()357 self.assertRegex(358 thread.frames[0].name,359 expected_name,360 "Still in call_me(bool) after StepInto",361 )362 thread.StepOver(False)363 self.assertRegex(364 thread.frames[0].name,365 expected_name,366 "Still in call_me(bool) after StepOver",367 )368