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