177 lines · python
1import lldb2 3from lldbsuite.test.decorators import *4from lldbsuite.test.lldbtest import *5from lldbsuite.test import lldbutil6from lldbgdbserverutils import get_debugserver_exe7 8import os9import platform10import shutil11import time12import socket13 14 15class TestStopAtEntry(TestBase):16 NO_DEBUG_INFO_TESTCASE = True17 18 # The port used by debugserver.19 PORT = 5463820 21 # The number of attempts.22 ATTEMPTS = 1023 24 # Time given to the binary to launch and to debugserver to attach to it for25 # every attempt. We'll wait a maximum of 10 times 2 seconds while the26 # inferior will wait 10 times 10 seconds.27 TIMEOUT = 228 29 def no_debugserver(self):30 if get_debugserver_exe() is None:31 return "no debugserver"32 return None33 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 None39 40 @skipUnlessDarwin41 @skipIfRemote42 def test_stop_default_platform_sync(self):43 self.do_test_stop_at_entry(True, False)44 45 @skipUnlessDarwin46 @skipIfRemote47 def test_stop_default_platform_async(self):48 self.do_test_stop_at_entry(False, False)49 50 @skipUnlessDarwin51 @skipIfRemote52 @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 @skipUnlessDarwin58 @skipIfRemote59 @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 the114 # 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.name121 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 bug132 # but not the one I'm testing for, so for now just fetch as133 # many as were sent.134 num_running = 0135 state = lldb.eStateRunning136 while state == lldb.eStateRunning:137 num_running += 1138 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 event144 self.assertState(state, lldb.eStateExited, "Got exit event")145 else:146 # Make sure that the process has indeed exited147 state = process.GetState()148 self.assertState(state, lldb.eStateExited)149 150 def setup_remote_platform(self):151 return152 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 debugserver166 interpreter = self.dbg.GetCommandInterpreter()167 connected = False168 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 break174 time.sleep(self.TIMEOUT)175 176 self.assertTrue(connected, "could not connect to debugserver")177