irinasha created this revision.
Herald added a subscriber: emaste.
Herald added a project: All.
irinasha requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

We want to allow the client to detect when the process forks and to attach the 
debugger to a newly created process. For this we propose to implement two 
options in LLDB*: to propagate fork/vfork events to the client and to 
automatically detach the child/parent in a _stopped_ state:

- stop-on-clone-events (**false**/true);
- detach-keeps-stopped (**false**/true).

The client (IDE in our case) can then decide to attach another debugger to that 
process.

- By default these options will be disabled, so the default behavior won't 
change.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D134642

Files:
  lldb/include/lldb/Host/common/NativeProcessProtocol.h
  lldb/include/lldb/Target/Process.h
  lldb/include/lldb/Utility/StringExtractorGDBRemote.h
  lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
  lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
  lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
  lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
  lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
  lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
  lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  lldb/source/Target/Process.cpp
  lldb/source/Target/StopInfo.cpp
  lldb/source/Target/TargetProperties.td
  lldb/source/Utility/StringExtractorGDBRemote.cpp
  lldb/test/API/linux/clone-events-notification/Makefile
  
lldb/test/API/linux/clone-events-notification/TestCloneEventSentAndChildStopped.py
  lldb/test/API/linux/clone-events-notification/main.cpp
  lldb/test/Shell/Subprocess/clone-stops-on-fork
  lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
  lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h

Index: lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h
===================================================================
--- lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h
+++ lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h
@@ -47,7 +47,7 @@
 
   MOCK_METHOD1(Resume, Status(const ResumeActionList &ResumeActions));
   MOCK_METHOD0(Halt, Status());
-  MOCK_METHOD0(Detach, Status());
+  MOCK_METHOD1(Detach, Status(bool KeepStopped));
   MOCK_METHOD1(Signal, Status(int Signo));
   MOCK_METHOD0(Kill, Status());
   MOCK_METHOD0(GetSharedLibraryInfoAddress, addr_t());
Index: lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
===================================================================
--- lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
+++ lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
@@ -591,3 +591,22 @@
                  std::vector<uint8_t>{0x99}, "QMemTags:456789,0:80000000:99",
                  "E03", false);
 }
