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