Add a NativeProcessProtocol Factory class

Summary:
This replaces the static functions used for creating
NativeProcessProtocol instances with a factory pattern, and modernizes
the interface of the new class in the process -- I use llvm::Expected
instead of the Status+value combo. I also move some of the common code
(like the Delegate registration into the base class). The new
arrangement has multiple benefits:
- it removes the NativeProcess*** dependency from Process/gdb-remote
  (which for example means that liblldb no longer pulls in this code).
- it enables unit testing of the GDBRemoteCommunicationServerLLGS class
  (by providing a mock Native Process).
- serves as another example on how to use the llvm::Expected class (I
  couldn't get rid of the Initialize-type functions completely here
  because of the use of shared_from_this, but that's the next thing on
  my list here)

Tests still pass on Linux and I've made sure NetBSD compiles after this.

Reviewers: zturner, eugene, krytarowski

Subscribers: srhines, lldb-commits, mgorny

Differential Revision: https://reviews.llvm.org/D33778

llvm-svn: 307390
This commit is contained in:
Pavel Labath
2017-07-07 11:02:19 +00:00
parent d4550baf3b
commit 96e600fcf5
12 changed files with 361 additions and 557 deletions

View File

@@ -64,81 +64,101 @@ static Status EnsureFDFlags(int fd, int flags) {
// Public Static Methods
// -----------------------------------------------------------------------------
Status NativeProcessProtocol::Launch(
ProcessLaunchInfo &launch_info,
NativeProcessProtocol::NativeDelegate &native_delegate, MainLoop &mainloop,
NativeProcessProtocolSP &native_process_sp) {
llvm::Expected<NativeProcessProtocolSP>
NativeProcessNetBSD::Factory::Launch(ProcessLaunchInfo &launch_info,
NativeDelegate &native_delegate,
MainLoop &mainloop) const {
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
Status error;
// Verify the working directory is valid if one was specified.
FileSpec working_dir{launch_info.GetWorkingDirectory()};
if (working_dir && (!working_dir.ResolvePath() ||
!llvm::sys::fs::is_directory(working_dir.GetPath()))) {
error.SetErrorStringWithFormat("No such file or directory: %s",
working_dir.GetCString());
return error;
Status status;
::pid_t pid = ProcessLauncherPosixFork()
.LaunchProcess(launch_info, status)
.GetProcessId();
LLDB_LOG(log, "pid = {0:x}", pid);
if (status.Fail()) {
LLDB_LOG(log, "failed to launch process: {0}", status);
return status.ToError();
}
// Create the NativeProcessNetBSD in launch mode.
native_process_sp.reset(new NativeProcessNetBSD());
if (!native_process_sp->RegisterNativeDelegate(native_delegate)) {
native_process_sp.reset();
error.SetErrorStringWithFormat("failed to register the native delegate");
return error;
// Wait for the child process to trap on its call to execve.
int wstatus;
::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0);
assert(wpid == pid);
(void)wpid;
if (!WIFSTOPPED(wstatus)) {
LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}",
WaitStatus::Decode(wstatus));
return llvm::make_error<StringError>("Could not sync with inferior process",
llvm::inconvertibleErrorCode());
}
LLDB_LOG(log, "inferior started, now in stopped state");
error = std::static_pointer_cast<NativeProcessNetBSD>(native_process_sp)
->LaunchInferior(mainloop, launch_info);
ArchSpec arch;
if ((status = ResolveProcessArchitecture(pid, arch)).Fail())
return status.ToError();
if (error.Fail()) {
native_process_sp.reset();
LLDB_LOG(log, "failed to launch process: {0}", error);
return error;
// Set the architecture to the exe architecture.
LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid,
arch.GetArchitectureName());
std::shared_ptr<NativeProcessNetBSD> process_sp(new NativeProcessNetBSD(
pid, launch_info.GetPTY().ReleaseMasterFileDescriptor(), native_delegate,
arch, mainloop));
status = process_sp->ReinitializeThreads();
if (status.Fail())
return status.ToError();
for (const auto &thread_sp : process_sp->m_threads) {
static_pointer_cast<NativeThreadNetBSD>(thread_sp)->SetStoppedBySignal(
SIGSTOP);
}
process_sp->SetState(StateType::eStateStopped);
launch_info.SetProcessID(native_process_sp->GetID());
return error;
return process_sp;
}
Status NativeProcessProtocol::Attach(
llvm::Expected<NativeProcessProtocolSP> NativeProcessNetBSD::Factory::Attach(
lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate,
MainLoop &mainloop, NativeProcessProtocolSP &native_process_sp) {
MainLoop &mainloop) const {
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
LLDB_LOG(log, "pid = {0:x}", pid);
// Retrieve the architecture for the running process.
ArchSpec process_arch;
Status error = ResolveProcessArchitecture(pid, process_arch);
if (!error.Success())
return error;
ArchSpec arch;
Status status = ResolveProcessArchitecture(pid, arch);
if (!status.Success())
return status.ToError();
std::shared_ptr<NativeProcessNetBSD> native_process_netbsd_sp(
new NativeProcessNetBSD());
std::shared_ptr<NativeProcessNetBSD> process_sp(
new NativeProcessNetBSD(pid, -1, native_delegate, arch, mainloop));
if (!native_process_netbsd_sp->RegisterNativeDelegate(native_delegate)) {
error.SetErrorStringWithFormat("failed to register the native delegate");
return error;
}
status = process_sp->Attach();
if (!status.Success())
return status.ToError();
native_process_netbsd_sp->AttachToInferior(mainloop, pid, error);
if (!error.Success())
return error;
native_process_sp = native_process_netbsd_sp;
return error;
return process_sp;
}
// -----------------------------------------------------------------------------
// Public Instance Methods
// -----------------------------------------------------------------------------
NativeProcessNetBSD::NativeProcessNetBSD()
: NativeProcessProtocol(LLDB_INVALID_PROCESS_ID), m_arch(),
m_supports_mem_region(eLazyBoolCalculate), m_mem_region_cache() {}
NativeProcessNetBSD::NativeProcessNetBSD(::pid_t pid, int terminal_fd,
NativeDelegate &delegate,
const ArchSpec &arch,
MainLoop &mainloop)
: NativeProcessProtocol(pid, terminal_fd, delegate), m_arch(arch) {
if (m_terminal_fd != -1) {
Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
assert(status.Success());
}
Status status;
m_sigchld_handle = mainloop.RegisterSignal(
SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, status);
assert(m_sigchld_handle && status.Success());
}
// Handles all waitpid events from the inferior process.
void NativeProcessNetBSD::MonitorCallback(lldb::pid_t pid, int signal) {
@@ -710,113 +730,6 @@ Status NativeProcessNetBSD::GetFileLoadAddress(const llvm::StringRef &file_name,
return Status();
}
Status NativeProcessNetBSD::LaunchInferior(MainLoop &mainloop,
ProcessLaunchInfo &launch_info) {
Status error;
m_sigchld_handle = mainloop.RegisterSignal(
SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, error);
if (!m_sigchld_handle)
return error;
SetState(eStateLaunching);
::pid_t pid = ProcessLauncherPosixFork()
.LaunchProcess(launch_info, error)
.GetProcessId();
if (error.Fail())
return error;
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
// Wait for the child process to trap on its call to execve.
::pid_t wpid;
int status;
if ((wpid = waitpid(pid, &status, 0)) < 0) {
error.SetErrorToErrno();
LLDB_LOG(log, "waitpid for inferior failed with %s", error);
// Mark the inferior as invalid.
// FIXME this could really use a new state - eStateLaunchFailure. For
// now, using eStateInvalid.
SetState(StateType::eStateInvalid);
return error;
}
assert(WIFSTOPPED(status) && (wpid == static_cast<::pid_t>(pid)) &&
"Could not sync with inferior process.");
LLDB_LOG(log, "inferior started, now in stopped state");
// Release the master terminal descriptor and pass it off to the
// NativeProcessNetBSD instance. Similarly stash the inferior pid.
m_terminal_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
m_pid = pid;
launch_info.SetProcessID(pid);
if (m_terminal_fd != -1) {
error = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
if (error.Fail()) {
LLDB_LOG(log,
"inferior EnsureFDFlags failed for ensuring terminal "
"O_NONBLOCK setting: {0}",
error);
// Mark the inferior as invalid.
// FIXME this could really use a new state - eStateLaunchFailure. For
// now, using eStateInvalid.
SetState(StateType::eStateInvalid);
return error;
}
}
LLDB_LOG(log, "adding pid = {0}", pid);
ResolveProcessArchitecture(m_pid, m_arch);
error = ReinitializeThreads();
if (error.Fail()) {
SetState(StateType::eStateInvalid);
return error;
}
for (const auto &thread_sp : m_threads) {
static_pointer_cast<NativeThreadNetBSD>(thread_sp)->SetStoppedBySignal(
SIGSTOP);
}
/* Set process stopped */
SetState(StateType::eStateStopped);
if (error.Fail())
LLDB_LOG(log, "inferior launching failed {0}", error);
return error;
}
void NativeProcessNetBSD::AttachToInferior(MainLoop &mainloop, lldb::pid_t pid,
Status &error) {
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
LLDB_LOG(log, "pid = {0:x}", pid);
m_sigchld_handle = mainloop.RegisterSignal(
SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, error);
if (!m_sigchld_handle)
return;
error = ResolveProcessArchitecture(pid, m_arch);
if (!error.Success())
return;
// Set the architecture to the exe architecture.
LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid,
m_arch.GetArchitectureName());
m_pid = pid;
SetState(eStateAttaching);
Attach(pid, error);
}
void NativeProcessNetBSD::SigchldHandler() {
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
// Process all pending waitpid notifications.
@@ -879,33 +792,23 @@ NativeThreadNetBSDSP NativeProcessNetBSD::AddThread(lldb::tid_t thread_id) {
return thread_sp;
}
::pid_t NativeProcessNetBSD::Attach(lldb::pid_t pid, Status &error) {
if (pid <= 1) {
error.SetErrorToGenericError();
error.SetErrorString("Attaching to process 1 is not allowed.");
return -1;
}
Status NativeProcessNetBSD::Attach() {
// Attach to the requested process.
// An attach will cause the thread to stop with a SIGSTOP.
error = PtraceWrapper(PT_ATTACH, pid);
if (error.Fail())
return -1;
Status status = PtraceWrapper(PT_ATTACH, m_pid);
if (status.Fail())
return status;
int status;
int wstatus;
// Need to use WALLSIG otherwise we receive an error with errno=ECHLD
// At this point we should have a thread stopped if waitpid succeeds.
if ((status = waitpid(pid, NULL, WALLSIG)) < 0)
return -1;
m_pid = pid;
if ((wstatus = waitpid(m_pid, NULL, WALLSIG)) < 0)
return Status(errno, eErrorTypePOSIX);
/* Initialize threads */
error = ReinitializeThreads();
if (error.Fail()) {
SetState(StateType::eStateInvalid);
return -1;
}
status = ReinitializeThreads();
if (status.Fail())
return status;
for (const auto &thread_sp : m_threads) {
static_pointer_cast<NativeThreadNetBSD>(thread_sp)->SetStoppedBySignal(
@@ -914,8 +817,7 @@ NativeThreadNetBSDSP NativeProcessNetBSD::AddThread(lldb::tid_t thread_id) {
// Let our process instance know the thread has stopped.
SetState(StateType::eStateStopped);
return pid;
return Status();
}
Status NativeProcessNetBSD::ReadMemory(lldb::addr_t addr, void *buf,