199451b44SJordan Rupprecht"""
299451b44SJordan RupprechtTest lldb Python event APIs.
399451b44SJordan Rupprecht"""
499451b44SJordan Rupprecht
599451b44SJordan Rupprechtfrom __future__ import print_function
699451b44SJordan Rupprecht
799451b44SJordan Rupprecht
899451b44SJordan Rupprechtimport re
999451b44SJordan Rupprechtimport lldb
1099451b44SJordan Rupprechtfrom lldbsuite.test.decorators import *
1199451b44SJordan Rupprechtfrom lldbsuite.test.lldbtest import *
1299451b44SJordan Rupprechtfrom lldbsuite.test import lldbutil
1399451b44SJordan Rupprecht
1499451b44SJordan Rupprecht
1599451b44SJordan Rupprecht@skipIfLinux   # llvm.org/pr25924, sometimes generating SIGSEGV
1699451b44SJordan Rupprechtclass EventAPITestCase(TestBase):
1799451b44SJordan Rupprecht    NO_DEBUG_INFO_TESTCASE = True
1899451b44SJordan Rupprecht
1999451b44SJordan Rupprecht    def setUp(self):
2099451b44SJordan Rupprecht        # Call super's setUp().
2199451b44SJordan Rupprecht        TestBase.setUp(self)
2299451b44SJordan Rupprecht        # Find the line number to of function 'c'.
2399451b44SJordan Rupprecht        self.line = line_number(
2499451b44SJordan Rupprecht            'main.c', '// Find the line number of function "c" here.')
2599451b44SJordan Rupprecht
2699451b44SJordan Rupprecht    @expectedFailureAll(
2799451b44SJordan Rupprecht        oslist=["linux"],
2899451b44SJordan Rupprecht        bugnumber="llvm.org/pr23730 Flaky, fails ~1/10 cases")
2999451b44SJordan Rupprecht    @skipIfWindows # This is flakey on Windows AND when it fails, it hangs: llvm.org/pr38373
3099451b44SJordan Rupprecht    @skipIfNetBSD
3199451b44SJordan Rupprecht    def test_listen_for_and_print_event(self):
3299451b44SJordan Rupprecht        """Exercise SBEvent API."""
3399451b44SJordan Rupprecht        self.build()
3499451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
3599451b44SJordan Rupprecht
3699451b44SJordan Rupprecht        self.dbg.SetAsync(True)
3799451b44SJordan Rupprecht
3899451b44SJordan Rupprecht        # Create a target by the debugger.
3999451b44SJordan Rupprecht        target = self.dbg.CreateTarget(exe)
4099451b44SJordan Rupprecht        self.assertTrue(target, VALID_TARGET)
4199451b44SJordan Rupprecht
4299451b44SJordan Rupprecht        # Now create a breakpoint on main.c by name 'c'.
4399451b44SJordan Rupprecht        breakpoint = target.BreakpointCreateByName('c', 'a.out')
4499451b44SJordan Rupprecht
4599451b44SJordan Rupprecht        listener = lldb.SBListener("my listener")
4699451b44SJordan Rupprecht
4799451b44SJordan Rupprecht        # Now launch the process, and do not stop at the entry point.
4899451b44SJordan Rupprecht        error = lldb.SBError()
492ddba09eSJonas Devlieghere        flags = target.GetLaunchInfo().GetLaunchFlags()
5099451b44SJordan Rupprecht        process = target.Launch(listener,
5199451b44SJordan Rupprecht                                None,      # argv
5299451b44SJordan Rupprecht                                None,      # envp
5399451b44SJordan Rupprecht                                None,      # stdin_path
5499451b44SJordan Rupprecht                                None,      # stdout_path
5599451b44SJordan Rupprecht                                None,      # stderr_path
5699451b44SJordan Rupprecht                                None,      # working directory
572ddba09eSJonas Devlieghere                                flags,     # launch flags
5899451b44SJordan Rupprecht                                False,     # Stop at entry
5999451b44SJordan Rupprecht                                error)     # error
6099451b44SJordan Rupprecht
610ed758b2SDave Lee        self.assertEqual(
620ed758b2SDave Lee            process.GetState(), lldb.eStateStopped,
6399451b44SJordan Rupprecht            PROCESS_STOPPED)
6499451b44SJordan Rupprecht
6599451b44SJordan Rupprecht        # Create an empty event object.
6699451b44SJordan Rupprecht        event = lldb.SBEvent()
6799451b44SJordan Rupprecht
6899451b44SJordan Rupprecht        traceOn = self.TraceOn()
6999451b44SJordan Rupprecht        if traceOn:
7099451b44SJordan Rupprecht            lldbutil.print_stacktraces(process)
7199451b44SJordan Rupprecht
7299451b44SJordan Rupprecht        # Create MyListeningThread class to wait for any kind of event.
7399451b44SJordan Rupprecht        import threading
7499451b44SJordan Rupprecht
7599451b44SJordan Rupprecht        class MyListeningThread(threading.Thread):
7699451b44SJordan Rupprecht
7799451b44SJordan Rupprecht            def run(self):
7899451b44SJordan Rupprecht                count = 0
7999451b44SJordan Rupprecht                # Let's only try at most 4 times to retrieve any kind of event.
8099451b44SJordan Rupprecht                # After that, the thread exits.
8199451b44SJordan Rupprecht                while not count > 3:
8299451b44SJordan Rupprecht                    if traceOn:
8399451b44SJordan Rupprecht                        print("Try wait for event...")
8499451b44SJordan Rupprecht                    if listener.WaitForEvent(5, event):
8599451b44SJordan Rupprecht                        if traceOn:
8699451b44SJordan Rupprecht                            desc = lldbutil.get_description(event)
8799451b44SJordan Rupprecht                            print("Event description:", desc)
8899451b44SJordan Rupprecht                            print("Event data flavor:", event.GetDataFlavor())
8999451b44SJordan Rupprecht                            print(
9099451b44SJordan Rupprecht                                "Process state:",
9199451b44SJordan Rupprecht                                lldbutil.state_type_to_str(
9299451b44SJordan Rupprecht                                    process.GetState()))
9399451b44SJordan Rupprecht                            print()
9499451b44SJordan Rupprecht                    else:
9599451b44SJordan Rupprecht                        if traceOn:
9699451b44SJordan Rupprecht                            print("timeout occurred waiting for event...")
9799451b44SJordan Rupprecht                    count = count + 1
9899451b44SJordan Rupprecht                listener.Clear()
9999451b44SJordan Rupprecht                return
10099451b44SJordan Rupprecht
10199451b44SJordan Rupprecht        # Let's start the listening thread to retrieve the events.
10299451b44SJordan Rupprecht        my_thread = MyListeningThread()
10399451b44SJordan Rupprecht        my_thread.start()
10499451b44SJordan Rupprecht
10599451b44SJordan Rupprecht        # Use Python API to continue the process.  The listening thread should be
10699451b44SJordan Rupprecht        # able to receive the state changed events.
10799451b44SJordan Rupprecht        process.Continue()
10899451b44SJordan Rupprecht
10999451b44SJordan Rupprecht        # Use Python API to kill the process.  The listening thread should be
11099451b44SJordan Rupprecht        # able to receive the state changed event, too.
11199451b44SJordan Rupprecht        process.Kill()
11299451b44SJordan Rupprecht
11399451b44SJordan Rupprecht        # Wait until the 'MyListeningThread' terminates.
11499451b44SJordan Rupprecht        my_thread.join()
11599451b44SJordan Rupprecht
11699451b44SJordan Rupprecht        # Shouldn't we be testing against some kind of expectation here?
11799451b44SJordan Rupprecht
11899451b44SJordan Rupprecht    @expectedFlakeyLinux("llvm.org/pr23730")  # Flaky, fails ~1/100 cases
11999451b44SJordan Rupprecht    @skipIfWindows # This is flakey on Windows AND when it fails, it hangs: llvm.org/pr38373
12099451b44SJordan Rupprecht    @skipIfNetBSD
12199451b44SJordan Rupprecht    def test_wait_for_event(self):
12299451b44SJordan Rupprecht        """Exercise SBListener.WaitForEvent() API."""
12399451b44SJordan Rupprecht        self.build()
12499451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
12599451b44SJordan Rupprecht
12699451b44SJordan Rupprecht        self.dbg.SetAsync(True)
12799451b44SJordan Rupprecht
12899451b44SJordan Rupprecht        # Create a target by the debugger.
12999451b44SJordan Rupprecht        target = self.dbg.CreateTarget(exe)
13099451b44SJordan Rupprecht        self.assertTrue(target, VALID_TARGET)
13199451b44SJordan Rupprecht
13299451b44SJordan Rupprecht        # Now create a breakpoint on main.c by name 'c'.
13399451b44SJordan Rupprecht        breakpoint = target.BreakpointCreateByName('c', 'a.out')
134b321b429SJonas Devlieghere        self.trace("breakpoint:", breakpoint)
13599451b44SJordan Rupprecht        self.assertTrue(breakpoint and
13699451b44SJordan Rupprecht                        breakpoint.GetNumLocations() == 1,
13799451b44SJordan Rupprecht                        VALID_BREAKPOINT)
13899451b44SJordan Rupprecht
13999451b44SJordan Rupprecht        # Get the debugger listener.
14099451b44SJordan Rupprecht        listener = self.dbg.GetListener()
14199451b44SJordan Rupprecht
14299451b44SJordan Rupprecht        # Now launch the process, and do not stop at entry point.
14399451b44SJordan Rupprecht        error = lldb.SBError()
1442ddba09eSJonas Devlieghere        flags = target.GetLaunchInfo().GetLaunchFlags()
14599451b44SJordan Rupprecht        process = target.Launch(listener,
14699451b44SJordan Rupprecht                                None,      # argv
14799451b44SJordan Rupprecht                                None,      # envp
14899451b44SJordan Rupprecht                                None,      # stdin_path
14999451b44SJordan Rupprecht                                None,      # stdout_path
15099451b44SJordan Rupprecht                                None,      # stderr_path
15199451b44SJordan Rupprecht                                None,      # working directory
1522ddba09eSJonas Devlieghere                                flags,     # launch flags
15399451b44SJordan Rupprecht                                False,     # Stop at entry
15499451b44SJordan Rupprecht                                error)     # error
15599451b44SJordan Rupprecht        self.assertTrue(error.Success() and process, PROCESS_IS_VALID)
15699451b44SJordan Rupprecht
15799451b44SJordan Rupprecht        # Create an empty event object.
15899451b44SJordan Rupprecht        event = lldb.SBEvent()
15999451b44SJordan Rupprecht        self.assertFalse(event, "Event should not be valid initially")
16099451b44SJordan Rupprecht
16199451b44SJordan Rupprecht        # Create MyListeningThread to wait for any kind of event.
16299451b44SJordan Rupprecht        import threading
16399451b44SJordan Rupprecht
16499451b44SJordan Rupprecht        class MyListeningThread(threading.Thread):
16599451b44SJordan Rupprecht
16699451b44SJordan Rupprecht            def run(self):
16799451b44SJordan Rupprecht                count = 0
16899451b44SJordan Rupprecht                # Let's only try at most 3 times to retrieve any kind of event.
16999451b44SJordan Rupprecht                while not count > 3:
17099451b44SJordan Rupprecht                    if listener.WaitForEvent(5, event):
171*2a29c3f7SMed Ismail Bennani                        self.context.trace("Got a valid event:", event)
172*2a29c3f7SMed Ismail Bennani                        self.context.trace("Event data flavor:", event.GetDataFlavor())
173*2a29c3f7SMed Ismail Bennani                        self.context.trace("Event type:", lldbutil.state_type_to_str(event.GetType()))
17499451b44SJordan Rupprecht                        listener.Clear()
17599451b44SJordan Rupprecht                        return
17699451b44SJordan Rupprecht                    count = count + 1
17799451b44SJordan Rupprecht                    print("Timeout: listener.WaitForEvent")
17899451b44SJordan Rupprecht                listener.Clear()
17999451b44SJordan Rupprecht                return
18099451b44SJordan Rupprecht
18199451b44SJordan Rupprecht        # Use Python API to kill the process.  The listening thread should be
18299451b44SJordan Rupprecht        # able to receive a state changed event.
18399451b44SJordan Rupprecht        process.Kill()
18499451b44SJordan Rupprecht
18599451b44SJordan Rupprecht        # Let's start the listening thread to retrieve the event.
18699451b44SJordan Rupprecht        my_thread = MyListeningThread()
187*2a29c3f7SMed Ismail Bennani        my_thread.context = self
18899451b44SJordan Rupprecht        my_thread.start()
18999451b44SJordan Rupprecht
19099451b44SJordan Rupprecht        # Wait until the 'MyListeningThread' terminates.
19199451b44SJordan Rupprecht        my_thread.join()
19299451b44SJordan Rupprecht
19399451b44SJordan Rupprecht        self.assertTrue(event,
19499451b44SJordan Rupprecht                        "My listening thread successfully received an event")
19599451b44SJordan Rupprecht
19699451b44SJordan Rupprecht    @expectedFailureAll(
19799451b44SJordan Rupprecht        oslist=["linux"],
19899451b44SJordan Rupprecht        bugnumber="llvm.org/pr23617 Flaky, fails ~1/10 cases")
19999451b44SJordan Rupprecht    @skipIfWindows # This is flakey on Windows AND when it fails, it hangs: llvm.org/pr38373
20099562332SMichał Górny    @expectedFailureNetBSD
20199451b44SJordan Rupprecht    def test_add_listener_to_broadcaster(self):
20299451b44SJordan Rupprecht        """Exercise some SBBroadcaster APIs."""
20399451b44SJordan Rupprecht        self.build()
20499451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
20599451b44SJordan Rupprecht
20699451b44SJordan Rupprecht        self.dbg.SetAsync(True)
20799451b44SJordan Rupprecht
20899451b44SJordan Rupprecht        # Create a target by the debugger.
20999451b44SJordan Rupprecht        target = self.dbg.CreateTarget(exe)
21099451b44SJordan Rupprecht        self.assertTrue(target, VALID_TARGET)
21199451b44SJordan Rupprecht
21299451b44SJordan Rupprecht        # Now create a breakpoint on main.c by name 'c'.
21399451b44SJordan Rupprecht        breakpoint = target.BreakpointCreateByName('c', 'a.out')
214b321b429SJonas Devlieghere        self.trace("breakpoint:", breakpoint)
21599451b44SJordan Rupprecht        self.assertTrue(breakpoint and
21699451b44SJordan Rupprecht                        breakpoint.GetNumLocations() == 1,
21799451b44SJordan Rupprecht                        VALID_BREAKPOINT)
21899451b44SJordan Rupprecht
21999451b44SJordan Rupprecht        listener = lldb.SBListener("my listener")
22099451b44SJordan Rupprecht
22199451b44SJordan Rupprecht        # Now launch the process, and do not stop at the entry point.
22299451b44SJordan Rupprecht        error = lldb.SBError()
2232ddba09eSJonas Devlieghere        flags = target.GetLaunchInfo().GetLaunchFlags()
22499451b44SJordan Rupprecht        process = target.Launch(listener,
22599451b44SJordan Rupprecht                                None,      # argv
22699451b44SJordan Rupprecht                                None,      # envp
22799451b44SJordan Rupprecht                                None,      # stdin_path
22899451b44SJordan Rupprecht                                None,      # stdout_path
22999451b44SJordan Rupprecht                                None,      # stderr_path
23099451b44SJordan Rupprecht                                None,      # working directory
2312ddba09eSJonas Devlieghere                                flags,     # launch flags
23299451b44SJordan Rupprecht                                False,     # Stop at entry
23399451b44SJordan Rupprecht                                error)     # error
23499451b44SJordan Rupprecht
23599451b44SJordan Rupprecht        # Create an empty event object.
23699451b44SJordan Rupprecht        event = lldb.SBEvent()
23799451b44SJordan Rupprecht        self.assertFalse(event, "Event should not be valid initially")
23899451b44SJordan Rupprecht
23999451b44SJordan Rupprecht        # The finite state machine for our custom listening thread, with an
24099451b44SJordan Rupprecht        # initial state of None, which means no event has been received.
24199451b44SJordan Rupprecht        # It changes to 'connected' after 'connected' event is received (for remote platforms)
24299451b44SJordan Rupprecht        # It changes to 'running' after 'running' event is received (should happen only if the
24399451b44SJordan Rupprecht        # currentstate is either 'None' or 'connected')
24499451b44SJordan Rupprecht        # It changes to 'stopped' if a 'stopped' event is received (should happen only if the
24599451b44SJordan Rupprecht        # current state is 'running'.)
24699451b44SJordan Rupprecht        self.state = None
24799451b44SJordan Rupprecht
24899451b44SJordan Rupprecht        # Create MyListeningThread to wait for state changed events.
24999451b44SJordan Rupprecht        # By design, a "running" event is expected following by a "stopped"
25099451b44SJordan Rupprecht        # event.
25199451b44SJordan Rupprecht        import threading
25299451b44SJordan Rupprecht
25399451b44SJordan Rupprecht        class MyListeningThread(threading.Thread):
25499451b44SJordan Rupprecht
25599451b44SJordan Rupprecht            def run(self):
256*2a29c3f7SMed Ismail Bennani                self.context.trace("Running MyListeningThread:", self)
25799451b44SJordan Rupprecht
25899451b44SJordan Rupprecht                # Regular expression pattern for the event description.
25999451b44SJordan Rupprecht                pattern = re.compile("data = {.*, state = (.*)}$")
26099451b44SJordan Rupprecht
26199451b44SJordan Rupprecht                # Let's only try at most 6 times to retrieve our events.
26299451b44SJordan Rupprecht                count = 0
26399451b44SJordan Rupprecht                while True:
26499451b44SJordan Rupprecht                    if listener.WaitForEvent(5, event):
26599451b44SJordan Rupprecht                        desc = lldbutil.get_description(event)
266*2a29c3f7SMed Ismail Bennani                        self.context.trace("Event description:", desc)
26799451b44SJordan Rupprecht                        match = pattern.search(desc)
26899451b44SJordan Rupprecht                        if not match:
26999451b44SJordan Rupprecht                            break
27099451b44SJordan Rupprecht                        if match.group(1) == 'connected':
27199451b44SJordan Rupprecht                            # When debugging remote targets with lldb-server, we
27299451b44SJordan Rupprecht                            # first get the 'connected' event.
27399451b44SJordan Rupprecht                            self.context.assertTrue(self.context.state is None)
27499451b44SJordan Rupprecht                            self.context.state = 'connected'
27599451b44SJordan Rupprecht                            continue
27699451b44SJordan Rupprecht                        elif match.group(1) == 'running':
27799451b44SJordan Rupprecht                            self.context.assertTrue(
27899451b44SJordan Rupprecht                                self.context.state is None or self.context.state == 'connected')
27999451b44SJordan Rupprecht                            self.context.state = 'running'
28099451b44SJordan Rupprecht                            continue
28199451b44SJordan Rupprecht                        elif match.group(1) == 'stopped':
28299451b44SJordan Rupprecht                            self.context.assertTrue(
28399451b44SJordan Rupprecht                                self.context.state == 'running')
28499451b44SJordan Rupprecht                            # Whoopee, both events have been received!
28599451b44SJordan Rupprecht                            self.context.state = 'stopped'
28699451b44SJordan Rupprecht                            break
28799451b44SJordan Rupprecht                        else:
28899451b44SJordan Rupprecht                            break
28999451b44SJordan Rupprecht                    print("Timeout: listener.WaitForEvent")
29099451b44SJordan Rupprecht                    count = count + 1
29199451b44SJordan Rupprecht                    if count > 6:
29299451b44SJordan Rupprecht                        break
29399451b44SJordan Rupprecht                listener.Clear()
29499451b44SJordan Rupprecht                return
29599451b44SJordan Rupprecht
29699451b44SJordan Rupprecht        # Use Python API to continue the process.  The listening thread should be
29799451b44SJordan Rupprecht        # able to receive the state changed events.
29899451b44SJordan Rupprecht        process.Continue()
29999451b44SJordan Rupprecht
30099451b44SJordan Rupprecht        # Start the listening thread to receive the "running" followed by the
30199451b44SJordan Rupprecht        # "stopped" events.
30299451b44SJordan Rupprecht        my_thread = MyListeningThread()
30399451b44SJordan Rupprecht        # Supply the enclosing context so that our listening thread can access
30499451b44SJordan Rupprecht        # the 'state' variable.
30599451b44SJordan Rupprecht        my_thread.context = self
30699451b44SJordan Rupprecht        my_thread.start()
30799451b44SJordan Rupprecht
30899451b44SJordan Rupprecht        # Wait until the 'MyListeningThread' terminates.
30999451b44SJordan Rupprecht        my_thread.join()
31099451b44SJordan Rupprecht
31199451b44SJordan Rupprecht        # The final judgement. :-)
312619e2e09SDave Lee        self.assertEqual(self.state, 'stopped',
31399451b44SJordan Rupprecht                        "Both expected state changed events received")
314