1*99451b44SJordan Rupprecht"""
2*99451b44SJordan RupprechtTest lldb Python event APIs.
3*99451b44SJordan Rupprecht"""
4*99451b44SJordan Rupprecht
5*99451b44SJordan Rupprechtfrom __future__ import print_function
6*99451b44SJordan Rupprecht
7*99451b44SJordan Rupprecht
8*99451b44SJordan Rupprechtimport re
9*99451b44SJordan Rupprechtimport lldb
10*99451b44SJordan Rupprechtfrom lldbsuite.test.decorators import *
11*99451b44SJordan Rupprechtfrom lldbsuite.test.lldbtest import *
12*99451b44SJordan Rupprechtfrom lldbsuite.test import lldbutil
13*99451b44SJordan Rupprecht
14*99451b44SJordan Rupprecht
15*99451b44SJordan Rupprecht@skipIfLinux   # llvm.org/pr25924, sometimes generating SIGSEGV
16*99451b44SJordan Rupprecht@skipIfDarwin
17*99451b44SJordan Rupprechtclass EventAPITestCase(TestBase):
18*99451b44SJordan Rupprecht
19*99451b44SJordan Rupprecht    mydir = TestBase.compute_mydir(__file__)
20*99451b44SJordan Rupprecht    NO_DEBUG_INFO_TESTCASE = True
21*99451b44SJordan Rupprecht
22*99451b44SJordan Rupprecht    def setUp(self):
23*99451b44SJordan Rupprecht        # Call super's setUp().
24*99451b44SJordan Rupprecht        TestBase.setUp(self)
25*99451b44SJordan Rupprecht        # Find the line number to of function 'c'.
26*99451b44SJordan Rupprecht        self.line = line_number(
27*99451b44SJordan Rupprecht            'main.c', '// Find the line number of function "c" here.')
28*99451b44SJordan Rupprecht
29*99451b44SJordan Rupprecht    @add_test_categories(['pyapi'])
30*99451b44SJordan Rupprecht    @expectedFailureAll(
31*99451b44SJordan Rupprecht        oslist=["linux"],
32*99451b44SJordan Rupprecht        bugnumber="llvm.org/pr23730 Flaky, fails ~1/10 cases")
33*99451b44SJordan Rupprecht    @skipIfWindows # This is flakey on Windows AND when it fails, it hangs: llvm.org/pr38373
34*99451b44SJordan Rupprecht    @skipIfNetBSD
35*99451b44SJordan Rupprecht    def test_listen_for_and_print_event(self):
36*99451b44SJordan Rupprecht        """Exercise SBEvent API."""
37*99451b44SJordan Rupprecht        self.build()
38*99451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
39*99451b44SJordan Rupprecht
40*99451b44SJordan Rupprecht        self.dbg.SetAsync(True)
41*99451b44SJordan Rupprecht
42*99451b44SJordan Rupprecht        # Create a target by the debugger.
43*99451b44SJordan Rupprecht        target = self.dbg.CreateTarget(exe)
44*99451b44SJordan Rupprecht        self.assertTrue(target, VALID_TARGET)
45*99451b44SJordan Rupprecht
46*99451b44SJordan Rupprecht        # Now create a breakpoint on main.c by name 'c'.
47*99451b44SJordan Rupprecht        breakpoint = target.BreakpointCreateByName('c', 'a.out')
48*99451b44SJordan Rupprecht
49*99451b44SJordan Rupprecht        listener = lldb.SBListener("my listener")
50*99451b44SJordan Rupprecht
51*99451b44SJordan Rupprecht        # Now launch the process, and do not stop at the entry point.
52*99451b44SJordan Rupprecht        error = lldb.SBError()
53*99451b44SJordan Rupprecht        process = target.Launch(listener,
54*99451b44SJordan Rupprecht                                None,      # argv
55*99451b44SJordan Rupprecht                                None,      # envp
56*99451b44SJordan Rupprecht                                None,      # stdin_path
57*99451b44SJordan Rupprecht                                None,      # stdout_path
58*99451b44SJordan Rupprecht                                None,      # stderr_path
59*99451b44SJordan Rupprecht                                None,      # working directory
60*99451b44SJordan Rupprecht                                0,         # launch flags
61*99451b44SJordan Rupprecht                                False,     # Stop at entry
62*99451b44SJordan Rupprecht                                error)     # error
63*99451b44SJordan Rupprecht
64*99451b44SJordan Rupprecht        self.assertTrue(
65*99451b44SJordan Rupprecht            process.GetState() == lldb.eStateStopped,
66*99451b44SJordan Rupprecht            PROCESS_STOPPED)
67*99451b44SJordan Rupprecht
68*99451b44SJordan Rupprecht        # Create an empty event object.
69*99451b44SJordan Rupprecht        event = lldb.SBEvent()
70*99451b44SJordan Rupprecht
71*99451b44SJordan Rupprecht        traceOn = self.TraceOn()
72*99451b44SJordan Rupprecht        if traceOn:
73*99451b44SJordan Rupprecht            lldbutil.print_stacktraces(process)
74*99451b44SJordan Rupprecht
75*99451b44SJordan Rupprecht        # Create MyListeningThread class to wait for any kind of event.
76*99451b44SJordan Rupprecht        import threading
77*99451b44SJordan Rupprecht
78*99451b44SJordan Rupprecht        class MyListeningThread(threading.Thread):
79*99451b44SJordan Rupprecht
80*99451b44SJordan Rupprecht            def run(self):
81*99451b44SJordan Rupprecht                count = 0
82*99451b44SJordan Rupprecht                # Let's only try at most 4 times to retrieve any kind of event.
83*99451b44SJordan Rupprecht                # After that, the thread exits.
84*99451b44SJordan Rupprecht                while not count > 3:
85*99451b44SJordan Rupprecht                    if traceOn:
86*99451b44SJordan Rupprecht                        print("Try wait for event...")
87*99451b44SJordan Rupprecht                    if listener.WaitForEvent(5, event):
88*99451b44SJordan Rupprecht                        if traceOn:
89*99451b44SJordan Rupprecht                            desc = lldbutil.get_description(event)
90*99451b44SJordan Rupprecht                            print("Event description:", desc)
91*99451b44SJordan Rupprecht                            print("Event data flavor:", event.GetDataFlavor())
92*99451b44SJordan Rupprecht                            print(
93*99451b44SJordan Rupprecht                                "Process state:",
94*99451b44SJordan Rupprecht                                lldbutil.state_type_to_str(
95*99451b44SJordan Rupprecht                                    process.GetState()))
96*99451b44SJordan Rupprecht                            print()
97*99451b44SJordan Rupprecht                    else:
98*99451b44SJordan Rupprecht                        if traceOn:
99*99451b44SJordan Rupprecht                            print("timeout occurred waiting for event...")
100*99451b44SJordan Rupprecht                    count = count + 1
101*99451b44SJordan Rupprecht                listener.Clear()
102*99451b44SJordan Rupprecht                return
103*99451b44SJordan Rupprecht
104*99451b44SJordan Rupprecht        # Let's start the listening thread to retrieve the events.
105*99451b44SJordan Rupprecht        my_thread = MyListeningThread()
106*99451b44SJordan Rupprecht        my_thread.start()
107*99451b44SJordan Rupprecht
108*99451b44SJordan Rupprecht        # Use Python API to continue the process.  The listening thread should be
109*99451b44SJordan Rupprecht        # able to receive the state changed events.
110*99451b44SJordan Rupprecht        process.Continue()
111*99451b44SJordan Rupprecht
112*99451b44SJordan Rupprecht        # Use Python API to kill the process.  The listening thread should be
113*99451b44SJordan Rupprecht        # able to receive the state changed event, too.
114*99451b44SJordan Rupprecht        process.Kill()
115*99451b44SJordan Rupprecht
116*99451b44SJordan Rupprecht        # Wait until the 'MyListeningThread' terminates.
117*99451b44SJordan Rupprecht        my_thread.join()
118*99451b44SJordan Rupprecht
119*99451b44SJordan Rupprecht        # Shouldn't we be testing against some kind of expectation here?
120*99451b44SJordan Rupprecht
121*99451b44SJordan Rupprecht    @add_test_categories(['pyapi'])
122*99451b44SJordan Rupprecht    @expectedFlakeyLinux("llvm.org/pr23730")  # Flaky, fails ~1/100 cases
123*99451b44SJordan Rupprecht    @skipIfWindows # This is flakey on Windows AND when it fails, it hangs: llvm.org/pr38373
124*99451b44SJordan Rupprecht    @skipIfNetBSD
125*99451b44SJordan Rupprecht    def test_wait_for_event(self):
126*99451b44SJordan Rupprecht        """Exercise SBListener.WaitForEvent() API."""
127*99451b44SJordan Rupprecht        self.build()
128*99451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
129*99451b44SJordan Rupprecht
130*99451b44SJordan Rupprecht        self.dbg.SetAsync(True)
131*99451b44SJordan Rupprecht
132*99451b44SJordan Rupprecht        # Create a target by the debugger.
133*99451b44SJordan Rupprecht        target = self.dbg.CreateTarget(exe)
134*99451b44SJordan Rupprecht        self.assertTrue(target, VALID_TARGET)
135*99451b44SJordan Rupprecht
136*99451b44SJordan Rupprecht        # Now create a breakpoint on main.c by name 'c'.
137*99451b44SJordan Rupprecht        breakpoint = target.BreakpointCreateByName('c', 'a.out')
138*99451b44SJordan Rupprecht        #print("breakpoint:", breakpoint)
139*99451b44SJordan Rupprecht        self.assertTrue(breakpoint and
140*99451b44SJordan Rupprecht                        breakpoint.GetNumLocations() == 1,
141*99451b44SJordan Rupprecht                        VALID_BREAKPOINT)
142*99451b44SJordan Rupprecht
143*99451b44SJordan Rupprecht        # Get the debugger listener.
144*99451b44SJordan Rupprecht        listener = self.dbg.GetListener()
145*99451b44SJordan Rupprecht
146*99451b44SJordan Rupprecht        # Now launch the process, and do not stop at entry point.
147*99451b44SJordan Rupprecht        error = lldb.SBError()
148*99451b44SJordan Rupprecht        process = target.Launch(listener,
149*99451b44SJordan Rupprecht                                None,      # argv
150*99451b44SJordan Rupprecht                                None,      # envp
151*99451b44SJordan Rupprecht                                None,      # stdin_path
152*99451b44SJordan Rupprecht                                None,      # stdout_path
153*99451b44SJordan Rupprecht                                None,      # stderr_path
154*99451b44SJordan Rupprecht                                None,      # working directory
155*99451b44SJordan Rupprecht                                0,         # launch flags
156*99451b44SJordan Rupprecht                                False,     # Stop at entry
157*99451b44SJordan Rupprecht                                error)     # error
158*99451b44SJordan Rupprecht        self.assertTrue(error.Success() and process, PROCESS_IS_VALID)
159*99451b44SJordan Rupprecht
160*99451b44SJordan Rupprecht        # Create an empty event object.
161*99451b44SJordan Rupprecht        event = lldb.SBEvent()
162*99451b44SJordan Rupprecht        self.assertFalse(event, "Event should not be valid initially")
163*99451b44SJordan Rupprecht
164*99451b44SJordan Rupprecht        # Create MyListeningThread to wait for any kind of event.
165*99451b44SJordan Rupprecht        import threading
166*99451b44SJordan Rupprecht
167*99451b44SJordan Rupprecht        class MyListeningThread(threading.Thread):
168*99451b44SJordan Rupprecht
169*99451b44SJordan Rupprecht            def run(self):
170*99451b44SJordan Rupprecht                count = 0
171*99451b44SJordan Rupprecht                # Let's only try at most 3 times to retrieve any kind of event.
172*99451b44SJordan Rupprecht                while not count > 3:
173*99451b44SJordan Rupprecht                    if listener.WaitForEvent(5, event):
174*99451b44SJordan Rupprecht                        #print("Got a valid event:", event)
175*99451b44SJordan Rupprecht                        #print("Event data flavor:", event.GetDataFlavor())
176*99451b44SJordan Rupprecht                        #print("Event type:", lldbutil.state_type_to_str(event.GetType()))
177*99451b44SJordan Rupprecht                        listener.Clear()
178*99451b44SJordan Rupprecht                        return
179*99451b44SJordan Rupprecht                    count = count + 1
180*99451b44SJordan Rupprecht                    print("Timeout: listener.WaitForEvent")
181*99451b44SJordan Rupprecht                listener.Clear()
182*99451b44SJordan Rupprecht                return
183*99451b44SJordan Rupprecht
184*99451b44SJordan Rupprecht        # Use Python API to kill the process.  The listening thread should be
185*99451b44SJordan Rupprecht        # able to receive a state changed event.
186*99451b44SJordan Rupprecht        process.Kill()
187*99451b44SJordan Rupprecht
188*99451b44SJordan Rupprecht        # Let's start the listening thread to retrieve the event.
189*99451b44SJordan Rupprecht        my_thread = MyListeningThread()
190*99451b44SJordan Rupprecht        my_thread.start()
191*99451b44SJordan Rupprecht
192*99451b44SJordan Rupprecht        # Wait until the 'MyListeningThread' terminates.
193*99451b44SJordan Rupprecht        my_thread.join()
194*99451b44SJordan Rupprecht
195*99451b44SJordan Rupprecht        self.assertTrue(event,
196*99451b44SJordan Rupprecht                        "My listening thread successfully received an event")
197*99451b44SJordan Rupprecht
198*99451b44SJordan Rupprecht    @skipIfFreeBSD  # llvm.org/pr21325
199*99451b44SJordan Rupprecht    @add_test_categories(['pyapi'])
200*99451b44SJordan Rupprecht    @expectedFailureAll(
201*99451b44SJordan Rupprecht        oslist=["linux"],
202*99451b44SJordan Rupprecht        bugnumber="llvm.org/pr23617 Flaky, fails ~1/10 cases")
203*99451b44SJordan Rupprecht    @skipIfWindows # This is flakey on Windows AND when it fails, it hangs: llvm.org/pr38373
204*99451b44SJordan Rupprecht    @expectedFlakeyNetBSD
205*99451b44SJordan Rupprecht    def test_add_listener_to_broadcaster(self):
206*99451b44SJordan Rupprecht        """Exercise some SBBroadcaster APIs."""
207*99451b44SJordan Rupprecht        self.build()
208*99451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
209*99451b44SJordan Rupprecht
210*99451b44SJordan Rupprecht        self.dbg.SetAsync(True)
211*99451b44SJordan Rupprecht
212*99451b44SJordan Rupprecht        # Create a target by the debugger.
213*99451b44SJordan Rupprecht        target = self.dbg.CreateTarget(exe)
214*99451b44SJordan Rupprecht        self.assertTrue(target, VALID_TARGET)
215*99451b44SJordan Rupprecht
216*99451b44SJordan Rupprecht        # Now create a breakpoint on main.c by name 'c'.
217*99451b44SJordan Rupprecht        breakpoint = target.BreakpointCreateByName('c', 'a.out')
218*99451b44SJordan Rupprecht        #print("breakpoint:", breakpoint)
219*99451b44SJordan Rupprecht        self.assertTrue(breakpoint and
220*99451b44SJordan Rupprecht                        breakpoint.GetNumLocations() == 1,
221*99451b44SJordan Rupprecht                        VALID_BREAKPOINT)
222*99451b44SJordan Rupprecht
223*99451b44SJordan Rupprecht        listener = lldb.SBListener("my listener")
224*99451b44SJordan Rupprecht
225*99451b44SJordan Rupprecht        # Now launch the process, and do not stop at the entry point.
226*99451b44SJordan Rupprecht        error = lldb.SBError()
227*99451b44SJordan Rupprecht        process = target.Launch(listener,
228*99451b44SJordan Rupprecht                                None,      # argv
229*99451b44SJordan Rupprecht                                None,      # envp
230*99451b44SJordan Rupprecht                                None,      # stdin_path
231*99451b44SJordan Rupprecht                                None,      # stdout_path
232*99451b44SJordan Rupprecht                                None,      # stderr_path
233*99451b44SJordan Rupprecht                                None,      # working directory
234*99451b44SJordan Rupprecht                                0,         # launch flags
235*99451b44SJordan Rupprecht                                False,     # Stop at entry
236*99451b44SJordan Rupprecht                                error)     # error
237*99451b44SJordan Rupprecht
238*99451b44SJordan Rupprecht        # Create an empty event object.
239*99451b44SJordan Rupprecht        event = lldb.SBEvent()
240*99451b44SJordan Rupprecht        self.assertFalse(event, "Event should not be valid initially")
241*99451b44SJordan Rupprecht
242*99451b44SJordan Rupprecht        # The finite state machine for our custom listening thread, with an
243*99451b44SJordan Rupprecht        # initial state of None, which means no event has been received.
244*99451b44SJordan Rupprecht        # It changes to 'connected' after 'connected' event is received (for remote platforms)
245*99451b44SJordan Rupprecht        # It changes to 'running' after 'running' event is received (should happen only if the
246*99451b44SJordan Rupprecht        # currentstate is either 'None' or 'connected')
247*99451b44SJordan Rupprecht        # It changes to 'stopped' if a 'stopped' event is received (should happen only if the
248*99451b44SJordan Rupprecht        # current state is 'running'.)
249*99451b44SJordan Rupprecht        self.state = None
250*99451b44SJordan Rupprecht
251*99451b44SJordan Rupprecht        # Create MyListeningThread to wait for state changed events.
252*99451b44SJordan Rupprecht        # By design, a "running" event is expected following by a "stopped"
253*99451b44SJordan Rupprecht        # event.
254*99451b44SJordan Rupprecht        import threading
255*99451b44SJordan Rupprecht
256*99451b44SJordan Rupprecht        class MyListeningThread(threading.Thread):
257*99451b44SJordan Rupprecht
258*99451b44SJordan Rupprecht            def run(self):
259*99451b44SJordan Rupprecht                #print("Running MyListeningThread:", self)
260*99451b44SJordan Rupprecht
261*99451b44SJordan Rupprecht                # Regular expression pattern for the event description.
262*99451b44SJordan Rupprecht                pattern = re.compile("data = {.*, state = (.*)}$")
263*99451b44SJordan Rupprecht
264*99451b44SJordan Rupprecht                # Let's only try at most 6 times to retrieve our events.
265*99451b44SJordan Rupprecht                count = 0
266*99451b44SJordan Rupprecht                while True:
267*99451b44SJordan Rupprecht                    if listener.WaitForEvent(5, event):
268*99451b44SJordan Rupprecht                        desc = lldbutil.get_description(event)
269*99451b44SJordan Rupprecht                        #print("Event description:", desc)
270*99451b44SJordan Rupprecht                        match = pattern.search(desc)
271*99451b44SJordan Rupprecht                        if not match:
272*99451b44SJordan Rupprecht                            break
273*99451b44SJordan Rupprecht                        if match.group(1) == 'connected':
274*99451b44SJordan Rupprecht                            # When debugging remote targets with lldb-server, we
275*99451b44SJordan Rupprecht                            # first get the 'connected' event.
276*99451b44SJordan Rupprecht                            self.context.assertTrue(self.context.state is None)
277*99451b44SJordan Rupprecht                            self.context.state = 'connected'
278*99451b44SJordan Rupprecht                            continue
279*99451b44SJordan Rupprecht                        elif match.group(1) == 'running':
280*99451b44SJordan Rupprecht                            self.context.assertTrue(
281*99451b44SJordan Rupprecht                                self.context.state is None or self.context.state == 'connected')
282*99451b44SJordan Rupprecht                            self.context.state = 'running'
283*99451b44SJordan Rupprecht                            continue
284*99451b44SJordan Rupprecht                        elif match.group(1) == 'stopped':
285*99451b44SJordan Rupprecht                            self.context.assertTrue(
286*99451b44SJordan Rupprecht                                self.context.state == 'running')
287*99451b44SJordan Rupprecht                            # Whoopee, both events have been received!
288*99451b44SJordan Rupprecht                            self.context.state = 'stopped'
289*99451b44SJordan Rupprecht                            break
290*99451b44SJordan Rupprecht                        else:
291*99451b44SJordan Rupprecht                            break
292*99451b44SJordan Rupprecht                    print("Timeout: listener.WaitForEvent")
293*99451b44SJordan Rupprecht                    count = count + 1
294*99451b44SJordan Rupprecht                    if count > 6:
295*99451b44SJordan Rupprecht                        break
296*99451b44SJordan Rupprecht                listener.Clear()
297*99451b44SJordan Rupprecht                return
298*99451b44SJordan Rupprecht
299*99451b44SJordan Rupprecht        # Use Python API to continue the process.  The listening thread should be
300*99451b44SJordan Rupprecht        # able to receive the state changed events.
301*99451b44SJordan Rupprecht        process.Continue()
302*99451b44SJordan Rupprecht
303*99451b44SJordan Rupprecht        # Start the listening thread to receive the "running" followed by the
304*99451b44SJordan Rupprecht        # "stopped" events.
305*99451b44SJordan Rupprecht        my_thread = MyListeningThread()
306*99451b44SJordan Rupprecht        # Supply the enclosing context so that our listening thread can access
307*99451b44SJordan Rupprecht        # the 'state' variable.
308*99451b44SJordan Rupprecht        my_thread.context = self
309*99451b44SJordan Rupprecht        my_thread.start()
310*99451b44SJordan Rupprecht
311*99451b44SJordan Rupprecht        # Wait until the 'MyListeningThread' terminates.
312*99451b44SJordan Rupprecht        my_thread.join()
313*99451b44SJordan Rupprecht
314*99451b44SJordan Rupprecht        # The final judgement. :-)
315*99451b44SJordan Rupprecht        self.assertTrue(self.state == 'stopped',
316*99451b44SJordan Rupprecht                        "Both expected state changed events received")
317