+
+TEST_F(GDBRemoteCommunicationClientTest, DetachAndKeepStopped) {
+  llvm::Triple triple("i386-pc-linux");
+  const lldb::tid_t tid = 0x18d915;
+  std::future<Status> async_result = std::async(
+      std::launch::async, [&] { return client.Detach(true, tid); });
+  HandlePacket(server, "qSupportsDetachAndStayStopped:", "OK");
+  HandlePacket(server, "D1;000000000018d915", "OK");
+  EXPECT_TRUE(async_result.get().Success());
+}
+
+TEST_F(GDBRemoteCommunicationClientTest, DetachAndContinue) {
+  llvm::Triple triple("i386-pc-linux");
+  const lldb::tid_t tid = 0x18d915;
+  std::future<Status> async_result = std::async(
+      std::launch::async, [&] { return client.Detach(false, tid); });
+  HandlePacket(server, "D;000000000018d915", "OK");
+  EXPECT_TRUE(async_result.get().Success());
+}
Index: lldb/test/Shell/Subprocess/clone-stops-on-fork
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/clone-stops-on-fork
@@ -0,0 +1,10 @@
+# REQUIRES: native && system-linux
+# RUN: %clangxx_host -g %p/Inputs/fork.cpp -DTEST_CLONE -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.stop-on-clone-events true
+process launch -s
+continue
+# CHECK: stop reason = fork
+continue
+# CHECK: function run in parent
+# CHECK: function run in exec'd child
\ No newline at end of file
Index: lldb/test/API/linux/clone-events-notification/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/linux/clone-events-notification/main.cpp
@@ -0,0 +1,90 @@
+/* This sample is a modified version of the example in
+https://linux.die.net/man/2/waitpid. The expected output
+when running in the shell is:
+(lldb) settings set target.process.stop-on-clone-events true
+(lldb) settings set target.process.detach-keeps-stopped true
+(lldb) file a.out
+Current executable set to 'a.out' (x86_64).
+(lldb) run
+Process <parent_pid> launched: 'a.out' (x86_64)
+Process <parent_pid> stopped
+* thread #1, name = 'a.out', stop reason = fork
+    frame #0: 0x00007ffff7e9bb85 libc.so.6`__libc_fork at arch-fork.h:49:9
+(lldb) c
+Process <parent_pid> resuming
+Process <parent_pid> stopped and restarted: thread 1 received signal: SIGCHLD
+stopped by signal 19
+continued
+Child PID is <child_pid>
+Process <parent_pid> stopped and restarted: thread 1 received signal: SIGCHLD
+Process <parent_pid> stopped and restarted: thread 1 received signal: SIGCHLD
+exited, status=0
+Process <parent_pid> exited with status = 83 (0x00000053)
+(lldb) quit */
+
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/* Function to record both flags (up to 3 bits, possible
+   values at the moment are in the range [1, 4]) and the
+   order in which they occurred. Left most 3 bits are the
+   first seen flag, second triad is the second flag, etc.
+
+   `update_seen(0, 4)` changes current_value to 0b100
+   `update_seen(3, 1)` changes current_value to 0b011`001
+   `update_seen(83, 2)` changes current_value to 0b001`010`011`010
+*/
+void
+update_seen(long long& current_value, long long flag) {
+  if (current_value == 0)
+    current_value = flag;
+  else
+    current_value = (current_value << 3) | flag;
+}
+
+int
+main(int argc, char *argv[])
+{
+  pid_t cpid, w;
+  int status;
+  long long seen_statuses = 0;
+  cpid = fork();
+
+  if (cpid == -1) {
+    perror("fork");
+    exit(EXIT_FAILURE);
+  }
+  if (cpid == 0) {            /* Code executed by child */
+    printf("Child PID is %ld\n", (long) getpid());
+    exit(EXIT_SUCCESS);
+  } else {                /* Code executed by parent */
+    do {
+      w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
+      if (w == -1) {
+        perror("waitpid");
+        exit(EXIT_FAILURE);
+      }
+      // Record signals seen by parent.
+      if (WIFSTOPPED(status)) {
+        update_seen(seen_statuses, 0b001);
+        printf("stopped by signal %d\n", WSTOPSIG(status));
+        // Send SIGCONT to the child.
+        kill(cpid, SIGCONT);
+      } else if (WIFCONTINUED(status)) {
+        update_seen(seen_statuses, 0b010);
+        printf("continued\n");
+      } else if (WIFEXITED(status)) {
+        update_seen(seen_statuses, 0b011);
+        printf("exited, status=%d\n", WEXITSTATUS(status));
+      } else if (WIFSIGNALED(status)) {
+        update_seen(seen_statuses, 0b100);
+        printf("killed by signal %d\n", WTERMSIG(status));
+      }
+    } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+    // Expected seen_statuses to be 83 (0b001`010`011) which is
+    // WIFSTOPPED, followed by WIFCONTINUED, and by WIFEXITED.
+    exit(seen_statuses);
+  }
+}
\ No newline at end of file
Index: lldb/test/API/linux/clone-events-notification/TestCloneEventSentAndChildStopped.py
===================================================================
--- /dev/null
+++ lldb/test/API/linux/clone-events-notification/TestCloneEventSentAndChildStopped.py
@@ -0,0 +1,50 @@
+"""
+Test the keep_stopped setting and possibility to attach to the client event.
+"""
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestProcessKeepStoppedOnDetach(TestBase):
+    mydir = TestBase.compute_mydir(__file__)
+
+    @no_debug_info_test  # Prevent the generation of the dwarf version of this test
+    @add_test_categories(["pyapi"])
+    @skipUnlessPlatform(["linux"])
+    def test_keep_stopped(self):
+        self.build()
+        self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
+
+        self.runCmd("settings set target.process.stop-on-clone-events true")
+        self.runCmd("settings set target.process.detach-keeps-stopped true")
+
+        self.runCmd("run", RUN_SUCCEEDED)
+        process = self.dbg.GetSelectedTarget().process
+        self.assertEqual(
+            process.GetState(),
+            lldb.eStateStopped,
+            PROCESS_STOPPED)
+
+        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonFork)
+        self.assertIsNotNone(
+            thread, "Expected one thread to be stopped at the fork")
+
+        pid = thread.GetStopReasonDataAtIndex(0)
+        status = self.procStatus(pid)
+        self.assertEqual(status, 'T',
+            "Expected child to be stopped, but had '%s' Status" % status)
+
+        process.Continue()
+
+        self.assertEqual(process.GetState(), lldb.eStateExited, PROCESS_EXITED)
+        # Expected exitStatus to equal 83 (0b001`010`011) which means
+        # WIFSTOPPED, followed by WIFCONTINUED, and then by WIFEXITED.
+        self.assertEqual(process.GetExitStatus(), 0b001010011)
+
+    def procStatus(self, pid):
+        for line in open("/proc/%d/status" % pid).readlines():
+            if line.startswith("State:"):
+                return line.split(":",1)[1].strip().split(' ')[0]
+        return None
\ No newline at end of file
Index: lldb/test/API/linux/clone-events-notification/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/linux/clone-events-notification/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
\ No newline at end of file
Index: lldb/source/Utility/StringExtractorGDBRemote.cpp
===================================================================
--- lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -270,6 +270,8 @@
         return eServerPacketType_qStepPacketSupported;
       if (PACKET_STARTS_WITH("qSupported"))
         return eServerPacketType_qSupported;
