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 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 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 ) 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 executable 77 # compiled from main2.cpp. 78 self.exe_name = self.testMethodName 79 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 description 124 # 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 def step_out_of_malloc_into_function_b(self, exe_name): 142 """Test Python SBThread.StepOut() API to step out of a malloc call where the call site is at function b().""" 143 exe = self.getBuildArtifact(exe_name) 144 145 target = self.dbg.CreateTarget(exe) 146 self.assertTrue(target, VALID_TARGET) 147 148 breakpoint = target.BreakpointCreateByName("malloc") 149 self.assertTrue(breakpoint, VALID_BREAKPOINT) 150 151 # Launch the process, and do not stop at the entry point. 152 process = target.LaunchSimple(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(), "There should be a thread stopped due to breakpoint" 158 ) 159 caller_symbol = get_caller_symbol(thread) 160 if not caller_symbol: 161 self.fail("Test failed: could not locate the caller symbol of malloc") 162 163 # Our top frame may be an inlined function in malloc() (e.g., on 164 # FreeBSD). Apply a simple heuristic of stepping out until we find 165 # a non-malloc caller 166 while caller_symbol.startswith("malloc"): 167 thread.StepOut() 168 self.assertTrue( 169 thread.IsValid(), "Thread valid after stepping to outer malloc" 170 ) 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.assertEqual( 184 thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), 185 self.step_out_of_malloc, 186 "step out of malloc into function b is successful", 187 ) 188 189 def step_over_3_times(self, exe_name): 190 """Test Python SBThread.StepOver() API.""" 191 exe = self.getBuildArtifact(exe_name) 192 193 target = self.dbg.CreateTarget(exe) 194 self.assertTrue(target, VALID_TARGET) 195 196 breakpoint = target.BreakpointCreateByLocation( 197 "main2.cpp", self.step_out_of_malloc 198 ) 199 self.assertTrue(breakpoint, VALID_BREAKPOINT) 200 self.runCmd("breakpoint list") 201 202 # Launch the process, and do not stop at the entry point. 203 process = target.LaunchSimple(None, None, self.get_process_working_directory()) 204 205 self.assertTrue(process, PROCESS_IS_VALID) 206 207 # Frame #0 should be on self.step_out_of_malloc. 208 self.assertState(process.GetState(), lldb.eStateStopped) 209 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) 210 self.assertTrue( 211 thread.IsValid(), 212 "There should be a thread stopped due to breakpoint condition", 213 ) 214 self.runCmd("thread backtrace") 215 frame0 = thread.GetFrameAtIndex(0) 216 lineEntry = frame0.GetLineEntry() 217 self.assertEqual(lineEntry.GetLine(), self.step_out_of_malloc) 218 219 thread.StepOver() 220 thread.StepOver() 221 thread.StepOver() 222 self.runCmd("thread backtrace") 223 224 # Verify that we are stopped at the correct source line number in 225 # main2.cpp. 226 frame0 = thread.GetFrameAtIndex(0) 227 lineEntry = frame0.GetLineEntry() 228 self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete) 229 # Expected failure with clang as the compiler. 230 # rdar://problem/9223880 231 # 232 # Which has been fixed on the lldb by compensating for inaccurate line 233 # table information with r140416. 234 self.assertEqual(lineEntry.GetLine(), self.after_3_step_overs) 235 236 def run_to_address(self, exe_name): 237 """Test Python SBThread.RunToAddress() API.""" 238 exe = self.getBuildArtifact(exe_name) 239 240 target = self.dbg.CreateTarget(exe) 241 self.assertTrue(target, VALID_TARGET) 242 243 breakpoint = target.BreakpointCreateByLocation( 244 "main2.cpp", self.step_out_of_malloc 245 ) 246 self.assertTrue(breakpoint, VALID_BREAKPOINT) 247 self.runCmd("breakpoint list") 248 249 # Launch the process, and do not stop at the entry point. 250 process = target.LaunchSimple(None, None, self.get_process_working_directory()) 251 252 self.assertTrue(process, PROCESS_IS_VALID) 253 254 # Frame #0 should be on self.step_out_of_malloc. 255 self.assertState(process.GetState(), lldb.eStateStopped) 256 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) 257 self.assertTrue( 258 thread.IsValid(), 259 "There should be a thread stopped due to breakpoint condition", 260 ) 261 self.runCmd("thread backtrace") 262 frame0 = thread.GetFrameAtIndex(0) 263 lineEntry = frame0.GetLineEntry() 264 self.assertEqual(lineEntry.GetLine(), self.step_out_of_malloc) 265 266 # Get the start/end addresses for this line entry. 267 start_addr = lineEntry.GetStartAddress().GetLoadAddress(target) 268 end_addr = lineEntry.GetEndAddress().GetLoadAddress(target) 269 if self.TraceOn(): 270 print("start addr:", hex(start_addr)) 271 print("end addr:", hex(end_addr)) 272 273 # Disable the breakpoint. 274 self.assertTrue(target.DisableAllBreakpoints()) 275 self.runCmd("breakpoint list") 276 277 thread.StepOver() 278 thread.StepOver() 279 thread.StepOver() 280 self.runCmd("thread backtrace") 281 282 # Now ask SBThread to run to the address 'start_addr' we got earlier, which 283 # corresponds to self.step_out_of_malloc line entry's start address. 284 thread.RunToAddress(start_addr) 285 self.runCmd("process status") 286 # self.runCmd("thread backtrace") 287 288 def validate_negative_indexing(self): 289 exe = self.getBuildArtifact("a.out") 290 291 target = self.dbg.CreateTarget(exe) 292 self.assertTrue(target, VALID_TARGET) 293 294 breakpoint = target.BreakpointCreateByLocation("main.cpp", self.break_line) 295 self.assertTrue(breakpoint, VALID_BREAKPOINT) 296 self.runCmd("breakpoint list") 297 298 # Launch the process, and do not stop at the entry point. 299 process = target.LaunchSimple(None, None, self.get_process_working_directory()) 300 301 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) 302 self.assertTrue( 303 thread.IsValid(), "There should be a thread stopped due to breakpoint" 304 ) 305 self.runCmd("process status") 306 307 pos_range = range(thread.num_frames) 308 neg_range = range(thread.num_frames, 0, -1) 309 for pos, neg in zip(pos_range, neg_range): 310 self.assertEqual(thread.frame[pos].idx, thread.frame[-neg].idx) 311 312 def step_instruction_in_called_function(self): 313 main_file_spec = lldb.SBFileSpec("main.cpp") 314 target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( 315 self, "Set break point at this line", main_file_spec 316 ) 317 options = lldb.SBExpressionOptions() 318 options.SetIgnoreBreakpoints(False) 319 320 call_me_bkpt = target.BreakpointCreateBySourceRegex( 321 "Set a breakpoint in call_me", main_file_spec 322 ) 323 self.assertGreater( 324 call_me_bkpt.GetNumLocations(), 0, "Got at least one location in call_me" 325 ) 326 327 # On Windows this may be the full name "void __cdecl call_me(bool)", 328 # elsewhere it's just "call_me(bool)". 329 expected_name = r".*call_me\(bool\)$" 330 331 # Now run the expression, this will fail because we stopped at a breakpoint: 332 self.runCmd("expr -i 0 -- call_me(true)", check=False) 333 # Now we should be stopped in call_me: 334 self.assertRegex( 335 thread.frames[0].name, expected_name, "Stopped in call_me(bool)" 336 ) 337 338 # Now do a various API steps. These should not cause the expression context to get unshipped: 339 thread.StepInstruction(False) 340 self.assertRegex( 341 thread.frames[0].name, 342 expected_name, 343 "Still in call_me(bool) after StepInstruction", 344 ) 345 thread.StepInstruction(True) 346 self.assertRegex( 347 thread.frames[0].name, 348 expected_name, 349 "Still in call_me(bool) after NextInstruction", 350 ) 351 thread.StepInto() 352 self.assertRegex( 353 thread.frames[0].name, 354 expected_name, 355 "Still in call_me(bool) after StepInto", 356 ) 357 thread.StepOver(False) 358 self.assertRegex( 359 thread.frames[0].name, 360 expected_name, 361 "Still in call_me(bool) after StepOver", 362 ) 363