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