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