""" Make sure that we handle an expression on a thread, if the thread exits while the expression is running. """ import lldb from lldbsuite.test.decorators import * import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * class TestExitDuringExpression(TestBase): NO_DEBUG_INFO_TESTCASE = True @skipIfWindows @skipIf(oslist=["linux"], archs=["arm", "aarch64"], bugnumber="llvm.org/pr48414") @expectedFailureAll(oslist=["freebsd"], bugnumber="llvm.org/pr48414") @expectedFailureNetBSD def test_exit_before_one_thread_unwind(self): """Test the case where we exit within the one thread timeout""" self.exiting_expression_test(True, True) @skipIfWindows @skipIf(oslist=["linux"], archs=["arm", "aarch64"], bugnumber="llvm.org/pr48414") @expectedFailureAll(oslist=["freebsd"], bugnumber="llvm.org/pr48414") @expectedFailureNetBSD def test_exit_before_one_thread_no_unwind(self): """Test the case where we exit within the one thread timeout""" self.exiting_expression_test(True, False) @skipIfWindows def test_exit_after_one_thread_unwind(self): """Test the case where we exit within the one thread timeout""" self.exiting_expression_test(False, True) @skipIfWindows def test_exit_after_one_thread_no_unwind(self): """Test the case where we exit within the one thread timeout""" self.exiting_expression_test(False, False) def setUp(self): TestBase.setUp(self) self.main_source_file = lldb.SBFileSpec("main.c") self.build() def exiting_expression_test(self, before_one_thread_timeout, unwind): """function_to_call sleeps for g_timeout microseconds, then calls pthread_exit. This test calls function_to_call with an overall timeout of 500 microseconds, and a one_thread_timeout as passed in. It also sets unwind_on_exit for the call to the unwind passed in. This allows you to have the thread exit either before the one thread timeout is passed.""" (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( self, "Break here and cause the thread to exit", self.main_source_file ) # We'll continue to this breakpoint after running our expression: return_bkpt = target.BreakpointCreateBySourceRegex( "Break here to make sure the thread exited", self.main_source_file ) frame = thread.frames[0] tid = thread.GetThreadID() # Find the timeout: var_options = lldb.SBVariablesOptions() var_options.SetIncludeArguments(False) var_options.SetIncludeLocals(False) var_options.SetIncludeStatics(True) value_list = frame.GetVariables(var_options) g_timeout = value_list.GetFirstValueByName("g_timeout") self.assertTrue(g_timeout.IsValid(), "Found g_timeout") error = lldb.SBError() timeout_value = g_timeout.GetValueAsUnsigned(error) self.assertSuccess(error, "Couldn't get timeout value") one_thread_timeout = 0 if before_one_thread_timeout: one_thread_timeout = timeout_value * 2 else: one_thread_timeout = int(timeout_value / 2) options = lldb.SBExpressionOptions() options.SetUnwindOnError(unwind) options.SetOneThreadTimeoutInMicroSeconds(one_thread_timeout) options.SetTimeoutInMicroSeconds(4 * timeout_value) result = frame.EvaluateExpression("function_to_call()", options) # Make sure the thread actually exited: thread = process.GetThreadByID(tid) self.assertFalse(thread.IsValid(), "The thread exited") # Make sure the expression failed: self.assertFalse(result.GetError().Success(), "Expression failed.") # Make sure we can keep going: threads = lldbutil.continue_to_breakpoint(process, return_bkpt) if not threads: self.fail("didn't get any threads back after continuing") self.assertEqual(len(threads), 1, "One thread hit our breakpoint") thread = threads[0] frame = thread.frames[0] # Now get the return value, if we successfully caused the thread to exit # it should be 10, not 20. ret_val = frame.FindVariable("ret_val") self.assertSuccess(ret_val.GetError(), "Found ret_val") ret_val_value = ret_val.GetValueAsSigned(error) self.assertSuccess(error, "Got ret_val's value") self.assertEqual(ret_val_value, 10, "We put the right value in ret_val")