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