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

Reply via email to