Skip to content

Commit 1893335

Browse files
authored
Merge pull request #3859 from apple/🍒/austria/e655769c4a7b5a17b17eace3bd160b3b015b75ed
Fix a bug in Launch when using an async debugger & remote platform.
2 parents 59972a8 + 7f69dff commit 1893335

File tree

4 files changed

+174
-0
lines changed

4 files changed

+174
-0
lines changed

‎lldb/source/Target/Process.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2719,6 +2719,9 @@ Status Process::Launch(ProcessLaunchInfo &launch_info) {
27192719
// stopped or crashed. Directly set the state. This is done to
27202720
// prevent a stop message with a bunch of spurious output on thread
27212721
// status, as well as not pop a ProcessIOHandler.
2722+
// We are done with the launch hijack listener, and this stop should
2723+
// go to the public state listener:
2724+
RestoreProcessEvents();
27222725
SetPublicState(state, false);
27232726

27242727
if (PrivateStateThreadIsValid())
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import lldb
2+
3+
from lldbsuite.test.decorators import *
4+
from lldbsuite.test.lldbtest import *
5+
from lldbsuite.test import lldbutil
6+
from lldbgdbserverutils import get_debugserver_exe
7+
8+
import os
9+
import platform
10+
import shutil
11+
import time
12+
import socket
13+
14+
15+
class TestStopAtEntry(TestBase):
16+
17+
mydir = TestBase.compute_mydir(__file__)
18+
NO_DEBUG_INFO_TESTCASE = True
19+
20+
# The port used by debugserver.
21+
PORT = 54637
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")
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
int main(int argc, char **argv) {
2+
/* We just want to make sure this ran, so
3+
it doesn't actually need to do anything. */
4+
return 0;
5+
}

0 commit comments

Comments
 (0)