"""
Make sure that if threads are suspended outside of lldb, debugserver
won't make them run, even if we call an expression on the thread.
"""
import lldb
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.lldbtest import *
class TestSuspendedThreadHandling(TestBase):
NO_DEBUG_INFO_TESTCASE = True
@skipUnlessDarwin
def test_suspended_threads(self):
"""Test that debugserver doesn't disturb the suspend count of a thread
that has been suspended from within a program, when navigating breakpoints
on other threads, or calling functions both on the suspended thread and
on other threads."""
self.build()
self.main_source_file = lldb.SBFileSpec("main.c")
self.suspended_thread_test()
def setUp(self):
TestBase.setUp(self)
def try_an_expression(self, thread, correct_value, test_bp):
frame = thread.frames[0]
value = frame.EvaluateExpression("function_to_call()")
self.assertSuccess(value.GetError(), "Successfully called the function")
self.assertEqual(
value.GetValueAsSigned(), correct_value, "Got expected value for expression"
)
self.assertEqual(
test_bp.GetHitCount(),
0,
"First expression allowed the suspend thread to run",
)
def make_bkpt(self, pattern):
bp = self.target.BreakpointCreateBySourceRegex(pattern, self.main_source_file)
self.assertEqual(bp.GetNumLocations(), 1, "Locations for %s" % (pattern))
return bp
def suspended_thread_test(self):
(self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
self, "Stop here to get things going", self.main_source_file
)
rt_bp = self.make_bkpt("Break here to show we can handle breakpoints")
rt_exit_bp = self.make_bkpt("Break here after thread_join")
st_bp = self.make_bkpt("We allowed the suspend thread to run")
st_exit_bp = self.make_bkpt(
" Break here to make sure the thread exited normally"
)
threads = lldbutil.continue_to_breakpoint(process, rt_bp)
self.assertEqual(len(threads), 1, "Hit the running_func breakpoint")
self.assertEqual(
st_bp.GetHitCount(), 0, "Continue allowed the suspend thread to run"
)
self.try_an_expression(threads[0], 0, st_bp)
threads = lldbutil.continue_to_breakpoint(process, rt_bp)
self.assertEqual(len(threads), 1, "We didn't hit running breakpoint")
thread = lldb.SBThread()
for thread in process.threads:
th_name = thread.GetName()
if th_name is None:
continue
if "Look for me" in th_name:
break
self.assertTrue(thread.IsValid(), "We found the suspend thread.")
self.try_an_expression(thread, 1, st_bp)
rt_bp.SetAutoContinue(True)
threads = lldbutil.continue_to_breakpoint(process, rt_exit_bp)
self.assertEqual(len(threads), 1)
self.assertEqual(
st_bp.GetHitCount(), 0, "Continue again let suspended thread run"
)
threads = lldbutil.continue_to_breakpoint(process, st_bp)
self.assertEqual(len(threads), 1, "The thread resumed successfully")
threads = lldbutil.continue_to_breakpoint(process, st_exit_bp)
self.assertEqual(len(threads), 1, "pthread_join exited successfully")