[Lldb-commits] [lldb] [lldb] Add stop-on-fork and stop-on-vfork settings (PR #188710)

Sergei Druzhkov via lldb-commits lldb-commits at lists.llvm.org
Thu Mar 26 02:25:20 PDT 2026


https://github.com/DrSergei created https://github.com/llvm/llvm-project/pull/188710

Added support for new `target.process.stop-on-fork` and `target.process.stop-on-vfork` settings. GDB already has ability to set [catchpoints](https://www.sourceware.org/gdb/current/onlinedocs/gdb.html/Set-Catchpoints.html) on `fork` and `vfork`, so having this feature in LLDB might be useful (e.g. use it to get pid of new process and attach to the new process from different console).

>From 255a9a05734630c80ddc527e370c944f75cdbfd4 Mon Sep 17 00:00:00 2001
From: Sergei Druzhkov <serzhdruzhok at gmail.com>
Date: Thu, 26 Mar 2026 12:07:49 +0300
Subject: [PATCH] [lldb] Add stop-on-fork and stop-on-vfork settings

---
 lldb/include/lldb/Target/Process.h            |  2 +
 lldb/source/Target/Process.cpp                | 12 ++++++
 lldb/source/Target/StopInfo.cpp               | 14 +++++-
 lldb/source/Target/TargetProperties.td        |  8 ++++
 .../API/functionalities/fork/stop/Makefile    |  3 ++
 .../fork/stop/TestStopOnForkAndVFork.py       | 43 +++++++++++++++++++
 .../test/API/functionalities/fork/stop/main.c | 15 +++++++
 lldb/test/Shell/Subprocess/stop-on-fork.test  | 11 +++++
 lldb/test/Shell/Subprocess/stop-on-vfork.test | 11 +++++
 9 files changed, 117 insertions(+), 2 deletions(-)
 create mode 100644 lldb/test/API/functionalities/fork/stop/Makefile
 create mode 100644 lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
 create mode 100644 lldb/test/API/functionalities/fork/stop/main.c
 create mode 100644 lldb/test/Shell/Subprocess/stop-on-fork.test
 create mode 100644 lldb/test/Shell/Subprocess/stop-on-vfork.test

diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index e3ce5cd7c16ee..25227f178f56c 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -106,6 +106,8 @@ class ProcessProperties : public Properties {
   bool GetWarningsOptimization() const;
   bool GetWarningsUnsupportedLanguage() const;
   bool GetStopOnExec() const;
+  bool GetStopOnFork() const;
+  bool GetStopOnVFork() const;
   std::chrono::seconds GetUtilityExpressionTimeout() const;
   std::chrono::seconds GetInterruptTimeout() const;
   bool GetOSPluginReportsAllThreads() const;
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 587d9f6408ae4..b1f480e127fdf 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -322,6 +322,18 @@ bool ProcessProperties::GetStopOnExec() const {
       idx, g_process_properties[idx].default_uint_value != 0);
 }
 
+bool ProcessProperties::GetStopOnFork() const {
+  const uint32_t idx = ePropertyStopOnFork;
+  return GetPropertyAtIndexAs<bool>(
+      idx, g_process_properties[idx].default_uint_value != 0);
+}
+
+bool ProcessProperties::GetStopOnVFork() const {
+  const uint32_t idx = ePropertyStopOnVFork;
+  return GetPropertyAtIndexAs<bool>(
+      idx, g_process_properties[idx].default_uint_value != 0);
+}
+
 std::chrono::seconds ProcessProperties::GetUtilityExpressionTimeout() const {
   const uint32_t idx = ePropertyUtilityExpressionTimeout;
   uint64_t value = GetPropertyAtIndexAs<uint64_t>(
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index 5110ed16edc91..e8faf75540728 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -1476,7 +1476,12 @@ class StopInfoFork : public StopInfo {
 
   ~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()->GetStopOnFork();
+    return false;
+  }
 
   StopReason GetStopReason() const override { return eStopReasonFork; }
 
@@ -1518,7 +1523,12 @@ class StopInfoVFork : public StopInfo {
 
   ~StopInfoVFork() 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()->GetStopOnVFork();
+    return false;
+  }
 
   StopReason GetStopReason() const override { return eStopReasonVFork; }
 
diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td
index 2361314d506ac..ee8e3b024d607 100644
--- a/lldb/source/Target/TargetProperties.td
+++ b/lldb/source/Target/TargetProperties.td
@@ -272,6 +272,14 @@ let Definition = "process", Path = "target.process" in {
     Global,
     DefaultTrue,
     Desc<"If true, stop when the inferior exec's.">;
+  def StopOnFork: Property<"stop-on-fork", "Boolean">,
+    Global,
+    DefaultFalse,
+    Desc<"If true, stop when the inferior fork's.">;
+  def StopOnVFork: Property<"stop-on-vfork", "Boolean">,
+    Global,
+    DefaultFalse,
+    Desc<"If true, stop when the inferior vfork's.">;
   def UtilityExpressionTimeout: Property<"utility-expression-timeout", "UInt64">,
 #ifdef LLDB_SANITIZED
     DefaultUnsignedValue<60>,
diff --git a/lldb/test/API/functionalities/fork/stop/Makefile b/lldb/test/API/functionalities/fork/stop/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/functionalities/fork/stop/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py b/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
new file mode 100644
index 0000000000000..14a8daa66e232
--- /dev/null
+++ b/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
@@ -0,0 +1,43 @@
+"""
+Make sure that we stop on fork and follow mode works.
+"""
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+import os
+
+
+class TestForkResumesChild(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def do_test(self, mode):
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+
+        (target, process, _, _) = lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.c")
+        )
+        self.runCmd("settings set target.process.stop-on-fork true")
+        self.runCmd(f"settings set target.process.follow-fork-mode {mode}")
+
+        process.Continue()
+        self.assertState(
+            process.GetState(), lldb.eStateStopped, f"Process should be stopped at fork"
+        )
+        threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonFork)
+        self.assertEqual(len(threads), 1, f"We got a thread stopped for fork.")
+
+        self.expect(
+            "continue",
+            substrs=[f"exited with status = {0 if mode == "parent" else 47}"],
+        )
+
+    @skipIfWindows
+    def test_stop_on_fork_and_follow_parent(self):
+        self.do_test("parent")
+
+    @skipIfWindows
+    def test_stop_on_fork_and_follow_child(self):
+        self.do_test("child")
diff --git a/lldb/test/API/functionalities/fork/stop/main.c b/lldb/test/API/functionalities/fork/stop/main.c
new file mode 100644
index 0000000000000..b049abb201266
--- /dev/null
+++ b/lldb/test/API/functionalities/fork/stop/main.c
@@ -0,0 +1,15 @@
+#include <assert.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+int main() {
+  // break here
+  pid_t pid = fork();
+  if (pid == 0) {
+    // child
+    _exit(47);
+  }
+  // parent
+  _exit(0);
+}
+
diff --git a/lldb/test/Shell/Subprocess/stop-on-fork.test b/lldb/test/Shell/Subprocess/stop-on-fork.test
new file mode 100644
index 0000000000000..a83dd613620d7
--- /dev/null
+++ b/lldb/test/Shell/Subprocess/stop-on-fork.test
@@ -0,0 +1,11 @@
+# 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.stop-on-fork true
+process launch
+# CHECK-NOT: function run in parent
+# CHECK: stop reason = fork
+continue
+# CHECK: function run in parent
+# CHECK: function run in exec'd child
diff --git a/lldb/test/Shell/Subprocess/stop-on-vfork.test b/lldb/test/Shell/Subprocess/stop-on-vfork.test
new file mode 100644
index 0000000000000..728858170273a
--- /dev/null
+++ b/lldb/test/Shell/Subprocess/stop-on-vfork.test
@@ -0,0 +1,11 @@
+# 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.stop-on-vfork true
+process launch
+# CHECK-NOT: function run in parent
+# CHECK: stop reason = vfork
+continue
+# CHECK: function run in parent
+# CHECK: function run in exec'd child



More information about the lldb-commits mailing list