xref: /openbsd-src/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.cpp (revision be691f3bb6417f04a68938fadbcaee2d5795e764)
1061da546Spatrick //===-- libdebugserver.cpp --------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9*be691f3bSpatrick #include <cerrno>
10061da546Spatrick #include <getopt.h>
11061da546Spatrick #include <netinet/in.h>
12061da546Spatrick #include <sys/select.h>
13061da546Spatrick #include <sys/socket.h>
14061da546Spatrick #include <sys/sysctl.h>
15061da546Spatrick #include <sys/types.h>
16061da546Spatrick 
17061da546Spatrick #include <memory>
18061da546Spatrick 
19061da546Spatrick #include "DNB.h"
20061da546Spatrick #include "DNBLog.h"
21061da546Spatrick #include "DNBTimer.h"
22061da546Spatrick #include "PseudoTerminal.h"
23061da546Spatrick #include "RNBContext.h"
24061da546Spatrick #include "RNBRemote.h"
25061da546Spatrick #include "RNBServices.h"
26061da546Spatrick #include "RNBSocket.h"
27061da546Spatrick #include "SysSignal.h"
28061da546Spatrick 
29061da546Spatrick // Run loop modes which determine which run loop function will be called
30061da546Spatrick enum RNBRunLoopMode {
31061da546Spatrick   eRNBRunLoopModeInvalid = 0,
32061da546Spatrick   eRNBRunLoopModeGetStartModeFromRemoteProtocol,
33061da546Spatrick   eRNBRunLoopModeInferiorExecuting,
34061da546Spatrick   eRNBRunLoopModeExit
35061da546Spatrick };
36061da546Spatrick 
37061da546Spatrick // Global Variables
38061da546Spatrick RNBRemoteSP g_remoteSP;
39061da546Spatrick int g_disable_aslr = 0;
40061da546Spatrick int g_isatty = 0;
41061da546Spatrick 
42061da546Spatrick #define RNBLogSTDOUT(fmt, ...)                                                 \
43061da546Spatrick   do {                                                                         \
44061da546Spatrick     if (g_isatty) {                                                            \
45061da546Spatrick       fprintf(stdout, fmt, ##__VA_ARGS__);                                     \
46061da546Spatrick     } else {                                                                   \
47061da546Spatrick       _DNBLog(0, fmt, ##__VA_ARGS__);                                          \
48061da546Spatrick     }                                                                          \
49061da546Spatrick   } while (0)
50061da546Spatrick #define RNBLogSTDERR(fmt, ...)                                                 \
51061da546Spatrick   do {                                                                         \
52061da546Spatrick     if (g_isatty) {                                                            \
53061da546Spatrick       fprintf(stderr, fmt, ##__VA_ARGS__);                                     \
54061da546Spatrick     } else {                                                                   \
55061da546Spatrick       _DNBLog(0, fmt, ##__VA_ARGS__);                                          \
56061da546Spatrick     }                                                                          \
57061da546Spatrick   } while (0)
58061da546Spatrick 
59061da546Spatrick // Get our program path and arguments from the remote connection.
60061da546Spatrick // We will need to start up the remote connection without a PID, get the
61061da546Spatrick // arguments, wait for the new process to finish launching and hit its
62061da546Spatrick // entry point,  and then return the run loop mode that should come next.
RNBRunLoopGetStartModeFromRemote(RNBRemoteSP & remoteSP)63061da546Spatrick RNBRunLoopMode RNBRunLoopGetStartModeFromRemote(RNBRemoteSP &remoteSP) {
64061da546Spatrick   std::string packet;
65061da546Spatrick 
66061da546Spatrick   if (remoteSP.get() != NULL) {
67061da546Spatrick     RNBRemote *remote = remoteSP.get();
68061da546Spatrick     RNBContext &ctx = remote->Context();
69061da546Spatrick     uint32_t event_mask = RNBContext::event_read_packet_available;
70061da546Spatrick 
71061da546Spatrick     // Spin waiting to get the A packet.
72061da546Spatrick     while (true) {
73061da546Spatrick       DNBLogThreadedIf(LOG_RNB_MAX,
74061da546Spatrick                        "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",
75061da546Spatrick                        __FUNCTION__, event_mask);
76061da546Spatrick       nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
77061da546Spatrick       DNBLogThreadedIf(LOG_RNB_MAX,
78061da546Spatrick                        "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x",
79061da546Spatrick                        __FUNCTION__, event_mask, set_events);
80061da546Spatrick 
81061da546Spatrick       if (set_events & RNBContext::event_read_packet_available) {
82061da546Spatrick         rnb_err_t err = rnb_err;
83061da546Spatrick         RNBRemote::PacketEnum type;
84061da546Spatrick 
85061da546Spatrick         err = remote->HandleReceivedPacket(&type);
86061da546Spatrick 
87061da546Spatrick         // check if we tried to attach to a process
88061da546Spatrick         if (type == RNBRemote::vattach || type == RNBRemote::vattachwait) {
89061da546Spatrick           if (err == rnb_success)
90061da546Spatrick             return eRNBRunLoopModeInferiorExecuting;
91061da546Spatrick           else {
92061da546Spatrick             RNBLogSTDERR("error: attach failed.");
93061da546Spatrick             return eRNBRunLoopModeExit;
94061da546Spatrick           }
95061da546Spatrick         }
96061da546Spatrick 
97061da546Spatrick         if (err == rnb_success) {
98061da546Spatrick           DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Got success...", __FUNCTION__);
99061da546Spatrick           continue;
100061da546Spatrick         } else if (err == rnb_not_connected) {
101061da546Spatrick           RNBLogSTDERR("error: connection lost.");
102061da546Spatrick           return eRNBRunLoopModeExit;
103061da546Spatrick         } else {
104061da546Spatrick           // a catch all for any other gdb remote packets that failed
105061da546Spatrick           DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Error getting packet.",
106061da546Spatrick                            __FUNCTION__);
107061da546Spatrick           continue;
108061da546Spatrick         }
109061da546Spatrick 
110061da546Spatrick         DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
111061da546Spatrick       } else {
112061da546Spatrick         DNBLogThreadedIf(LOG_RNB_MINIMAL,
113061da546Spatrick                          "%s Connection closed before getting \"A\" packet.",
114061da546Spatrick                          __FUNCTION__);
115061da546Spatrick         return eRNBRunLoopModeExit;
116061da546Spatrick       }
117061da546Spatrick     }
118061da546Spatrick   }
119061da546Spatrick   return eRNBRunLoopModeExit;
120061da546Spatrick }
121061da546Spatrick 
122061da546Spatrick // Watch for signals:
123061da546Spatrick // SIGINT: so we can halt our inferior. (disabled for now)
124061da546Spatrick // SIGPIPE: in case our child process dies
125061da546Spatrick nub_process_t g_pid;
126061da546Spatrick int g_sigpipe_received = 0;
signal_handler(int signo)127061da546Spatrick void signal_handler(int signo) {
128061da546Spatrick   DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__,
129061da546Spatrick                    SysSignal::Name(signo));
130061da546Spatrick 
131061da546Spatrick   switch (signo) {
132061da546Spatrick   //  case SIGINT:
133061da546Spatrick   //      DNBProcessKill (g_pid, signo);
134061da546Spatrick   //      break;
135061da546Spatrick 
136061da546Spatrick   case SIGPIPE:
137061da546Spatrick     g_sigpipe_received = 1;
138061da546Spatrick     break;
139061da546Spatrick   }
140061da546Spatrick }
141061da546Spatrick 
142061da546Spatrick // Return the new run loop mode based off of the current process state
HandleProcessStateChange(RNBRemoteSP & remote,bool initialize)143061da546Spatrick RNBRunLoopMode HandleProcessStateChange(RNBRemoteSP &remote, bool initialize) {
144061da546Spatrick   RNBContext &ctx = remote->Context();
145061da546Spatrick   nub_process_t pid = ctx.ProcessID();
146061da546Spatrick 
147061da546Spatrick   if (pid == INVALID_NUB_PROCESS) {
148061da546Spatrick     DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...",
149061da546Spatrick                      __FUNCTION__);
150061da546Spatrick     return eRNBRunLoopModeExit;
151061da546Spatrick   }
152061da546Spatrick   nub_state_t pid_state = DNBProcessGetState(pid);
153061da546Spatrick 
154061da546Spatrick   DNBLogThreadedIf(LOG_RNB_MINIMAL,
155061da546Spatrick                    "%s (&remote, initialize=%i)  pid_state = %s", __FUNCTION__,
156061da546Spatrick                    (int)initialize, DNBStateAsString(pid_state));
157061da546Spatrick 
158061da546Spatrick   switch (pid_state) {
159061da546Spatrick   case eStateInvalid:
160061da546Spatrick   case eStateUnloaded:
161061da546Spatrick     // Something bad happened
162061da546Spatrick     return eRNBRunLoopModeExit;
163061da546Spatrick     break;
164061da546Spatrick 
165061da546Spatrick   case eStateAttaching:
166061da546Spatrick   case eStateLaunching:
167061da546Spatrick     return eRNBRunLoopModeInferiorExecuting;
168061da546Spatrick 
169061da546Spatrick   case eStateSuspended:
170061da546Spatrick   case eStateCrashed:
171061da546Spatrick   case eStateStopped:
172061da546Spatrick     if (!initialize) {
173061da546Spatrick       // Compare the last stop count to our current notion of a stop count
174061da546Spatrick       // to make sure we don't notify more than once for a given stop.
175061da546Spatrick       nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
176061da546Spatrick       bool pid_stop_count_changed =
177061da546Spatrick           ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
178061da546Spatrick       if (pid_stop_count_changed) {
179061da546Spatrick         remote->FlushSTDIO();
180061da546Spatrick 
181061da546Spatrick         if (ctx.GetProcessStopCount() == 1) {
182061da546Spatrick           DNBLogThreadedIf(
183061da546Spatrick               LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s "
184061da546Spatrick                                "pid_stop_count %zu (old %zu)) Notify??? no, "
185061da546Spatrick                                "first stop...",
186061da546Spatrick               __FUNCTION__, (int)initialize, DNBStateAsString(pid_state),
187061da546Spatrick               ctx.GetProcessStopCount(), prev_pid_stop_count);
188061da546Spatrick         } else {
189061da546Spatrick 
190061da546Spatrick           DNBLogThreadedIf(
191061da546Spatrick               LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s "
192061da546Spatrick                                "pid_stop_count %zu (old %zu)) Notify??? YES!!!",
193061da546Spatrick               __FUNCTION__, (int)initialize, DNBStateAsString(pid_state),
194061da546Spatrick               ctx.GetProcessStopCount(), prev_pid_stop_count);
195061da546Spatrick           remote->NotifyThatProcessStopped();
196061da546Spatrick         }
197061da546Spatrick       } else {
198061da546Spatrick         DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  "
199061da546Spatrick                                           "pid_state = %s pid_stop_count %zu "
200061da546Spatrick                                           "(old %zu)) Notify??? skipping...",
201061da546Spatrick                          __FUNCTION__, (int)initialize,
202061da546Spatrick                          DNBStateAsString(pid_state), ctx.GetProcessStopCount(),
203061da546Spatrick                          prev_pid_stop_count);
204061da546Spatrick       }
205061da546Spatrick     }
206061da546Spatrick     return eRNBRunLoopModeInferiorExecuting;
207061da546Spatrick 
208061da546Spatrick   case eStateStepping:
209061da546Spatrick   case eStateRunning:
210061da546Spatrick     return eRNBRunLoopModeInferiorExecuting;
211061da546Spatrick 
212061da546Spatrick   case eStateExited:
213061da546Spatrick     remote->HandlePacket_last_signal(NULL);
214061da546Spatrick     return eRNBRunLoopModeExit;
215061da546Spatrick   case eStateDetached:
216061da546Spatrick     return eRNBRunLoopModeExit;
217061da546Spatrick   }
218061da546Spatrick 
219061da546Spatrick   // Catch all...
220061da546Spatrick   return eRNBRunLoopModeExit;
221061da546Spatrick }
222061da546Spatrick // This function handles the case where our inferior program is stopped and
223061da546Spatrick // we are waiting for gdb remote protocol packets. When a packet occurs that
224061da546Spatrick // makes the inferior run, we need to leave this function with a new state
225061da546Spatrick // as the return code.
RNBRunLoopInferiorExecuting(RNBRemoteSP & remote)226061da546Spatrick RNBRunLoopMode RNBRunLoopInferiorExecuting(RNBRemoteSP &remote) {
227061da546Spatrick   DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
228061da546Spatrick   RNBContext &ctx = remote->Context();
229061da546Spatrick 
230061da546Spatrick   // Init our mode and set 'is_running' based on the current process state
231061da546Spatrick   RNBRunLoopMode mode = HandleProcessStateChange(remote, true);
232061da546Spatrick 
233061da546Spatrick   while (ctx.ProcessID() != INVALID_NUB_PROCESS) {
234061da546Spatrick 
235061da546Spatrick     std::string set_events_str;
236061da546Spatrick     uint32_t event_mask = ctx.NormalEventBits();
237061da546Spatrick 
238061da546Spatrick     if (!ctx.ProcessStateRunning()) {
239061da546Spatrick       // Clear the stdio bits if we are not running so we don't send any async
240061da546Spatrick       // packets
241061da546Spatrick       event_mask &= ~RNBContext::event_proc_stdio_available;
242061da546Spatrick     }
243061da546Spatrick 
244061da546Spatrick     // We want to make sure we consume all process state changes and have
245061da546Spatrick     // whomever is notifying us to wait for us to reset the event bit before
246061da546Spatrick     // continuing.
247061da546Spatrick     // ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
248061da546Spatrick 
249061da546Spatrick     DNBLogThreadedIf(LOG_RNB_EVENTS,
250061da546Spatrick                      "%s ctx.Events().WaitForSetEvents(0x%08x) ...",
251061da546Spatrick                      __FUNCTION__, event_mask);
252061da546Spatrick     nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
253061da546Spatrick     DNBLogThreadedIf(LOG_RNB_EVENTS,
254061da546Spatrick                      "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",
255061da546Spatrick                      __FUNCTION__, event_mask, set_events,
256061da546Spatrick                      ctx.EventsAsString(set_events, set_events_str));
257061da546Spatrick 
258061da546Spatrick     if (set_events) {
259061da546Spatrick       if ((set_events & RNBContext::event_proc_thread_exiting) ||
260061da546Spatrick           (set_events & RNBContext::event_proc_stdio_available)) {
261061da546Spatrick         remote->FlushSTDIO();
262061da546Spatrick       }
263061da546Spatrick 
264061da546Spatrick       if (set_events & RNBContext::event_read_packet_available) {
265061da546Spatrick         // handleReceivedPacket will take care of resetting the
266061da546Spatrick         // event_read_packet_available events when there are no more...
267061da546Spatrick         set_events ^= RNBContext::event_read_packet_available;
268061da546Spatrick 
269061da546Spatrick         if (ctx.ProcessStateRunning()) {
270061da546Spatrick           if (remote->HandleAsyncPacket() == rnb_not_connected) {
271061da546Spatrick             // TODO: connect again? Exit?
272061da546Spatrick           }
273061da546Spatrick         } else {
274061da546Spatrick           if (remote->HandleReceivedPacket() == rnb_not_connected) {
275061da546Spatrick             // TODO: connect again? Exit?
276061da546Spatrick           }
277061da546Spatrick         }
278061da546Spatrick       }
279061da546Spatrick 
280061da546Spatrick       if (set_events & RNBContext::event_proc_state_changed) {
281061da546Spatrick         mode = HandleProcessStateChange(remote, false);
282061da546Spatrick         ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
283061da546Spatrick         set_events ^= RNBContext::event_proc_state_changed;
284061da546Spatrick       }
285061da546Spatrick 
286061da546Spatrick       if (set_events & RNBContext::event_proc_thread_exiting) {
287061da546Spatrick         mode = eRNBRunLoopModeExit;
288061da546Spatrick       }
289061da546Spatrick 
290061da546Spatrick       if (set_events & RNBContext::event_read_thread_exiting) {
291061da546Spatrick         // Out remote packet receiving thread exited, exit for now.
292061da546Spatrick         if (ctx.HasValidProcessID()) {
293061da546Spatrick           // TODO: We should add code that will leave the current process
294061da546Spatrick           // in its current state and listen for another connection...
295061da546Spatrick           if (ctx.ProcessStateRunning()) {
296061da546Spatrick             DNBProcessKill(ctx.ProcessID());
297061da546Spatrick           }
298061da546Spatrick         }
299061da546Spatrick         mode = eRNBRunLoopModeExit;
300061da546Spatrick       }
301061da546Spatrick     }
302061da546Spatrick 
303061da546Spatrick     // Reset all event bits that weren't reset for now...
304061da546Spatrick     if (set_events != 0)
305061da546Spatrick       ctx.Events().ResetEvents(set_events);
306061da546Spatrick 
307061da546Spatrick     if (mode != eRNBRunLoopModeInferiorExecuting)
308061da546Spatrick       break;
309061da546Spatrick   }
310061da546Spatrick 
311061da546Spatrick   return mode;
312061da546Spatrick }
313061da546Spatrick 
ASLLogCallback(void * baton,uint32_t flags,const char * format,va_list args)314061da546Spatrick void ASLLogCallback(void *baton, uint32_t flags, const char *format,
315061da546Spatrick                     va_list args) {
316061da546Spatrick #if 0
317061da546Spatrick 	vprintf(format, args);
318061da546Spatrick #endif
319061da546Spatrick }
320061da546Spatrick 
debug_server_main(int fd)321061da546Spatrick extern "C" int debug_server_main(int fd) {
322061da546Spatrick #if 1
323061da546Spatrick   g_isatty = 0;
324061da546Spatrick #else
325061da546Spatrick   g_isatty = ::isatty(STDIN_FILENO);
326061da546Spatrick 
327061da546Spatrick   DNBLogSetDebug(1);
328061da546Spatrick   DNBLogSetVerbose(1);
329061da546Spatrick   DNBLogSetLogMask(-1);
330061da546Spatrick   DNBLogSetLogCallback(ASLLogCallback, NULL);
331061da546Spatrick #endif
332061da546Spatrick 
333061da546Spatrick   signal(SIGPIPE, signal_handler);
334061da546Spatrick 
335061da546Spatrick   g_remoteSP = std::make_shared<RNBRemote>();
336061da546Spatrick 
337061da546Spatrick   RNBRemote *remote = g_remoteSP.get();
338061da546Spatrick   if (remote == NULL) {
339061da546Spatrick     RNBLogSTDERR("error: failed to create a remote connection class\n");
340061da546Spatrick     return -1;
341061da546Spatrick   }
342061da546Spatrick 
343061da546Spatrick   RNBRunLoopMode mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
344061da546Spatrick 
345061da546Spatrick   while (mode != eRNBRunLoopModeExit) {
346061da546Spatrick     switch (mode) {
347061da546Spatrick     case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
348061da546Spatrick       if (g_remoteSP->Comm().useFD(fd) == rnb_success) {
349061da546Spatrick         RNBLogSTDOUT("Starting remote data thread.\n");
350061da546Spatrick         g_remoteSP->StartReadRemoteDataThread();
351061da546Spatrick 
352061da546Spatrick         RNBLogSTDOUT("Waiting for start mode from remote.\n");
353061da546Spatrick         mode = RNBRunLoopGetStartModeFromRemote(g_remoteSP);
354061da546Spatrick       } else {
355061da546Spatrick         mode = eRNBRunLoopModeExit;
356061da546Spatrick       }
357061da546Spatrick       break;
358061da546Spatrick 
359061da546Spatrick     case eRNBRunLoopModeInferiorExecuting:
360061da546Spatrick       mode = RNBRunLoopInferiorExecuting(g_remoteSP);
361061da546Spatrick       break;
362061da546Spatrick 
363061da546Spatrick     default:
364061da546Spatrick       mode = eRNBRunLoopModeExit;
365061da546Spatrick       break;
366061da546Spatrick 
367061da546Spatrick     case eRNBRunLoopModeExit:
368061da546Spatrick       break;
369061da546Spatrick     }
370061da546Spatrick   }
371061da546Spatrick 
372061da546Spatrick   g_remoteSP->StopReadRemoteDataThread();
373061da546Spatrick   g_remoteSP->Context().SetProcessID(INVALID_NUB_PROCESS);
374061da546Spatrick 
375061da546Spatrick   return 0;
376061da546Spatrick }
377