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

>From d089e878ac99296d57093d59ae320b4c17eb44ab Mon Sep 17 00:00:00 2001
From: Sergei Druzhkov <[email protected]>
Date: Thu, 26 Mar 2026 12:07:49 +0300
Subject: [PATCH 1/5] [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               | 10 +++--
 lldb/source/Target/TargetProperties.td        |  8 ++++
 .../API/functionalities/fork/stop/Makefile    |  3 ++
 .../fork/stop/TestStopOnForkAndVFork.py       | 43 +++++++++++++++++++
 .../test/API/functionalities/fork/stop/main.c | 14 ++++++
 lldb/test/Shell/Subprocess/stop-on-fork.test  | 11 +++++
 lldb/test/Shell/Subprocess/stop-on-vfork.test | 11 +++++
 9 files changed, 110 insertions(+), 4 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 8432c326d3281..e94b12b1d70f7 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -107,6 +107,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 ac1357f7d00a1..7fb5e1335e057 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -341,6 +341,18 @@ bool ProcessProperties::GetUseDelayedBreakpoints() 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 f438d368c9ba8..5db0809317cbd 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -1554,8 +1554,9 @@ class StopInfoFork : public StopInfo {
     ThreadSP thread_sp(m_thread_wp.lock());
     if (thread_sp) {
       ProcessSP process_sp = thread_sp->GetProcess();
-      if (process_sp && process_sp->GetModIDRef().IsRunningExpression() &&
-          thread_sp->IsRunningCallFunctionPlan())
+      if (process_sp && ((process_sp->GetModIDRef().IsRunningExpression() &&
+                          thread_sp->IsRunningCallFunctionPlan()) ||
+                         process_sp->GetStopOnFork()))
         return true;
     }
     return false;
@@ -1610,8 +1611,9 @@ class StopInfoVFork : public StopInfo {
     ThreadSP thread_sp(m_thread_wp.lock());
     if (thread_sp) {
       ProcessSP process_sp = thread_sp->GetProcess();
-      if (process_sp && process_sp->GetModIDRef().IsRunningExpression() &&
-          thread_sp->IsRunningCallFunctionPlan())
+      if (process_sp && ((process_sp->GetModIDRef().IsRunningExpression() &&
+                          thread_sp->IsRunningCallFunctionPlan()) ||
+                         process_sp->GetStopOnVFork()))
         return true;
     }
     return false;
diff --git a/lldb/source/Target/TargetProperties.td 
b/lldb/source/Target/TargetProperties.td
index d63bc741e66ed..1802271cf940e 100644
--- a/lldb/source/Target/TargetProperties.td
+++ b/lldb/source/Target/TargetProperties.td
@@ -282,6 +282,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..c1fb6e8b313e7
--- /dev/null
+++ b/lldb/test/API/functionalities/fork/stop/main.c
@@ -0,0 +1,14 @@
+#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

>From 412c87d43ab6e0f792ceca2069938d3a62c70ba4 Mon Sep 17 00:00:00 2001
From: Sergei Druzhkov <[email protected]>
Date: Thu, 26 Mar 2026 12:40:22 +0300
Subject: [PATCH 2/5] Fix python formatting

---
 .../API/functionalities/fork/stop/TestStopOnForkAndVFork.py     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py 
b/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
index 14a8daa66e232..09822c5875d70 100644
--- a/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
+++ b/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
@@ -31,7 +31,7 @@ def do_test(self, mode):
 
         self.expect(
             "continue",
-            substrs=[f"exited with status = {0 if mode == "parent" else 47}"],
+            substrs=[f"exited with status = {0 if mode == 'parent' else 47}"],
         )
 
     @skipIfWindows

>From 74c5ea587c354bc58c03c43fa40809be08105b5d Mon Sep 17 00:00:00 2001
From: Sergei Druzhkov <[email protected]>
Date: Wed, 29 Apr 2026 23:00:43 +0300
Subject: [PATCH 3/5] Fix review comments

---
 lldb/source/Target/StopInfo.cpp               |  4 +--
 lldb/source/Target/TargetProperties.td        |  4 +--
 .../fork/stop/TestStopOnForkAndVFork.py       | 36 ++++++++++++-------
 .../test/API/functionalities/fork/stop/main.c |  8 +++--
 4 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index 5db0809317cbd..910a97f7740a1 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -1545,8 +1545,8 @@ class StopInfoFork : public StopInfo {
   bool ShouldStop(Event *event_ptr) override {
     // During expression evaluation, return true so that the fork event
     // reaches RunThreadPlan as a real stop (not auto-restarted by
-    // DoOnRemoval). RunThreadPlan decides whether to stop or continue
-    // based on the stop-on-fork option.
+    // DoOnRemoval) or target.process.stop-on-fork is true. RunThreadPlan
+    // decides whether to stop or continue based on the stop-on-fork option.
     //
     // We check per-thread (not just process-wide IsRunningExpression)
     // because other threads may fork concurrently after the
diff --git a/lldb/source/Target/TargetProperties.td 
b/lldb/source/Target/TargetProperties.td
index 1802271cf940e..5a9a85a363d6e 100644
--- a/lldb/source/Target/TargetProperties.td
+++ b/lldb/source/Target/TargetProperties.td
@@ -285,11 +285,11 @@ let Definition = "process", Path = "target.process" in {
   def StopOnFork: Property<"stop-on-fork", "Boolean">,
     Global,
     DefaultFalse,
-    Desc<"If true, stop when the inferior fork's.">;
+    Desc<"If true, stop when the inferior forks.">;
   def StopOnVFork: Property<"stop-on-vfork", "Boolean">,
     Global,
     DefaultFalse,
-    Desc<"If true, stop when the inferior vfork's.">;
+    Desc<"If true, stop when the inferior vforks.">;
   def UtilityExpressionTimeout: Property<"utility-expression-timeout", 
"UInt64">,
 #ifdef LLDB_SANITIZED
     DefaultUnsignedValue<60>,
diff --git a/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py 
b/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
index 09822c5875d70..bbd7bc7e6682d 100644
--- a/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
+++ b/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
@@ -6,28 +6,32 @@
 import lldbsuite.test.lldbutil as lldbutil
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test.decorators import *
-import os
 
 
-class TestForkResumesChild(TestBase):
+class TestStopOnForkAndVFork(TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
-    def do_test(self, mode):
+    def do_test(self, mode, fork):
         self.build()
-        exe = self.getBuildArtifact("a.out")
 
-        (target, process, _, _) = lldbutil.run_to_source_breakpoint(
-            self, "// break here", lldb.SBFileSpec("main.c")
+        args = [fork]
+        launch_info = lldb.SBLaunchInfo(args)
+        launch_info.SetWorkingDirectory(self.getBuildDir())
+
+        (_, process, _, _) = lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.c"), launch_info
         )
-        self.runCmd("settings set target.process.stop-on-fork true")
+        self.runCmd(f"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"
+            process.GetState(), lldb.eStateStopped, f"Process should be 
stopped at {fork}"
+        )
+        threads = lldbutil.get_stopped_threads(
+            process, lldb.eStopReasonVFork if fork == "vfork" else 
lldb.eStopReasonFork
         )
-        threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonFork)
-        self.assertEqual(len(threads), 1, f"We got a thread stopped for fork.")
+        self.assertEqual(len(threads), 1, f"We got a thread stopped for 
{fork}.")
 
         self.expect(
             "continue",
@@ -36,8 +40,16 @@ def do_test(self, mode):
 
     @skipIfWindows
     def test_stop_on_fork_and_follow_parent(self):
-        self.do_test("parent")
+        self.do_test("parent", "fork")
 
     @skipIfWindows
     def test_stop_on_fork_and_follow_child(self):
-        self.do_test("child")
+        self.do_test("child", "fork")
+
+    @skipIfWindows
+    def test_stop_on_vfork_and_follow_parent(self):
+        self.do_test("parent", "vfork")
+
+    @skipIfWindows
+    def test_stop_on_vfork_and_follow_child(self):
+        self.do_test("child", "vfork")
diff --git a/lldb/test/API/functionalities/fork/stop/main.c 
b/lldb/test/API/functionalities/fork/stop/main.c
index c1fb6e8b313e7..3849e2fc24a6c 100644
--- a/lldb/test/API/functionalities/fork/stop/main.c
+++ b/lldb/test/API/functionalities/fork/stop/main.c
@@ -1,10 +1,14 @@
 #include <assert.h>
+#include <string.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
-int main() {
+int main(int argc, char *argv[]) {
+  if (argc != 2) {
+    _exit(1);
+  }
   // break here
-  pid_t pid = fork();
+  pid_t pid = strcmp(argv[1], "fork") == 0 ? fork() : vfork();
   if (pid == 0) {
     // child
     _exit(47);

>From 5136618e784427a3823997a73016a226f4cf8ab2 Mon Sep 17 00:00:00 2001
From: Sergei Druzhkov <[email protected]>
Date: Wed, 29 Apr 2026 23:03:23 +0300
Subject: [PATCH 4/5] Fix python formatting

---
 .../API/functionalities/fork/stop/TestStopOnForkAndVFork.py   | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py 
b/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
index bbd7bc7e6682d..075d75fb66517 100644
--- a/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
+++ b/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
@@ -26,7 +26,9 @@ def do_test(self, mode, fork):
 
         process.Continue()
         self.assertState(
-            process.GetState(), lldb.eStateStopped, f"Process should be 
stopped at {fork}"
+            process.GetState(),
+            lldb.eStateStopped,
+            f"Process should be stopped at {fork}",
         )
         threads = lldbutil.get_stopped_threads(
             process, lldb.eStopReasonVFork if fork == "vfork" else 
lldb.eStopReasonFork

>From f5a1f6bc4b95522dec122af774ae22fccf471783 Mon Sep 17 00:00:00 2001
From: Sergei Druzhkov <[email protected]>
Date: Tue, 30 Jun 2026 12:40:35 +0300
Subject: [PATCH 5/5] Fix description and tests

---
 lldb/source/Target/TargetProperties.td        |  4 ++--
 .../fork/stop/TestStopOnForkAndVFork.py       | 22 +++++++++++++++----
 2 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/lldb/source/Target/TargetProperties.td 
b/lldb/source/Target/TargetProperties.td
index 5a9a85a363d6e..bf016b52afb83 100644
--- a/lldb/source/Target/TargetProperties.td
+++ b/lldb/source/Target/TargetProperties.td
@@ -285,11 +285,11 @@ let Definition = "process", Path = "target.process" in {
   def StopOnFork: Property<"stop-on-fork", "Boolean">,
     Global,
     DefaultFalse,
-    Desc<"If true, stop when the inferior forks.">;
+    Desc<"If true, stop when the inferior forks. The stop location depends on 
target.process.follow-fork-mode: 'child' stops in the child process, while 
'parent' stops in the original process.">;
   def StopOnVFork: Property<"stop-on-vfork", "Boolean">,
     Global,
     DefaultFalse,
-    Desc<"If true, stop when the inferior vforks.">;
+    Desc<"If true, stop when the inferior vforks. The stop location depends on 
target.process.follow-fork-mode: 'child' stops in the child process, while 
'parent' stops in the original process.">;
   def UtilityExpressionTimeout: Property<"utility-expression-timeout", 
"UInt64">,
 #ifdef LLDB_SANITIZED
     DefaultUnsignedValue<60>,
diff --git a/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py 
b/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
index 075d75fb66517..9fc85020e66f4 100644
--- a/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
+++ b/lldb/test/API/functionalities/fork/stop/TestStopOnForkAndVFork.py
@@ -24,6 +24,8 @@ def do_test(self, mode, fork):
         self.runCmd(f"settings set target.process.stop-on-{fork} true")
         self.runCmd(f"settings set target.process.follow-fork-mode {mode}")
 
+        pid = process.GetProcessID()
+
         process.Continue()
         self.assertState(
             process.GetState(),
@@ -35,10 +37,22 @@ def do_test(self, mode, fork):
         )
         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}"],
-        )
+        if mode == "parent":
+            self.assertEqual(
+                self.dbg.GetSelectedTarget().GetProcess().GetProcessID(), pid
+            )
+            self.expect(
+                "continue",
+                substrs=[f"exited with status = 0"],
+            )
+        else:  # child
+            self.assertNotEqual(
+                self.dbg.GetSelectedTarget().GetProcess().GetProcessID(), pid
+            )
+            self.expect(
+                "continue",
+                substrs=[f"exited with status = 47"],
+            )
 
     @skipIfWindows
     def test_stop_on_fork_and_follow_parent(self):

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to