1*d676074dSJason Molenda /*
2*d676074dSJason Molenda  *  libdebugserver.cpp
3*d676074dSJason Molenda  *  debugserver
4*d676074dSJason Molenda  *
5*d676074dSJason Molenda  *  Copyright 2008-2012 Apple Inc. All rights reserved.
6*d676074dSJason Molenda  *
7*d676074dSJason Molenda  */
8*d676074dSJason Molenda 
9*d676074dSJason Molenda #include <sys/socket.h>
10*d676074dSJason Molenda #include <sys/types.h>
11*d676074dSJason Molenda #include <errno.h>
12*d676074dSJason Molenda #include <getopt.h>
13*d676074dSJason Molenda #include <netinet/in.h>
14*d676074dSJason Molenda #include <sys/select.h>
15*d676074dSJason Molenda #include <sys/sysctl.h>
16*d676074dSJason Molenda 
17*d676074dSJason Molenda #include "DNB.h"
18*d676074dSJason Molenda #include "DNBLog.h"
19*d676074dSJason Molenda #include "DNBTimer.h"
20*d676074dSJason Molenda #include "PseudoTerminal.h"
21*d676074dSJason Molenda #include "RNBContext.h"
22*d676074dSJason Molenda #include "RNBServices.h"
23*d676074dSJason Molenda #include "RNBSocket.h"
24*d676074dSJason Molenda #include "RNBRemote.h"
25*d676074dSJason Molenda #include "SysSignal.h"
26*d676074dSJason Molenda 
27*d676074dSJason Molenda //----------------------------------------------------------------------
28*d676074dSJason Molenda // Run loop modes which determine which run loop function will be called
29*d676074dSJason Molenda //----------------------------------------------------------------------
30*d676074dSJason Molenda typedef enum
31*d676074dSJason Molenda {
32*d676074dSJason Molenda     eRNBRunLoopModeInvalid = 0,
33*d676074dSJason Molenda     eRNBRunLoopModeGetStartModeFromRemoteProtocol,
34*d676074dSJason Molenda     eRNBRunLoopModeInferiorExecuting,
35*d676074dSJason Molenda     eRNBRunLoopModeExit
36*d676074dSJason Molenda } RNBRunLoopMode;
37*d676074dSJason Molenda 
38*d676074dSJason Molenda 
39*d676074dSJason Molenda //----------------------------------------------------------------------
40*d676074dSJason Molenda // Global Variables
41*d676074dSJason Molenda //----------------------------------------------------------------------
42*d676074dSJason Molenda RNBRemoteSP g_remoteSP;
43*d676074dSJason Molenda int g_disable_aslr = 0;
44*d676074dSJason Molenda int g_isatty = 0;
45*d676074dSJason Molenda 
46*d676074dSJason Molenda #define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
47*d676074dSJason Molenda #define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
48*d676074dSJason Molenda 
49*d676074dSJason Molenda 
50*d676074dSJason Molenda //----------------------------------------------------------------------
51*d676074dSJason Molenda // Get our program path and arguments from the remote connection.
52*d676074dSJason Molenda // We will need to start up the remote connection without a PID, get the
53*d676074dSJason Molenda // arguments, wait for the new process to finish launching and hit its
54*d676074dSJason Molenda // entry point,  and then return the run loop mode that should come next.
55*d676074dSJason Molenda //----------------------------------------------------------------------
56*d676074dSJason Molenda RNBRunLoopMode
57*d676074dSJason Molenda RNBRunLoopGetStartModeFromRemote (RNBRemoteSP &remoteSP)
58*d676074dSJason Molenda {
59*d676074dSJason Molenda     std::string packet;
60*d676074dSJason Molenda 
61*d676074dSJason Molenda     if (remoteSP.get() != NULL)
62*d676074dSJason Molenda     {
63*d676074dSJason Molenda         RNBRemote* remote = remoteSP.get();
64*d676074dSJason Molenda         RNBContext& ctx = remote->Context();
65*d676074dSJason Molenda         uint32_t event_mask = RNBContext::event_read_packet_available;
66*d676074dSJason Molenda 
67*d676074dSJason Molenda         // Spin waiting to get the A packet.
68*d676074dSJason Molenda         while (1)
69*d676074dSJason Molenda         {
70*d676074dSJason Molenda             DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",__FUNCTION__, event_mask);
71*d676074dSJason Molenda             nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
72*d676074dSJason Molenda             DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x", __FUNCTION__, event_mask, set_events);
73*d676074dSJason Molenda 
74*d676074dSJason Molenda             if (set_events & RNBContext::event_read_packet_available)
75*d676074dSJason Molenda             {
76*d676074dSJason Molenda                 rnb_err_t err = rnb_err;
77*d676074dSJason Molenda                 RNBRemote::PacketEnum type;
78*d676074dSJason Molenda 
79*d676074dSJason Molenda                 err = remote->HandleReceivedPacket (&type);
80*d676074dSJason Molenda 
81*d676074dSJason Molenda                 // check if we tried to attach to a process
82*d676074dSJason Molenda                 if (type == RNBRemote::vattach || type == RNBRemote::vattachwait)
83*d676074dSJason Molenda                 {
84*d676074dSJason Molenda                     if (err == rnb_success)
85*d676074dSJason Molenda                         return eRNBRunLoopModeInferiorExecuting;
86*d676074dSJason Molenda                     else
87*d676074dSJason Molenda                     {
88*d676074dSJason Molenda                         RNBLogSTDERR ("error: attach failed.");
89*d676074dSJason Molenda                         return eRNBRunLoopModeExit;
90*d676074dSJason Molenda                     }
91*d676074dSJason Molenda                 }
92*d676074dSJason Molenda 
93*d676074dSJason Molenda 
94*d676074dSJason Molenda                 if (err == rnb_success)
95*d676074dSJason Molenda                 {
96*d676074dSJason Molenda                     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Got success...",__FUNCTION__);
97*d676074dSJason Molenda 					continue;
98*d676074dSJason Molenda 				}
99*d676074dSJason Molenda 				else if (err == rnb_not_connected)
100*d676074dSJason Molenda                 {
101*d676074dSJason Molenda                     RNBLogSTDERR ("error: connection lost.");
102*d676074dSJason Molenda                     return eRNBRunLoopModeExit;
103*d676074dSJason Molenda                 }
104*d676074dSJason Molenda                 else
105*d676074dSJason Molenda                 {
106*d676074dSJason Molenda                     // a catch all for any other gdb remote packets that failed
107*d676074dSJason Molenda                     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.",__FUNCTION__);
108*d676074dSJason Molenda                     continue;
109*d676074dSJason Molenda                 }
110*d676074dSJason Molenda 
111*d676074dSJason Molenda                 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
112*d676074dSJason Molenda             }
113*d676074dSJason Molenda             else
114*d676074dSJason Molenda             {
115*d676074dSJason Molenda                 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Connection closed before getting \"A\" packet.", __FUNCTION__);
116*d676074dSJason Molenda                 return eRNBRunLoopModeExit;
117*d676074dSJason Molenda             }
118*d676074dSJason Molenda         }
119*d676074dSJason Molenda     }
120*d676074dSJason Molenda     return eRNBRunLoopModeExit;
121*d676074dSJason Molenda }
122*d676074dSJason Molenda 
123*d676074dSJason Molenda 
124*d676074dSJason Molenda //----------------------------------------------------------------------
125*d676074dSJason Molenda // Watch for signals:
126*d676074dSJason Molenda // SIGINT: so we can halt our inferior. (disabled for now)
127*d676074dSJason Molenda // SIGPIPE: in case our child process dies
128*d676074dSJason Molenda //----------------------------------------------------------------------
129*d676074dSJason Molenda nub_process_t g_pid;
130*d676074dSJason Molenda int g_sigpipe_received = 0;
131*d676074dSJason Molenda void
132*d676074dSJason Molenda signal_handler(int signo)
133*d676074dSJason Molenda {
134*d676074dSJason Molenda     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo));
135*d676074dSJason Molenda 
136*d676074dSJason Molenda     switch (signo)
137*d676074dSJason Molenda     {
138*d676074dSJason Molenda 			//  case SIGINT:
139*d676074dSJason Molenda 			//      DNBProcessKill (g_pid, signo);
140*d676074dSJason Molenda 			//      break;
141*d676074dSJason Molenda 
142*d676074dSJason Molenda 		case SIGPIPE:
143*d676074dSJason Molenda 			g_sigpipe_received = 1;
144*d676074dSJason Molenda 			break;
145*d676074dSJason Molenda     }
146*d676074dSJason Molenda }
147*d676074dSJason Molenda 
148*d676074dSJason Molenda // Return the new run loop mode based off of the current process state
149*d676074dSJason Molenda RNBRunLoopMode
150*d676074dSJason Molenda HandleProcessStateChange (RNBRemoteSP &remote, bool initialize)
151*d676074dSJason Molenda {
152*d676074dSJason Molenda     RNBContext& ctx = remote->Context();
153*d676074dSJason Molenda     nub_process_t pid = ctx.ProcessID();
154*d676074dSJason Molenda 
155*d676074dSJason Molenda     if (pid == INVALID_NUB_PROCESS)
156*d676074dSJason Molenda     {
157*d676074dSJason Molenda         DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__);
158*d676074dSJason Molenda         return eRNBRunLoopModeExit;
159*d676074dSJason Molenda     }
160*d676074dSJason Molenda     nub_state_t pid_state = DNBProcessGetState (pid);
161*d676074dSJason Molenda 
162*d676074dSJason Molenda     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state));
163*d676074dSJason Molenda 
164*d676074dSJason Molenda     switch (pid_state)
165*d676074dSJason Molenda     {
166*d676074dSJason Molenda 		case eStateInvalid:
167*d676074dSJason Molenda 		case eStateUnloaded:
168*d676074dSJason Molenda 			// Something bad happened
169*d676074dSJason Molenda 			return eRNBRunLoopModeExit;
170*d676074dSJason Molenda 			break;
171*d676074dSJason Molenda 
172*d676074dSJason Molenda 		case eStateAttaching:
173*d676074dSJason Molenda 		case eStateLaunching:
174*d676074dSJason Molenda 			return eRNBRunLoopModeInferiorExecuting;
175*d676074dSJason Molenda 
176*d676074dSJason Molenda 		case eStateSuspended:
177*d676074dSJason Molenda 		case eStateCrashed:
178*d676074dSJason Molenda 		case eStateStopped:
179*d676074dSJason Molenda 			if (initialize == false)
180*d676074dSJason Molenda 			{
181*d676074dSJason Molenda 				// Compare the last stop count to our current notion of a stop count
182*d676074dSJason Molenda 				// to make sure we don't notify more than once for a given stop.
183*d676074dSJason Molenda 				nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
184*d676074dSJason Molenda 				bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
185*d676074dSJason Molenda 				if (pid_stop_count_changed)
186*d676074dSJason Molenda 				{
187*d676074dSJason Molenda 					remote->FlushSTDIO();
188*d676074dSJason Molenda 
189*d676074dSJason Molenda 					if (ctx.GetProcessStopCount() == 1)
190*d676074dSJason Molenda 					{
191*d676074dSJason Molenda 						DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s pid_stop_count %u (old %u)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
192*d676074dSJason Molenda 					}
193*d676074dSJason Molenda 					else
194*d676074dSJason Molenda 					{
195*d676074dSJason Molenda 
196*d676074dSJason Molenda 						DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s pid_stop_count %u (old %u)) Notify??? YES!!!", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
197*d676074dSJason Molenda 						remote->NotifyThatProcessStopped ();
198*d676074dSJason Molenda 					}
199*d676074dSJason Molenda 				}
200*d676074dSJason Molenda 				else
201*d676074dSJason Molenda 				{
202*d676074dSJason Molenda 					DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s pid_stop_count %u (old %u)) Notify??? skipping...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
203*d676074dSJason Molenda 				}
204*d676074dSJason Molenda 			}
205*d676074dSJason Molenda 			return eRNBRunLoopModeInferiorExecuting;
206*d676074dSJason Molenda 
207*d676074dSJason Molenda 		case eStateStepping:
208*d676074dSJason Molenda 		case eStateRunning:
209*d676074dSJason Molenda 			return eRNBRunLoopModeInferiorExecuting;
210*d676074dSJason Molenda 
211*d676074dSJason Molenda 		case eStateExited:
212*d676074dSJason Molenda 			remote->HandlePacket_last_signal(NULL);
213*d676074dSJason Molenda 			return eRNBRunLoopModeExit;
214*d676074dSJason Molenda 		case eStateDetached:
215*d676074dSJason Molenda             return eRNBRunLoopModeExit;
216*d676074dSJason Molenda 
217*d676074dSJason Molenda     }
218*d676074dSJason Molenda 
219*d676074dSJason Molenda     // Catch all...
220*d676074dSJason Molenda     return eRNBRunLoopModeExit;
221*d676074dSJason Molenda }
222*d676074dSJason Molenda // This function handles the case where our inferior program is stopped and
223*d676074dSJason Molenda // we are waiting for gdb remote protocol packets. When a packet occurs that
224*d676074dSJason Molenda // makes the inferior run, we need to leave this function with a new state
225*d676074dSJason Molenda // as the return code.
226*d676074dSJason Molenda RNBRunLoopMode
227*d676074dSJason Molenda RNBRunLoopInferiorExecuting (RNBRemoteSP &remote)
228*d676074dSJason Molenda {
229*d676074dSJason Molenda     DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
230*d676074dSJason Molenda     RNBContext& ctx = remote->Context();
231*d676074dSJason Molenda 
232*d676074dSJason Molenda     // Init our mode and set 'is_running' based on the current process state
233*d676074dSJason Molenda     RNBRunLoopMode mode = HandleProcessStateChange (remote, true);
234*d676074dSJason Molenda 
235*d676074dSJason Molenda     while (ctx.ProcessID() != INVALID_NUB_PROCESS)
236*d676074dSJason Molenda     {
237*d676074dSJason Molenda 
238*d676074dSJason Molenda         std::string set_events_str;
239*d676074dSJason Molenda         uint32_t event_mask = ctx.NormalEventBits();
240*d676074dSJason Molenda 
241*d676074dSJason Molenda         if (!ctx.ProcessStateRunning())
242*d676074dSJason Molenda         {
243*d676074dSJason Molenda             // Clear the stdio bits if we are not running so we don't send any async packets
244*d676074dSJason Molenda             event_mask &= ~RNBContext::event_proc_stdio_available;
245*d676074dSJason Molenda         }
246*d676074dSJason Molenda 
247*d676074dSJason Molenda         // We want to make sure we consume all process state changes and have
248*d676074dSJason Molenda         // whomever is notifying us to wait for us to reset the event bit before
249*d676074dSJason Molenda         // continuing.
250*d676074dSJason Molenda         //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
251*d676074dSJason Molenda 
252*d676074dSJason Molenda         DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
253*d676074dSJason Molenda         nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
254*d676074dSJason Molenda         DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",__FUNCTION__, event_mask, set_events, ctx.EventsAsString(set_events, set_events_str));
255*d676074dSJason Molenda 
256*d676074dSJason Molenda         if (set_events)
257*d676074dSJason Molenda         {
258*d676074dSJason Molenda             if ((set_events & RNBContext::event_proc_thread_exiting) ||
259*d676074dSJason Molenda                 (set_events & RNBContext::event_proc_stdio_available))
260*d676074dSJason Molenda             {
261*d676074dSJason Molenda                 remote->FlushSTDIO();
262*d676074dSJason Molenda             }
263*d676074dSJason Molenda 
264*d676074dSJason Molenda             if (set_events & RNBContext::event_read_packet_available)
265*d676074dSJason Molenda             {
266*d676074dSJason Molenda                 // handleReceivedPacket will take care of resetting the
267*d676074dSJason Molenda                 // event_read_packet_available events when there are no more...
268*d676074dSJason Molenda                 set_events ^= RNBContext::event_read_packet_available;
269*d676074dSJason Molenda 
270*d676074dSJason Molenda                 if (ctx.ProcessStateRunning())
271*d676074dSJason Molenda                 {
272*d676074dSJason Molenda                     if (remote->HandleAsyncPacket() == rnb_not_connected)
273*d676074dSJason Molenda                     {
274*d676074dSJason Molenda                         // TODO: connect again? Exit?
275*d676074dSJason Molenda                     }
276*d676074dSJason Molenda                 }
277*d676074dSJason Molenda                 else
278*d676074dSJason Molenda                 {
279*d676074dSJason Molenda                     if (remote->HandleReceivedPacket() == rnb_not_connected)
280*d676074dSJason Molenda                     {
281*d676074dSJason Molenda                         // TODO: connect again? Exit?
282*d676074dSJason Molenda                     }
283*d676074dSJason Molenda                 }
284*d676074dSJason Molenda             }
285*d676074dSJason Molenda 
286*d676074dSJason Molenda             if (set_events & RNBContext::event_proc_state_changed)
287*d676074dSJason Molenda             {
288*d676074dSJason Molenda                 mode = HandleProcessStateChange (remote, false);
289*d676074dSJason Molenda                 ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
290*d676074dSJason Molenda                 set_events ^= RNBContext::event_proc_state_changed;
291*d676074dSJason Molenda             }
292*d676074dSJason Molenda 
293*d676074dSJason Molenda             if (set_events & RNBContext::event_proc_thread_exiting)
294*d676074dSJason Molenda             {
295*d676074dSJason Molenda                 mode = eRNBRunLoopModeExit;
296*d676074dSJason Molenda             }
297*d676074dSJason Molenda 
298*d676074dSJason Molenda             if (set_events & RNBContext::event_read_thread_exiting)
299*d676074dSJason Molenda             {
300*d676074dSJason Molenda                 // Out remote packet receiving thread exited, exit for now.
301*d676074dSJason Molenda                 if (ctx.HasValidProcessID())
302*d676074dSJason Molenda                 {
303*d676074dSJason Molenda                     // TODO: We should add code that will leave the current process
304*d676074dSJason Molenda                     // in its current state and listen for another connection...
305*d676074dSJason Molenda                     if (ctx.ProcessStateRunning())
306*d676074dSJason Molenda                     {
307*d676074dSJason Molenda                         DNBProcessKill (ctx.ProcessID(), SIGINT);
308*d676074dSJason Molenda                     }
309*d676074dSJason Molenda                 }
310*d676074dSJason Molenda                 mode = eRNBRunLoopModeExit;
311*d676074dSJason Molenda             }
312*d676074dSJason Molenda         }
313*d676074dSJason Molenda 
314*d676074dSJason Molenda         // Reset all event bits that weren't reset for now...
315*d676074dSJason Molenda         if (set_events != 0)
316*d676074dSJason Molenda 			ctx.Events().ResetEvents(set_events);
317*d676074dSJason Molenda 
318*d676074dSJason Molenda         if (mode != eRNBRunLoopModeInferiorExecuting)
319*d676074dSJason Molenda 			break;
320*d676074dSJason Molenda     }
321*d676074dSJason Molenda 
322*d676074dSJason Molenda     return mode;
323*d676074dSJason Molenda }
324*d676074dSJason Molenda 
325*d676074dSJason Molenda void
326*d676074dSJason Molenda ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
327*d676074dSJason Molenda {
328*d676074dSJason Molenda #if 0
329*d676074dSJason Molenda 	vprintf(format, args);
330*d676074dSJason Molenda #endif
331*d676074dSJason Molenda }
332*d676074dSJason Molenda 
333*d676074dSJason Molenda extern "C" int
334*d676074dSJason Molenda debug_server_main(int fd)
335*d676074dSJason Molenda {
336*d676074dSJason Molenda #if 1
337*d676074dSJason Molenda 	g_isatty = 0;
338*d676074dSJason Molenda #else
339*d676074dSJason Molenda 	g_isatty = ::isatty (STDIN_FILENO);
340*d676074dSJason Molenda 
341*d676074dSJason Molenda 	DNBLogSetDebug(1);
342*d676074dSJason Molenda 	DNBLogSetVerbose(1);
343*d676074dSJason Molenda 	DNBLogSetLogMask(-1);
344*d676074dSJason Molenda 	DNBLogSetLogCallback(ASLLogCallback, NULL);
345*d676074dSJason Molenda #endif
346*d676074dSJason Molenda 
347*d676074dSJason Molenda     signal (SIGPIPE, signal_handler);
348*d676074dSJason Molenda 
349*d676074dSJason Molenda     g_remoteSP.reset (new RNBRemote);
350*d676074dSJason Molenda 
351*d676074dSJason Molenda     RNBRemote *remote = g_remoteSP.get();
352*d676074dSJason Molenda     if (remote == NULL)
353*d676074dSJason Molenda     {
354*d676074dSJason Molenda         RNBLogSTDERR ("error: failed to create a remote connection class\n");
355*d676074dSJason Molenda         return -1;
356*d676074dSJason Molenda     }
357*d676074dSJason Molenda 
358*d676074dSJason Molenda 
359*d676074dSJason Molenda     RNBRunLoopMode mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
360*d676074dSJason Molenda 
361*d676074dSJason Molenda     while (mode != eRNBRunLoopModeExit)
362*d676074dSJason Molenda     {
363*d676074dSJason Molenda         switch (mode)
364*d676074dSJason Molenda         {
365*d676074dSJason Molenda 			case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
366*d676074dSJason Molenda 				if (g_remoteSP->Comm().useFD(fd) == rnb_success) {
367*d676074dSJason Molenda 					RNBLogSTDOUT("Starting remote data thread.\n");
368*d676074dSJason Molenda 					g_remoteSP->StartReadRemoteDataThread();
369*d676074dSJason Molenda 
370*d676074dSJason Molenda 					RNBLogSTDOUT("Waiting for start mode from remote.\n");
371*d676074dSJason Molenda 					mode = RNBRunLoopGetStartModeFromRemote(g_remoteSP);
372*d676074dSJason Molenda 				}
373*d676074dSJason Molenda 				else
374*d676074dSJason Molenda 				{
375*d676074dSJason Molenda 					mode = eRNBRunLoopModeExit;
376*d676074dSJason Molenda 				}
377*d676074dSJason Molenda 				break;
378*d676074dSJason Molenda 
379*d676074dSJason Molenda 			case eRNBRunLoopModeInferiorExecuting:
380*d676074dSJason Molenda 				mode = RNBRunLoopInferiorExecuting(g_remoteSP);
381*d676074dSJason Molenda 				break;
382*d676074dSJason Molenda 
383*d676074dSJason Molenda 			default:
384*d676074dSJason Molenda 				mode = eRNBRunLoopModeExit;
385*d676074dSJason Molenda 				break;
386*d676074dSJason Molenda 
387*d676074dSJason Molenda 			case eRNBRunLoopModeExit:
388*d676074dSJason Molenda 				break;
389*d676074dSJason Molenda         }
390*d676074dSJason Molenda     }
391*d676074dSJason Molenda 
392*d676074dSJason Molenda     g_remoteSP->StopReadRemoteDataThread ();
393*d676074dSJason Molenda     g_remoteSP->Context().SetProcessID(INVALID_NUB_PROCESS);
394*d676074dSJason Molenda 
395*d676074dSJason Molenda     return 0;
396*d676074dSJason Molenda }
397