mgorny updated this revision to Diff 356440.
mgorny added a comment.

Rebase. Do not run tests on NetBSD yet.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D100503/new/

https://reviews.llvm.org/D100503

Files:
  lldb/include/lldb/Target/Process.h
  lldb/include/lldb/lldb-private-enumerations.h
  lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
  lldb/source/Target/Process.cpp
  lldb/source/Target/TargetProperties.td
  lldb/test/Shell/Subprocess/clone-follow-child-softbp.test
  lldb/test/Shell/Subprocess/clone-follow-child-wp.test
  lldb/test/Shell/Subprocess/clone-follow-child.test
  lldb/test/Shell/Subprocess/fork-follow-child-softbp.test
  lldb/test/Shell/Subprocess/fork-follow-child-wp.test
  lldb/test/Shell/Subprocess/fork-follow-child.test
  lldb/test/Shell/Subprocess/fork-follow-parent-softbp.test
  lldb/test/Shell/Subprocess/vfork-follow-child-softbp.test
  lldb/test/Shell/Subprocess/vfork-follow-child-wp.test
  lldb/test/Shell/Subprocess/vfork-follow-child.test

Index: lldb/test/Shell/Subprocess/vfork-follow-child.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/vfork-follow-child.test
@@ -0,0 +1,9 @@
+# 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
+settings set target.process.follow-fork-mode child
+b parent_func
+process launch
+# CHECK: function run in parent
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/vfork-follow-child-wp.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/vfork-follow-child-wp.test
@@ -0,0 +1,11 @@
+# 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
+settings set target.process.follow-fork-mode child
+process launch -s
+watchpoint set variable -w write g_val
+# CHECK: Watchpoint created:
+continue
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/vfork-follow-child-softbp.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/vfork-follow-child-softbp.test
@@ -0,0 +1,10 @@
+# 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
+settings set target.process.follow-fork-mode child
+b child_func
+b parent_func
+process launch
+# CHECK: function run in parent
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/fork-follow-parent-softbp.test
===================================================================
--- lldb/test/Shell/Subprocess/fork-follow-parent-softbp.test
+++ lldb/test/Shell/Subprocess/fork-follow-parent-softbp.test
@@ -7,6 +7,7 @@
 process launch
 # CHECK-NOT: function run in parent
 # CHECK: stop reason = breakpoint
+# CHECK-NEXT: parent_func
 continue
 # CHECK: function run in parent
 # CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/fork-follow-child.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/fork-follow-child.test
@@ -0,0 +1,9 @@
+# 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
+settings set target.process.follow-fork-mode child
+b parent_func
+process launch
+# CHECK: function run in parent
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/fork-follow-child-wp.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/fork-follow-child-wp.test
@@ -0,0 +1,14 @@
+# 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
+settings set target.process.follow-fork-mode child
+process launch -s
+watchpoint set variable -w write g_val
+# CHECK: Watchpoint created:
+continue
+# CHECK: stop reason = watchpoint
+continue
+# CHECK: stop reason = watchpoint
+continue
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/fork-follow-child-softbp.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/fork-follow-child-softbp.test
@@ -0,0 +1,12 @@
+# 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
+settings set target.process.follow-fork-mode child
+b child_func
+b parent_func
+process launch
+# CHECK: stop reason = breakpoint
+# CHECK-NEXT: child_func
+continue
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/clone-follow-child.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/clone-follow-child.test
@@ -0,0 +1,10 @@
+# REQUIRES: native && system-linux
+# clone() tests fails on arm64 Linux, PR #49899
+# UNSUPPORTED: system-linux && target-aarch64
+# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_CLONE -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.follow-fork-mode child
+b parent_func
+process launch
+# CHECK: function run in parent
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/clone-follow-child-wp.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/clone-follow-child-wp.test
@@ -0,0 +1,15 @@
+# REQUIRES: native && system-linux && dbregs-set
+# clone() tests fails on arm64 Linux, PR #49899
+# UNSUPPORTED: system-linux && target-aarch64
+# RUN: %clangxx_host -g %p/Inputs/fork.cpp -DTEST_CLONE -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.follow-fork-mode child
+process launch -s
+watchpoint set variable -w write g_val
+# CHECK: Watchpoint created:
+continue
+# CHECK: stop reason = watchpoint
+continue
+# CHECK: stop reason = watchpoint
+continue
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/clone-follow-child-softbp.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/clone-follow-child-softbp.test
@@ -0,0 +1,13 @@
+# REQUIRES: native && system-linux
+# clone() tests fails on arm64 Linux, PR #49899
+# UNSUPPORTED: system-linux && target-aarch64
+# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_CLONE -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.follow-fork-mode child
+b child_func
+b parent_func
+process launch
+# CHECK: stop reason = breakpoint
+# CHECK-NEXT: child_func
+continue
+# CHECK: child exited: 0
Index: lldb/source/Target/TargetProperties.td
===================================================================
--- lldb/source/Target/TargetProperties.td
+++ lldb/source/Target/TargetProperties.td
@@ -236,6 +236,10 @@
   def VirtualAddressableBits: Property<"virtual-addressable-bits", "UInt64">,
     DefaultUnsignedValue<0>,
     Desc<"The number of bits used for addressing. If the value is 39, then bits 0..38 are used for addressing. The default value of 0 means unspecified.">;
