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