Revert "[NativeProcessLinux] Integrate MainLoop"
This seems to be causing major slowdows on the android buildbot. Reverting while I investigate. llvm-svn: 242391
This commit is contained in:
parent
d783a98303
commit
5abe726911
@ -16,7 +16,6 @@
|
||||
#include "lldb/lldb-types.h"
|
||||
#include "lldb/Core/Error.h"
|
||||
#include "lldb/Host/Mutex.h"
|
||||
#include "lldb/Host/MainLoop.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
#include "NativeBreakpointList.h"
|
||||
@ -285,6 +284,10 @@ namespace lldb_private
|
||||
bool
|
||||
UnregisterNativeDelegate (NativeDelegate &native_delegate);
|
||||
|
||||
// Called before termination of NativeProcessProtocol's instance.
|
||||
virtual void
|
||||
Terminate ();
|
||||
|
||||
virtual Error
|
||||
GetLoadedModuleFileSpec(const char* module_path, FileSpec& file_spec) = 0;
|
||||
|
||||
@ -304,11 +307,6 @@ namespace lldb_private
|
||||
/// inferior. Must outlive the NativeProcessProtocol
|
||||
/// instance.
|
||||
///
|
||||
/// @param[in] mainloop
|
||||
/// The mainloop instance with which the process can register
|
||||
/// callbacks. Must outlive the NativeProcessProtocol
|
||||
/// instance.
|
||||
///
|
||||
/// @param[out] process_sp
|
||||
/// On successful return from the method, this parameter
|
||||
/// contains the shared pointer to the
|
||||
@ -322,7 +320,6 @@ namespace lldb_private
|
||||
static Error
|
||||
Launch (ProcessLaunchInfo &launch_info,
|
||||
NativeDelegate &native_delegate,
|
||||
MainLoop &mainloop,
|
||||
NativeProcessProtocolSP &process_sp);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
@ -338,11 +335,6 @@ namespace lldb_private
|
||||
/// inferior. Must outlive the NativeProcessProtocol
|
||||
/// instance.
|
||||
///
|
||||
/// @param[in] mainloop
|
||||
/// The mainloop instance with which the process can register
|
||||
/// callbacks. Must outlive the NativeProcessProtocol
|
||||
/// instance.
|
||||
///
|
||||
/// @param[out] process_sp
|
||||
/// On successful return from the method, this parameter
|
||||
/// contains the shared pointer to the
|
||||
@ -356,7 +348,6 @@ namespace lldb_private
|
||||
static Error
|
||||
Attach (lldb::pid_t pid,
|
||||
NativeDelegate &native_delegate,
|
||||
MainLoop &mainloop,
|
||||
NativeProcessProtocolSP &process_sp);
|
||||
|
||||
protected:
|
||||
|
||||
@ -436,6 +436,12 @@ NativeProcessProtocol::DoStopIDBumped (uint32_t /* newBumpId */)
|
||||
// Default implementation does nothing.
|
||||
}
|
||||
|
||||
void
|
||||
NativeProcessProtocol::Terminate ()
|
||||
{
|
||||
// Default implementation does nothing.
|
||||
}
|
||||
|
||||
#ifndef __linux__
|
||||
// These need to be implemented to support lldb-gdb-server on a given platform. Stubs are
|
||||
// provided to make the rest of the code link on non-supported platforms.
|
||||
@ -443,7 +449,6 @@ NativeProcessProtocol::DoStopIDBumped (uint32_t /* newBumpId */)
|
||||
Error
|
||||
NativeProcessProtocol::Launch (ProcessLaunchInfo &launch_info,
|
||||
NativeDelegate &native_delegate,
|
||||
MainLoop &mainloop,
|
||||
NativeProcessProtocolSP &process_sp)
|
||||
{
|
||||
llvm_unreachable("Platform has no NativeProcessProtocol support");
|
||||
@ -452,7 +457,6 @@ NativeProcessProtocol::Launch (ProcessLaunchInfo &launch_info,
|
||||
Error
|
||||
NativeProcessProtocol::Attach (lldb::pid_t pid,
|
||||
NativeDelegate &native_delegate,
|
||||
MainLoop &mainloop,
|
||||
NativeProcessProtocolSP &process_sp)
|
||||
{
|
||||
llvm_unreachable("Platform has no NativeProcessProtocol support");
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
// C Includes
|
||||
#include <errno.h>
|
||||
#include <semaphore.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
@ -212,8 +213,157 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr unsigned k_ptrace_word_size = sizeof(void*);
|
||||
static_assert(sizeof(long) >= k_ptrace_word_size, "Size of long must be larger than ptrace word size");
|
||||
//------------------------------------------------------------------------------
|
||||
// Static implementations of NativeProcessLinux::ReadMemory and
|
||||
// NativeProcessLinux::WriteMemory. This enables mutual recursion between these
|
||||
// functions without needed to go thru the thread funnel.
|
||||
|
||||
Error
|
||||
DoReadMemory(
|
||||
lldb::pid_t pid,
|
||||
lldb::addr_t vm_addr,
|
||||
void *buf,
|
||||
size_t size,
|
||||
size_t &bytes_read)
|
||||
{
|
||||
// ptrace word size is determined by the host, not the child
|
||||
static const unsigned word_size = sizeof(void*);
|
||||
unsigned char *dst = static_cast<unsigned char*>(buf);
|
||||
size_t remainder;
|
||||
long data;
|
||||
|
||||
Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_ALL));
|
||||
if (log)
|
||||
ProcessPOSIXLog::IncNestLevel();
|
||||
if (log && ProcessPOSIXLog::AtTopNestLevel() && log->GetMask().Test(POSIX_LOG_MEMORY))
|
||||
log->Printf ("NativeProcessLinux::%s(%" PRIu64 ", %d, %p, %p, %zd, _)", __FUNCTION__,
|
||||
pid, word_size, (void*)vm_addr, buf, size);
|
||||
|
||||
assert(sizeof(data) >= word_size);
|
||||
for (bytes_read = 0; bytes_read < size; bytes_read += remainder)
|
||||
{
|
||||
Error error = NativeProcessLinux::PtraceWrapper(PTRACE_PEEKDATA, pid, (void*)vm_addr, nullptr, 0, &data);
|
||||
if (error.Fail())
|
||||
{
|
||||
if (log)
|
||||
ProcessPOSIXLog::DecNestLevel();
|
||||
return error;
|
||||
}
|
||||
|
||||
remainder = size - bytes_read;
|
||||
remainder = remainder > word_size ? word_size : remainder;
|
||||
|
||||
// Copy the data into our buffer
|
||||
for (unsigned i = 0; i < remainder; ++i)
|
||||
dst[i] = ((data >> i*8) & 0xFF);
|
||||
|
||||
if (log && ProcessPOSIXLog::AtTopNestLevel() &&
|
||||
(log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
|
||||
(log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
|
||||
size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
|
||||
{
|
||||
uintptr_t print_dst = 0;
|
||||
// Format bytes from data by moving into print_dst for log output
|
||||
for (unsigned i = 0; i < remainder; ++i)
|
||||
print_dst |= (((data >> i*8) & 0xFF) << i*8);
|
||||
log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
|
||||
(void*)vm_addr, print_dst, (unsigned long)data);
|
||||
}
|
||||
vm_addr += word_size;
|
||||
dst += word_size;
|
||||
}
|
||||
|
||||
if (log)
|
||||
ProcessPOSIXLog::DecNestLevel();
|
||||
return Error();
|
||||
}
|
||||
|
||||
Error
|
||||
DoWriteMemory(
|
||||
lldb::pid_t pid,
|
||||
lldb::addr_t vm_addr,
|
||||
const void *buf,
|
||||
size_t size,
|
||||
size_t &bytes_written)
|
||||
{
|
||||
// ptrace word size is determined by the host, not the child
|
||||
static const unsigned word_size = sizeof(void*);
|
||||
const unsigned char *src = static_cast<const unsigned char*>(buf);
|
||||
size_t remainder;
|
||||
Error error;
|
||||
|
||||
Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_ALL));
|
||||
if (log)
|
||||
ProcessPOSIXLog::IncNestLevel();
|
||||
if (log && ProcessPOSIXLog::AtTopNestLevel() && log->GetMask().Test(POSIX_LOG_MEMORY))
|
||||
log->Printf ("NativeProcessLinux::%s(%" PRIu64 ", %u, %p, %p, %" PRIu64 ")", __FUNCTION__,
|
||||
pid, word_size, (void*)vm_addr, buf, size);
|
||||
|
||||
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;
|
||||
assert(sizeof(data) >= word_size);
|
||||
for (unsigned i = 0; i < word_size; ++i)
|
||||
data |= (unsigned long)src[i] << i*8;
|
||||
|
||||
if (log && ProcessPOSIXLog::AtTopNestLevel() &&
|
||||
(log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
|
||||
(log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
|
||||
size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
|
||||
log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
|
||||
(void*)vm_addr, *(const unsigned long*)src, data);
|
||||
|
||||
error = NativeProcessLinux::PtraceWrapper(PTRACE_POKEDATA, pid, (void*)vm_addr, (void*)data);
|
||||
if (error.Fail())
|
||||
{
|
||||
if (log)
|
||||
ProcessPOSIXLog::DecNestLevel();
|
||||
return error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char buff[8];
|
||||
size_t bytes_read;
|
||||
error = DoReadMemory(pid, vm_addr, buff, word_size, bytes_read);
|
||||
if (error.Fail())
|
||||
{
|
||||
if (log)
|
||||
ProcessPOSIXLog::DecNestLevel();
|
||||
return error;
|
||||
}
|
||||
|
||||
memcpy(buff, src, remainder);
|
||||
|
||||
size_t bytes_written_rec;
|
||||
error = DoWriteMemory(pid, vm_addr, buff, word_size, bytes_written_rec);
|
||||
if (error.Fail())
|
||||
{
|
||||
if (log)
|
||||
ProcessPOSIXLog::DecNestLevel();
|
||||
return error;
|
||||
}
|
||||
|
||||
if (log && ProcessPOSIXLog::AtTopNestLevel() &&
|
||||
(log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
|
||||
(log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
|
||||
size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
|
||||
log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
|
||||
(void*)vm_addr, *(const unsigned long*)src, *(unsigned long*)buff);
|
||||
}
|
||||
|
||||
vm_addr += word_size;
|
||||
src += word_size;
|
||||
}
|
||||
if (log)
|
||||
ProcessPOSIXLog::DecNestLevel();
|
||||
return error;
|
||||
}
|
||||
} // end of anonymous namespace
|
||||
|
||||
// Simple helper function to ensure flags are enabled on the given file
|
||||
@ -239,6 +389,406 @@ EnsureFDFlags(int fd, int flags)
|
||||
return error;
|
||||
}
|
||||
|
||||
// This class encapsulates the privileged thread which performs all ptrace and wait operations on
|
||||
// the inferior. The thread consists of a main loop which waits for events and processes them
|
||||
// - SIGCHLD (delivered over a signalfd file descriptor): These signals notify us of events in
|
||||
// the inferior process. Upon receiving this signal we do a waitpid to get more information
|
||||
// and dispatch to NativeProcessLinux::MonitorCallback.
|
||||
// - requests for ptrace operations: These initiated via the DoOperation method, which funnels
|
||||
// them to the Monitor thread via m_operation member. The Monitor thread is signaled over a
|
||||
// pipe, and the completion of the operation is signalled over the semaphore.
|
||||
// - thread exit event: this is signaled from the Monitor destructor by closing the write end
|
||||
// of the command pipe.
|
||||
class NativeProcessLinux::Monitor
|
||||
{
|
||||
private:
|
||||
// The initial monitor operation (launch or attach). It returns a inferior process id.
|
||||
std::unique_ptr<InitialOperation> m_initial_operation_up;
|
||||
|
||||
::pid_t m_child_pid = -1;
|
||||
NativeProcessLinux * m_native_process;
|
||||
|
||||
enum { READ, WRITE };
|
||||
int m_pipefd[2] = {-1, -1};
|
||||
int m_signal_fd = -1;
|
||||
HostThread m_thread;
|
||||
|
||||
// current operation which must be executed on the priviliged thread
|
||||
Mutex m_operation_mutex;
|
||||
const Operation *m_operation = nullptr;
|
||||
sem_t m_operation_sem;
|
||||
Error m_operation_error;
|
||||
|
||||
unsigned m_operation_nesting_level = 0;
|
||||
|
||||
static constexpr char operation_command = 'o';
|
||||
static constexpr char begin_block_command = '{';
|
||||
static constexpr char end_block_command = '}';
|
||||
|
||||
void
|
||||
HandleSignals();
|
||||
|
||||
void
|
||||
HandleWait();
|
||||
|
||||
// Returns true if the thread should exit.
|
||||
bool
|
||||
HandleCommands();
|
||||
|
||||
void
|
||||
MainLoop();
|
||||
|
||||
static void *
|
||||
RunMonitor(void *arg);
|
||||
|
||||
Error
|
||||
WaitForAck();
|
||||
|
||||
void
|
||||
BeginOperationBlock()
|
||||
{
|
||||
write(m_pipefd[WRITE], &begin_block_command, sizeof operation_command);
|
||||
WaitForAck();
|
||||
}
|
||||
|
||||
void
|
||||
EndOperationBlock()
|
||||
{
|
||||
write(m_pipefd[WRITE], &end_block_command, sizeof operation_command);
|
||||
WaitForAck();
|
||||
}
|
||||
|
||||
public:
|
||||
Monitor(const InitialOperation &initial_operation,
|
||||
NativeProcessLinux *native_process)
|
||||
: m_initial_operation_up(new InitialOperation(initial_operation)),
|
||||
m_native_process(native_process)
|
||||
{
|
||||
sem_init(&m_operation_sem, 0, 0);
|
||||
}
|
||||
|
||||
~Monitor();
|
||||
|
||||
Error
|
||||
Initialize();
|
||||
|
||||
void
|
||||
Terminate();
|
||||
|
||||
Error
|
||||
DoOperation(const Operation &op);
|
||||
|
||||
class ScopedOperationLock {
|
||||
Monitor &m_monitor;
|
||||
|
||||
public:
|
||||
ScopedOperationLock(Monitor &monitor)
|
||||
: m_monitor(monitor)
|
||||
{ m_monitor.BeginOperationBlock(); }
|
||||
|
||||
~ScopedOperationLock()
|
||||
{ m_monitor.EndOperationBlock(); }
|
||||
};
|
||||
};
|
||||
constexpr char NativeProcessLinux::Monitor::operation_command;
|
||||
constexpr char NativeProcessLinux::Monitor::begin_block_command;
|
||||
constexpr char NativeProcessLinux::Monitor::end_block_command;
|
||||
|
||||
Error
|
||||
NativeProcessLinux::Monitor::Initialize()
|
||||
{
|
||||
Error error;
|
||||
|
||||
// We get a SIGCHLD every time something interesting happens with the inferior. We shall be
|
||||
// listening for these signals over a signalfd file descriptors. This allows us to wait for
|
||||
// multiple kinds of events with select.
|
||||
sigset_t signals;
|
||||
sigemptyset(&signals);
|
||||
sigaddset(&signals, SIGCHLD);
|
||||
m_signal_fd = signalfd(-1, &signals, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
if (m_signal_fd < 0)
|
||||
{
|
||||
return Error("NativeProcessLinux::Monitor::%s failed due to signalfd failure. Monitoring the inferior will be impossible: %s",
|
||||
__FUNCTION__, strerror(errno));
|
||||
|
||||
}
|
||||
|
||||
if (pipe2(m_pipefd, O_CLOEXEC) == -1)
|
||||
{
|
||||
error.SetErrorToErrno();
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = EnsureFDFlags(m_pipefd[READ], O_NONBLOCK)).Fail()) {
|
||||
return error;
|
||||
}
|
||||
|
||||
static const char g_thread_name[] = "lldb.process.nativelinux.monitor";
|
||||
m_thread = ThreadLauncher::LaunchThread(g_thread_name, Monitor::RunMonitor, this, nullptr);
|
||||
if (!m_thread.IsJoinable())
|
||||
return Error("Failed to create monitor thread for NativeProcessLinux.");
|
||||
|
||||
// Wait for initial operation to complete.
|
||||
return WaitForAck();
|
||||
}
|
||||
|
||||
Error
|
||||
NativeProcessLinux::Monitor::DoOperation(const Operation &op)
|
||||
{
|
||||
if (m_thread.EqualsThread(pthread_self())) {
|
||||
// If we're on the Monitor thread, we can simply execute the operation.
|
||||
return op();
|
||||
}
|
||||
|
||||
// Otherwise we need to pass the operation to the Monitor thread so it can handle it.
|
||||
Mutex::Locker lock(m_operation_mutex);
|
||||
|
||||
m_operation = &op;
|
||||
|
||||
// notify the thread that an operation is ready to be processed
|
||||
write(m_pipefd[WRITE], &operation_command, sizeof operation_command);
|
||||
|
||||
return WaitForAck();
|
||||
}
|
||||
|
||||
void
|
||||
NativeProcessLinux::Monitor::Terminate()
|
||||
{
|
||||
if (m_pipefd[WRITE] >= 0)
|
||||
{
|
||||
close(m_pipefd[WRITE]);
|
||||
m_pipefd[WRITE] = -1;
|
||||
}
|
||||
if (m_thread.IsJoinable())
|
||||
m_thread.Join(nullptr);
|
||||
}
|
||||
|
||||
NativeProcessLinux::Monitor::~Monitor()
|
||||
{
|
||||
Terminate();
|
||||
if (m_pipefd[READ] >= 0)
|
||||
close(m_pipefd[READ]);
|
||||
if (m_signal_fd >= 0)
|
||||
close(m_signal_fd);
|
||||
sem_destroy(&m_operation_sem);
|
||||
}
|
||||
|
||||
void
|
||||
NativeProcessLinux::Monitor::HandleSignals()
|
||||
{
|
||||
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
||||
|
||||
// We don't really care about the content of the SIGCHLD siginfo structure, as we will get
|
||||
// all the information from waitpid(). We just need to read all the signals so that we can
|
||||
// sleep next time we reach select().
|
||||
while (true)
|
||||
{
|
||||
signalfd_siginfo info;
|
||||
ssize_t size = read(m_signal_fd, &info, sizeof info);
|
||||
if (size == -1)
|
||||
{
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
break; // We are done.
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
if (log)
|
||||
log->Printf("NativeProcessLinux::Monitor::%s reading from signalfd file descriptor failed: %s",
|
||||
__FUNCTION__, strerror(errno));
|
||||
break;
|
||||
}
|
||||
if (size != sizeof info)
|
||||
{
|
||||
// We got incomplete information structure. This should not happen, let's just log
|
||||
// that.
|
||||
if (log)
|
||||
log->Printf("NativeProcessLinux::Monitor::%s reading from signalfd file descriptor returned incomplete data: "
|
||||
"structure size is %zd, read returned %zd bytes",
|
||||
__FUNCTION__, sizeof info, size);
|
||||
break;
|
||||
}
|
||||
if (log)
|
||||
log->Printf("NativeProcessLinux::Monitor::%s received signal %s(%d).", __FUNCTION__,
|
||||
Host::GetSignalAsCString(info.ssi_signo), info.ssi_signo);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NativeProcessLinux::Monitor::HandleWait()
|
||||
{
|
||||
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
||||
// Process all pending waitpid notifications.
|
||||
while (true)
|
||||
{
|
||||
int status = -1;
|
||||
::pid_t wait_pid = waitpid(-1, &status, __WALL | __WNOTHREAD | WNOHANG);
|
||||
|
||||
if (wait_pid == 0)
|
||||
break; // We are done.
|
||||
|
||||
if (wait_pid == -1)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
if (log)
|
||||
log->Printf("NativeProcessLinux::Monitor::%s waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG) failed: %s",
|
||||
__FUNCTION__, strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
bool exited = false;
|
||||
int signal = 0;
|
||||
int exit_status = 0;
|
||||
const char *status_cstr = NULL;
|
||||
if (WIFSTOPPED(status))
|
||||
{
|
||||
signal = WSTOPSIG(status);
|
||||
status_cstr = "STOPPED";
|
||||
}
|
||||
else if (WIFEXITED(status))
|
||||
{
|
||||
exit_status = WEXITSTATUS(status);
|
||||
status_cstr = "EXITED";
|
||||
exited = true;
|
||||
}
|
||||
else if (WIFSIGNALED(status))
|
||||
{
|
||||
signal = WTERMSIG(status);
|
||||
status_cstr = "SIGNALED";
|
||||
if (wait_pid == m_child_pid) {
|
||||
exited = true;
|
||||
exit_status = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
status_cstr = "(\?\?\?)";
|
||||
|
||||
if (log)
|
||||
log->Printf("NativeProcessLinux::Monitor::%s: waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG)"
|
||||
"=> pid = %" PRIi32 ", status = 0x%8.8x (%s), signal = %i, exit_state = %i",
|
||||
__FUNCTION__, wait_pid, status, status_cstr, signal, exit_status);
|
||||
|
||||
m_native_process->MonitorCallback (wait_pid, exited, signal, exit_status);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
NativeProcessLinux::Monitor::HandleCommands()
|
||||
{
|
||||
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
||||
|
||||
while (true)
|
||||
{
|
||||
char command = 0;
|
||||
ssize_t size = read(m_pipefd[READ], &command, sizeof command);
|
||||
if (size == -1)
|
||||
{
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
return false;
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
if (log)
|
||||
log->Printf("NativeProcessLinux::Monitor::%s exiting because read from command file descriptor failed: %s", __FUNCTION__, strerror(errno));
|
||||
return true;
|
||||
}
|
||||
if (size == 0) // end of file - write end closed
|
||||
{
|
||||
if (log)
|
||||
log->Printf("NativeProcessLinux::Monitor::%s exit command received, exiting...", __FUNCTION__);
|
||||
assert(m_operation_nesting_level == 0 && "Unbalanced begin/end block commands detected");
|
||||
return true; // We are done.
|
||||
}
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case operation_command:
|
||||
m_operation_error = (*m_operation)();
|
||||
break;
|
||||
case begin_block_command:
|
||||
++m_operation_nesting_level;
|
||||
break;
|
||||
case end_block_command:
|
||||
assert(m_operation_nesting_level > 0);
|
||||
--m_operation_nesting_level;
|
||||
break;
|
||||
default:
|
||||
if (log)
|
||||
log->Printf("NativeProcessLinux::Monitor::%s received unknown command '%c'",
|
||||
__FUNCTION__, command);
|
||||
}
|
||||
|
||||
// notify calling thread that the command has been processed
|
||||
sem_post(&m_operation_sem);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NativeProcessLinux::Monitor::MainLoop()
|
||||
{
|
||||
::pid_t child_pid = (*m_initial_operation_up)(m_operation_error);
|
||||
m_initial_operation_up.reset();
|
||||
m_child_pid = child_pid;
|
||||
sem_post(&m_operation_sem);
|
||||
|
||||
while (true)
|
||||
{
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
// Only process waitpid events if we are outside of an operation block. Any pending
|
||||
// events will be processed after we leave the block.
|
||||
if (m_operation_nesting_level == 0)
|
||||
FD_SET(m_signal_fd, &fds);
|
||||
FD_SET(m_pipefd[READ], &fds);
|
||||
|
||||
int max_fd = std::max(m_signal_fd, m_pipefd[READ]) + 1;
|
||||
int r = select(max_fd, &fds, nullptr, nullptr, nullptr);
|
||||
if (r < 0)
|
||||
{
|
||||
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
||||
if (log)
|
||||
log->Printf("NativeProcessLinux::Monitor::%s exiting because select failed: %s",
|
||||
__FUNCTION__, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (FD_ISSET(m_pipefd[READ], &fds))
|
||||
{
|
||||
if (HandleCommands())
|
||||
return;
|
||||
}
|
||||
|
||||
if (FD_ISSET(m_signal_fd, &fds))
|
||||
{
|
||||
HandleSignals();
|
||||
HandleWait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Error
|
||||
NativeProcessLinux::Monitor::WaitForAck()
|
||||
{
|
||||
Error error;
|
||||
while (sem_wait(&m_operation_sem) != 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
error.SetErrorToErrno();
|
||||
return error;
|
||||
}
|
||||
|
||||
return m_operation_error;
|
||||
}
|
||||
|
||||
void *
|
||||
NativeProcessLinux::Monitor::RunMonitor(void *arg)
|
||||
{
|
||||
static_cast<Monitor *>(arg)->MainLoop();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
NativeProcessLinux::LaunchArgs::LaunchArgs(Module *module,
|
||||
char const **argv,
|
||||
char const **envp,
|
||||
@ -269,7 +819,6 @@ Error
|
||||
NativeProcessProtocol::Launch (
|
||||
ProcessLaunchInfo &launch_info,
|
||||
NativeProcessProtocol::NativeDelegate &native_delegate,
|
||||
MainLoop &mainloop,
|
||||
NativeProcessProtocolSP &native_process_sp)
|
||||
{
|
||||
Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
|
||||
@ -356,7 +905,6 @@ NativeProcessProtocol::Launch (
|
||||
}
|
||||
|
||||
std::static_pointer_cast<NativeProcessLinux> (native_process_sp)->LaunchInferior (
|
||||
mainloop,
|
||||
exe_module_sp.get(),
|
||||
launch_info.GetArguments ().GetConstArgumentVector (),
|
||||
launch_info.GetEnvironmentEntries ().GetConstArgumentVector (),
|
||||
@ -384,7 +932,6 @@ Error
|
||||
NativeProcessProtocol::Attach (
|
||||
lldb::pid_t pid,
|
||||
NativeProcessProtocol::NativeDelegate &native_delegate,
|
||||
MainLoop &mainloop,
|
||||
NativeProcessProtocolSP &native_process_sp)
|
||||
{
|
||||
Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
|
||||
@ -411,7 +958,7 @@ NativeProcessProtocol::Attach (
|
||||
return error;
|
||||
}
|
||||
|
||||
native_process_linux_sp->AttachToInferior (mainloop, pid, error);
|
||||
native_process_linux_sp->AttachToInferior (pid, error);
|
||||
if (!error.Success ())
|
||||
return error;
|
||||
|
||||
@ -432,9 +979,12 @@ NativeProcessLinux::NativeProcessLinux () :
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// NativeProcessLinux spawns a new thread which performs all operations on the inferior process.
|
||||
// Refer to Monitor and Operation classes to see why this is necessary.
|
||||
//------------------------------------------------------------------------------
|
||||
void
|
||||
NativeProcessLinux::LaunchInferior (
|
||||
MainLoop &mainloop,
|
||||
Module *module,
|
||||
const char *argv[],
|
||||
const char *envp[],
|
||||
@ -445,11 +995,6 @@ NativeProcessLinux::LaunchInferior (
|
||||
const ProcessLaunchInfo &launch_info,
|
||||
Error &error)
|
||||
{
|
||||
m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD,
|
||||
[this] (MainLoopBase &) { SigchldHandler(); }, error);
|
||||
if (! m_sigchld_handle)
|
||||
return;
|
||||
|
||||
if (module)
|
||||
m_arch = module->GetArchitecture ();
|
||||
|
||||
@ -463,21 +1008,18 @@ NativeProcessLinux::LaunchInferior (
|
||||
working_dir,
|
||||
launch_info));
|
||||
|
||||
Launch(args.get(), error);
|
||||
StartMonitorThread ([&] (Error &e) { return Launch(args.get(), e); }, error);
|
||||
if (!error.Success ())
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
NativeProcessLinux::AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error &error)
|
||||
NativeProcessLinux::AttachToInferior (lldb::pid_t pid, Error &error)
|
||||
{
|
||||
Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
|
||||
if (log)
|
||||
log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ")", __FUNCTION__, pid);
|
||||
|
||||
m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD,
|
||||
[this] (MainLoopBase &) { SigchldHandler(); }, error);
|
||||
if (! m_sigchld_handle)
|
||||
return;
|
||||
|
||||
// We can use the Host for everything except the ResolveExecutable portion.
|
||||
PlatformSP platform_sp = Platform::GetHostPlatform ();
|
||||
if (!platform_sp)
|
||||
@ -515,7 +1057,15 @@ NativeProcessLinux::AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error
|
||||
m_pid = pid;
|
||||
SetState(eStateAttaching);
|
||||
|
||||
Attach(pid, error);
|
||||
StartMonitorThread ([=] (Error &e) { return Attach(pid, e); }, error);
|
||||
if (!error.Success ())
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
NativeProcessLinux::Terminate ()
|
||||
{
|
||||
m_monitor_up->Terminate();
|
||||
}
|
||||
|
||||
::pid_t
|
||||
@ -1737,6 +2287,7 @@ NativeProcessLinux::Resume (const ResumeActionList &resume_actions)
|
||||
|
||||
bool software_single_step = !SupportHardwareSingleStepping();
|
||||
|
||||
Monitor::ScopedOperationLock monitor_lock(*m_monitor_up);
|
||||
Mutex::Locker locker (m_threads_mutex);
|
||||
|
||||
if (software_single_step)
|
||||
@ -1856,7 +2407,7 @@ NativeProcessLinux::Detach ()
|
||||
error = Detach (GetID ());
|
||||
|
||||
// Stop monitoring the inferior.
|
||||
m_sigchld_handle.reset();
|
||||
m_monitor_up->Terminate();
|
||||
|
||||
// No error.
|
||||
return error;
|
||||
@ -1891,6 +2442,7 @@ NativeProcessLinux::Interrupt ()
|
||||
if (log)
|
||||
log->Printf ("NativeProcessLinux::%s selecting running thread for interrupt target", __FUNCTION__);
|
||||
|
||||
Monitor::ScopedOperationLock monitor_lock(*m_monitor_up);
|
||||
Mutex::Locker locker (m_threads_mutex);
|
||||
|
||||
for (auto thread_sp : m_threads)
|
||||
@ -2545,6 +3097,24 @@ NativeProcessLinux::GetCrashReasonForSIGBUS(const siginfo_t *info)
|
||||
}
|
||||
#endif
|
||||
|
||||
Error
|
||||
NativeProcessLinux::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware)
|
||||
{
|
||||
// The base SetWatchpoint will end up executing monitor operations. Let's lock the monitor
|
||||
// for it.
|
||||
Monitor::ScopedOperationLock monitor_lock(*m_monitor_up);
|
||||
return NativeProcessProtocol::SetWatchpoint(addr, size, watch_flags, hardware);
|
||||
}
|
||||
|
||||
Error
|
||||
NativeProcessLinux::RemoveWatchpoint (lldb::addr_t addr)
|
||||
{
|
||||
// The base RemoveWatchpoint will end up executing monitor operations. Let's lock the monitor
|
||||
// for it.
|
||||
Monitor::ScopedOperationLock monitor_lock(*m_monitor_up);
|
||||
return NativeProcessProtocol::RemoveWatchpoint(addr);
|
||||
}
|
||||
|
||||
Error
|
||||
NativeProcessLinux::ReadMemory (lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read)
|
||||
{
|
||||
@ -2574,52 +3144,7 @@ NativeProcessLinux::ReadMemory (lldb::addr_t addr, void *buf, size_t size, size_
|
||||
// the call failed for some reason, let's retry the read using ptrace api.
|
||||
}
|
||||
|
||||
unsigned char *dst = static_cast<unsigned char*>(buf);
|
||||
size_t remainder;
|
||||
long data;
|
||||
|
||||
Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_ALL));
|
||||
if (log)
|
||||
ProcessPOSIXLog::IncNestLevel();
|
||||
if (log && ProcessPOSIXLog::AtTopNestLevel() && log->GetMask().Test(POSIX_LOG_MEMORY))
|
||||
log->Printf ("NativeProcessLinux::%s(%p, %p, %zd, _)", __FUNCTION__, (void*)addr, buf, size);
|
||||
|
||||
for (bytes_read = 0; bytes_read < size; bytes_read += remainder)
|
||||
{
|
||||
Error error = NativeProcessLinux::PtraceWrapper(PTRACE_PEEKDATA, GetID(), (void*)addr, nullptr, 0, &data);
|
||||
if (error.Fail())
|
||||
{
|
||||
if (log)
|
||||
ProcessPOSIXLog::DecNestLevel();
|
||||
return error;
|
||||
}
|
||||
|
||||
remainder = size - bytes_read;
|
||||
remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder;
|
||||
|
||||
// Copy the data into our buffer
|
||||
for (unsigned i = 0; i < remainder; ++i)
|
||||
dst[i] = ((data >> i*8) & 0xFF);
|
||||
|
||||
if (log && ProcessPOSIXLog::AtTopNestLevel() &&
|
||||
(log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
|
||||
(log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
|
||||
size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
|
||||
{
|
||||
uintptr_t print_dst = 0;
|
||||
// Format bytes from data by moving into print_dst for log output
|
||||
for (unsigned i = 0; i < remainder; ++i)
|
||||
print_dst |= (((data >> i*8) & 0xFF) << i*8);
|
||||
log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
|
||||
(void*)addr, print_dst, (unsigned long)data);
|
||||
}
|
||||
addr += k_ptrace_word_size;
|
||||
dst += k_ptrace_word_size;
|
||||
}
|
||||
|
||||
if (log)
|
||||
ProcessPOSIXLog::DecNestLevel();
|
||||
return Error();
|
||||
return DoOperation([&] { return DoReadMemory(GetID(), addr, buf, size, bytes_read); });
|
||||
}
|
||||
|
||||
Error
|
||||
@ -2633,79 +3158,7 @@ NativeProcessLinux::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t s
|
||||
Error
|
||||
NativeProcessLinux::WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written)
|
||||
{
|
||||
const unsigned char *src = static_cast<const unsigned char*>(buf);
|
||||
size_t remainder;
|
||||
Error error;
|
||||
|
||||
Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_ALL));
|
||||
if (log)
|
||||
ProcessPOSIXLog::IncNestLevel();
|
||||
if (log && ProcessPOSIXLog::AtTopNestLevel() && log->GetMask().Test(POSIX_LOG_MEMORY))
|
||||
log->Printf ("NativeProcessLinux::%s(%p, %p, %" PRIu64 ")", __FUNCTION__, (void*)addr, buf, size);
|
||||
|
||||
for (bytes_written = 0; bytes_written < size; bytes_written += remainder)
|
||||
{
|
||||
remainder = size - bytes_written;
|
||||
remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder;
|
||||
|
||||
if (remainder == k_ptrace_word_size)
|
||||
{
|
||||
unsigned long data = 0;
|
||||
for (unsigned i = 0; i < k_ptrace_word_size; ++i)
|
||||
data |= (unsigned long)src[i] << i*8;
|
||||
|
||||
if (log && ProcessPOSIXLog::AtTopNestLevel() &&
|
||||
(log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
|
||||
(log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
|
||||
size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
|
||||
log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
|
||||
(void*)addr, *(const unsigned long*)src, data);
|
||||
|
||||
error = NativeProcessLinux::PtraceWrapper(PTRACE_POKEDATA, GetID(), (void*)addr, (void*)data);
|
||||
if (error.Fail())
|
||||
{
|
||||
if (log)
|
||||
ProcessPOSIXLog::DecNestLevel();
|
||||
return error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char buff[8];
|
||||
size_t bytes_read;
|
||||
error = ReadMemory(addr, buff, k_ptrace_word_size, bytes_read);
|
||||
if (error.Fail())
|
||||
{
|
||||
if (log)
|
||||
ProcessPOSIXLog::DecNestLevel();
|
||||
return error;
|
||||
}
|
||||
|
||||
memcpy(buff, src, remainder);
|
||||
|
||||
size_t bytes_written_rec;
|
||||
error = WriteMemory(addr, buff, k_ptrace_word_size, bytes_written_rec);
|
||||
if (error.Fail())
|
||||
{
|
||||
if (log)
|
||||
ProcessPOSIXLog::DecNestLevel();
|
||||
return error;
|
||||
}
|
||||
|
||||
if (log && ProcessPOSIXLog::AtTopNestLevel() &&
|
||||
(log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
|
||||
(log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
|
||||
size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
|
||||
log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
|
||||
(void*)addr, *(const unsigned long*)src, *(unsigned long*)buff);
|
||||
}
|
||||
|
||||
addr += k_ptrace_word_size;
|
||||
src += k_ptrace_word_size;
|
||||
}
|
||||
if (log)
|
||||
ProcessPOSIXLog::DecNestLevel();
|
||||
return error;
|
||||
return DoOperation([&] { return DoWriteMemory(GetID(), addr, buf, size, bytes_written); });
|
||||
}
|
||||
|
||||
Error
|
||||
@ -2724,7 +3177,7 @@ NativeProcessLinux::Resume (lldb::tid_t tid, uint32_t signo)
|
||||
if (signo != LLDB_INVALID_SIGNAL_NUMBER)
|
||||
data = signo;
|
||||
|
||||
Error error = PtraceWrapper(PTRACE_CONT, tid, nullptr, (void*)data);
|
||||
Error error = DoOperation([&] { return PtraceWrapper(PTRACE_CONT, tid, nullptr, (void*)data); });
|
||||
|
||||
if (log)
|
||||
log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " result = %s", __FUNCTION__, tid, error.Success() ? "true" : "false");
|
||||
@ -2739,19 +3192,19 @@ NativeProcessLinux::SingleStep(lldb::tid_t tid, uint32_t signo)
|
||||
if (signo != LLDB_INVALID_SIGNAL_NUMBER)
|
||||
data = signo;
|
||||
|
||||
return PtraceWrapper(PTRACE_SINGLESTEP, tid, nullptr, (void*)data);
|
||||
return DoOperation([&] { return PtraceWrapper(PTRACE_SINGLESTEP, tid, nullptr, (void*)data); });
|
||||
}
|
||||
|
||||
Error
|
||||
NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo)
|
||||
{
|
||||
return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo);
|
||||
return DoOperation([&] { return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo); });
|
||||
}
|
||||
|
||||
Error
|
||||
NativeProcessLinux::GetEventMessage(lldb::tid_t tid, unsigned long *message)
|
||||
{
|
||||
return PtraceWrapper(PTRACE_GETEVENTMSG, tid, nullptr, message);
|
||||
return DoOperation([&] { return PtraceWrapper(PTRACE_GETEVENTMSG, tid, nullptr, message); });
|
||||
}
|
||||
|
||||
Error
|
||||
@ -2760,7 +3213,7 @@ NativeProcessLinux::Detach(lldb::tid_t tid)
|
||||
if (tid == LLDB_INVALID_THREAD_ID)
|
||||
return Error();
|
||||
|
||||
return PtraceWrapper(PTRACE_DETACH, tid);
|
||||
return DoOperation([&] { return PtraceWrapper(PTRACE_DETACH, tid); });
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2777,6 +3230,16 @@ NativeProcessLinux::DupDescriptor(const FileSpec &file_spec, int fd, int flags)
|
||||
return (close(target_fd) == -1) ? false : true;
|
||||
}
|
||||
|
||||
void
|
||||
NativeProcessLinux::StartMonitorThread(const InitialOperation &initial_operation, Error &error)
|
||||
{
|
||||
m_monitor_up.reset(new Monitor(initial_operation, this));
|
||||
error = m_monitor_up->Initialize();
|
||||
if (error.Fail()) {
|
||||
m_monitor_up.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
NativeProcessLinux::HasThreadNoLock (lldb::tid_t thread_id)
|
||||
{
|
||||
@ -3245,65 +3708,10 @@ NativeProcessLinux::ThreadWasCreated (lldb::tid_t tid)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NativeProcessLinux::SigchldHandler()
|
||||
Error
|
||||
NativeProcessLinux::DoOperation(const Operation &op)
|
||||
{
|
||||
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
||||
// Process all pending waitpid notifications.
|
||||
while (true)
|
||||
{
|
||||
int status = -1;
|
||||
::pid_t wait_pid = waitpid(-1, &status, __WALL | __WNOTHREAD | WNOHANG);
|
||||
|
||||
if (wait_pid == 0)
|
||||
break; // We are done.
|
||||
|
||||
if (wait_pid == -1)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
Error error(errno, eErrorTypePOSIX);
|
||||
if (log)
|
||||
log->Printf("NativeProcessLinux::%s waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG) failed: %s",
|
||||
__FUNCTION__, error.AsCString());
|
||||
break;
|
||||
}
|
||||
|
||||
bool exited = false;
|
||||
int signal = 0;
|
||||
int exit_status = 0;
|
||||
const char *status_cstr = nullptr;
|
||||
if (WIFSTOPPED(status))
|
||||
{
|
||||
signal = WSTOPSIG(status);
|
||||
status_cstr = "STOPPED";
|
||||
}
|
||||
else if (WIFEXITED(status))
|
||||
{
|
||||
exit_status = WEXITSTATUS(status);
|
||||
status_cstr = "EXITED";
|
||||
exited = true;
|
||||
}
|
||||
else if (WIFSIGNALED(status))
|
||||
{
|
||||
signal = WTERMSIG(status);
|
||||
status_cstr = "SIGNALED";
|
||||
if (wait_pid == static_cast<::pid_t>(GetID())) {
|
||||
exited = true;
|
||||
exit_status = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
status_cstr = "(\?\?\?)";
|
||||
|
||||
if (log)
|
||||
log->Printf("NativeProcessLinux::%s: waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG)"
|
||||
"=> pid = %" PRIi32 ", status = 0x%8.8x (%s), signal = %i, exit_state = %i",
|
||||
__FUNCTION__, wait_pid, status, status_cstr, signal, exit_status);
|
||||
|
||||
MonitorCallback (wait_pid, exited, signal, exit_status);
|
||||
}
|
||||
return m_monitor_up->DoOperation(op);
|
||||
}
|
||||
|
||||
// Wrapper for ptrace to catch errors and log calls.
|
||||
|
||||
@ -43,16 +43,25 @@ namespace process_linux {
|
||||
friend Error
|
||||
NativeProcessProtocol::Launch (ProcessLaunchInfo &launch_info,
|
||||
NativeDelegate &native_delegate,
|
||||
MainLoop &mainloop,
|
||||
NativeProcessProtocolSP &process_sp);
|
||||
|
||||
friend Error
|
||||
NativeProcessProtocol::Attach (lldb::pid_t pid,
|
||||
NativeProcessProtocol::NativeDelegate &native_delegate,
|
||||
MainLoop &mainloop,
|
||||
NativeProcessProtocolSP &process_sp);
|
||||
NativeProcessProtocol::NativeDelegate &native_delegate,
|
||||
NativeProcessProtocolSP &native_process_sp);
|
||||
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
/// @class Operation
|
||||
/// @brief Represents a NativeProcessLinux 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 NativeProcessLinux 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.
|
||||
typedef std::function<Error()> Operation;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// NativeProcessProtocol Interface
|
||||
// ---------------------------------------------------------------------
|
||||
@ -104,9 +113,18 @@ namespace process_linux {
|
||||
Error
|
||||
SetBreakpoint (lldb::addr_t addr, uint32_t size, bool hardware) override;
|
||||
|
||||
Error
|
||||
SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) override;
|
||||
|
||||
Error
|
||||
RemoveWatchpoint (lldb::addr_t addr) override;
|
||||
|
||||
void
|
||||
DoStopIDBumped (uint32_t newBumpId) override;
|
||||
|
||||
void
|
||||
Terminate () override;
|
||||
|
||||
Error
|
||||
GetLoadedModuleFileSpec(const char* module_path, FileSpec& file_spec) override;
|
||||
|
||||
@ -116,6 +134,9 @@ namespace process_linux {
|
||||
// ---------------------------------------------------------------------
|
||||
// Interface used by NativeRegisterContext-derived classes.
|
||||
// ---------------------------------------------------------------------
|
||||
Error
|
||||
DoOperation(const Operation &op);
|
||||
|
||||
static Error
|
||||
PtraceWrapper(int req,
|
||||
lldb::pid_t pid,
|
||||
@ -133,9 +154,12 @@ namespace process_linux {
|
||||
|
||||
private:
|
||||
|
||||
MainLoop::SignalHandleUP m_sigchld_handle;
|
||||
class Monitor;
|
||||
|
||||
ArchSpec m_arch;
|
||||
|
||||
std::unique_ptr<Monitor> m_monitor_up;
|
||||
|
||||
LazyBool m_supports_mem_region;
|
||||
std::vector<MemoryRegionInfo> m_mem_region_cache;
|
||||
Mutex m_mem_region_cache_mutex;
|
||||
@ -182,7 +206,6 @@ namespace process_linux {
|
||||
/// implementation of Process::DoLaunch.
|
||||
void
|
||||
LaunchInferior (
|
||||
MainLoop &mainloop,
|
||||
Module *module,
|
||||
char const *argv[],
|
||||
char const *envp[],
|
||||
@ -196,7 +219,10 @@ namespace process_linux {
|
||||
/// Attaches to an existing process. Forms the
|
||||
/// implementation of Process::DoAttach
|
||||
void
|
||||
AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error &error);
|
||||
AttachToInferior (lldb::pid_t pid, Error &error);
|
||||
|
||||
void
|
||||
StartMonitorThread(const InitialOperation &operation, Error &error);
|
||||
|
||||
::pid_t
|
||||
Launch(LaunchArgs *args, Error &error);
|
||||
@ -344,9 +370,6 @@ namespace process_linux {
|
||||
void
|
||||
ThreadWasCreated (lldb::tid_t tid);
|
||||
|
||||
void
|
||||
SigchldHandler();
|
||||
|
||||
// Member variables.
|
||||
PendingNotificationUP m_pending_notification_up;
|
||||
};
|
||||
|
||||
@ -50,7 +50,14 @@ NativeRegisterContextLinux::ReadRegisterRaw(uint32_t reg_index, RegisterValue &r
|
||||
if (!reg_info)
|
||||
return Error("register %" PRIu32 " not found", reg_index);
|
||||
|
||||
return DoReadRegisterValue(reg_info->byte_offset, reg_info->name, reg_info->byte_size, reg_value);
|
||||
NativeProcessProtocolSP process_sp(m_thread.GetProcess());
|
||||
if (!process_sp)
|
||||
return Error("NativeProcessProtocol is NULL");
|
||||
|
||||
NativeProcessLinux* process_p = static_cast<NativeProcessLinux*>(process_sp.get());
|
||||
return process_p->DoOperation([&] {
|
||||
return DoReadRegisterValue(reg_info->byte_offset, reg_info->name, reg_info->byte_size, reg_value);
|
||||
});
|
||||
}
|
||||
|
||||
Error
|
||||
@ -101,70 +108,111 @@ NativeRegisterContextLinux::WriteRegisterRaw(uint32_t reg_index, const RegisterV
|
||||
}
|
||||
}
|
||||
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess ());
|
||||
if (!process_sp)
|
||||
return Error("NativeProcessProtocol is NULL");
|
||||
|
||||
const RegisterInfo *const register_to_write_info_p = GetRegisterInfoAtIndex (reg_to_write);
|
||||
assert (register_to_write_info_p && "register to write does not have valid RegisterInfo");
|
||||
if (!register_to_write_info_p)
|
||||
return Error("NativeRegisterContextLinux::%s failed to get RegisterInfo for write register index %" PRIu32, __FUNCTION__, reg_to_write);
|
||||
|
||||
return DoWriteRegisterValue(reg_info->byte_offset, reg_info->name, reg_value);
|
||||
NativeProcessLinux* process_p = static_cast<NativeProcessLinux*> (process_sp.get ());
|
||||
return process_p->DoOperation([&] {
|
||||
return DoWriteRegisterValue(reg_info->byte_offset, reg_info->name, reg_value);
|
||||
});
|
||||
}
|
||||
|
||||
Error
|
||||
NativeRegisterContextLinux::ReadGPR()
|
||||
{
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess ());
|
||||
if (!process_sp)
|
||||
return Error("NativeProcessProtocol is NULL");
|
||||
|
||||
void* buf = GetGPRBuffer();
|
||||
if (!buf)
|
||||
return Error("GPR buffer is NULL");
|
||||
size_t buf_size = GetGPRSize();
|
||||
|
||||
return DoReadGPR(buf, buf_size);
|
||||
NativeProcessLinux* process_p = static_cast<NativeProcessLinux*>(process_sp.get());
|
||||
return process_p->DoOperation([&] { return DoReadGPR(buf, buf_size); });
|
||||
}
|
||||
|
||||
Error
|
||||
NativeRegisterContextLinux::WriteGPR()
|
||||
{
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess ());
|
||||
if (!process_sp)
|
||||
return Error("NativeProcessProtocol is NULL");
|
||||
|
||||
void* buf = GetGPRBuffer();
|
||||
if (!buf)
|
||||
return Error("GPR buffer is NULL");
|
||||
size_t buf_size = GetGPRSize();
|
||||
|
||||
return DoWriteGPR(buf, buf_size);
|
||||
NativeProcessLinux* process_p = static_cast<NativeProcessLinux*>(process_sp.get());
|
||||
return process_p->DoOperation([&] { return DoWriteGPR(buf, buf_size); });
|
||||
}
|
||||
|
||||
Error
|
||||
NativeRegisterContextLinux::ReadFPR()
|
||||
{
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess ());
|
||||
if (!process_sp)
|
||||
return Error("NativeProcessProtocol is NULL");
|
||||
|
||||
void* buf = GetFPRBuffer();
|
||||
if (!buf)
|
||||
return Error("GPR buffer is NULL");
|
||||
size_t buf_size = GetFPRSize();
|
||||
|
||||
return DoReadFPR(buf, buf_size);
|
||||
NativeProcessLinux* process_p = static_cast<NativeProcessLinux*>(process_sp.get());
|
||||
return process_p->DoOperation([&] { return DoReadFPR(buf, buf_size); });
|
||||
}
|
||||
|
||||
Error
|
||||
NativeRegisterContextLinux::WriteFPR()
|
||||
{
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess ());
|
||||
if (!process_sp)
|
||||
return Error("NativeProcessProtocol is NULL");
|
||||
|
||||
void* buf = GetFPRBuffer();
|
||||
if (!buf)
|
||||
return Error("GPR buffer is NULL");
|
||||
size_t buf_size = GetFPRSize();
|
||||
|
||||
return DoWriteFPR(buf, buf_size);
|
||||
NativeProcessLinux* process_p = static_cast<NativeProcessLinux*>(process_sp.get());
|
||||
return process_p->DoOperation([&] { return DoWriteFPR(buf, buf_size); });
|
||||
}
|
||||
|
||||
Error
|
||||
NativeRegisterContextLinux::ReadRegisterSet(void *buf, size_t buf_size, unsigned int regset)
|
||||
{
|
||||
return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(),
|
||||
static_cast<void *>(®set), buf, buf_size);
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess());
|
||||
if (!process_sp)
|
||||
return Error("NativeProcessProtocol is NULL");
|
||||
NativeProcessLinux* process_p = static_cast<NativeProcessLinux*>(process_sp.get());
|
||||
|
||||
return process_p->DoOperation([&] {
|
||||
return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(),
|
||||
static_cast<void *>(®set), buf, buf_size);
|
||||
});
|
||||
}
|
||||
|
||||
Error
|
||||
NativeRegisterContextLinux::WriteRegisterSet(void *buf, size_t buf_size, unsigned int regset)
|
||||
{
|
||||
return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
|
||||
static_cast<void *>(®set), buf, buf_size);
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess());
|
||||
if (!process_sp)
|
||||
return Error("NativeProcessProtocol is NULL");
|
||||
NativeProcessLinux* process_p = static_cast<NativeProcessLinux*>(process_sp.get());
|
||||
|
||||
return process_p->DoOperation([&] {
|
||||
return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
|
||||
static_cast<void *>(®set), buf, buf_size);
|
||||
});
|
||||
}
|
||||
|
||||
Error
|
||||
|
||||
@ -715,23 +715,29 @@ Error
|
||||
NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo(unsigned int &watch_count,
|
||||
unsigned int &break_count)
|
||||
{
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess());
|
||||
if (!process_sp)
|
||||
return Error("NativeProcessProtocol is NULL");
|
||||
NativeProcessLinux *const process_p = reinterpret_cast<NativeProcessLinux*>(process_sp.get());
|
||||
::pid_t tid = m_thread.GetID();
|
||||
|
||||
int regset = NT_ARM_HW_WATCH;
|
||||
struct iovec ioVec;
|
||||
struct user_hwdebug_state dreg_state;
|
||||
Error error;
|
||||
return process_p->DoOperation([&] {
|
||||
int regset = NT_ARM_HW_WATCH;
|
||||
struct iovec ioVec;
|
||||
struct user_hwdebug_state dreg_state;
|
||||
Error error;
|
||||
|
||||
ioVec.iov_base = &dreg_state;
|
||||
ioVec.iov_len = sizeof (dreg_state);
|
||||
error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, ioVec.iov_len);
|
||||
watch_count = dreg_state.dbg_info & 0xff;
|
||||
ioVec.iov_base = &dreg_state;
|
||||
ioVec.iov_len = sizeof (dreg_state);
|
||||
error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, ioVec.iov_len);
|
||||
watch_count = dreg_state.dbg_info & 0xff;
|
||||
|
||||
regset = NT_ARM_HW_BREAK;
|
||||
error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, ioVec.iov_len);
|
||||
break_count = dreg_state.dbg_info & 0xff;
|
||||
regset = NT_ARM_HW_BREAK;
|
||||
error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, ioVec.iov_len);
|
||||
break_count = dreg_state.dbg_info & 0xff;
|
||||
|
||||
return error;
|
||||
return error;
|
||||
});
|
||||
}
|
||||
|
||||
Error
|
||||
@ -740,26 +746,33 @@ NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(lldb::addr_t *addr_buf,
|
||||
int type,
|
||||
int count)
|
||||
{
|
||||
struct iovec ioVec;
|
||||
struct user_hwdebug_state dreg_state;
|
||||
Error error;
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess());
|
||||
if (!process_sp)
|
||||
return Error("NativeProcessProtocol is NULL");
|
||||
NativeProcessLinux *const process_p = reinterpret_cast<NativeProcessLinux*>(process_sp.get());
|
||||
|
||||
memset (&dreg_state, 0, sizeof (dreg_state));
|
||||
ioVec.iov_base = &dreg_state;
|
||||
ioVec.iov_len = sizeof (dreg_state);
|
||||
return process_p->DoOperation([&] {
|
||||
struct iovec ioVec;
|
||||
struct user_hwdebug_state dreg_state;
|
||||
Error error;
|
||||
|
||||
if (type == 0)
|
||||
type = NT_ARM_HW_WATCH;
|
||||
else
|
||||
type = NT_ARM_HW_BREAK;
|
||||
memset (&dreg_state, 0, sizeof (dreg_state));
|
||||
ioVec.iov_base = &dreg_state;
|
||||
ioVec.iov_len = sizeof (dreg_state);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dreg_state.dbg_regs[i].addr = addr_buf[i];
|
||||
dreg_state.dbg_regs[i].ctrl = cntrl_buf[i];
|
||||
}
|
||||
if (type == 0)
|
||||
type = NT_ARM_HW_WATCH;
|
||||
else
|
||||
type = NT_ARM_HW_BREAK;
|
||||
|
||||
return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), &type, &ioVec, ioVec.iov_len);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dreg_state.dbg_regs[i].addr = addr_buf[i];
|
||||
dreg_state.dbg_regs[i].ctrl = cntrl_buf[i];
|
||||
}
|
||||
|
||||
return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), &type, &ioVec, ioVec.iov_len);
|
||||
});
|
||||
}
|
||||
|
||||
Error
|
||||
|
||||
@ -877,13 +877,19 @@ NativeRegisterContextLinux_mips64::IsWatchpointHit (uint32_t wp_index, bool &is_
|
||||
|
||||
// reading the current state of watch regs
|
||||
struct pt_watch_regs watch_readback;
|
||||
Error error = DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&watch_readback));
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess ());
|
||||
NativeProcessLinux *const process_p = static_cast<NativeProcessLinux*> (process_sp.get ());
|
||||
Error error = process_p->DoOperation([&] {
|
||||
return DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&watch_readback));
|
||||
});
|
||||
|
||||
if (GetWatchHi (&watch_readback, wp_index) & (IRW))
|
||||
{
|
||||
// clear hit flag in watchhi
|
||||
SetWatchHi (&watch_readback, wp_index, (GetWatchHi (&watch_readback, wp_index) & ~(IRW)));
|
||||
DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&watch_readback));
|
||||
process_p->DoOperation([&] {
|
||||
return DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&watch_readback));
|
||||
});
|
||||
|
||||
is_hit = true;
|
||||
return error;
|
||||
@ -924,7 +930,11 @@ NativeRegisterContextLinux_mips64::ClearHardwareWatchpoint(uint32_t wp_index)
|
||||
|
||||
struct pt_watch_regs regs;
|
||||
// First reading the current state of watch regs
|
||||
DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void*>(®s));
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess ());
|
||||
NativeProcessLinux *const process_p = static_cast<NativeProcessLinux*> (process_sp.get ());
|
||||
process_p->DoOperation([&] {
|
||||
return DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void*>(®s));
|
||||
});
|
||||
|
||||
if (regs.style == pt_watch_style_mips32)
|
||||
{
|
||||
@ -939,7 +949,9 @@ NativeRegisterContextLinux_mips64::ClearHardwareWatchpoint(uint32_t wp_index)
|
||||
regs.mips64.watch_masks[wp_index] = default_watch_regs.mips64.watch_masks[wp_index];
|
||||
}
|
||||
|
||||
Error error = DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s));
|
||||
Error error = process_p->DoOperation([&] {
|
||||
return DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s));
|
||||
});
|
||||
if(!error.Fail())
|
||||
{
|
||||
hw_addr_map[wp_index] = LLDB_INVALID_ADDRESS;
|
||||
@ -951,7 +963,11 @@ NativeRegisterContextLinux_mips64::ClearHardwareWatchpoint(uint32_t wp_index)
|
||||
Error
|
||||
NativeRegisterContextLinux_mips64::ClearAllHardwareWatchpoints()
|
||||
{
|
||||
return DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&default_watch_regs));
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess ());
|
||||
NativeProcessLinux *const process_p = static_cast<NativeProcessLinux *> (process_sp.get ());
|
||||
return process_p->DoOperation([&] {
|
||||
return DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&default_watch_regs));
|
||||
});
|
||||
}
|
||||
|
||||
Error
|
||||
@ -970,7 +986,11 @@ NativeRegisterContextLinux_mips64::SetHardwareWatchpoint (
|
||||
struct pt_watch_regs regs;
|
||||
|
||||
// First reading the current state of watch regs
|
||||
DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s));
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess ());
|
||||
NativeProcessLinux *const process_p = static_cast<NativeProcessLinux*> (process_sp.get ());
|
||||
process_p->DoOperation([&] {
|
||||
return DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s));
|
||||
});
|
||||
|
||||
// Try if a new watch point fits in this state
|
||||
int index = GetVacantWatchIndex (®s, addr, size, watch_flags, NumSupportedHardwareWatchpoints());
|
||||
@ -981,7 +1001,9 @@ NativeRegisterContextLinux_mips64::SetHardwareWatchpoint (
|
||||
|
||||
|
||||
// It fits, so we go ahead with updating the state of watch regs
|
||||
DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s));
|
||||
process_p->DoOperation([&] {
|
||||
return DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s));
|
||||
});
|
||||
|
||||
// Storing exact address
|
||||
hw_addr_map[index] = addr;
|
||||
@ -1005,7 +1027,17 @@ NativeRegisterContextLinux_mips64::NumSupportedHardwareWatchpoints ()
|
||||
static int num_valid = 0;
|
||||
if (!num_valid)
|
||||
{
|
||||
DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s));
|
||||
NativeProcessProtocolSP process_sp (m_thread.GetProcess ());
|
||||
if (!process_sp)
|
||||
{
|
||||
printf ("NativeProcessProtocol is NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
NativeProcessLinux *const process_p = static_cast<NativeProcessLinux*> (process_sp.get ());
|
||||
process_p->DoOperation([&] {
|
||||
return DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s));
|
||||
});
|
||||
default_watch_regs = regs; // Keeping default watch regs values for future use
|
||||
switch (regs.style)
|
||||
{
|
||||
|
||||
@ -96,6 +96,20 @@ GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS(
|
||||
RegisterPacketHandlers();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Destructor
|
||||
//----------------------------------------------------------------------
|
||||
GDBRemoteCommunicationServerLLGS::~GDBRemoteCommunicationServerLLGS()
|
||||
{
|
||||
Mutex::Locker locker (m_debugged_process_mutex);
|
||||
|
||||
if (m_debugged_process_sp)
|
||||
{
|
||||
m_debugged_process_sp->Terminate ();
|
||||
m_debugged_process_sp.reset ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers()
|
||||
{
|
||||
@ -209,7 +223,6 @@ GDBRemoteCommunicationServerLLGS::LaunchProcess ()
|
||||
error = NativeProcessProtocol::Launch(
|
||||
m_process_launch_info,
|
||||
*this,
|
||||
m_mainloop,
|
||||
m_debugged_process_sp);
|
||||
}
|
||||
|
||||
@ -295,7 +308,7 @@ GDBRemoteCommunicationServerLLGS::AttachToProcess (lldb::pid_t pid)
|
||||
}
|
||||
|
||||
// Try to attach.
|
||||
error = NativeProcessProtocol::Attach(pid, *this, m_mainloop, m_debugged_process_sp);
|
||||
error = NativeProcessProtocol::Attach(pid, *this, m_debugged_process_sp);
|
||||
if (!error.Success ())
|
||||
{
|
||||
fprintf (stderr, "%s: failed to attach to process %" PRIu64 ": %s", __FUNCTION__, pid, error.AsCString ());
|
||||
|
||||
@ -42,6 +42,9 @@ public:
|
||||
//------------------------------------------------------------------
|
||||
GDBRemoteCommunicationServerLLGS(const lldb::PlatformSP& platform_sp, MainLoop &mainloop);
|
||||
|
||||
virtual
|
||||
~GDBRemoteCommunicationServerLLGS();
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Specify the program to launch and its arguments.
|
||||
///
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user