1dbbed971SJim Ingham"""
2dbbed971SJim InghamMake sure that we handle an expression on a thread, if
3dbbed971SJim Inghamthe thread exits while the expression is running.
4dbbed971SJim Ingham"""
5dbbed971SJim Ingham
6dbbed971SJim Inghamimport lldb
7dbbed971SJim Inghamfrom lldbsuite.test.decorators import *
8dbbed971SJim Inghamimport lldbsuite.test.lldbutil as lldbutil
9dbbed971SJim Inghamfrom lldbsuite.test.lldbtest import *
10dbbed971SJim Ingham
11dbbed971SJim Inghamclass TestExitDuringExpression(TestBase):
12dbbed971SJim Ingham
13dbbed971SJim Ingham    NO_DEBUG_INFO_TESTCASE = True
14dbbed971SJim Ingham
15dbbed971SJim Ingham    @skipIfWindows
167dd76cccSMuhammad Omair Javaid    @skipIf(oslist=["linux"], archs=["arm", "aarch64"], bugnumber="llvm.org/pr48414")
17266c90feSMichał Górny    @expectedFailureAll(oslist=["freebsd"], bugnumber="llvm.org/pr48414")
1899562332SMichał Górny    @expectedFailureNetBSD
19dbbed971SJim Ingham    def test_exit_before_one_thread_unwind(self):
20dbbed971SJim Ingham        """Test the case where we exit within the one thread timeout"""
21dbbed971SJim Ingham        self.exiting_expression_test(True, True)
22dbbed971SJim Ingham
23dbbed971SJim Ingham    @skipIfWindows
2428058d4cSMuhammad Omair Javaid    @skipIf(oslist=["linux"], archs=["arm", "aarch64"], bugnumber="llvm.org/pr48414")
25266c90feSMichał Górny    @expectedFailureAll(oslist=["freebsd"], bugnumber="llvm.org/pr48414")
2699562332SMichał Górny    @expectedFailureNetBSD
27dbbed971SJim Ingham    def test_exit_before_one_thread_no_unwind(self):
28dbbed971SJim Ingham        """Test the case where we exit within the one thread timeout"""
29dbbed971SJim Ingham        self.exiting_expression_test(True, False)
30dbbed971SJim Ingham
31dbbed971SJim Ingham    @skipIfWindows
32dbbed971SJim Ingham    def test_exit_after_one_thread_unwind(self):
33dbbed971SJim Ingham        """Test the case where we exit within the one thread timeout"""
34dbbed971SJim Ingham        self.exiting_expression_test(False, True)
35dbbed971SJim Ingham
36dbbed971SJim Ingham    @skipIfWindows
37dbbed971SJim Ingham    def test_exit_after_one_thread_no_unwind(self):
38dbbed971SJim Ingham        """Test the case where we exit within the one thread timeout"""
39dbbed971SJim Ingham        self.exiting_expression_test(False, False)
40dbbed971SJim Ingham
41dbbed971SJim Ingham    def setUp(self):
42dbbed971SJim Ingham        TestBase.setUp(self)
43dbbed971SJim Ingham        self.main_source_file = lldb.SBFileSpec("main.c")
44dbbed971SJim Ingham        self.build()
45dbbed971SJim Ingham
46dbbed971SJim Ingham    def exiting_expression_test(self, before_one_thread_timeout , unwind):
47dbbed971SJim Ingham        """function_to_call sleeps for g_timeout microseconds, then calls pthread_exit.
48dbbed971SJim Ingham           This test calls function_to_call with an overall timeout of 500
49dbbed971SJim Ingham           microseconds, and a one_thread_timeout as passed in.
50dbbed971SJim Ingham           It also sets unwind_on_exit for the call to the unwind passed in.
51dbbed971SJim Ingham           This allows you to have the thread exit either before the one thread
52dbbed971SJim Ingham           timeout is passed. """
53dbbed971SJim Ingham
54dbbed971SJim Ingham        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
55dbbed971SJim Ingham                                   "Break here and cause the thread to exit", self.main_source_file)
56dbbed971SJim Ingham
57dbbed971SJim Ingham        # We'll continue to this breakpoint after running our expression:
58dbbed971SJim Ingham        return_bkpt = target.BreakpointCreateBySourceRegex("Break here to make sure the thread exited", self.main_source_file)
59dbbed971SJim Ingham        frame = thread.frames[0]
60dbbed971SJim Ingham        tid = thread.GetThreadID()
61dbbed971SJim Ingham        # Find the timeout:
62dbbed971SJim Ingham        var_options = lldb.SBVariablesOptions()
63dbbed971SJim Ingham        var_options.SetIncludeArguments(False)
64dbbed971SJim Ingham        var_options.SetIncludeLocals(False)
65dbbed971SJim Ingham        var_options.SetIncludeStatics(True)
66dbbed971SJim Ingham
67dbbed971SJim Ingham        value_list = frame.GetVariables(var_options)
68dbbed971SJim Ingham        g_timeout = value_list.GetFirstValueByName("g_timeout")
69dbbed971SJim Ingham        self.assertTrue(g_timeout.IsValid(), "Found g_timeout")
70dbbed971SJim Ingham
71dbbed971SJim Ingham        error = lldb.SBError()
72dbbed971SJim Ingham        timeout_value = g_timeout.GetValueAsUnsigned(error)
73*779bbbf2SDave Lee        self.assertSuccess(error, "Couldn't get timeout value")
74dbbed971SJim Ingham
75dbbed971SJim Ingham        one_thread_timeout = 0
76dbbed971SJim Ingham        if (before_one_thread_timeout):
77dbbed971SJim Ingham            one_thread_timeout = timeout_value * 2
78dbbed971SJim Ingham        else:
79dbbed971SJim Ingham            one_thread_timeout = int(timeout_value / 2)
80dbbed971SJim Ingham
81dbbed971SJim Ingham        options = lldb.SBExpressionOptions()
82dbbed971SJim Ingham        options.SetUnwindOnError(unwind)
83dbbed971SJim Ingham        options.SetOneThreadTimeoutInMicroSeconds(one_thread_timeout)
84dbbed971SJim Ingham        options.SetTimeoutInMicroSeconds(4 * timeout_value)
85dbbed971SJim Ingham
86dbbed971SJim Ingham        result = frame.EvaluateExpression("function_to_call()", options)
87dbbed971SJim Ingham
88dbbed971SJim Ingham        # Make sure the thread actually exited:
89dbbed971SJim Ingham        thread = process.GetThreadByID(tid)
90dbbed971SJim Ingham        self.assertFalse(thread.IsValid(), "The thread exited")
91dbbed971SJim Ingham
92dbbed971SJim Ingham        # Make sure the expression failed:
93dbbed971SJim Ingham        self.assertFalse(result.GetError().Success(), "Expression failed.")
94dbbed971SJim Ingham
95dbbed971SJim Ingham        # Make sure we can keep going:
96dbbed971SJim Ingham        threads = lldbutil.continue_to_breakpoint(process, return_bkpt)
97dbbed971SJim Ingham        if not threads:
98dbbed971SJim Ingham            self.fail("didn't get any threads back after continuing")
99dbbed971SJim Ingham
100dbbed971SJim Ingham        self.assertEqual(len(threads), 1, "One thread hit our breakpoint")
101dbbed971SJim Ingham        thread = threads[0]
102dbbed971SJim Ingham        frame = thread.frames[0]
103dbbed971SJim Ingham        # Now get the return value, if we successfully caused the thread to exit
104dbbed971SJim Ingham        # it should be 10, not 20.
105dbbed971SJim Ingham        ret_val = frame.FindVariable("ret_val")
106*779bbbf2SDave Lee        self.assertSuccess(ret_val.GetError(), "Found ret_val")
107dbbed971SJim Ingham        ret_val_value = ret_val.GetValueAsSigned(error)
108*779bbbf2SDave Lee        self.assertSuccess(error, "Got ret_val's value")
109dbbed971SJim Ingham        self.assertEqual(ret_val_value, 10, "We put the right value in ret_val")
110