diff options
| author | Stephen Wilson <wilsons@start.ca> | 2010-07-24 02:19:04 +0000 |
|---|---|---|
| committer | Stephen Wilson <wilsons@start.ca> | 2010-07-24 02:19:04 +0000 |
| commit | e6f9f66b39c05f2b5f0b708095686d6fa17dc6e8 (patch) | |
| tree | fc94bdc38261cce829aa4975a17500356de9a64f /lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp | |
| parent | ddb46efcca906855905d301c42f321a391428ac5 (diff) | |
| download | bcm5719-llvm-e6f9f66b39c05f2b5f0b708095686d6fa17dc6e8.tar.gz bcm5719-llvm-e6f9f66b39c05f2b5f0b708095686d6fa17dc6e8.zip | |
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
Diffstat (limited to 'lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp')
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp | 925 |
1 files changed, 925 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp new file mode 100644 index 00000000000..22b65e1a4b4 --- /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; +} |

