1"""
2Test calling a function that hits a signal set to auto-restart, make sure the call completes.
3"""
4
5import unittest2
6import lldb
7import lldbutil
8from lldbtest import *
9
10class ExprCommandWithTimeoutsTestCase(TestBase):
11
12    mydir = os.path.join("expression_command", "call-restarts")
13
14    def setUp(self):
15        # Call super's setUp().
16        TestBase.setUp(self)
17
18        self.main_source = "lotta-signals.c"
19        self.main_source_spec = lldb.SBFileSpec (self.main_source)
20
21
22    @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
23    @dsym_test
24    def test_with_dsym(self):
25        """Test calling std::String member function."""
26        self.buildDsym()
27        self.call_function()
28
29    @skipIfLinux # llvm.org/pr15278: handle expressions that generate signals on Linux
30    @dwarf_test
31    def test_with_dwarf(self):
32        """Test calling std::String member function."""
33        self.buildDwarf()
34        self.call_function()
35
36    def check_after_call (self, num_sigchld):
37        after_call = self.sigchld_no.GetValueAsSigned(-1)
38        self.assertTrue (after_call - self.start_sigchld_no == num_sigchld, "Really got %d SIGCHLD signals through the call."%(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.assertTrue (self.orig_frame_pc == frame.GetPC(), "Restored the zeroth frame correctly")
44
45
46    def call_function(self):
47        """Test calling function with timeout."""
48        exe_name = "a.out"
49        exe = os.path.join(os.getcwd(), exe_name)
50
51        target = self.dbg.CreateTarget(exe)
52        self.assertTrue(target, VALID_TARGET)
53        empty = lldb.SBFileSpec()
54        breakpoint = target.BreakpointCreateBySourceRegex('Stop here in main.',self.main_source_spec)
55        self.assertTrue(breakpoint.GetNumLocations() > 0, VALID_BREAKPOINT)
56
57        # Launch the process, and do not stop at the entry point.
58        process = target.LaunchSimple(None, None, os.getcwd())
59
60        self.assertTrue(process, PROCESS_IS_VALID)
61
62        # Frame #0 should be at our breakpoint.
63        threads = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint)
64
65        self.assertTrue(len(threads) == 1)
66        self.thread = threads[0]
67
68        # Make sure the SIGCHLD behavior is pass/no-stop/no-notify:
69        return_obj = lldb.SBCommandReturnObject()
70        self.dbg.GetCommandInterpreter().HandleCommand("process handle SIGCHLD -s 0 -p 1 -n 0", return_obj)
71        self.assertTrue (return_obj.Succeeded() == True, "Set SIGCHLD to pass, no-stop")
72
73        # The sigchld_no variable should be 0 at this point.
74        self.sigchld_no = target.FindFirstGlobalVariable("sigchld_no")
75        self.assertTrue (self.sigchld_no.IsValid(), "Got a value for sigchld_no")
76
77        self.start_sigchld_no = self.sigchld_no.GetValueAsSigned (-1)
78        self.assertTrue (self.start_sigchld_no != -1, "Got an actual value for sigchld_no")
79
80        options = lldb.SBExpressionOptions()
81        options.SetUnwindOnError(True)
82
83        frame = self.thread.GetFrameAtIndex(0)
84        # Store away the PC to check that the functions unwind to the right place after calls
85        self.orig_frame_pc = frame.GetPC()
86
87        num_sigchld = 30
88        value = frame.EvaluateExpression ("call_me (%d)"%(num_sigchld), options)
89        self.assertTrue (value.IsValid())
90        self.assertTrue (value.GetError().Success() == True)
91        self.assertTrue (value.GetValueAsSigned(-1) == num_sigchld)
92
93        self.check_after_call(num_sigchld)
94
95        # Okay, now try with a breakpoint in the called code in the case where
96        # we are ignoring breakpoint hits.
97        handler_bkpt = target.BreakpointCreateBySourceRegex("Got sigchld %d.", self.main_source_spec)
98        self.assertTrue (handler_bkpt.GetNumLocations() > 0)
99        options.SetIgnoreBreakpoints(True)
100        options.SetUnwindOnError(True)
101
102        value = frame.EvaluateExpression("call_me (%d)"%(num_sigchld), options)
103
104        self.assertTrue (value.IsValid() and value.GetError().Success() == True)
105        self.assertTrue (value.GetValueAsSigned(-1) == num_sigchld)
106        self.check_after_call(num_sigchld)
107
108        # Now set the signal to print but not stop and make sure that calling still works:
109        self.dbg.GetCommandInterpreter().HandleCommand("process handle SIGCHLD -s 0 -p 1 -n 1", return_obj)
110        self.assertTrue (return_obj.Succeeded() == True, "Set SIGCHLD to pass, no-stop, notify")
111
112        value = frame.EvaluateExpression("call_me (%d)"%(num_sigchld), options)
113
114        self.assertTrue (value.IsValid() and value.GetError().Success() == True)
115        self.assertTrue (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 complete the call:
119        options.SetUnwindOnError(False)
120        value = frame.EvaluateExpression("call_me (%d)"%(num_sigchld), options)
121
122        self.assertTrue (value.IsValid() and value.GetError().Success() == True)
123        self.assertTrue (value.GetValueAsSigned(-1) == num_sigchld)
124        self.check_after_call(num_sigchld)
125
126        # Okay, now set UnwindOnError to true, and then make the signal behavior to stop
127        # and see that now we do stop at the signal point:
128
129        self.dbg.GetCommandInterpreter().HandleCommand("process handle SIGCHLD -s 1 -p 1 -n 1", return_obj)
130        self.assertTrue (return_obj.Succeeded() == True, "Set SIGCHLD to pass, stop, notify")
131
132        value = frame.EvaluateExpression("call_me (%d)"%(num_sigchld), options)
133        self.assertTrue (value.IsValid() and value.GetError().Success() == False)
134
135        # Set signal handling back to no-stop, and continue and we should end up back in out starting frame:
136        self.dbg.GetCommandInterpreter().HandleCommand("process handle SIGCHLD -s 0 -p 1 -n 1", return_obj)
137        self.assertTrue (return_obj.Succeeded() == True, "Set SIGCHLD to pass, no-stop, notify")
138
139        error = process.Continue()
140        self.assertTrue (error.Success(), "Continuing after stopping for signal succeeds.")
141
142        frame = self.thread.GetFrameAtIndex(0)
143        self.assertTrue (frame.GetPC() == self.orig_frame_pc, "Continuing returned to the place we started.")
144
145if __name__ == '__main__':
146    import atexit
147    lldb.SBDebugger.Initialize()
148    atexit.register(lambda: lldb.SBDebugger.Terminate())
149    unittest2.main()
150