""" 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)