+  def FollowForkMode: Property<"follow-fork-mode", "Enum">,
+    DefaultEnumValue<"eFollowParent">,
+    EnumValues<"OptionEnumValues(g_follow_fork_mode_values)">,
+    Desc<"Debugger's behavior upon fork or vfork.">;
 }
 
 let Definition = "platform" in {
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -108,6 +108,19 @@
   }
 };
 
+static constexpr OptionEnumValueElement g_follow_fork_mode_values[] = {
+    {
+        eFollowParent,
+        "parent",
+        "Continue tracing the parent process and detach the child.",
+    },
+    {
+        eFollowChild,
+        "child",
+        "Trace the child process and detach the parent.",
+    },
+};
+
 #define LLDB_PROPERTIES_process
 #include "TargetProperties.inc"
 
@@ -325,6 +338,12 @@
         nullptr, ePropertyOSPluginReportsAllThreads, does_report);
 }
 
+FollowForkMode ProcessProperties::GetFollowForkMode() const {
+  const uint32_t idx = ePropertyFollowForkMode;
+  return (FollowForkMode)m_collection_sp->GetPropertyAtIndexAsEnumeration(
+      nullptr, idx, g_process_properties[idx].default_uint_value);
+}
+
 ProcessSP Process::FindPlugin(lldb::TargetSP target_sp,
                               llvm::StringRef plugin_name,
                               ListenerSP listener_sp,
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -233,6 +233,7 @@
   void DidFork(lldb::pid_t child_pid, lldb::tid_t child_tid) override;
   void DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) override;
   void DidVForkDone() override;
+  void DidExec() override;
 
 protected:
   friend class ThreadGDBRemote;
@@ -465,6 +466,7 @@
 
   // fork helpers
   void DidForkSwitchSoftwareBreakpoints(bool enable);
+  void DidForkSwitchHardwareTraps(bool enable);
 };
 
 } // namespace process_gdb_remote
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
@@ -5489,6 +5489,30 @@
   });
 }
 
+void ProcessGDBRemote::DidForkSwitchHardwareTraps(bool enable) {
+  if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) {
+    GetBreakpointSiteList().ForEach([this, enable](BreakpointSite *bp_site) {
+      if (bp_site->IsEnabled() &&
+          bp_site->GetType() == BreakpointSite::eHardware) {
+        m_gdb_comm.SendGDBStoppointTypePacket(
+            eBreakpointHardware, enable, bp_site->GetLoadAddress(),
+            bp_site->GetTrapOpcodeMaxByteSize());
+      }
+    });
+  }
+
+  WatchpointList &wps = GetTarget().GetWatchpointList();
+  size_t wp_count = wps.GetSize();
+  for (size_t i = 0; i < wp_count; ++i) {
+    WatchpointSP wp = wps.GetByIndex(i);
+    if (wp->IsEnabled()) {
+      GDBStoppointType type = GetGDBStoppointType(wp.get());
+      m_gdb_comm.SendGDBStoppointTypePacket(type, enable, wp->GetLoadAddress(),
+                                            wp->GetByteSize());
+    }
+  }
+}
+
 void ProcessGDBRemote::DidFork(lldb::pid_t child_pid, lldb::tid_t child_tid) {
   Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
 
@@ -5497,30 +5521,58 @@
   // anyway.
   lldb::tid_t parent_tid = m_thread_ids.front();
 
-  if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) {
-    // Switch to the new process to clear breakpoints there.
-    if (!m_gdb_comm.SetCurrentThread(child_tid, child_pid)) {
-      LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to set pid/tid");
-      return;
-    }
+  lldb::pid_t follow_pid, detach_pid;
+  lldb::tid_t follow_tid, detach_tid;
+
+  switch (GetFollowForkMode()) {
+  case eFollowParent:
+    follow_pid = parent_pid;
+    follow_tid = parent_tid;
+    detach_pid = child_pid;
+    detach_tid = child_tid;
+    break;
+  case eFollowChild:
+    follow_pid = child_pid;
+    follow_tid = child_tid;
+    detach_pid = parent_pid;
+    detach_tid = parent_tid;
+    break;
+  }
 
-    // Disable all software breakpoints in the forked process.
+  // Switch to the process that is going to be detached.
+  if (!m_gdb_comm.SetCurrentThread(detach_tid, detach_pid)) {
+    LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to set pid/tid");
+    return;
+  }
+
+  // Disable all software breakpoints in the forked process.
+  if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware))
     DidForkSwitchSoftwareBreakpoints(false);
 
