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