Reland "[lldb] [Process] Watch for fork/vfork notifications" for FreeBSD

The original commit was reverted because of the problems it introduced
on Linux.  However, FreeBSD should not be affected, so restore that part
and we will address Linux separately.

While at it, remove the dbreg hack as the underlying issue has been
fixed in the FreeBSD kernel and the problem is unlikely to happen
in real life use anyway.

Differential Revision: https://reviews.llvm.org/D98822
This commit is contained in:
Michał Górny
2021-04-13 12:21:24 +02:00
parent 808a5a2534
commit 63d7564105
8 changed files with 154 additions and 3 deletions

View File

@@ -247,6 +247,19 @@ void NativeProcessFreeBSD::MonitorSIGTRAP(lldb::pid_t pid) {
return;
}
if (info.pl_flags & PL_FLAG_FORKED) {
MonitorClone(info.pl_child_pid);
return;
}
if (info.pl_flags & PL_FLAG_VFORK_DONE) {
Status error =
PtraceWrapper(PT_CONTINUE, pid, reinterpret_cast<void *>(1), 0);
if (error.Fail())
SetState(StateType::eStateInvalid);
return;
}
if (info.pl_lwpid > 0) {
for (const auto &t : m_threads) {
if (t->GetID() == static_cast<lldb::tid_t>(info.pl_lwpid))
@@ -705,17 +718,17 @@ NativeProcessFreeBSD::GetFileLoadAddress(const llvm::StringRef &file_name,
void NativeProcessFreeBSD::SigchldHandler() {
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
// Process all pending waitpid notifications.
int status;
::pid_t wait_pid =
llvm::sys::RetryAfterSignal(-1, waitpid, GetID(), &status, WNOHANG);
if (wait_pid == 0)
return; // We are done.
return;
if (wait_pid == -1) {
Status error(errno, eErrorTypePOSIX);
LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error);
return;
}
WaitStatus wait_status = WaitStatus::Decode(status);
@@ -885,7 +898,7 @@ Status NativeProcessFreeBSD::SetupTrace() {
PtraceWrapper(PT_GET_EVENT_MASK, GetID(), &events, sizeof(events));
if (status.Fail())
return status;
events |= PTRACE_LWP;
events |= PTRACE_LWP | PTRACE_FORK | PTRACE_VFORK;
status = PtraceWrapper(PT_SET_EVENT_MASK, GetID(), &events, sizeof(events));
if (status.Fail())
return status;
@@ -919,3 +932,39 @@ Status NativeProcessFreeBSD::ReinitializeThreads() {
bool NativeProcessFreeBSD::SupportHardwareSingleStepping() const {
return !m_arch.IsMIPS();
}
void NativeProcessFreeBSD::MonitorClone(::pid_t child_pid) {
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
LLDB_LOG(log, "fork, child_pid={0}", child_pid);
int status;
::pid_t wait_pid =
llvm::sys::RetryAfterSignal(-1, ::waitpid, child_pid, &status, 0);
if (wait_pid != child_pid) {
LLDB_LOG(log,
"waiting for pid {0} failed. Assuming the pid has "
"disappeared in the meantime",
child_pid);
return;
}
if (WIFEXITED(status)) {
LLDB_LOG(log,
"waiting for pid {0} returned an 'exited' event. Not "
"tracking it.",
child_pid);
return;
}
MainLoop unused_loop;
NativeProcessFreeBSD child_process{static_cast<::pid_t>(child_pid),
m_terminal_fd, m_delegate, m_arch,
unused_loop};
child_process.Detach();
Status pt_error =
PtraceWrapper(PT_CONTINUE, GetID(), reinterpret_cast<void *>(1), 0);
if (pt_error.Fail()) {
LLDB_LOG_ERROR(log, pt_error.ToError(),
"unable to resume parent process {1}: {0}", GetID());
SetState(StateType::eStateInvalid);
}
}

View File

@@ -113,6 +113,7 @@ private:
void MonitorSIGSTOP(lldb::pid_t pid);
void MonitorSIGTRAP(lldb::pid_t pid);
void MonitorSignal(lldb::pid_t pid, int signal);
void MonitorClone(::pid_t child_pid);
Status PopulateMemoryRegionCache();
void SigchldHandler();

View File

@@ -0,0 +1,50 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>
#if defined(TEST_CLONE)
#include <sched.h>
#endif
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
int g_val = 0;
void parent_func() {
g_val = 1;
printf("function run in parent\n");
}
int child_func(void *unused) {
// we need to avoid memory modifications for vfork(), yet we want
// to be able to test watchpoints, so do the next best thing
// and restore the original value
g_val = 2;
g_val = 0;
return 0;
}
int main() {
alignas(uintmax_t) char stack[4096];
#if defined(TEST_CLONE)
pid_t pid = clone(child_func, &stack[sizeof(stack)], 0, NULL);
#elif defined(TEST_FORK)
pid_t pid = TEST_FORK();
if (pid == 0)
_exit(child_func(NULL));
#endif
assert(pid != -1);
parent_func();
int status, wait_flags = 0;
#if defined(TEST_CLONE)
wait_flags = __WALL;
#endif
pid_t waited = waitpid(pid, &status, wait_flags);
assert(waited == pid);
assert(WIFEXITED(status));
printf("child exited: %d\n", WEXITSTATUS(status));
return 0;
}

View File

@@ -0,0 +1,13 @@
# REQUIRES: native && dbregs-set
# UNSUPPORTED: system-windows
# RUN: %clangxx_host -g %p/Inputs/fork.cpp -DTEST_FORK=fork -o %t
# RUN: %lldb -b -s %s %t | FileCheck %s
process launch -s
watchpoint set variable -w write g_val
# CHECK: Watchpoint created:
continue
# CHECK-NOT: function run in parent
# CHECK: stop reason = watchpoint
continue
# CHECK: function run in parent
# CHECK: child exited: 0

View File

@@ -0,0 +1,11 @@
# REQUIRES: native
# UNSUPPORTED: system-windows
# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_FORK=fork -o %t
# RUN: %lldb -b -s %s %t | FileCheck %s
b parent_func
process launch
# CHECK-NOT: function run in parent
# CHECK: stop reason = breakpoint
continue
# CHECK: function run in parent
# CHECK: child exited: 0

View File

@@ -0,0 +1,2 @@
if 'lldb-repro' in config.available_features:
config.unsupported = True

View File

@@ -0,0 +1,14 @@
# REQUIRES: native && dbregs-set
# UNSUPPORTED: system-windows
# UNSUPPORTED: system-darwin
# RUN: %clangxx_host -g %p/Inputs/fork.cpp -DTEST_FORK=vfork -o %t
# RUN: %lldb -b -s %s %t | FileCheck %s
process launch -s
watchpoint set variable -w write g_val
# CHECK: Watchpoint created:
continue
# CHECK-NOT: function run in parent
# CHECK: stop reason = watchpoint
continue
# CHECK: function run in parent
# CHECK: child exited: 0

View File

@@ -0,0 +1,11 @@
# REQUIRES: native
# UNSUPPORTED: system-windows
# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_FORK=vfork -o %t
# RUN: %lldb -b -s %s %t | FileCheck %s
b parent_func
process launch
# CHECK-NOT: function run in parent
# CHECK: stop reason = breakpoint
continue
# CHECK: function run in parent
# CHECK: child exited: 0