| """ | |
| Test that single thread step over deadlock issue can be resolved | |
| after timeout. | |
| """ | |
| import lldb | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test import lldbutil | |
| class SingleThreadStepTimeoutTestCase(TestBase): | |
| NO_DEBUG_INFO_TESTCASE = True | |
| def setUp(self): | |
| TestBase.setUp(self) | |
| self.main_source = "main.cpp" | |
| self.build() | |
| def verify_hit_correct_line(self, pattern): | |
| target_line = line_number(self.main_source, pattern) | |
| self.assertNotEqual(target_line, 0, "Could not find source pattern " + pattern) | |
| cur_line = self.thread.frames[0].GetLineEntry().GetLine() | |
| self.assertEqual( | |
| cur_line, | |
| target_line, | |
| "Stepped to line %d instead of expected %d with pattern '%s'." | |
| % (cur_line, target_line, pattern), | |
| ) | |
| def step_over_deadlock_helper(self): | |
| (target, _, self.thread, _) = lldbutil.run_to_source_breakpoint( | |
| self, "// Set breakpoint1 here", lldb.SBFileSpec(self.main_source) | |
| ) | |
| signal_main_thread_value = target.FindFirstGlobalVariable("signal_main_thread") | |
| self.assertTrue(signal_main_thread_value.IsValid()) | |
| # Change signal_main_thread global variable to 1 so that worker thread loop can | |
| # terminate and move forward to signal main thread | |
| signal_main_thread_value.SetValueFromCString("1") | |
| self.thread.StepOver(lldb.eOnlyThisThread) | |
| self.verify_hit_correct_line("// Finish step-over from breakpoint1") | |
| @skipIfWindows | |
| def test_step_over_deadlock_small_timeout_fast_stepping(self): | |
| """Test single thread step over deadlock on other threads can be resolved after timeout with small timeout and fast stepping.""" | |
| self.dbg.HandleCommand( | |
| "settings set target.process.thread.single-thread-plan-timeout 10" | |
| ) | |
| self.dbg.HandleCommand("settings set target.use-fast-stepping true") | |
| self.step_over_deadlock_helper() | |
| @skipIfWindows | |
| def test_step_over_deadlock_small_timeout_slow_stepping(self): | |
| """Test single thread step over deadlock on other threads can be resolved after timeout with small timeout and slow stepping.""" | |
| self.dbg.HandleCommand( | |
| "settings set target.process.thread.single-thread-plan-timeout 10" | |
| ) | |
| self.dbg.HandleCommand("settings set target.use-fast-stepping false") | |
| self.step_over_deadlock_helper() | |
| @skipIfWindows | |
| def test_step_over_deadlock_large_timeout_fast_stepping(self): | |
| """Test single thread step over deadlock on other threads can be resolved after timeout with large timeout and fast stepping.""" | |
| self.dbg.HandleCommand( | |
| "settings set target.process.thread.single-thread-plan-timeout 2000" | |
| ) | |
| self.dbg.HandleCommand("settings set target.use-fast-stepping true") | |
| self.step_over_deadlock_helper() | |
| @skipIfWindows | |
| def test_step_over_deadlock_large_timeout_slow_stepping(self): | |
| """Test single thread step over deadlock on other threads can be resolved after timeout with large timeout and slow stepping.""" | |
| self.dbg.HandleCommand( | |
| "settings set target.process.thread.single-thread-plan-timeout 2000" | |
| ) | |
| self.dbg.HandleCommand("settings set target.use-fast-stepping false") | |
| self.step_over_deadlock_helper() | |
| def step_over_multi_calls_helper(self): | |
| (target, _, self.thread, _) = lldbutil.run_to_source_breakpoint( | |
| self, "// Set breakpoint2 here", lldb.SBFileSpec(self.main_source) | |
| ) | |
| self.thread.StepOver(lldb.eOnlyThisThread) | |
| self.verify_hit_correct_line("// Finish step-over from breakpoint2") | |
| @skipIfWindows | |
| def test_step_over_multi_calls_small_timeout_fast_stepping(self): | |
| """Test step over source line with multiple call instructions works fine with small timeout and fast stepping.""" | |
| self.dbg.HandleCommand( | |
| "settings set target.process.thread.single-thread-plan-timeout 10" | |
| ) | |
| self.dbg.HandleCommand("settings set target.use-fast-stepping true") | |
| self.step_over_multi_calls_helper() | |
| @skipIfWindows | |
| def test_step_over_multi_calls_small_timeout_slow_stepping(self): | |
| """Test step over source line with multiple call instructions works fine with small timeout and slow stepping.""" | |
| self.dbg.HandleCommand( | |
| "settings set target.process.thread.single-thread-plan-timeout 10" | |
| ) | |
| self.dbg.HandleCommand("settings set target.use-fast-stepping false") | |
| self.step_over_multi_calls_helper() | |
| @skipIfWindows | |
| def test_step_over_multi_calls_large_timeout_fast_stepping(self): | |
| """Test step over source line with multiple call instructions works fine with large timeout and fast stepping.""" | |
| self.dbg.HandleCommand( | |
| "settings set target.process.thread.single-thread-plan-timeout 2000" | |
| ) | |
| self.dbg.HandleCommand("settings set target.use-fast-stepping true") | |
| self.step_over_multi_calls_helper() | |
| @skipIfWindows | |
| def test_step_over_multi_calls_large_timeout_slow_stepping(self): | |
| """Test step over source line with multiple call instructions works fine with large timeout and slow stepping.""" | |
| self.dbg.HandleCommand( | |
| "settings set target.process.thread.single-thread-plan-timeout 2000" | |
| ) | |
| self.dbg.HandleCommand("settings set target.use-fast-stepping false") | |
| self.step_over_multi_calls_helper() | |
| @skipIfWindows | |
| def test_step_over_deadlock_with_inner_breakpoint_continue(self): | |
| """Test step over deadlock function with inner breakpoint will trigger the breakpoint | |
| and later continue will finish the stepping. | |
| """ | |
| self.dbg.HandleCommand( | |
| "settings set target.process.thread.single-thread-plan-timeout 2000" | |
| ) | |
| (target, process, self.thread, _) = lldbutil.run_to_source_breakpoint( | |
| self, "// Set breakpoint1 here", lldb.SBFileSpec(self.main_source) | |
| ) | |
| signal_main_thread_value = target.FindFirstGlobalVariable("signal_main_thread") | |
| self.assertTrue(signal_main_thread_value.IsValid()) | |
| # Change signal_main_thread global variable to 1 so that worker thread loop can | |
| # terminate and move forward to signal main thread | |
| signal_main_thread_value.SetValueFromCString("1") | |
| # Set breakpoint on inner function call | |
| inner_breakpoint = target.BreakpointCreateByLocation( | |
| lldb.SBFileSpec(self.main_source), | |
| line_number("main.cpp", "// Set interrupt breakpoint here"), | |
| 0, | |
| 0, | |
| lldb.SBFileSpecList(), | |
| False, | |
| ) | |
| # Step over will hit the inner breakpoint and stop | |
| self.thread.StepOver(lldb.eOnlyThisThread) | |
| self.assertStopReason(self.thread.GetStopReason(), lldb.eStopReasonBreakpoint) | |
| thread1 = lldbutil.get_one_thread_stopped_at_breakpoint( | |
| process, inner_breakpoint | |
| ) | |
| self.assertTrue( | |
| thread1.IsValid(), | |
| "We are indeed stopped at inner breakpoint inside deadlock_func", | |
| ) | |
| # Continue the process should complete the step-over | |
| process.Continue() | |
| self.assertState(process.GetState(), lldb.eStateStopped) | |
| self.assertStopReason(self.thread.GetStopReason(), lldb.eStopReasonPlanComplete) | |
| self.verify_hit_correct_line("// Finish step-over from breakpoint1") | |
| @skipIfWindows | |
| def test_step_over_deadlock_with_inner_breakpoint_step(self): | |
| """Test step over deadlock function with inner breakpoint will trigger the breakpoint | |
| and later step still works | |
| """ | |
| self.dbg.HandleCommand( | |
| "settings set target.process.thread.single-thread-plan-timeout 2000" | |
| ) | |
| (target, process, self.thread, _) = lldbutil.run_to_source_breakpoint( | |
| self, "// Set breakpoint1 here", lldb.SBFileSpec(self.main_source) | |
| ) | |
| signal_main_thread_value = target.FindFirstGlobalVariable("signal_main_thread") | |
| self.assertTrue(signal_main_thread_value.IsValid()) | |
| # Change signal_main_thread global variable to 1 so that worker thread loop can | |
| # terminate and move forward to signal main thread | |
| signal_main_thread_value.SetValueFromCString("1") | |
| # Set breakpoint on inner function call | |
| inner_breakpoint = target.BreakpointCreateByLocation( | |
| lldb.SBFileSpec(self.main_source), | |
| line_number("main.cpp", "// Set interrupt breakpoint here"), | |
| 0, | |
| 0, | |
| lldb.SBFileSpecList(), | |
| False, | |
| ) | |
| # Step over will hit the inner breakpoint and stop | |
| self.thread.StepOver(lldb.eOnlyThisThread) | |
| self.assertStopReason(self.thread.GetStopReason(), lldb.eStopReasonBreakpoint) | |
| thread1 = lldbutil.get_one_thread_stopped_at_breakpoint( | |
| process, inner_breakpoint | |
| ) | |
| self.assertTrue( | |
| thread1.IsValid(), | |
| "We are indeed stopped at inner breakpoint inside deadlock_func", | |
| ) | |
| # Step still works | |
| self.thread.StepOver(lldb.eOnlyThisThread) | |
| self.assertState(process.GetState(), lldb.eStateStopped) | |
| self.assertStopReason(self.thread.GetStopReason(), lldb.eStopReasonPlanComplete) | |
| self.verify_hit_correct_line("// Finish step-over from inner breakpoint") | |
| @skipIfWindows | |
| def test_step_over_deadlock_with_user_async_interrupt(self): | |
| """Test step over deadlock function with large timeout then send async interrupt | |
| should report correct stop reason | |
| """ | |
| self.dbg.HandleCommand( | |
| "settings set target.process.thread.single-thread-plan-timeout 2000000" | |
| ) | |
| (target, process, self.thread, _) = lldbutil.run_to_source_breakpoint( | |
| self, "// Set breakpoint1 here", lldb.SBFileSpec(self.main_source) | |
| ) | |
| signal_main_thread_value = target.FindFirstGlobalVariable("signal_main_thread") | |
| self.assertTrue(signal_main_thread_value.IsValid()) | |
| # Change signal_main_thread global variable to 1 so that worker thread loop can | |
| # terminate and move forward to signal main thread | |
| signal_main_thread_value.SetValueFromCString("1") | |
| self.dbg.SetAsync(True) | |
| # This stepping should block due to large timeout and should be interrupted by the | |
| # async interrupt from the worker thread | |
| self.thread.StepOver(lldb.eOnlyThisThread) | |
| time.sleep(1) | |
| listener = self.dbg.GetListener() | |
| lldbutil.expect_state_changes(self, listener, process, [lldb.eStateRunning]) | |
| self.dbg.SetAsync(False) | |
| process.SendAsyncInterrupt() | |
| lldbutil.expect_state_changes(self, listener, process, [lldb.eStateStopped]) | |
| self.assertStopReason(self.thread.GetStopReason(), lldb.eStopReasonSignal) |