aboutsummaryrefslogtreecommitdiff
path: root/tools/debugserver/source/libdebugserver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/debugserver/source/libdebugserver.cpp')
-rw-r--r--tools/debugserver/source/libdebugserver.cpp397
1 files changed, 397 insertions, 0 deletions
diff --git a/tools/debugserver/source/libdebugserver.cpp b/tools/debugserver/source/libdebugserver.cpp
new file mode 100644
index 000000000000..63d76eb26aef
--- /dev/null
+++ b/tools/debugserver/source/libdebugserver.cpp
@@ -0,0 +1,397 @@
+//===-- libdebugserver.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <sys/select.h>
+#include <sys/sysctl.h>
+
+#include "DNB.h"
+#include "DNBLog.h"
+#include "DNBTimer.h"
+#include "PseudoTerminal.h"
+#include "RNBContext.h"
+#include "RNBServices.h"
+#include "RNBSocket.h"
+#include "RNBRemote.h"
+#include "SysSignal.h"
+
+//----------------------------------------------------------------------
+// Run loop modes which determine which run loop function will be called
+//----------------------------------------------------------------------
+typedef enum
+{
+ eRNBRunLoopModeInvalid = 0,
+ eRNBRunLoopModeGetStartModeFromRemoteProtocol,
+ eRNBRunLoopModeInferiorExecuting,
+ eRNBRunLoopModeExit
+} RNBRunLoopMode;
+
+
+//----------------------------------------------------------------------
+// Global Variables
+//----------------------------------------------------------------------
+RNBRemoteSP g_remoteSP;
+int g_disable_aslr = 0;
+int g_isatty = 0;
+
+#define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
+#define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
+
+
+//----------------------------------------------------------------------
+// Get our program path and arguments from the remote connection.
+// We will need to start up the remote connection without a PID, get the
+// arguments, wait for the new process to finish launching and hit its
+// entry point, and then return the run loop mode that should come next.
+//----------------------------------------------------------------------
+RNBRunLoopMode
+RNBRunLoopGetStartModeFromRemote (RNBRemoteSP &remoteSP)
+{
+ std::string packet;
+
+ if (remoteSP.get() != NULL)
+ {
+ RNBRemote* remote = remoteSP.get();
+ RNBContext& ctx = remote->Context();
+ uint32_t event_mask = RNBContext::event_read_packet_available;
+
+ // Spin waiting to get the A packet.
+ while (1)
+ {
+ DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",__FUNCTION__, event_mask);
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+ DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x", __FUNCTION__, event_mask, set_events);
+
+ if (set_events & RNBContext::event_read_packet_available)
+ {
+ rnb_err_t err = rnb_err;
+ RNBRemote::PacketEnum type;
+
+ err = remote->HandleReceivedPacket (&type);
+
+ // check if we tried to attach to a process
+ if (type == RNBRemote::vattach || type == RNBRemote::vattachwait)
+ {
+ if (err == rnb_success)
+ return eRNBRunLoopModeInferiorExecuting;
+ else
+ {
+ RNBLogSTDERR ("error: attach failed.");
+ return eRNBRunLoopModeExit;
+ }
+ }
+
+
+ if (err == rnb_success)
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Got success...",__FUNCTION__);
+ continue;
+ }
+ else if (err == rnb_not_connected)
+ {
+ RNBLogSTDERR ("error: connection lost.");
+ return eRNBRunLoopModeExit;
+ }
+ else
+ {
+ // a catch all for any other gdb remote packets that failed
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.",__FUNCTION__);
+ continue;
+ }
+
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
+ }
+ else
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Connection closed before getting \"A\" packet.", __FUNCTION__);
+ return eRNBRunLoopModeExit;
+ }
+ }
+ }
+ return eRNBRunLoopModeExit;
+}
+
+
+//----------------------------------------------------------------------
+// Watch for signals:
+// SIGINT: so we can halt our inferior. (disabled for now)
+// SIGPIPE: in case our child process dies
+//----------------------------------------------------------------------
+nub_process_t g_pid;
+int g_sigpipe_received = 0;
+void
+signal_handler(int signo)
+{
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo));
+
+ switch (signo)
+ {
+ // case SIGINT:
+ // DNBProcessKill (g_pid, signo);
+ // break;
+
+ case SIGPIPE:
+ g_sigpipe_received = 1;
+ break;
+ }
+}
+
+// Return the new run loop mode based off of the current process state
+RNBRunLoopMode
+HandleProcessStateChange (RNBRemoteSP &remote, bool initialize)
+{
+ RNBContext& ctx = remote->Context();
+ nub_process_t pid = ctx.ProcessID();
+
+ if (pid == INVALID_NUB_PROCESS)
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__);
+ return eRNBRunLoopModeExit;
+ }
+ nub_state_t pid_state = DNBProcessGetState (pid);
+
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state));
+
+ switch (pid_state)
+ {
+ case eStateInvalid:
+ case eStateUnloaded:
+ // Something bad happened
+ return eRNBRunLoopModeExit;
+ break;
+
+ case eStateAttaching:
+ case eStateLaunching:
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateSuspended:
+ case eStateCrashed:
+ case eStateStopped:
+ if (initialize == false)
+ {
+ // Compare the last stop count to our current notion of a stop count
+ // to make sure we don't notify more than once for a given stop.
+ nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
+ bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
+ if (pid_stop_count_changed)
+ {
+ remote->FlushSTDIO();
+
+ if (ctx.GetProcessStopCount() == 1)
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %zu (old %zu)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
+ }
+ else
+ {
+
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %zu (old %zu)) Notify??? YES!!!", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
+ remote->NotifyThatProcessStopped ();
+ }
+ }
+ else
+ {
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %zu (old %zu)) Notify??? skipping...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
+ }
+ }
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateStepping:
+ case eStateRunning:
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateExited:
+ remote->HandlePacket_last_signal(NULL);
+ return eRNBRunLoopModeExit;
+ case eStateDetached:
+ return eRNBRunLoopModeExit;
+
+ }
+
+ // Catch all...
+ return eRNBRunLoopModeExit;
+}
+// This function handles the case where our inferior program is stopped and
+// we are waiting for gdb remote protocol packets. When a packet occurs that
+// makes the inferior run, we need to leave this function with a new state
+// as the return code.
+RNBRunLoopMode
+RNBRunLoopInferiorExecuting (RNBRemoteSP &remote)
+{
+ DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
+ RNBContext& ctx = remote->Context();
+
+ // Init our mode and set 'is_running' based on the current process state
+ RNBRunLoopMode mode = HandleProcessStateChange (remote, true);
+
+ while (ctx.ProcessID() != INVALID_NUB_PROCESS)
+ {
+
+ std::string set_events_str;
+ uint32_t event_mask = ctx.NormalEventBits();
+
+ if (!ctx.ProcessStateRunning())
+ {
+ // Clear the stdio bits if we are not running so we don't send any async packets
+ event_mask &= ~RNBContext::event_proc_stdio_available;
+ }
+
+ // We want to make sure we consume all process state changes and have
+ // whomever is notifying us to wait for us to reset the event bit before
+ // continuing.
+ //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
+
+ DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+ 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));
+
+ if (set_events)
+ {
+ if ((set_events & RNBContext::event_proc_thread_exiting) ||
+ (set_events & RNBContext::event_proc_stdio_available))
+ {
+ remote->FlushSTDIO();
+ }
+
+ if (set_events & RNBContext::event_read_packet_available)
+ {
+ // handleReceivedPacket will take care of resetting the
+ // event_read_packet_available events when there are no more...
+ set_events ^= RNBContext::event_read_packet_available;
+
+ if (ctx.ProcessStateRunning())
+ {
+ if (remote->HandleAsyncPacket() == rnb_not_connected)
+ {
+ // TODO: connect again? Exit?
+ }
+ }
+ else
+ {
+ if (remote->HandleReceivedPacket() == rnb_not_connected)
+ {
+ // TODO: connect again? Exit?
+ }
+ }
+ }
+
+ if (set_events & RNBContext::event_proc_state_changed)
+ {
+ mode = HandleProcessStateChange (remote, false);
+ ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
+ set_events ^= RNBContext::event_proc_state_changed;
+ }
+
+ if (set_events & RNBContext::event_proc_thread_exiting)
+ {
+ mode = eRNBRunLoopModeExit;
+ }
+
+ if (set_events & RNBContext::event_read_thread_exiting)
+ {
+ // Out remote packet receiving thread exited, exit for now.
+ if (ctx.HasValidProcessID())
+ {
+ // TODO: We should add code that will leave the current process
+ // in its current state and listen for another connection...
+ if (ctx.ProcessStateRunning())
+ {
+ DNBProcessKill (ctx.ProcessID());
+ }
+ }
+ mode = eRNBRunLoopModeExit;
+ }
+ }
+
+ // Reset all event bits that weren't reset for now...
+ if (set_events != 0)
+ ctx.Events().ResetEvents(set_events);
+
+ if (mode != eRNBRunLoopModeInferiorExecuting)
+ break;
+ }
+
+ return mode;
+}
+
+void
+ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
+{
+#if 0
+ vprintf(format, args);
+#endif
+}
+
+extern "C" int
+debug_server_main(int fd)
+{
+#if 1
+ g_isatty = 0;
+#else
+ g_isatty = ::isatty (STDIN_FILENO);
+
+ DNBLogSetDebug(1);
+ DNBLogSetVerbose(1);
+ DNBLogSetLogMask(-1);
+ DNBLogSetLogCallback(ASLLogCallback, NULL);
+#endif
+
+ signal (SIGPIPE, signal_handler);
+
+ g_remoteSP.reset (new RNBRemote);
+
+ RNBRemote *remote = g_remoteSP.get();
+ if (remote == NULL)
+ {
+ RNBLogSTDERR ("error: failed to create a remote connection class\n");
+ return -1;
+ }
+
+
+ RNBRunLoopMode mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
+
+ while (mode != eRNBRunLoopModeExit)
+ {
+ switch (mode)
+ {
+ case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
+ if (g_remoteSP->Comm().useFD(fd) == rnb_success) {
+ RNBLogSTDOUT("Starting remote data thread.\n");
+ g_remoteSP->StartReadRemoteDataThread();
+
+ RNBLogSTDOUT("Waiting for start mode from remote.\n");
+ mode = RNBRunLoopGetStartModeFromRemote(g_remoteSP);
+ }
+ else
+ {
+ mode = eRNBRunLoopModeExit;
+ }
+ break;
+
+ case eRNBRunLoopModeInferiorExecuting:
+ mode = RNBRunLoopInferiorExecuting(g_remoteSP);
+ break;
+
+ default:
+ mode = eRNBRunLoopModeExit;
+ break;
+
+ case eRNBRunLoopModeExit:
+ break;
+ }
+ }
+
+ g_remoteSP->StopReadRemoteDataThread ();
+ g_remoteSP->Context().SetProcessID(INVALID_NUB_PROCESS);
+
+ return 0;
+}