1"""
2Test calling a function that hits a signal set to auto-restart, make sure the call completes.
3"""
4
5
6
7import lldb
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10from lldbsuite.test import lldbutil
11
12
13class ExprCommandThatRestartsTestCase(TestBase):
14
15    mydir = TestBase.compute_mydir(__file__)
16    NO_DEBUG_INFO_TESTCASE = True
17
18    def setUp(self):
19        # Call super's setUp().
20        TestBase.setUp(self)
21
22        self.main_source = "lotta-signals.c"
23        self.main_source_spec = lldb.SBFileSpec(self.main_source)
24
25    @skipIfDarwin  # llvm.org/pr19246: intermittent failure
26    @skipIfWindows  # Test relies on signals, unsupported on Windows
27    @expectedFlakeyAndroid(bugnumber="llvm.org/pr19246")
28    @expectedFailureNetBSD
29    def test(self):
30        """Test calling function that hits a signal and restarts."""
31        self.build()
32        self.call_function()
33
34    def check_after_call(self, num_sigchld):
35        after_call = self.sigchld_no.GetValueAsSigned(-1)
36        self.assertTrue(
37            after_call -
38            self.start_sigchld_no == num_sigchld,
39            "Really got %d SIGCHLD signals through the call." %
40            (num_sigchld))
41        self.start_sigchld_no = after_call
42
43        # Check that we are back where we were before:
44        frame = self.thread.GetFrameAtIndex(0)
45        self.assertTrue(
46            self.orig_frame_pc == frame.GetPC(),
47            "Restored the zeroth frame correctly")
48
49    def call_function(self):
50        (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
51                                      'Stop here in main.', self.main_source_spec)
52
53        # Make sure the SIGCHLD behavior is pass/no-stop/no-notify:
54        self.runCmd("process handle SIGCHLD -s 0 -p 1 -n 0")
55
56        # The sigchld_no variable should be 0 at this point.
57        self.sigchld_no = target.FindFirstGlobalVariable("sigchld_no")
58        self.assertTrue(
59            self.sigchld_no.IsValid(),
60            "Got a value for sigchld_no")
61
62        self.start_sigchld_no = self.sigchld_no.GetValueAsSigned(-1)
63        self.assertTrue(
64            self.start_sigchld_no != -1,
65            "Got an actual value for sigchld_no")
66
67        options = lldb.SBExpressionOptions()
68        # processing 30 signals takes a while, increase the expression timeout
69        # a bit
70        options.SetTimeoutInMicroSeconds(3000000)  # 3s
71        options.SetUnwindOnError(True)
72
73        frame = self.thread.GetFrameAtIndex(0)
74        # Store away the PC to check that the functions unwind to the right
75        # place after calls
76        self.orig_frame_pc = frame.GetPC()
77
78        num_sigchld = 30
79        value = frame.EvaluateExpression(
80            "call_me (%d)" %
81            (num_sigchld), options)
82        self.assertTrue(value.IsValid())
83        self.assertSuccess(value.GetError())
84        self.assertEquals(value.GetValueAsSigned(-1), num_sigchld)
85
86        self.check_after_call(num_sigchld)
87
88        # Okay, now try with a breakpoint in the called code in the case where
89        # we are ignoring breakpoint hits.
90        handler_bkpt = target.BreakpointCreateBySourceRegex(
91            "Got sigchld %d.", self.main_source_spec)
92        self.assertTrue(handler_bkpt.GetNumLocations() > 0)
93        options.SetIgnoreBreakpoints(True)
94        options.SetUnwindOnError(True)
95
96        value = frame.EvaluateExpression(
97            "call_me (%d)" %
98            (num_sigchld), options)
99
100        self.assertTrue(value.IsValid())
101        self.assertSuccess(value.GetError())
102        self.assertEquals(value.GetValueAsSigned(-1), num_sigchld)
103        self.check_after_call(num_sigchld)
104
105        # Now set the signal to print but not stop and make sure that calling
106        # still works:
107        self.runCmd("process handle SIGCHLD -s 0 -p 1 -n 1")
108
109        value = frame.EvaluateExpression(
110            "call_me (%d)" %
111            (num_sigchld), options)
112
113        self.assertTrue(value.IsValid())
114        self.assertSuccess(value.GetError())
115        self.assertEquals(value.GetValueAsSigned(-1), num_sigchld)
116        self.check_after_call(num_sigchld)
117
118        # Now set this unwind on error to false, and make sure that we still
119        # complete the call:
120        options.SetUnwindOnError(False)
121        value = frame.EvaluateExpression(
122            "call_me (%d)" %
123            (num_sigchld), options)
124
125        self.assertTrue(value.IsValid())
126        self.assertSuccess(value.GetError())
127        self.assertEquals(value.GetValueAsSigned(-1), num_sigchld)
128        self.check_after_call(num_sigchld)
129
130        # Okay, now set UnwindOnError to true, and then make the signal behavior to stop
131        # and see that now we do stop at the signal point:
132
133        self.runCmd("process handle SIGCHLD -s 1 -p 1 -n 1")
134
135        value = frame.EvaluateExpression(
136            "call_me (%d)" %
137            (num_sigchld), options)
138        self.assertTrue(value.IsValid())
139        self.assertFalse(value.GetError().Success())
140
141        # Set signal handling back to no-stop, and continue and we should end
142        # up back in out starting frame:
143        self.runCmd("process handle SIGCHLD -s 0 -p 1 -n 1")
144
145        error = process.Continue()
146        self.assertSuccess(error,
147            "Continuing after stopping for signal succeeds.")
148
149        frame = self.thread.GetFrameAtIndex(0)
150        self.assertTrue(
151            frame.GetPC() == self.orig_frame_pc,
152            "Continuing returned to the place we started.")
153