[Lldb-commits] [lldb] [LLDB] Ptrace seize dead process (PR #137041)
Jacob Lalonde via lldb-commits
lldb-commits at lists.llvm.org
Thu May 8 13:02:22 PDT 2025
https://github.com/Jlalond updated https://github.com/llvm/llvm-project/pull/137041
>From fa08811c7a90b9fd1c644b051ed3d7d1157243fe Mon Sep 17 00:00:00 2001
From: Jacob Lalonde <jalalonde at fb.com>
Date: Thu, 8 May 2025 12:56:14 -0700
Subject: [PATCH] Implement PTRACE_SEIZE when a process is coredumping
---
.../lldb/Host/common/NativeProcessProtocol.h | 2 +
.../Process/Linux/NativeProcessLinux.cpp | 120 +++++++++++++++++-
.../Process/Linux/NativeProcessLinux.h | 5 +-
3 files changed, 120 insertions(+), 7 deletions(-)
diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 1d5fecfcd5c27..f50e9da0957f5 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -189,6 +189,8 @@ class NativeProcessProtocol {
bool CanResume() const { return m_state == lldb::eStateStopped; }
+ bool IsStopped() const { return m_state == lldb::eStateStopped; }
+
lldb::ByteOrder GetByteOrder() const {
return GetArchitecture().GetByteOrder();
}
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index 7f2aba0e4eb2c..c8e8f47524fb6 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -312,10 +312,26 @@ NativeProcessLinux::Manager::Attach(
Log *log = GetLog(POSIXLog::Process);
LLDB_LOG(log, "pid = {0:x}", pid);
- auto tids_or = NativeProcessLinux::Attach(pid);
- if (!tids_or)
- return tids_or.takeError();
- ArrayRef<::pid_t> tids = *tids_or;
+ // This safety check lets us decide if we should
+ // seize or attach.
+ ProcessInstanceInfo process_info;
+ if (!Host::GetProcessInfo(pid, process_info))
+ return llvm::make_error<StringError>("Unable to read process info",
+ llvm::inconvertibleErrorCode());
+
+ std::vector<::pid_t> tids;
+ if (process_info.IsCoreDumping()) {
+ auto attached_or = NativeProcessLinux::Seize(pid);
+ if (!attached_or)
+ return attached_or.takeError();
+ tids = std::move(*attached_or);
+ } else {
+ auto attached_or = NativeProcessLinux::Attach(pid);
+ if (!attached_or)
+ return attached_or.takeError();
+ tids = std::move(*attached_or);
+ }
+
llvm::Expected<ArchSpec> arch_or =
NativeRegisterContextLinux::DetermineArchitecture(tids[0]);
if (!arch_or)
@@ -444,6 +460,93 @@ NativeProcessLinux::NativeProcessLinux(::pid_t pid, int terminal_fd,
SetState(StateType::eStateStopped, false);
}
+llvm::Expected<std::vector<::pid_t>> NativeProcessLinux::Seize(::pid_t pid) {
+ // TODO: Because the Seize during coredumping change introduces the
+ // concept of a non resumable stop, we should also check for
+ // PTRACE_O_TRACEEXIT, which per the man page the status will equal
+ // status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC<<8))
+ // and if this is true, we should say we can't resume.
+ Log *log = GetLog(POSIXLog::Process);
+
+ uint64_t options = GetDefaultPtraceOpts();
+ Status status;
+ // Use a map to keep track of the threads which we have attached/need to
+ // attach.
+ Host::TidMap tids_to_attach;
+ while (Host::FindProcessThreads(pid, tids_to_attach)) {
+ for (Host::TidMap::iterator it = tids_to_attach.begin();
+ it != tids_to_attach.end();) {
+ if (it->second == true) {
+ continue;
+ }
+ lldb::tid_t tid = it->first;
+ if ((status = PtraceWrapper(PTRACE_SEIZE, tid, nullptr, (void *)options))
+ .Fail()) {
+ // No such thread. The thread may have exited. More error handling
+ // may be needed.
+ if (status.GetError() == ESRCH) {
+ it = tids_to_attach.erase(it);
+ continue;
+ }
+ if (status.GetError() == EPERM) {
+ // Depending on the value of ptrace_scope, we can return a
+ // different error that suggests how to fix it.
+ return AddPtraceScopeNote(status.ToError());
+ }
+ return status.ToError();
+ }
+
+ if ((status = PtraceWrapper(PTRACE_INTERRUPT, tid)).Fail()) {
+ // No such thread. The thread may have exited. More error handling
+ // may be needed.
+ if (status.GetError() == ESRCH) {
+ it = tids_to_attach.erase(it);
+ continue;
+ }
+ if (status.GetError() == EPERM) {
+ // Depending on the value of ptrace_scope, we can return a
+ // different error that suggests how to fix it.
+ return AddPtraceScopeNote(status.ToError());
+ }
+ return status.ToError();
+ }
+
+ int wpid =
+ llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, nullptr, __WALL);
+ // Need to use __WALL otherwise we receive an error with errno=ECHLD At
+ // this point we should have a thread stopped if waitpid succeeds.
+ if (wpid < 0) {
+ // No such thread. The thread may have exited. More error handling
+ // may be needed.
+ if (errno == ESRCH) {
+ it = tids_to_attach.erase(it);
+ continue;
+ }
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::generic_category()));
+ }
+
+ LLDB_LOG(log, "adding tid = {0}", tid);
+ it->second = true;
+
+ // move the loop forward
+ ++it;
+ }
+ }
+
+ size_t tid_count = tids_to_attach.size();
+ if (tid_count == 0)
+ return llvm::make_error<StringError>("No such process",
+ llvm::inconvertibleErrorCode());
+
+ std::vector<::pid_t> tids;
+ tids.reserve(tid_count);
+ for (const auto &p : tids_to_attach)
+ tids.push_back(p.first);
+
+ return std::move(tids);
+}
+
llvm::Expected<std::vector<::pid_t>> NativeProcessLinux::Attach(::pid_t pid) {
Log *log = GetLog(POSIXLog::Process);
@@ -513,8 +616,8 @@ llvm::Expected<std::vector<::pid_t>> NativeProcessLinux::Attach(::pid_t pid) {
return std::move(tids);
}
-Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) {
- long ptrace_opts = 0;
+uint64_t NativeProcessLinux::GetDefaultPtraceOpts() {
+ uint64_t ptrace_opts = 0;
// Have the child raise an event on exit. This is used to keep the child in
// limbo until it is destroyed.
@@ -537,6 +640,11 @@ Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) {
// the child finishes sharing memory.
ptrace_opts |= PTRACE_O_TRACEVFORKDONE;
+ return ptrace_opts;
+}
+
+Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) {
+ uint64_t ptrace_opts = GetDefaultPtraceOpts();
return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts);
}
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
index d345f165a75d8..9ae4e57e74add 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -175,7 +175,6 @@ class NativeProcessLinux : public NativeProcessELF,
private:
Manager &m_manager;
ArchSpec m_arch;
-
LazyBool m_supports_mem_region = eLazyBoolCalculate;
std::vector<std::pair<MemoryRegionInfo, FileSpec>> m_mem_region_cache;
@@ -191,9 +190,13 @@ class NativeProcessLinux : public NativeProcessELF,
// Returns a list of process threads that we have attached to.
static llvm::Expected<std::vector<::pid_t>> Attach(::pid_t pid);
+ // Returns a list of process threads that we have seized and interrupted.
+ static llvm::Expected<std::vector<::pid_t>> Seize(::pid_t pid);
static Status SetDefaultPtraceOpts(const lldb::pid_t);
+ static uint64_t GetDefaultPtraceOpts();
+
bool TryHandleWaitStatus(lldb::pid_t pid, WaitStatus status);
void MonitorCallback(NativeThreadLinux &thread, WaitStatus status);
More information about the lldb-commits
mailing list