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