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