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 Ingham
12*2238dcc3SJonas Devlieghereclass TestExitDuringExpression(TestBase):
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
54*2238dcc3SJonas Devlieghere        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
55*2238dcc3SJonas Devlieghere            self, "Break here and cause the thread to exit", self.main_source_file
56*2238dcc3SJonas Devlieghere        )
57dbbed971SJim Ingham
58dbbed971SJim Ingham        # We'll continue to this breakpoint after running our expression:
59*2238dcc3SJonas Devlieghere        return_bkpt = target.BreakpointCreateBySourceRegex(
60*2238dcc3SJonas Devlieghere            "Break here to make sure the thread exited", self.main_source_file
61*2238dcc3SJonas Devlieghere        )
62dbbed971SJim Ingham        frame = thread.frames[0]
63dbbed971SJim Ingham        tid = thread.GetThreadID()
64dbbed971SJim Ingham        # Find the timeout:
65dbbed971SJim Ingham        var_options = lldb.SBVariablesOptions()
66dbbed971SJim Ingham        var_options.SetIncludeArguments(False)
67dbbed971SJim Ingham        var_options.SetIncludeLocals(False)
68dbbed971SJim Ingham        var_options.SetIncludeStatics(True)
69dbbed971SJim Ingham
70dbbed971SJim Ingham        value_list = frame.GetVariables(var_options)
71dbbed971SJim Ingham        g_timeout = value_list.GetFirstValueByName("g_timeout")
72dbbed971SJim Ingham        self.assertTrue(g_timeout.IsValid(), "Found g_timeout")
73dbbed971SJim Ingham
74dbbed971SJim Ingham        error = lldb.SBError()
75dbbed971SJim Ingham        timeout_value = g_timeout.GetValueAsUnsigned(error)
76779bbbf2SDave Lee        self.assertSuccess(error, "Couldn't get timeout value")
77dbbed971SJim Ingham
78dbbed971SJim Ingham        one_thread_timeout = 0
79*2238dcc3SJonas Devlieghere        if before_one_thread_timeout:
80dbbed971SJim Ingham            one_thread_timeout = timeout_value * 2
81dbbed971SJim Ingham        else:
82dbbed971SJim Ingham            one_thread_timeout = int(timeout_value / 2)
83dbbed971SJim Ingham
84dbbed971SJim Ingham        options = lldb.SBExpressionOptions()
85dbbed971SJim Ingham        options.SetUnwindOnError(unwind)
86dbbed971SJim Ingham        options.SetOneThreadTimeoutInMicroSeconds(one_thread_timeout)
87dbbed971SJim Ingham        options.SetTimeoutInMicroSeconds(4 * timeout_value)
88dbbed971SJim Ingham
89dbbed971SJim Ingham        result = frame.EvaluateExpression("function_to_call()", options)
90dbbed971SJim Ingham
91dbbed971SJim Ingham        # Make sure the thread actually exited:
92dbbed971SJim Ingham        thread = process.GetThreadByID(tid)
93dbbed971SJim Ingham        self.assertFalse(thread.IsValid(), "The thread exited")
94dbbed971SJim Ingham
95dbbed971SJim Ingham        # Make sure the expression failed:
96dbbed971SJim Ingham        self.assertFalse(result.GetError().Success(), "Expression failed.")
97dbbed971SJim Ingham
98dbbed971SJim Ingham        # Make sure we can keep going:
99dbbed971SJim Ingham        threads = lldbutil.continue_to_breakpoint(process, return_bkpt)
100dbbed971SJim Ingham        if not threads:
101dbbed971SJim Ingham            self.fail("didn't get any threads back after continuing")
102dbbed971SJim Ingham
103dbbed971SJim Ingham        self.assertEqual(len(threads), 1, "One thread hit our breakpoint")
104dbbed971SJim Ingham        thread = threads[0]
105dbbed971SJim Ingham        frame = thread.frames[0]
106dbbed971SJim Ingham        # Now get the return value, if we successfully caused the thread to exit
107dbbed971SJim Ingham        # it should be 10, not 20.
108dbbed971SJim Ingham        ret_val = frame.FindVariable("ret_val")
109779bbbf2SDave Lee        self.assertSuccess(ret_val.GetError(), "Found ret_val")
110dbbed971SJim Ingham        ret_val_value = ret_val.GetValueAsSigned(error)
111779bbbf2SDave Lee        self.assertSuccess(error, "Got ret_val's value")
112dbbed971SJim Ingham        self.assertEqual(ret_val_value, 10, "We put the right value in ret_val")
113