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 Rupprecht@skipIfDarwin
1799451b44SJordan Rupprechtclass EventAPITestCase(TestBase):
1899451b44SJordan Rupprecht
1999451b44SJordan Rupprecht    mydir = TestBase.compute_mydir(__file__)
2099451b44SJordan Rupprecht    NO_DEBUG_INFO_TESTCASE = True
2199451b44SJordan Rupprecht
2299451b44SJordan Rupprecht    def setUp(self):
2399451b44SJordan Rupprecht        # Call super's setUp().
2499451b44SJordan Rupprecht        TestBase.setUp(self)
2599451b44SJordan Rupprecht        # Find the line number to of function 'c'.
2699451b44SJordan Rupprecht        self.line = line_number(
2799451b44SJordan Rupprecht            'main.c', '// Find the line number of function "c" here.')
2899451b44SJordan Rupprecht
2999451b44SJordan Rupprecht    @add_test_categories(['pyapi'])
3099451b44SJordan Rupprecht    @expectedFailureAll(
3199451b44SJordan Rupprecht        oslist=["linux"],
3299451b44SJordan Rupprecht        bugnumber="llvm.org/pr23730 Flaky, fails ~1/10 cases")
3399451b44SJordan Rupprecht    @skipIfWindows # This is flakey on Windows AND when it fails, it hangs: llvm.org/pr38373
3499451b44SJordan Rupprecht    @skipIfNetBSD
3599451b44SJordan Rupprecht    def test_listen_for_and_print_event(self):
3699451b44SJordan Rupprecht        """Exercise SBEvent API."""
3799451b44SJordan Rupprecht        self.build()
3899451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
3999451b44SJordan Rupprecht
4099451b44SJordan Rupprecht        self.dbg.SetAsync(True)
4199451b44SJordan Rupprecht
4299451b44SJordan Rupprecht        # Create a target by the debugger.
4399451b44SJordan Rupprecht        target = self.dbg.CreateTarget(exe)
4499451b44SJordan Rupprecht        self.assertTrue(target, VALID_TARGET)
4599451b44SJordan Rupprecht
4699451b44SJordan Rupprecht        # Now create a breakpoint on main.c by name 'c'.
4799451b44SJordan Rupprecht        breakpoint = target.BreakpointCreateByName('c', 'a.out')
4899451b44SJordan Rupprecht
4999451b44SJordan Rupprecht        listener = lldb.SBListener("my listener")
5099451b44SJordan Rupprecht
5199451b44SJordan Rupprecht        # Now launch the process, and do not stop at the entry point.
5299451b44SJordan Rupprecht        error = lldb.SBError()
5399451b44SJordan Rupprecht        process = target.Launch(listener,
5499451b44SJordan Rupprecht                                None,      # argv
5599451b44SJordan Rupprecht                                None,      # envp
5699451b44SJordan Rupprecht                                None,      # stdin_path
5799451b44SJordan Rupprecht                                None,      # stdout_path
5899451b44SJordan Rupprecht                                None,      # stderr_path
5999451b44SJordan Rupprecht                                None,      # working directory
6099451b44SJordan Rupprecht                                0,         # launch flags
6199451b44SJordan Rupprecht                                False,     # Stop at entry
6299451b44SJordan Rupprecht                                error)     # error
6399451b44SJordan Rupprecht
6499451b44SJordan Rupprecht        self.assertTrue(
6599451b44SJordan Rupprecht            process.GetState() == lldb.eStateStopped,
6699451b44SJordan Rupprecht            PROCESS_STOPPED)
6799451b44SJordan Rupprecht
6899451b44SJordan Rupprecht        # Create an empty event object.
6999451b44SJordan Rupprecht        event = lldb.SBEvent()
7099451b44SJordan Rupprecht
7199451b44SJordan Rupprecht        traceOn = self.TraceOn()
7299451b44SJordan Rupprecht        if traceOn:
7399451b44SJordan Rupprecht            lldbutil.print_stacktraces(process)
7499451b44SJordan Rupprecht
7599451b44SJordan Rupprecht        # Create MyListeningThread class to wait for any kind of event.
7699451b44SJordan Rupprecht        import threading
7799451b44SJordan Rupprecht
7899451b44SJordan Rupprecht        class MyListeningThread(threading.Thread):
7999451b44SJordan Rupprecht
8099451b44SJordan Rupprecht            def run(self):
8199451b44SJordan Rupprecht                count = 0
8299451b44SJordan Rupprecht                # Let's only try at most 4 times to retrieve any kind of event.
8399451b44SJordan Rupprecht                # After that, the thread exits.
8499451b44SJordan Rupprecht                while not count > 3:
8599451b44SJordan Rupprecht                    if traceOn:
8699451b44SJordan Rupprecht                        print("Try wait for event...")
8799451b44SJordan Rupprecht                    if listener.WaitForEvent(5, event):
8899451b44SJordan Rupprecht                        if traceOn:
8999451b44SJordan Rupprecht                            desc = lldbutil.get_description(event)
9099451b44SJordan Rupprecht                            print("Event description:", desc)
9199451b44SJordan Rupprecht                            print("Event data flavor:", event.GetDataFlavor())
9299451b44SJordan Rupprecht                            print(
9399451b44SJordan Rupprecht                                "Process state:",
9499451b44SJordan Rupprecht                                lldbutil.state_type_to_str(
9599451b44SJordan Rupprecht                                    process.GetState()))
9699451b44SJordan Rupprecht                            print()
9799451b44SJordan Rupprecht                    else:
9899451b44SJordan Rupprecht                        if traceOn:
9999451b44SJordan Rupprecht                            print("timeout occurred waiting for event...")
10099451b44SJordan Rupprecht                    count = count + 1
10199451b44SJordan Rupprecht                listener.Clear()
10299451b44SJordan Rupprecht                return
10399451b44SJordan Rupprecht
10499451b44SJordan Rupprecht        # Let's start the listening thread to retrieve the events.
10599451b44SJordan Rupprecht        my_thread = MyListeningThread()
10699451b44SJordan Rupprecht        my_thread.start()
10799451b44SJordan Rupprecht
10899451b44SJordan Rupprecht        # Use Python API to continue the process.  The listening thread should be
10999451b44SJordan Rupprecht        # able to receive the state changed events.
11099451b44SJordan Rupprecht        process.Continue()
11199451b44SJordan Rupprecht
11299451b44SJordan Rupprecht        # Use Python API to kill the process.  The listening thread should be
11399451b44SJordan Rupprecht        # able to receive the state changed event, too.
11499451b44SJordan Rupprecht        process.Kill()
11599451b44SJordan Rupprecht
11699451b44SJordan Rupprecht        # Wait until the 'MyListeningThread' terminates.
11799451b44SJordan Rupprecht        my_thread.join()
11899451b44SJordan Rupprecht
11999451b44SJordan Rupprecht        # Shouldn't we be testing against some kind of expectation here?
12099451b44SJordan Rupprecht
12199451b44SJordan Rupprecht    @add_test_categories(['pyapi'])
12299451b44SJordan Rupprecht    @expectedFlakeyLinux("llvm.org/pr23730")  # Flaky, fails ~1/100 cases
12399451b44SJordan Rupprecht    @skipIfWindows # This is flakey on Windows AND when it fails, it hangs: llvm.org/pr38373
12499451b44SJordan Rupprecht    @skipIfNetBSD
12599451b44SJordan Rupprecht    def test_wait_for_event(self):
12699451b44SJordan Rupprecht        """Exercise SBListener.WaitForEvent() API."""
12799451b44SJordan Rupprecht        self.build()
12899451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
12999451b44SJordan Rupprecht
13099451b44SJordan Rupprecht        self.dbg.SetAsync(True)
13199451b44SJordan Rupprecht
13299451b44SJordan Rupprecht        # Create a target by the debugger.
13399451b44SJordan Rupprecht        target = self.dbg.CreateTarget(exe)
13499451b44SJordan Rupprecht        self.assertTrue(target, VALID_TARGET)
13599451b44SJordan Rupprecht
13699451b44SJordan Rupprecht        # Now create a breakpoint on main.c by name 'c'.
13799451b44SJordan Rupprecht        breakpoint = target.BreakpointCreateByName('c', 'a.out')
138*b321b429SJonas Devlieghere        self.trace("breakpoint:", breakpoint)
13999451b44SJordan Rupprecht        self.assertTrue(breakpoint and
14099451b44SJordan Rupprecht                        breakpoint.GetNumLocations() == 1,
14199451b44SJordan Rupprecht                        VALID_BREAKPOINT)
14299451b44SJordan Rupprecht
14399451b44SJordan Rupprecht        # Get the debugger listener.
14499451b44SJordan Rupprecht        listener = self.dbg.GetListener()
14599451b44SJordan Rupprecht
14699451b44SJordan Rupprecht        # Now launch the process, and do not stop at entry point.
14799451b44SJordan Rupprecht        error = lldb.SBError()
14899451b44SJordan Rupprecht        process = target.Launch(listener,
14999451b44SJordan Rupprecht                                None,      # argv
15099451b44SJordan Rupprecht                                None,      # envp
15199451b44SJordan Rupprecht                                None,      # stdin_path
15299451b44SJordan Rupprecht                                None,      # stdout_path
15399451b44SJordan Rupprecht                                None,      # stderr_path
15499451b44SJordan Rupprecht                                None,      # working directory
15599451b44SJordan Rupprecht                                0,         # launch flags
15699451b44SJordan Rupprecht                                False,     # Stop at entry
15799451b44SJordan Rupprecht                                error)     # error
15899451b44SJordan Rupprecht        self.assertTrue(error.Success() and process, PROCESS_IS_VALID)
15999451b44SJordan Rupprecht
16099451b44SJordan Rupprecht        # Create an empty event object.
16199451b44SJordan Rupprecht        event = lldb.SBEvent()
16299451b44SJordan Rupprecht        self.assertFalse(event, "Event should not be valid initially")
16399451b44SJordan Rupprecht
16499451b44SJordan Rupprecht        # Create MyListeningThread to wait for any kind of event.
16599451b44SJordan Rupprecht        import threading
16699451b44SJordan Rupprecht
16799451b44SJordan Rupprecht        class MyListeningThread(threading.Thread):
16899451b44SJordan Rupprecht
16999451b44SJordan Rupprecht            def run(self):
17099451b44SJordan Rupprecht                count = 0
17199451b44SJordan Rupprecht                # Let's only try at most 3 times to retrieve any kind of event.
17299451b44SJordan Rupprecht                while not count > 3:
17399451b44SJordan Rupprecht                    if listener.WaitForEvent(5, event):
174*b321b429SJonas Devlieghere                        self.trace("Got a valid event:", event)
175*b321b429SJonas Devlieghere                        self.trace("Event data flavor:", event.GetDataFlavor())
176*b321b429SJonas Devlieghere                        self.trace("Event type:", lldbutil.state_type_to_str(event.GetType()))
17799451b44SJordan Rupprecht                        listener.Clear()
17899451b44SJordan Rupprecht                        return
17999451b44SJordan Rupprecht                    count = count + 1
18099451b44SJordan Rupprecht                    print("Timeout: listener.WaitForEvent")
18199451b44SJordan Rupprecht                listener.Clear()
18299451b44SJordan Rupprecht                return
18399451b44SJordan Rupprecht
18499451b44SJordan Rupprecht        # Use Python API to kill the process.  The listening thread should be
18599451b44SJordan Rupprecht        # able to receive a state changed event.
18699451b44SJordan Rupprecht        process.Kill()
18799451b44SJordan Rupprecht
18899451b44SJordan Rupprecht        # Let's start the listening thread to retrieve the event.
18999451b44SJordan Rupprecht        my_thread = MyListeningThread()
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    @skipIfFreeBSD  # llvm.org/pr21325
19999451b44SJordan Rupprecht    @add_test_categories(['pyapi'])
20099451b44SJordan Rupprecht    @expectedFailureAll(
20199451b44SJordan Rupprecht        oslist=["linux"],
20299451b44SJordan Rupprecht        bugnumber="llvm.org/pr23617 Flaky, fails ~1/10 cases")
20399451b44SJordan Rupprecht    @skipIfWindows # This is flakey on Windows AND when it fails, it hangs: llvm.org/pr38373
20499451b44SJordan Rupprecht    @expectedFlakeyNetBSD
20599451b44SJordan Rupprecht    def test_add_listener_to_broadcaster(self):
20699451b44SJordan Rupprecht        """Exercise some SBBroadcaster APIs."""
20799451b44SJordan Rupprecht        self.build()
20899451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
20999451b44SJordan Rupprecht
21099451b44SJordan Rupprecht        self.dbg.SetAsync(True)
21199451b44SJordan Rupprecht
21299451b44SJordan Rupprecht        # Create a target by the debugger.
21399451b44SJordan Rupprecht        target = self.dbg.CreateTarget(exe)
21499451b44SJordan Rupprecht        self.assertTrue(target, VALID_TARGET)
21599451b44SJordan Rupprecht
21699451b44SJordan Rupprecht        # Now create a breakpoint on main.c by name 'c'.
21799451b44SJordan Rupprecht        breakpoint = target.BreakpointCreateByName('c', 'a.out')
218*b321b429SJonas Devlieghere        self.trace("breakpoint:", breakpoint)
21999451b44SJordan Rupprecht        self.assertTrue(breakpoint and
22099451b44SJordan Rupprecht                        breakpoint.GetNumLocations() == 1,
22199451b44SJordan Rupprecht                        VALID_BREAKPOINT)
22299451b44SJordan Rupprecht
22399451b44SJordan Rupprecht        listener = lldb.SBListener("my listener")
22499451b44SJordan Rupprecht
22599451b44SJordan Rupprecht        # Now launch the process, and do not stop at the entry point.
22699451b44SJordan Rupprecht        error = lldb.SBError()
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
23499451b44SJordan Rupprecht                                0,         # 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*b321b429SJonas Devlieghere                self.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*b321b429SJonas Devlieghere                        self.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. :-)
31599451b44SJordan Rupprecht        self.assertTrue(self.state == 'stopped',
31699451b44SJordan Rupprecht                        "Both expected state changed events received")
317