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