xref: /llvm-project/lldb/test/API/functionalities/launch_stop_at_entry/TestStopAtEntry.py (revision d1e9514ac89b1ceed51114159b7dd4888ce58974)
1import lldb
2
3from lldbsuite.test.decorators import *
4from lldbsuite.test.lldbtest import *
5from lldbsuite.test import lldbutil
6from lldbgdbserverutils import get_debugserver_exe
7
8import os
9import platform
10import shutil
11import time
12import socket
13
14
15class TestStopAtEntry(TestBase):
16
17    mydir = TestBase.compute_mydir(__file__)
18    NO_DEBUG_INFO_TESTCASE = True
19
20    # The port used by debugserver.
21    PORT = 54638
22
23    # The number of attempts.
24    ATTEMPTS = 10
25
26    # Time given to the binary to launch and to debugserver to attach to it for
27    # every attempt. We'll wait a maximum of 10 times 2 seconds while the
28    # inferior will wait 10 times 10 seconds.
29    TIMEOUT = 2
30
31    def no_debugserver(self):
32        if get_debugserver_exe() is None:
33            return 'no debugserver'
34        return None
35
36    def port_not_available(self):
37        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
38        if s.connect_ex(('127.0.0.1', self.PORT)) == 0:
39            return '{} not available'.format(self.PORT)
40        return None
41
42    @skipUnlessDarwin
43    def test_stop_default_platform_sync(self):
44        self.do_test_stop_at_entry(True, False)
45
46    @skipUnlessDarwin
47    def test_stop_default_platform_async(self):
48        self.do_test_stop_at_entry(False, False)
49
50    @skipUnlessDarwin
51    @expectedFailureIfFn(no_debugserver)
52    @expectedFailureIfFn(port_not_available)
53    def test_stop_remote_platform_sync(self):
54        self.do_test_stop_at_entry(True, True)
55
56    @skipUnlessDarwin
57    @expectedFailureIfFn(no_debugserver)
58    @expectedFailureIfFn(port_not_available)
59    def test_stop_remote_platform_async(self):
60        self.do_test_stop_at_entry(False, True)
61
62    def do_test_stop_at_entry(self, synchronous, remote):
63        """Test the normal launch path in either sync or async mode"""
64        self.build()
65
66        target = lldbutil.run_to_breakpoint_make_target(self)
67        launch_info = target.GetLaunchInfo()
68        launch_info.SetLaunchFlags(lldb.eLaunchFlagStopAtEntry)
69        old_async = self.dbg.GetAsync()
70        def cleanup ():
71            self.dbg.SetAsync(old_async)
72        self.addTearDownHook(cleanup)
73
74        if not synchronous:
75            self.dbg.SetAsync(True)
76            listener = lldb.SBListener("test-process-listener")
77            mask = listener.StartListeningForEventClass(self.dbg, lldb.SBProcess.GetBroadcasterClassName(), lldb.SBProcess.eBroadcastBitStateChanged)
78            self.assertEqual(mask, lldb.SBProcess.eBroadcastBitStateChanged, "Got right mask for listener")
79            launch_info.SetListener(listener)
80        else:
81            self.dbg.SetAsync(False)
82
83        if remote:
84            self.setup_remote_platform()
85
86        error = lldb.SBError()
87
88        process = target.Launch(launch_info, error)
89        self.assertTrue(error.Success(), "Launch failed: {0}".format(error.description))
90        # If we are asynchronous, we have to wait for the events:
91        if not synchronous:
92            listener = launch_info.GetListener()
93            event = lldb.SBEvent()
94            result = listener.WaitForEvent(30, event)
95            self.assertTrue(result, "Timed out waiting for event from process")
96            state = lldb.SBProcess.GetStateFromEvent(event)
97            self.assertEqual(state, lldb.eStateStopped, "Didn't get a stopped state after launch")
98
99        # Okay, we should be stopped.  Make sure we are indeed at the
100        # entry point.  I only know how to do this on darwin:
101        self.assertEqual(len(process.threads), 1, "Should only have one thread at entry")
102        thread = process.threads[0]
103        frame = thread.GetFrameAtIndex(0)
104        stop_func = frame.name
105        self.assertEqual(stop_func, "_dyld_start")
106
107        # Now make sure that we can resume the process and have it exit.
108        error = process.Continue()
109        self.assertTrue(error.Success(), "Error continuing: {0}".format(error.description))
110        # Fetch events till we get eStateExited:
111        if not synchronous:
112            # Get events till exited.
113            listener = launch_info.GetListener()
114            event = lldb.SBEvent()
115            # We get two running events in a row here???  That's a bug
116            # but not the one I'm testing for, so for now just fetch as
117            # many as were sent.
118            num_running = 0
119            state = lldb.eStateRunning
120            while state == lldb.eStateRunning:
121                num_running += 1
122                result = listener.WaitForEvent(30, event)
123                self.assertTrue(result, "Timed out waiting for running")
124                state = lldb.SBProcess.GetStateFromEvent(event)
125                if num_running == 1:
126                    self.assertEqual(state, lldb.eStateRunning, "Got running event")
127            # The last event we should get is the exited event
128            self.assertEqual(state, lldb.eStateExited, "Got running event")
129        else:
130            # Make sure that the process has indeed exited
131            state = process.GetState()
132            self.assertEqual(state, lldb.eStateExited);
133
134    def setup_remote_platform(self):
135        return
136        self.build()
137
138        exe = self.getBuildArtifact('a.out')
139        # Launch our test binary.
140
141        # Attach to it with debugserver.
142        debugserver = get_debugserver_exe()
143        debugserver_args = [
144            'localhost:{}'.format(self.PORT)
145        ]
146        self.spawnSubprocess(debugserver, debugserver_args)
147
148        # Select the platform.
149        self.expect('platform select remote-macosx', substrs=[sdk_dir])
150
151        # Connect to debugserver
152        interpreter = self.dbg.GetCommandInterpreter()
153        connected = False
154        for i in range(self.ATTEMPTS):
155            result = lldb.SBCommandReturnObject()
156            interpreter.HandleCommand('gdb-remote {}'.format(self.PORT),
157                                      result)
158            connected = result.Succeeded()
159            if connected:
160                break
161            time.sleep(self.TIMEOUT)
162
163        self.assertTrue(connected, "could not connect to debugserver")
164