-    // Reset gdb-remote to the original process.
-    if (!m_gdb_comm.SetCurrentThread(parent_tid, parent_pid)) {
-      LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to reset pid/tid");
-      return;
-    }
+  // Remove hardware breakpoints / watchpoints from parent process if we're
+  // following child.
+  if (GetFollowForkMode() == eFollowChild)
+    DidForkSwitchHardwareTraps(false);
+
+  // Switch to the process that is going to be followed
+  if (!m_gdb_comm.SetCurrentThread(follow_tid, follow_pid) ||
+      !m_gdb_comm.SetCurrentThreadForRun(follow_tid, follow_pid)) {
+    LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to reset pid/tid");
+    return;
   }
 
-  LLDB_LOG(log, "Detaching forked child {0}", child_pid);
-  Status error = m_gdb_comm.Detach(false, child_pid);
+  LLDB_LOG(log, "Detaching process {0}", detach_pid);
+  Status error = m_gdb_comm.Detach(false, detach_pid);
   if (error.Fail()) {
     LLDB_LOG(log, "ProcessGDBRemote::DidFork() detach packet send failed: {0}",
              error.AsCString() ? error.AsCString() : "<unknown error>");
     return;
   }
+
+  // Hardware breakpoints/watchpoints are not inherited implicitly,
+  // so we need to readd them if we're following child.
+  if (GetFollowForkMode() == eFollowChild)
+    DidForkSwitchHardwareTraps(true);
 }
 
 void ProcessGDBRemote::DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) {
@@ -5533,8 +5585,40 @@
   if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware))
     DidForkSwitchSoftwareBreakpoints(false);
 
-  LLDB_LOG(log, "Detaching forked child {0}", child_pid);
-  Status error = m_gdb_comm.Detach(false, child_pid);
+  lldb::pid_t detach_pid;
+  lldb::tid_t detach_tid;
+
+  switch (GetFollowForkMode()) {
+  case eFollowParent:
+    detach_pid = child_pid;
+    detach_tid = child_tid;
+    break;
+  case eFollowChild:
+    detach_pid = m_gdb_comm.GetCurrentProcessID();
+    // Any valid TID will suffice, thread-relevant actions will set a proper TID
+    // anyway.
+    detach_tid = m_thread_ids.front();
+
+    // Switch to the parent process before detaching it.
+    if (!m_gdb_comm.SetCurrentThread(detach_tid, detach_pid)) {
+      LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to set pid/tid");
+      return;
+    }
+
+    // Remove hardware breakpoints / watchpoints from the parent process.
+    DidForkSwitchHardwareTraps(false);
+
+    // Switch to the child process.
+    if (!m_gdb_comm.SetCurrentThread(child_tid, child_pid) ||
+        !m_gdb_comm.SetCurrentThreadForRun(child_tid, child_pid)) {
+      LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to reset pid/tid");
+      return;
+    }
+    break;
+  }
+
+  LLDB_LOG(log, "Detaching process {0}", detach_pid);
+  Status error = m_gdb_comm.Detach(false, detach_pid);
   if (error.Fail()) {
       LLDB_LOG(log,
                "ProcessGDBRemote::DidFork() detach packet send failed: {0}",
@@ -5551,3 +5635,11 @@
   if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware))
     DidForkSwitchSoftwareBreakpoints(true);
 }
+
+void ProcessGDBRemote::DidExec() {
+  // If we are following children, vfork is finished by exec (rather than
+  // vforkdone that is submitted for parent).
+  if (GetFollowForkMode() == eFollowChild)
+    m_vfork_in_progress = false;
+  Process::DidExec();
+}
Index: lldb/include/lldb/lldb-private-enumerations.h
===================================================================
--- lldb/include/lldb/lldb-private-enumerations.h
+++ lldb/include/lldb/lldb-private-enumerations.h
@@ -172,6 +172,12 @@
   eMemoryModuleLoadLevelComplete, // Load sections and all symbols
 };
 
+// Behavior on fork/vfork
+enum FollowForkMode {
+  eFollowParent, // Follow parent process
+  eFollowChild,  // Follow child process
+};
+
 // Result enums for when reading multiple lines from IOHandlers
 enum class LineStatus {
   Success, // The line that was just edited if good and should be added to the
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -98,6 +98,7 @@
   bool GetOSPluginReportsAllThreads() const;
   void SetOSPluginReportsAllThreads(bool does_report);
   bool GetSteppingRunsAllThreads() const;
+  FollowForkMode GetFollowForkMode() const;
 
 protected:
   Process *m_process; // Can be nullptr for global ProcessProperties
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to