+      if (PACKET_STARTS_WITH("qSupportsDetachAndStayStopped"))
+        return eServerPacketType_qSupportsDetachAndStayStopped;
       if (PACKET_MATCHES("qSyncThreadStateSupported"))
         return eServerPacketType_qSyncThreadStateSupported;
       break;
Index: lldb/source/Target/TargetProperties.td
===================================================================
--- lldb/source/Target/TargetProperties.td
+++ lldb/source/Target/TargetProperties.td
@@ -243,6 +243,10 @@
     DefaultEnumValue<"eFollowParent">,
     EnumValues<"OptionEnumValues(g_follow_fork_mode_values)">,
     Desc<"Debugger's behavior upon fork or vfork.">;
+  def StopOnCloneEvents: Property<"stop-on-clone-events", "Boolean">,
+    Global,
+    DefaultFalse,
+    Desc<"If true, stop after a new child is spawned and the process to keep debugged selected (see 'follow-fork-mode').">;
 }
 
 let Definition = "platform" in {
Index: lldb/source/Target/StopInfo.cpp
===================================================================
--- lldb/source/Target/StopInfo.cpp
+++ lldb/source/Target/StopInfo.cpp
@@ -1164,7 +1164,12 @@
 
   ~StopInfoFork() override = default;
 
-  bool ShouldStop(Event *event_ptr) override { return false; }
+  bool ShouldStop(Event *event_ptr) override {
+    ThreadSP thread_sp(m_thread_wp.lock());
+    if (thread_sp)
+      return thread_sp->GetProcess()->GetStopOnCloneEvents();
+    return false;
+  }
 
   StopReason GetStopReason() const override { return eStopReasonFork; }
 
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -304,6 +304,17 @@
       nullptr, idx, g_process_properties[idx].default_uint_value != 0);
 }
 
+bool ProcessProperties::GetStopOnCloneEvents() const {
+  const uint32_t idx = ePropertyStopOnCloneEvents;
+  return m_collection_sp->GetPropertyAtIndexAsBoolean(
+      nullptr, idx, g_process_properties[idx].default_uint_value != 0);
+}
+
+void ProcessProperties::SetStopOnCloneEvents(bool stop) {
+  const uint32_t idx = ePropertyStopOnCloneEvents;
+  m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, stop);
+}
+
 std::chrono::seconds ProcessProperties::GetUtilityExpressionTimeout() const {
   const uint32_t idx = ePropertyUtilityExpressionTimeout;
   uint64_t value = m_collection_sp->GetPropertyAtIndexAsUInt64(
@@ -3975,7 +3986,7 @@
         still_should_stop = this_thread_wants_to_stop;
     }
   }
-
+  
   return still_should_stop;
 }
 
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -5392,8 +5392,9 @@
     return;
   }
 
