Add a new Process plugin for Linux.
This component is still at an early stage, but allows for simple
breakpoint/step-over operations and basic process control.
The makefiles are set up to build the plugin under Linux only.
llvm-svn: 109318
diff --git a/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp
new file mode 100644
index 0000000..22b65e1
--- /dev/null
+++ b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp
@@ -0,0 +1,925 @@
+//===-- ProcessMonitor.cpp ------------------------------------ -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+#include <errno.h>
+#include <poll.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Scalar.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/PseudoTerminal.h"
+
+#include "LinuxThread.h"
+#include "ProcessLinux.h"
+#include "ProcessMonitor.h"
+
+
+using namespace lldb_private;
+
+//------------------------------------------------------------------------------
+// Static implementations of ProcessMonitor::ReadMemory and
+// ProcessMonitor::WriteMemory. This enables mutual recursion between these
+// functions without needed to go thru the thread funnel.
+
+static size_t
+DoReadMemory(lldb::pid_t pid, unsigned word_size,
+ lldb::addr_t vm_addr, void *buf, size_t size, Error &error)
+{
+ unsigned char *dst = static_cast<unsigned char*>(buf);
+ size_t bytes_read;
+ size_t remainder;
+ long data;
+
+ for (bytes_read = 0; bytes_read < size; bytes_read += remainder)
+ {
+ errno = 0;
+ data = ptrace(PTRACE_PEEKDATA, pid, vm_addr, NULL);
+
+ if (data == -1L && errno)
+ {
+ error.SetErrorToErrno();
+ return bytes_read;
+ }
+
+ remainder = size - bytes_read;
+ remainder = remainder > word_size ? word_size : remainder;
+ for (unsigned i = 0; i < remainder; ++i)
+ dst[i] = ((data >> i*8) & 0xFF);
+ vm_addr += word_size;
+ dst += word_size;
+ }
+
+ return bytes_read;
+}
+
+static size_t
+DoWriteMemory(lldb::pid_t pid, unsigned word_size,
+ lldb::addr_t vm_addr, const void *buf, size_t size, Error &error)
+{
+ const unsigned char *src = static_cast<const unsigned char*>(buf);
+ size_t bytes_written = 0;
+ size_t remainder;
+
+ for (bytes_written = 0; bytes_written < size; bytes_written += remainder)
+ {
+ remainder = size - bytes_written;
+ remainder = remainder > word_size ? word_size : remainder;
+
+ if (remainder == word_size)
+ {
+ unsigned long data = 0;
+ for (unsigned i = 0; i < word_size; ++i)
+ data |= (unsigned long)src[i] << i*8;
+
+ if (ptrace(PTRACE_POKEDATA, pid, vm_addr, data))
+ {
+ error.SetErrorToErrno();
+ return bytes_written;
+ }
+ }
+ else
+ {
+ unsigned char buff[8];
+ if (DoReadMemory(pid, word_size, vm_addr,
+ buff, word_size, error) != word_size)
+ return bytes_written;
+
+ memcpy(buff, src, remainder);
+
+ if (DoWriteMemory(pid, word_size, vm_addr,
+ buff, word_size, error) != word_size)
+ return bytes_written;
+ }
+
+ vm_addr += word_size;
+ src += word_size;
+ }
+ return bytes_written;
+}
+
+
+//------------------------------------------------------------------------------
+/// @class Operation
+/// @brief Represents a ProcessMonitor operation.
+///
+/// Under Linux, it is not possible to ptrace() from any other thread but the
+/// one that spawned or attached to the process from the start. Therefore, when
+/// a ProcessMonitor is asked to deliver or change the state of an inferior
+/// process the operation must be "funneled" to a specific thread to perform the
+/// task. The Operation class provides an abstract base for all services the
+/// ProcessMonitor must perform via the single virtual function Execute, thus
+/// encapsulating the code that needs to run in the privileged context.
+class Operation
+{
+public:
+ virtual void Execute(ProcessMonitor *monitor) = 0;
+};
+
+//------------------------------------------------------------------------------
+/// @class ReadOperation
+/// @brief Implements ProcessMonitor::ReadMemory.
+class ReadOperation : public Operation
+{
+public:
+ ReadOperation(lldb::addr_t addr, void *buff, size_t size,
+ Error &error, size_t &result)
+ : m_addr(addr), m_buff(buff), m_size(size),
+ m_error(error), m_result(result)
+ { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::addr_t m_addr;
+ void *m_buff;
+ size_t m_size;
+ Error &m_error;
+ size_t &m_result;
+};
+
+void
+ReadOperation::Execute(ProcessMonitor *monitor)
+{
+ const unsigned word_size = monitor->GetProcess().GetAddressByteSize();
+ lldb::pid_t pid = monitor->GetPID();
+
+ m_result = DoReadMemory(pid, word_size, m_addr, m_buff, m_size, m_error);
+}
+
+//------------------------------------------------------------------------------
+/// @class ReadOperation
+/// @brief Implements ProcessMonitor::WriteMemory.
+class WriteOperation : public Operation
+{
+public:
+ WriteOperation(lldb::addr_t addr, const void *buff, size_t size,
+ Error &error, size_t &result)
+ : m_addr(addr), m_buff(buff), m_size(size),
+ m_error(error), m_result(result)
+ { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::addr_t m_addr;
+ const void *m_buff;
+ size_t m_size;
+ Error &m_error;
+ size_t &m_result;
+};
+
+void
+WriteOperation::Execute(ProcessMonitor *monitor)
+{
+ const unsigned word_size = monitor->GetProcess().GetAddressByteSize();
+ lldb::pid_t pid = monitor->GetPID();
+
+ m_result = DoWriteMemory(pid, word_size, m_addr, m_buff, m_size, m_error);
+}
+
+//------------------------------------------------------------------------------
+/// @class ReadRegOperation
+/// @brief Implements ProcessMonitor::ReadRegisterValue.
+class ReadRegOperation : public Operation
+{
+public:
+ ReadRegOperation(unsigned offset, Scalar &value, bool &result)
+ : m_offset(offset), m_value(value), m_result(result)
+ { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ unsigned m_offset;
+ Scalar &m_value;
+ bool &m_result;
+};
+
+void
+ReadRegOperation::Execute(ProcessMonitor *monitor)
+{
+ lldb::pid_t pid = monitor->GetPID();
+
+ // Set errno to zero so that we can detect a failed peek.
+ errno = 0;
+ unsigned long data = ptrace(PTRACE_PEEKUSER, pid, m_offset, NULL);
+
+ if (data == -1UL && errno)
+ m_result = false;
+ else
+ {
+ m_value = data;
+ m_result = true;
+ }
+}
+
+//------------------------------------------------------------------------------
+/// @class WriteRegOperation
+/// @brief Implements ProcessMonitor::WriteRegisterValue.
+class WriteRegOperation : public Operation
+{
+public:
+ WriteRegOperation(unsigned offset, const Scalar &value, bool &result)
+ : m_offset(offset), m_value(value), m_result(result)
+ { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ unsigned m_offset;
+ const Scalar &m_value;
+ bool &m_result;
+};
+
+void
+WriteRegOperation::Execute(ProcessMonitor *monitor)
+{
+ lldb::pid_t pid = monitor->GetPID();
+
+ if (ptrace(PTRACE_POKEUSER, pid, m_offset, m_value.ULong()))
+ m_result = false;
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class ResumeOperation
+/// @brief Implements ProcessMonitor::Resume.
+class ResumeOperation : public Operation
+{
+public:
+ ResumeOperation(lldb::tid_t tid, bool &result) :
+ m_tid(tid), m_result(result) { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ bool &m_result;
+};
+
+void
+ResumeOperation::Execute(ProcessMonitor *monitor)
+{
+ if (ptrace(PTRACE_CONT, m_tid, NULL, NULL))
+ m_result = false;
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class ResumeOperation
+/// @brief Implements ProcessMonitor::SingleStep.
+class SingleStepOperation : public Operation
+{
+public:
+ SingleStepOperation(lldb::tid_t tid, bool &result)
+ : m_tid(tid), m_result(result) { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ bool &m_result;
+};
+
+void
+SingleStepOperation::Execute(ProcessMonitor *monitor)
+{
+ if (ptrace(PTRACE_SINGLESTEP, m_tid, NULL, NULL))
+ m_result = false;
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class SiginfoOperation
+/// @brief Implements ProcessMonitor::GetSignalInfo.
+class SiginfoOperation : public Operation
+{
+public:
+ SiginfoOperation(lldb::tid_t tid, void *info, bool &result)
+ : m_tid(tid), m_info(info), m_result(result) { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_info;
+ bool &m_result;
+};
+
+void
+SiginfoOperation::Execute(ProcessMonitor *monitor)
+{
+ if (ptrace(PTRACE_GETSIGINFO, m_tid, NULL, m_info))
+ m_result = false;
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class EventMessageOperation
+/// @brief Implements ProcessMonitor::GetEventMessage.
+class EventMessageOperation : public Operation
+{
+public:
+ EventMessageOperation(lldb::tid_t tid, unsigned long *message, bool &result)
+ : m_tid(tid), m_message(message), m_result(result) { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned long *m_message;
+ bool &m_result;
+};
+
+void
+EventMessageOperation::Execute(ProcessMonitor *monitor)
+{
+ if (ptrace(PTRACE_GETEVENTMSG, m_tid, NULL, m_message))
+ m_result = false;
+ else
+ m_result = true;
+}
+
+//------------------------------------------------------------------------------
+/// @class KillOperation
+/// @brief Implements ProcessMonitor::BringProcessIntoLimbo.
+class KillOperation : public Operation
+{
+public:
+ KillOperation(bool &result) : m_result(result) { }
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ bool &m_result;
+};
+
+void
+KillOperation::Execute(ProcessMonitor *monitor)
+{
+ lldb::pid_t pid = monitor->GetPID();
+
+ if (ptrace(PTRACE_KILL, pid, NULL, NULL))
+ m_result = false;
+ else
+ m_result = true;
+
+#if 0
+ // First, stop the inferior process.
+ if (kill(pid, SIGSTOP))
+ {
+ m_result = false;
+ return;
+ }
+
+ // Clear any ptrace options. When PTRACE_O_TRACEEXIT is set, a plain
+ // PTRACE_KILL (or any termination signal) will not truely terminate the
+ // inferior process. Instead, the process is left in a state of "limbo"
+ // allowing us to interrogate its state. However in this case we really do
+ // want the process gone.
+ if (ptrace(PTRACE_SETOPTIONS, pid, NULL, 0UL))
+ {
+ m_result = false;
+ return;
+ }
+
+ // Kill it.
+ if (ptrace(PTRACE_KILL, pid, NULL, NULL))
+ m_result = false;
+ else
+ m_result = true;
+#endif
+}
+
+ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor,
+ lldb_private::Module *module,
+ char const **argv,
+ char const **envp,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path)
+ : m_monitor(monitor),
+ m_module(module),
+ m_argv(argv),
+ m_envp(envp),
+ m_stdin_path(stdin_path),
+ m_stdout_path(stdout_path),
+ m_stderr_path(stderr_path)
+{
+ sem_init(&m_semaphore, 0, 0);
+}
+
+ProcessMonitor::LaunchArgs::~LaunchArgs()
+{
+ sem_destroy(&m_semaphore);
+}
+
+//------------------------------------------------------------------------------
+/// The basic design of the ProcessMonitor is built around two threads.
+///
+/// One thread (@see SignalThread) simply blocks on a call to waitpid() looking
+/// for changes in the debugee state. When a change is detected a
+/// ProcessMessage is sent to the associated ProcessLinux instance. This thread
+/// "drives" state changes in the debugger.
+///
+/// The second thread (@see OperationThread) is responsible for two things 1)
+/// lauching or attaching to the inferior process, and then 2) servicing
+/// operations such as register reads/writes, stepping, etc. See the comments
+/// on the Operation class for more info as to why this is needed.
+ProcessMonitor::ProcessMonitor(ProcessLinux *process,
+ Module *module,
+ const char *argv[],
+ const char *envp[],
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ lldb_private::Error &error)
+ : m_process(process),
+ m_operation_thread(LLDB_INVALID_HOST_THREAD),
+ m_pid(LLDB_INVALID_PROCESS_ID),
+ m_terminal_fd(-1),
+ m_monitor_handle(0),
+ m_client_fd(-1),
+ m_server_fd(-1)
+{
+ LaunchArgs args(this, module, argv, envp,
+ stdin_path, stdout_path, stderr_path);
+
+ // Server/client descriptors.
+ if (!EnableIPC())
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Monitor failed to initialize.");
+ }
+
+ StartOperationThread(&args, error);
+ if (!error.Success())
+ return;
+
+WAIT_AGAIN:
+ // Wait for the operation thread to initialize.
+ if (sem_wait(&args.m_semaphore))
+ {
+ if (errno == EINTR)
+ goto WAIT_AGAIN;
+ else
+ {
+ error.SetErrorToErrno();
+ return;
+ }
+ }
+
+ // Check that the launch was a success.
+ if (!args.m_error.Success())
+ {
+ StopOperationThread();
+ error = args.m_error;
+ return;
+ }
+
+ // Finally, start monitoring the child process for change in state.
+ if (!(m_monitor_handle = Host::StartMonitoringChildProcess(
+ ProcessMonitor::MonitorCallback, this, GetPID(), true)))
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Process launch failed.");
+ return;
+ }
+}
+
+ProcessMonitor::~ProcessMonitor()
+{
+ Host::StopMonitoringChildProcess(m_monitor_handle);
+ StopOperationThread();
+
+ close(m_terminal_fd);
+ close(m_client_fd);
+ close(m_server_fd);
+}
+
+//------------------------------------------------------------------------------
+// Thread setup and tear down.
+void
+ProcessMonitor::StartOperationThread(LaunchArgs *args, Error &error)
+{
+ static const char *g_thread_name = "lldb.process.linux.operation";
+
+ if (m_operation_thread != LLDB_INVALID_HOST_THREAD)
+ return;
+
+ m_operation_thread =
+ Host::ThreadCreate(g_thread_name, OperationThread, args, &error);
+}
+
+void
+ProcessMonitor::StopOperationThread()
+{
+ lldb::thread_result_t result;
+
+ if (m_operation_thread == LLDB_INVALID_HOST_THREAD)
+ return;
+
+ Host::ThreadCancel(m_operation_thread, NULL);
+ Host::ThreadJoin(m_operation_thread, &result, NULL);
+}
+
+void *
+ProcessMonitor::OperationThread(void *arg)
+{
+ LaunchArgs *args = static_cast<LaunchArgs*>(arg);
+
+ if (!Launch(args))
+ return NULL;
+
+ ServeOperation(args->m_monitor);
+ return NULL;
+}
+
+bool
+ProcessMonitor::Launch(LaunchArgs *args)
+{
+ ProcessMonitor *monitor = args->m_monitor;
+ ProcessLinux &process = monitor->GetProcess();
+ const char **argv = args->m_argv;
+ const char **envp = args->m_envp;
+ const char *stdin_path = args->m_stdin_path;
+ const char *stdout_path = args->m_stdout_path;
+ const char *stderr_path = args->m_stderr_path;
+
+ lldb_utility::PseudoTerminal terminal;
+ const size_t err_len = 1024;
+ char err_str[err_len];
+ lldb::pid_t pid;
+
+ lldb::ThreadSP inferior;
+
+ // Pseudo terminal setup.
+ if (!terminal.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY, err_str, err_len))
+ {
+ args->m_error.SetErrorToGenericError();
+ args->m_error.SetErrorString("Could not open controlling TTY.");
+ goto FINISH;
+ }
+
+ if ((pid = terminal.Fork(err_str, err_len)) < 0)
+ {
+ args->m_error.SetErrorToGenericError();
+ args->m_error.SetErrorString("Process fork failed.");
+ goto FINISH;
+ }
+
+ // Child process.
+ if (pid == 0)
+ {
+ // Trace this process.
+ ptrace(PTRACE_TRACEME, 0, NULL, NULL);
+
+ // Do not inherit setgid powers.
+ setgid(getgid());
+
+ // Let us have our own process group.
+ setpgid(0, 0);
+
+ // Dup file discriptors if needed.
+ //
+ // FIXME: If two or more of the paths are the same we needlessly open
+ // the same file multiple times.
+ if (stdin_path != NULL && stdin_path[0])
+ if (!DupDescriptor(stdin_path, STDIN_FILENO, O_RDONLY | O_CREAT))
+ exit(1);
+
+ if (stdout_path != NULL && stdout_path[0])
+ if (!DupDescriptor(stdout_path, STDOUT_FILENO, O_WRONLY | O_CREAT))
+ exit(1);
+
+ if (stderr_path != NULL && stderr_path[0])
+ if (!DupDescriptor(stderr_path, STDOUT_FILENO, O_WRONLY | O_CREAT))
+ exit(1);
+
+ // Execute. We should never return.
+ execve(argv[0],
+ const_cast<char *const *>(argv),
+ const_cast<char *const *>(envp));
+ exit(-1);
+ }
+
+ // Wait for the child process to to trap on its call to execve.
+ int status;
+ if ((status = waitpid(pid, NULL, 0)) < 0)
+ {
+ // execve likely failed for some reason.
+ args->m_error.SetErrorToErrno();
+ goto FINISH;
+ }
+ assert(status == pid && "Could not sync with inferior process.");
+
+ // Have the child raise an event on exit. This is used to keep the child in
+ // limbo until it is destroyed.
+ if (ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEEXIT) < 0)
+ {
+ args->m_error.SetErrorToErrno();
+ goto FINISH;
+ }
+
+ // Release the master terminal descriptor and pass it off to the
+ // ProcessMonitor instance. Similarly stash the inferior pid.
+ monitor->m_terminal_fd = terminal.ReleaseMasterFileDescriptor();
+ monitor->m_pid = pid;
+
+ // Update the process thread list with this new thread and mark it as
+ // current.
+ inferior.reset(new LinuxThread(process, pid));
+ process.GetThreadList().AddThread(inferior);
+ process.GetThreadList().SetCurrentThreadByID(pid);
+
+ // Let our process instance know the thread has stopped.
+ process.SendMessage(ProcessMessage::Trace(pid));
+
+FINISH:
+ // Sync with our parent thread now that the launch operation is complete.
+ sem_post(&args->m_semaphore);
+ return args->m_error.Success();
+}
+
+bool
+ProcessMonitor::EnableIPC()
+{
+ int fd[2];
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd))
+ return false;
+
+ m_client_fd = fd[0];
+ m_server_fd = fd[1];
+ return true;
+}
+
+bool
+ProcessMonitor::MonitorCallback(void *callback_baton,
+ lldb::pid_t pid,
+ int signal,
+ int status)
+{
+ ProcessMessage message;
+ ProcessMonitor *monitor = static_cast<ProcessMonitor*>(callback_baton);
+ ProcessLinux *process = monitor->m_process;
+
+ switch (signal)
+ {
+ case 0:
+ // No signal. The child has exited normally.
+ message = ProcessMessage::Exit(pid, status);
+ break;
+
+ case SIGTRAP:
+ // Specially handle SIGTRAP and form the appropriate message.
+ message = MonitorSIGTRAP(monitor, pid);
+ break;
+
+ default:
+ // For all other signals simply notify the process instance. Note that
+ // the process exit status is set when the signal resulted in
+ // termination.
+ //
+ // FIXME: We need a specialized message to inform the process instance
+ // about "crashes".
+ if (status)
+ message = ProcessMessage::Exit(pid, status);
+ else
+ message = ProcessMessage::Signal(pid, signal);
+ }
+
+ process->SendMessage(message);
+ bool stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage;
+ return stop_monitoring;
+}
+
+ProcessMessage
+ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, lldb::pid_t pid)
+{
+ siginfo_t info;
+ ProcessMessage message;
+ bool status;
+
+ status = monitor->GetSignalInfo(pid, &info);
+ assert(status && "GetSignalInfo failed!");
+
+ assert(info.si_signo == SIGTRAP && "Unexpected child signal!");
+
+ switch (info.si_code)
+ {
+ default:
+ assert(false && "Unexpected SIGTRAP code!");
+ break;
+
+ case (SIGTRAP | (PTRACE_EVENT_EXIT << 8)):
+ {
+ // The inferior process is about to exit. Maintain the process in a
+ // state of "limbo" until we are explicitly commanded to detach,
+ // destroy, resume, etc.
+ unsigned long data = 0;
+ if (!monitor->GetEventMessage(pid, &data))
+ data = -1;
+ message = ProcessMessage::Exit(pid, (data >> 8));
+ break;
+ }
+
+ case 0:
+ case TRAP_TRACE:
+ message = ProcessMessage::Trace(pid);
+ break;
+
+ case SI_KERNEL:
+ case TRAP_BRKPT:
+ message = ProcessMessage::Break(pid);
+ break;
+ }
+
+ return message;
+}
+
+void
+ProcessMonitor::ServeOperation(ProcessMonitor *monitor)
+{
+ int status;
+ pollfd fdset;
+
+ fdset.fd = monitor->m_server_fd;
+ fdset.events = POLLIN | POLLPRI;
+ fdset.revents = 0;
+
+ for (;;)
+ {
+ if ((status = poll(&fdset, 1, -1)) < 0)
+ {
+ switch (errno)
+ {
+ default:
+ assert(false && "Unexpected poll() failure!");
+ continue;
+
+ case EINTR: continue; // Just poll again.
+ case EBADF: return; // Connection terminated.
+ }
+ }
+
+ assert(status == 1 && "Too many descriptors!");
+
+ if (fdset.revents & POLLIN)
+ {
+ Operation *op = NULL;
+
+ READ_AGAIN:
+ if ((status = read(fdset.fd, &op, sizeof(op))) < 0)
+ {
+ // There is only one acceptable failure.
+ assert(errno == EINTR);
+ goto READ_AGAIN;
+ }
+
+ assert(status == sizeof(op));
+ op->Execute(monitor);
+ write(fdset.fd, &op, sizeof(op));
+ }
+ }
+}
+
+void
+ProcessMonitor::DoOperation(Operation *op)
+{
+ int status;
+ Operation *ack = NULL;
+ Mutex::Locker lock(m_server_mutex);
+
+ // FIXME: Do proper error checking here.
+ write(m_client_fd, &op, sizeof(op));
+
+READ_AGAIN:
+ if ((status = read(m_client_fd, &ack, sizeof(ack))) < 0)
+ {
+ // If interrupted by a signal handler try again. Otherwise the monitor
+ // thread probably died and we have a stale file descriptor -- abort the
+ // operation.
+ if (errno == EINTR)
+ goto READ_AGAIN;
+ return;
+ }
+
+ assert(status == sizeof(ack));
+ assert(ack == op && "Invalid monitor thread response!");
+}
+
+size_t
+ProcessMonitor::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ Error &error)
+{
+ size_t result;
+ ReadOperation op(vm_addr, buf, size, error, result);
+ DoOperation(&op);
+ return result;
+}
+
+size_t
+ProcessMonitor::WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
+ lldb_private::Error &error)
+{
+ size_t result;
+ WriteOperation op(vm_addr, buf, size, error, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::ReadRegisterValue(unsigned offset, Scalar &value)
+{
+ bool result;
+ ReadRegOperation op(offset, value, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::WriteRegisterValue(unsigned offset, const Scalar &value)
+{
+ bool result;
+ WriteRegOperation op(offset, value, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::Resume(lldb::tid_t tid)
+{
+ bool result;
+ ResumeOperation op(tid, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::SingleStep(lldb::tid_t tid)
+{
+ bool result;
+ SingleStepOperation op(tid, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::BringProcessIntoLimbo()
+{
+ bool result;
+ KillOperation op(result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::GetSignalInfo(lldb::tid_t tid, void *siginfo)
+{
+ bool result;
+ SiginfoOperation op(tid, siginfo, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message)
+{
+ bool result;
+ EventMessageOperation op(tid, message, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool
+ProcessMonitor::DupDescriptor(const char *path, int fd, int flags)
+{
+ int target_fd = open(path, flags);
+
+ if (target_fd == -1)
+ return false;
+
+ return (dup2(fd, target_fd) == -1) ? false : true;
+}