xref: /llvm-project/lldb/test/API/functionalities/launch_stop_at_entry/TestStopAtEntry.py (revision ce825e46743be3e402820484bef639d12deefc2a)
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    @skipIfRemote
44    def test_stop_default_platform_sync(self):
45        self.do_test_stop_at_entry(True, False)
46
47    @skipUnlessDarwin
48    @skipIfRemote
49    def test_stop_default_platform_async(self):
50        self.do_test_stop_at_entry(False, False)
51
52    @skipUnlessDarwin
53    @skipIfRemote
54    @expectedFailureIfFn(no_debugserver)
55    @expectedFailureIfFn(port_not_available)
56    def test_stop_remote_platform_sync(self):
57        self.do_test_stop_at_entry(True, True)
58
59    @skipUnlessDarwin
60    @skipIfRemote
61    @expectedFailureIfFn(no_debugserver)
62    @expectedFailureIfFn(port_not_available)
63    def test_stop_remote_platform_async(self):
64        self.do_test_stop_at_entry(False, True)
65
66    def do_test_stop_at_entry(self, synchronous, remote):
67        """Test the normal launch path in either sync or async mode"""
68        self.build()
69
70        target = lldbutil.run_to_breakpoint_make_target(self)
71        launch_info = target.GetLaunchInfo()
72        launch_info.SetLaunchFlags(lldb.eLaunchFlagStopAtEntry)
73        old_async = self.dbg.GetAsync()
74        def cleanup ():
75            self.dbg.SetAsync(old_async)
76        self.addTearDownHook(cleanup)
77
78        if not synchronous:
79            self.dbg.SetAsync(True)
80            listener = lldb.SBListener("test-process-listener")
81            mask = listener.StartListeningForEventClass(self.dbg, lldb.SBProcess.GetBroadcasterClassName(), lldb.SBProcess.eBroadcastBitStateChanged)
82            self.assertEqual(mask, lldb.SBProcess.eBroadcastBitStateChanged, "Got right mask for listener")
83            launch_info.SetListener(listener)
84        else:
85            self.dbg.SetAsync(False)
86
87        if remote:
88            self.setup_remote_platform()
89
90        error = lldb.SBError()
91
92        process = target.Launch(launch_info, error)
93        self.assertSuccess(error, "Launch failed")
94        # If we are asynchronous, we have to wait for the events:
95        if not synchronous:
96            listener = launch_info.GetListener()
97            event = lldb.SBEvent()
98            result = listener.WaitForEvent(30, event)
99            self.assertTrue(result, "Timed out waiting for event from process")
100            state = lldb.SBProcess.GetStateFromEvent(event)
101            self.assertState(state, lldb.eStateStopped, "Didn't get a stopped state after launch")
102
103        # Okay, we should be stopped.  Make sure we are indeed at the
104        # entry point.  I only know how to do this on darwin:
105        self.assertEqual(len(process.threads), 1, "Should only have one thread at entry")
106        thread = process.threads[0]
107        frame = thread.GetFrameAtIndex(0)
108        stop_func = frame.name
109        self.assertEqual(stop_func, "_dyld_start")
110
111        # Now make sure that we can resume the process and have it exit.
112        error = process.Continue()
113        self.assertSuccess(error, "Error continuing")
114        # Fetch events till we get eStateExited:
115        if not synchronous:
116            # Get events till exited.
117            listener = launch_info.GetListener()
118            event = lldb.SBEvent()
119            # We get two running events in a row here???  That's a bug
120            # but not the one I'm testing for, so for now just fetch as
121            # many as were sent.
122            num_running = 0
123            state = lldb.eStateRunning
124            while state == lldb.eStateRunning:
125                num_running += 1
126                result = listener.WaitForEvent(30, event)
127                self.assertTrue(result, "Timed out waiting for running")
128                state = lldb.SBProcess.GetStateFromEvent(event)
129                if num_running == 1:
130                    self.assertState(state, lldb.eStateRunning, "Got running event")
131            # The last event we should get is the exited event
132            self.assertState(state, lldb.eStateExited, "Got exit event")
133        else:
134            # Make sure that the process has indeed exited
135            state = process.GetState()
136            self.assertState(state, lldb.eStateExited);
137
138    def setup_remote_platform(self):
139        return
140        self.build()
141
142        exe = self.getBuildArtifact('a.out')
143        # Launch our test binary.
144
145        # Attach to it with debugserver.
146        debugserver = get_debugserver_exe()
147        debugserver_args = [
148            'localhost:{}'.format(self.PORT)
149        ]
150        self.spawnSubprocess(debugserver, debugserver_args)
151
152        # Select the platform.
153        self.expect('platform select remote-macosx', substrs=[sdk_dir])
154
155        # Connect to debugserver
156        interpreter = self.dbg.GetCommandInterpreter()
157        connected = False
158        for i in range(self.ATTEMPTS):
159            result = lldb.SBCommandReturnObject()
160            interpreter.HandleCommand('gdb-remote {}'.format(self.PORT),
161                                      result)
162            connected = result.Succeeded()
163            if connected:
164                break
165            time.sleep(self.TIMEOUT)
166
167        self.assertTrue(connected, "could not connect to debugserver")
168