-  LLDB_LOG(log, "Detaching process {0}", detach_pid);
-  Status error = m_gdb_comm.Detach(false, detach_pid);
+  bool keep_stop_on_detach = GetDetachKeepsStopped();
+  LLDB_LOG(log, "Detaching process {0} (keep_stopped={1})", detach_pid, keep_stop_on_detach);
+  Status error = m_gdb_comm.Detach(keep_stop_on_detach, detach_pid);
   if (error.Fail()) {
     LLDB_LOG(log, "ProcessGDBRemote::DidFork() detach packet send failed: {0}",
              error.AsCString() ? error.AsCString() : "<unknown error>");
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -217,6 +217,8 @@
 
   PacketResult Handle_qSaveCore(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_qSupportsDetachAndStayStopped(StringExtractorGDBRemote &packet);
+
   PacketResult Handle_g(StringExtractorGDBRemote &packet);
 
   PacketResult Handle_qMemTags(StringExtractorGDBRemote &packet);
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -235,6 +235,10 @@
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_qLLDBSaveCore,
       &GDBRemoteCommunicationServerLLGS::Handle_qSaveCore);
+
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_qSupportsDetachAndStayStopped,
+      &GDBRemoteCommunicationServerLLGS::Handle_qSupportsDetachAndStayStopped);
 }
 
 void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(const ProcessLaunchInfo &info) {
@@ -3395,12 +3399,25 @@
   StopSTDIOForwarding();
 
   lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
-
-  // Consume the ';' after D.
+  bool keep_stopped = false;
+  // Consume the ';' and possibly '1' after D.
   packet.SetFilePos(1);
   if (packet.GetBytesLeft()) {
-    if (packet.GetChar() != ';')
-      return SendIllFormedResponse(packet, "D missing expected ';'");
+    const char ch = packet.GetChar();
+    switch (ch) {
+      case '1':
+      {
+        if (packet.GetChar() != ';')
+          return SendIllFormedResponse(packet, "D1 missing expected ';'");
+        keep_stopped = true;
+        break;
+      }
+      case ';': {
+        break;
+      }
+      default:
+        return SendIllFormedResponse(packet, "D missing expected ';'");
+    }
 
     // Grab the PID from which we will detach (assume hex encoding).
     pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16);
@@ -3415,7 +3432,7 @@
   for (auto it = m_debugged_processes.begin();
        it != m_debugged_processes.end();) {
     if (pid == LLDB_INVALID_PROCESS_ID || pid == it->first) {
-      if (llvm::Error e = it->second->Detach().ToError())
+      if (llvm::Error e = it->second->Detach(keep_stopped).ToError())
         detach_error = llvm::joinErrors(std::move(detach_error), std::move(e));
       else {
         if (it->second.get() == m_current_process)
@@ -3774,6 +3791,18 @@
   return SendPacketNoLock(response.GetString());
 }
 
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qSupportsDetachAndStayStopped(
+    StringExtractorGDBRemote &packet) {
+  using Extension = NativeProcessProtocol::Extension;
+  Extension plugin_features = m_process_factory.GetSupportedExtensions();
+  if (bool(plugin_features & Extension::keep_stopped)) {
+    return SendOKResponse();
+  }
+
+  return SendErrorResponse(Status("Unsupported qSupportsDetachAndStayStopped"));
+}
+
 void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() {
   Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
 
Index: lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
===================================================================
--- lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
+++ lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
@@ -46,7 +46,7 @@
 
   Status Halt() override;
 
-  Status Detach() override;
+  Status Detach(bool keep_stopped) override;
 
   Status Signal(int signo) override;
 
Index: lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
===================================================================
--- lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
+++ lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
@@ -166,7 +166,7 @@
   return Status();
 }
 
-Status NativeProcessWindows::Detach() {
+Status NativeProcessWindows::Detach(bool keep_stopped) {
   Status error;
   Log *log = GetLog(WindowsLog::Process);
   StateType state = GetState();
Index: lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
===================================================================
--- lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
+++ lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
@@ -45,7 +45,7 @@
 
   Status Halt() override;
 
-  Status Detach() override;
+  Status Detach(bool keep_stopped = false) override;
 
   Status Signal(int signo) override;
 
Index: lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
===================================================================
--- lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
+++ lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
@@ -535,7 +535,7 @@
 
 Status NativeProcessNetBSD::Halt() { return PtraceWrapper(PT_STOP, GetID()); }
 
-Status NativeProcessNetBSD::Detach() {
+Status NativeProcessNetBSD::Detach(bool keep_stopped) {
   Status error;
 
   // Stop monitoring the inferior.
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -58,7 +58,7 @@
 
   Status Halt() override;
 
-  Status Detach() override;
+  Status Detach(bool keep_stopped = false) override;
 
   Status Signal(int signo) override;
 
@@ -216,7 +216,7 @@
 
   void NotifyThreadDeath(lldb::tid_t tid);
 
-  Status Detach(lldb::tid_t tid);
+  Status Detach(lldb::tid_t tid, bool keep_stopped = false);
 
   // This method is requests a stop on all threads which are still running. It
   // sets up a
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -286,7 +286,7 @@
   NativeProcessLinux::Extension supported =
       Extension::multiprocess | Extension::fork | Extension::vfork |
       Extension::pass_signals | Extension::auxv | Extension::libraries_svr4 |
-      Extension::siginfo_read;
+      Extension::siginfo_read | Extension::keep_stopped;
 
 #ifdef __aarch64__
   // At this point we do not have a process so read auxv directly.
@@ -962,7 +962,7 @@
   return error;
 }
 
-Status NativeProcessLinux::Detach() {
+Status NativeProcessLinux::Detach(bool keep_stopped) {
   Status error;
 
   // Stop monitoring the inferior.
@@ -973,7 +973,9 @@
     return error;
 
   for (const auto &thread : m_threads) {
-    Status e = Detach(thread->GetID());
+    Status e = Detach(thread->GetID(), keep_stopped);
+    Log *log = GetLog(POSIXLog::Process);
+    LLDB_LOG(log, "Detaching from pid {0} returned {1}", thread->GetID(), e);
     if (e.Fail())
       error =
           e; // Save the error, but still attempt to detach from other threads.
@@ -1625,11 +1627,16 @@
   return PtraceWrapper(PTRACE_GETEVENTMSG, tid, nullptr, message);
 }
 
-Status NativeProcessLinux::Detach(lldb::tid_t tid) {
+Status NativeProcessLinux::Detach(lldb::tid_t tid, bool keep_stopped) {
   if (tid == LLDB_INVALID_THREAD_ID)
     return Status();
-
-  return PtraceWrapper(PTRACE_DETACH, tid);
+  if (keep_stopped) {
+    intptr_t data = SIGSTOP;
+    return PtraceWrapper(PTRACE_DETACH, tid, nullptr,
+                         reinterpret_cast<void *>(data));
+  } else {
+    return PtraceWrapper(PTRACE_DETACH, tid);
+  }
 }
 
 bool NativeProcessLinux::HasThreadNoLock(lldb::tid_t thread_id) {
Index: lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
===================================================================
--- lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
+++ lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
@@ -48,7 +48,7 @@
 
   Status Halt() override;
 
-  Status Detach() override;
+  Status Detach(bool keep_stopped = false) override;
 
   Status Signal(int signo) override;
 
Index: lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
===================================================================
--- lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
+++ lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
@@ -502,7 +502,7 @@
   return error;
 }
 
-Status NativeProcessFreeBSD::Detach() {
+Status NativeProcessFreeBSD::Detach(bool keep_stopped) {
   Status error;
 
   // Stop monitoring the inferior.
Index: lldb/include/lldb/Utility/StringExtractorGDBRemote.h
===================================================================
--- lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -174,6 +174,7 @@
     eServerPacketType_QMemTags, // write memory tags
 
     eServerPacketType_qLLDBSaveCore,
+    eServerPacketType_qSupportsDetachAndStayStopped,
   };
 
   ServerPacketType GetServerPacketType() const;
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -94,6 +94,8 @@
   bool GetWarningsOptimization() const;
   bool GetWarningsUnsupportedLanguage() const;
   bool GetStopOnExec() const;
+  bool GetStopOnCloneEvents() const;
+  void SetStopOnCloneEvents(bool stop);
   std::chrono::seconds GetUtilityExpressionTimeout() const;
   std::chrono::seconds GetInterruptTimeout() const;
   bool GetOSPluginReportsAllThreads() const;
Index: lldb/include/lldb/Host/common/NativeProcessProtocol.h
===================================================================
--- lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -52,7 +52,7 @@
 
   virtual Status Halt() = 0;
 
-  virtual Status Detach() = 0;
+  virtual Status Detach(bool keep_stopped = false) = 0;
 
   /// Sends a process a UNIX signal \a signal.
   ///
@@ -252,8 +252,9 @@
     memory_tagging = (1u << 6),
     savecore = (1u << 7),
     siginfo_read = (1u << 8),
+    keep_stopped = (1u << 9),
 
-    LLVM_MARK_AS_BITMASK_ENUM(siginfo_read)
+    LLVM_MARK_AS_BITMASK_ENUM(keep_stopped)
   };
 
   class Factory {
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to