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