https://github.com/Jlalond created https://github.com/llvm/llvm-project/pull/137041
This the actual PR to my [SEIZE RFC](https://discourse.llvm.org/t/rfc-ptrace-seize-when-attaching-to-dead-processes/85825/8). This is currently the bare bones on seizing a dead process, and being able to attach and introspect with LLDB. Some caveats that I need to address before we publish this PR is how to prevent LLDB from running any expressions or really anything that trys to SIGCONT, because that will immediately terminate the process, I would like this behavior to mimic how we inform the user post mortem processes can't run expressions. Additionally, right now I only check proc status before seize, and we should double check after seize that the process has not changed. Worth noting is once you seize a coredumping process (and it hits trace stop), Coredumping in status will now report 0. This is pretty complicated to test because it requires integration with the Kernel, thankfully the setup only involves some very simple toy programs, which I have outlined with instructions [in this gist](https://gist.github.com/Jlalond/a81a995dd14ff5d88b7f21f00879ce83) >From 87e133063341f6058c0a3d5bcc5e988824c2c762 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Tue, 22 Apr 2025 16:35:00 -0700 Subject: [PATCH 1/2] Create proc status reader --- .../Plugins/Process/Utility/CMakeLists.txt | 1 + .../Process/Utility/LinuxProcStatus.cpp | 42 +++++++++++++++++++ .../Plugins/Process/Utility/LinuxProcStatus.h | 22 ++++++++++ 3 files changed, 65 insertions(+) create mode 100644 lldb/source/Plugins/Process/Utility/LinuxProcStatus.cpp create mode 100644 lldb/source/Plugins/Process/Utility/LinuxProcStatus.h diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt index f269f5d7d4d74..e9be9634b9876 100644 --- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt +++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt @@ -6,6 +6,7 @@ add_lldb_library(lldbPluginProcessUtility HistoryUnwind.cpp InferiorCallPOSIX.cpp LinuxProcMaps.cpp + LinuxProcStatus.cpp LinuxSignals.cpp MemoryTagManagerAArch64MTE.cpp NativeProcessSoftwareSingleStep.cpp diff --git a/lldb/source/Plugins/Process/Utility/LinuxProcStatus.cpp b/lldb/source/Plugins/Process/Utility/LinuxProcStatus.cpp new file mode 100644 index 0000000000000..75575d62210f0 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/LinuxProcStatus.cpp @@ -0,0 +1,42 @@ +//===-- LinuxProcMaps.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "LinuxProcStatus.h" +#include <sstream> +#include <string> +#include <vector> + +static std::vector<std::string> SplitLines(llvm::StringRef proc_status) { + std::istringstream inputstream(proc_status.str()); + std::vector<std::string> lines; + std::string line; + while (std::getline(inputstream, line)) { + lines.push_back(line); + } + return lines; +} + +lldb_private::StructuredData::Dictionary +ParseProcStatus(llvm::StringRef proc_status) { + std::vector<std::string> lines = SplitLines(proc_status); + lldb_private::StructuredData::Dictionary proc_status_data; + for (auto &str : lines) { + // proc/pid/status is a delineated by a colon, so we split all the lines + // and then return a structureddata of each name : value. We keep these + // all as text, and let the caller sort the type they want them as. + size_t colonPos = str.find(':'); + if (colonPos == std::string::npos) { + continue; + } + std::string name = str.substr(0, colonPos); + std::string value = str.substr(colonPos + 1); + proc_status_data.AddStringItem(name, value); + } + + return proc_status_data; +} diff --git a/lldb/source/Plugins/Process/Utility/LinuxProcStatus.h b/lldb/source/Plugins/Process/Utility/LinuxProcStatus.h new file mode 100644 index 0000000000000..28e01ac9ff74e --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/LinuxProcStatus.h @@ -0,0 +1,22 @@ +//===-- LinuxProcStatus.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LINUXPROCSTATUS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LINUXPROCSTATUS_H + +#include "lldb/Utility/StructuredData.h" +#include "lldb/lldb-forward.h" +#include "llvm/ADT/StringRef.h" + +namespace lldb_private { + +StructuredData::Dictionary ParseProcStatus(llvm::StringRef proc_status); + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LINUXPROCSTATUS_H >From 50df550563e84cd5d551675c85af04dc18f25e62 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Wed, 23 Apr 2025 11:31:01 -0700 Subject: [PATCH 2/2] Fix tabs, newlines, and spaces getting in the structured data --- .../Process/Linux/NativeProcessLinux.cpp | 80 ++++++++++++++++--- .../Process/Utility/LinuxProcStatus.cpp | 15 +++- 2 files changed, 81 insertions(+), 14 deletions(-) diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp index 7f2aba0e4eb2c..f052931e1d377 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -23,6 +23,7 @@ #include "NativeThreadLinux.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "Plugins/Process/Utility/LinuxProcMaps.h" +#include "Plugins/Process/Utility/LinuxProcStatus.h" #include "Procfs.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/Host.h" @@ -443,10 +444,29 @@ NativeProcessLinux::NativeProcessLinux(::pid_t pid, int terminal_fd, SetCurrentThreadID(tids[0]); SetState(StateType::eStateStopped, false); } +static bool IsCoredumping(lldb::pid_t pid) { + auto BufferOrError = getProcFile(pid, "status"); + if (!BufferOrError) + return false; + + lldb_private::StructuredData::Dictionary proc_status = + ParseProcStatus(BufferOrError.get()->getBuffer()); + + if (!proc_status.HasKey("CoreDumping")) + return false; + + llvm::StringRef result; + if (!proc_status.GetValueForKeyAsString("CoreDumping", result)) + return false; + + return result == "1"; +} llvm::Expected<std::vector<::pid_t>> NativeProcessLinux::Attach(::pid_t pid) { Log *log = GetLog(POSIXLog::Process); + bool coreDumping = IsCoredumping(pid); + LLDB_LOG(log, "{0} coredumping: {1}", pid, coreDumping); Status status; // Use a map to keep track of the threads which we have attached/need to // attach. @@ -457,21 +477,55 @@ llvm::Expected<std::vector<::pid_t>> NativeProcessLinux::Attach(::pid_t pid) { if (it->second == false) { lldb::tid_t tid = it->first; - // Attach to the requested process. - // An attach will cause the thread to stop with a SIGSTOP. - if ((status = PtraceWrapper(PTRACE_ATTACH, 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 (!coreDumping) { + // Attach to the requested process. + // An attach will cause the thread to stop with a SIGSTOP. + if ((status = PtraceWrapper(PTRACE_ATTACH, 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(); } - 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()); + } else { + long options = PTRACE_O_TRACEEXIT | PTRACE_O_TRACESYSGOOD; + 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(); } - return status.ToError(); } int wpid = diff --git a/lldb/source/Plugins/Process/Utility/LinuxProcStatus.cpp b/lldb/source/Plugins/Process/Utility/LinuxProcStatus.cpp index 75575d62210f0..573925f35bf45 100644 --- a/lldb/source/Plugins/Process/Utility/LinuxProcStatus.cpp +++ b/lldb/source/Plugins/Process/Utility/LinuxProcStatus.cpp @@ -12,6 +12,8 @@ #include <vector> static std::vector<std::string> SplitLines(llvm::StringRef proc_status) { + // Push everything to an input stream and then read it to a vec + // line by line std::istringstream inputstream(proc_status.str()); std::vector<std::string> lines; std::string line; @@ -22,9 +24,12 @@ static std::vector<std::string> SplitLines(llvm::StringRef proc_status) { } lldb_private::StructuredData::Dictionary -ParseProcStatus(llvm::StringRef proc_status) { +lldb_private::ParseProcStatus(llvm::StringRef proc_status) { std::vector<std::string> lines = SplitLines(proc_status); lldb_private::StructuredData::Dictionary proc_status_data; + volatile bool debug = true; + while (debug) { + } for (auto &str : lines) { // proc/pid/status is a delineated by a colon, so we split all the lines // and then return a structureddata of each name : value. We keep these @@ -35,6 +40,14 @@ ParseProcStatus(llvm::StringRef proc_status) { } std::string name = str.substr(0, colonPos); std::string value = str.substr(colonPos + 1); + // All the leading values have a tab character, but + // that's not explicitly documented on the man page so + // we have this check to strip the tab, and any newline or + // space characters + value.erase(std::remove_if( + value.begin(), value.end(), + [](char c) { return c == '\t' || c == '\n' || c == ' '; }), + value.end()); proc_status_data.AddStringItem(name, value); } _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits