[Lldb-commits] [lldb] [lldb] Allow forks to occur in expression evaluation (PR #184815)
Philip DePetro via lldb-commits
lldb-commits at lists.llvm.org
Mon Apr 13 07:55:14 PDT 2026
https://github.com/pdepetro updated https://github.com/llvm/llvm-project/pull/184815
>From ef1fe58c7bc375bb1eb11df926242a82836082d1 Mon Sep 17 00:00:00 2001
From: Philip DePetro <pdepetro at meta.com>
Date: Thu, 19 Feb 2026 07:45:23 -0800
Subject: [PATCH 1/5] [lldb] Handle forking in expressions
Previously, forking events would cause expression evaluation to stop.
---
lldb/include/lldb/Target/StopInfo.h | 1 +
lldb/source/Target/ThreadPlanCallFunction.cpp | 15 +++++++++++++++
2 files changed, 16 insertions(+)
diff --git a/lldb/include/lldb/Target/StopInfo.h b/lldb/include/lldb/Target/StopInfo.h
index cdd6a6fbe6aa4..5c1f37edcbd3e 100644
--- a/lldb/include/lldb/Target/StopInfo.h
+++ b/lldb/include/lldb/Target/StopInfo.h
@@ -21,6 +21,7 @@ class StopInfo : public std::enable_shared_from_this<StopInfo> {
friend class Process::ProcessEventData;
friend class ThreadPlanBase;
friend class ThreadPlanReverseContinue;
+ friend class ThreadPlanCallFunction;
public:
// Constructors and Destructors
diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp
index 218111d4faf60..e555745754aab 100644
--- a/lldb/source/Target/ThreadPlanCallFunction.cpp
+++ b/lldb/source/Target/ThreadPlanCallFunction.cpp
@@ -284,6 +284,21 @@ bool ThreadPlanCallFunction::DoPlanExplainsStop(Event *event_ptr) {
if (stop_reason == eStopReasonBreakpoint && BreakpointsExplainStop())
return true;
+ if ((stop_reason == eStopReasonFork) ||
+ (stop_reason == eStopReasonVFork) ||
+ (stop_reason == eStopReasonVForkDone)) {
+ if (stop_reason == eStopReasonFork)
+ LLDB_LOGF(log, "ThreadPlanCallFunction::PlanExplainsStop hit a fork not stopping.");
+ else if (stop_reason == eStopReasonVFork)
+ LLDB_LOGF(log, "ThreadPlanCallFunction::PlanExplainsStop hit a vfork not stopping.");
+ else if (stop_reason == eStopReasonVForkDone)
+ LLDB_LOGF(log, "ThreadPlanCallFunction::PlanExplainsStop hit a vforkdone not stopping.");
+
+ m_real_stop_info_sp->PerformAction(event_ptr);
+ m_real_stop_info_sp->OverrideShouldStop(false);
+ return true;
+ }
+
// One more quirk here. If this event was from Halt interrupting the target,
// then we should not consider ourselves complete. Return true to
// acknowledge the stop.
>From e4177b490a6ff5d94841606d6cf540ae2a9c4e69 Mon Sep 17 00:00:00 2001
From: Philip DePetro <pdepetro at meta.com>
Date: Thu, 19 Feb 2026 09:12:35 -0800
Subject: [PATCH 2/5] Add a test for fork in an expression
---
.../expression/expr-with-fork/Makefile | 4 +++
.../expr-with-fork/TestExprWithFork.py | 33 +++++++++++++++++++
.../expression/expr-with-fork/main.cpp | 21 ++++++++++++
3 files changed, 58 insertions(+)
create mode 100644 lldb/test/API/commands/expression/expr-with-fork/Makefile
create mode 100644 lldb/test/API/commands/expression/expr-with-fork/TestExprWithFork.py
create mode 100644 lldb/test/API/commands/expression/expr-with-fork/main.cpp
diff --git a/lldb/test/API/commands/expression/expr-with-fork/Makefile b/lldb/test/API/commands/expression/expr-with-fork/Makefile
new file mode 100644
index 0000000000000..f016d5b15d839
--- /dev/null
+++ b/lldb/test/API/commands/expression/expr-with-fork/Makefile
@@ -0,0 +1,4 @@
+CXX_SOURCES := main.cpp
+USE_SYSTEM_STDLIB := 1
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/expression/expr-with-fork/TestExprWithFork.py b/lldb/test/API/commands/expression/expr-with-fork/TestExprWithFork.py
new file mode 100644
index 0000000000000..8de07a32ab7cd
--- /dev/null
+++ b/lldb/test/API/commands/expression/expr-with-fork/TestExprWithFork.py
@@ -0,0 +1,33 @@
+"""
+Test that expressions that call functions which fork
+can be evaluated successfully.
+
+This tests the ThreadPlanCallFunction handling of fork/vfork/vforkdone
+stop reasons, which should be silently resumed rather than causing the
+expression evaluation to fail.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class ExprWithForkTestCase(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ @skipIfWindows
+ @add_test_categories(["fork"])
+ def test_expr_with_fork(self):
+ """Test that expression evaluation succeeds when the expression calls fork()."""
+ self.build()
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "// break here", lldb.SBFileSpec("main.cpp")
+ )
+
+ # Evaluate an expression that calls fork() inside a user function.
+ # The fork will generate a fork stop event which ThreadPlanCallFunction
+ # must handle transparently for the expression to complete.
+ self.expect_expr(
+ "fork_and_return(42)", result_type="int", result_value="42"
+ )
diff --git a/lldb/test/API/commands/expression/expr-with-fork/main.cpp b/lldb/test/API/commands/expression/expr-with-fork/main.cpp
new file mode 100644
index 0000000000000..80565cf6c4420
--- /dev/null
+++ b/lldb/test/API/commands/expression/expr-with-fork/main.cpp
@@ -0,0 +1,21 @@
+#include <sys/wait.h>
+#include <unistd.h>
+
+int fork_and_return(int value) {
+ pid_t pid = fork();
+ if (pid == -1)
+ return -1;
+ if (pid == 0) {
+ // child
+ _exit(0);
+ }
+ // parent
+ int status;
+ waitpid(pid, &status, 0);
+ return value;
+}
+
+int main() {
+ int x = 42;
+ return 0; // break here
+}
>From 957817bc30fa975d6b1fb54a66dc3210085e8e62 Mon Sep 17 00:00:00 2001
From: Philip DePetro <pdepetro at meta.com>
Date: Wed, 4 Mar 2026 12:17:43 -0800
Subject: [PATCH 3/5] Add separate test for vfork and check exit status
---
.../expr-with-fork/TestExprWithFork.py | 18 +++++++++++++++++-
.../expression/expr-with-fork/main.cpp | 8 ++++----
2 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/lldb/test/API/commands/expression/expr-with-fork/TestExprWithFork.py b/lldb/test/API/commands/expression/expr-with-fork/TestExprWithFork.py
index 8de07a32ab7cd..80ee288ee709a 100644
--- a/lldb/test/API/commands/expression/expr-with-fork/TestExprWithFork.py
+++ b/lldb/test/API/commands/expression/expr-with-fork/TestExprWithFork.py
@@ -29,5 +29,21 @@ def test_expr_with_fork(self):
# The fork will generate a fork stop event which ThreadPlanCallFunction
# must handle transparently for the expression to complete.
self.expect_expr(
- "fork_and_return(42)", result_type="int", result_value="42"
+ "fork_and_return(42, false)", result_type="int", result_value="42"
+ )
+
+ @skipIfWindows
+ @add_test_categories(["fork"])
+ def test_expr_with_vfork(self):
+ """Test that expression evaluation succeeds when the expression calls vfork()."""
+ self.build()
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "// break here", lldb.SBFileSpec("main.cpp")
+ )
+
+ # Evaluate an expression that calls fork() inside a user function.
+ # The fork will generate a fork stop event which ThreadPlanCallFunction
+ # must handle transparently for the expression to complete.
+ self.expect_expr(
+ "fork_and_return(42, true)", result_type="int", result_value="42"
)
diff --git a/lldb/test/API/commands/expression/expr-with-fork/main.cpp b/lldb/test/API/commands/expression/expr-with-fork/main.cpp
index 80565cf6c4420..4e210df3d7682 100644
--- a/lldb/test/API/commands/expression/expr-with-fork/main.cpp
+++ b/lldb/test/API/commands/expression/expr-with-fork/main.cpp
@@ -1,18 +1,18 @@
#include <sys/wait.h>
#include <unistd.h>
-int fork_and_return(int value) {
- pid_t pid = fork();
+int fork_and_return(int value, bool use_vfork) {
+ pid_t pid = use_vfork ? vfork() : fork();
if (pid == -1)
return -1;
if (pid == 0) {
// child
- _exit(0);
+ _exit(value);
}
// parent
int status;
waitpid(pid, &status, 0);
- return value;
+ return WEXITSTATUS(status);
}
int main() {
>From 9b5183376e9a63437e52a39d64126962df8f8456 Mon Sep 17 00:00:00 2001
From: Philip DePetro <pdepetro at meta.com>
Date: Wed, 8 Apr 2026 12:45:29 -0700
Subject: [PATCH 4/5] Revert "[lldb] Handle forking in expressions"
This reverts commit ef1fe58c7bc375bb1eb11df926242a82836082d1.
---
lldb/include/lldb/Target/StopInfo.h | 1 -
lldb/source/Target/ThreadPlanCallFunction.cpp | 15 ---------------
2 files changed, 16 deletions(-)
diff --git a/lldb/include/lldb/Target/StopInfo.h b/lldb/include/lldb/Target/StopInfo.h
index 5c1f37edcbd3e..cdd6a6fbe6aa4 100644
--- a/lldb/include/lldb/Target/StopInfo.h
+++ b/lldb/include/lldb/Target/StopInfo.h
@@ -21,7 +21,6 @@ class StopInfo : public std::enable_shared_from_this<StopInfo> {
friend class Process::ProcessEventData;
friend class ThreadPlanBase;
friend class ThreadPlanReverseContinue;
- friend class ThreadPlanCallFunction;
public:
// Constructors and Destructors
diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp
index e555745754aab..218111d4faf60 100644
--- a/lldb/source/Target/ThreadPlanCallFunction.cpp
+++ b/lldb/source/Target/ThreadPlanCallFunction.cpp
@@ -284,21 +284,6 @@ bool ThreadPlanCallFunction::DoPlanExplainsStop(Event *event_ptr) {
if (stop_reason == eStopReasonBreakpoint && BreakpointsExplainStop())
return true;
- if ((stop_reason == eStopReasonFork) ||
- (stop_reason == eStopReasonVFork) ||
- (stop_reason == eStopReasonVForkDone)) {
- if (stop_reason == eStopReasonFork)
- LLDB_LOGF(log, "ThreadPlanCallFunction::PlanExplainsStop hit a fork not stopping.");
- else if (stop_reason == eStopReasonVFork)
- LLDB_LOGF(log, "ThreadPlanCallFunction::PlanExplainsStop hit a vfork not stopping.");
- else if (stop_reason == eStopReasonVForkDone)
- LLDB_LOGF(log, "ThreadPlanCallFunction::PlanExplainsStop hit a vforkdone not stopping.");
-
- m_real_stop_info_sp->PerformAction(event_ptr);
- m_real_stop_info_sp->OverrideShouldStop(false);
- return true;
- }
-
// One more quirk here. If this event was from Halt interrupting the target,
// then we should not consider ourselves complete. Return true to
// acknowledge the stop.
>From fd3438dcb55551ef94a4c97ec927da31ed803137 Mon Sep 17 00:00:00 2001
From: Philip DePetro <pdepetro at meta.com>
Date: Tue, 7 Apr 2026 08:32:27 -0700
Subject: [PATCH 5/5] [lldb] Do not stop if forks occur in expressions
---
lldb/source/Target/StopInfo.cpp | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index 5110ed16edc91..34a4598f131a8 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -1501,6 +1501,16 @@ class StopInfoFork : public StopInfo {
thread_sp->GetProcess()->DidFork(m_child_pid, m_child_tid);
}
+ bool ShouldStopSynchronous(Event *event_ptr) override {
+ if (!m_performed_action) {
+ m_performed_action = true;
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp)
+ thread_sp->GetProcess()->DidFork(m_child_pid, m_child_tid);
+ }
+ return false;
+ }
+
bool m_performed_action = false;
private:
@@ -1541,6 +1551,15 @@ class StopInfoVFork : public StopInfo {
if (thread_sp)
thread_sp->GetProcess()->DidVFork(m_child_pid, m_child_tid);
}
+ bool ShouldStopSynchronous(Event *event_ptr) override {
+ if (!m_performed_action) {
+ m_performed_action = true;
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp)
+ thread_sp->GetProcess()->DidVFork(m_child_pid, m_child_tid);
+ }
+ return false;
+ }
bool m_performed_action = false;
@@ -1573,6 +1592,15 @@ class StopInfoVForkDone : public StopInfo {
if (thread_sp)
thread_sp->GetProcess()->DidVForkDone();
}
+ bool ShouldStopSynchronous(Event *event_ptr) override {
+ if (!m_performed_action) {
+ m_performed_action = true;
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp)
+ thread_sp->GetProcess()->DidVForkDone();
+ }
+ return false;
+ }
bool m_performed_action = false;
};
More information about the lldb-commits
mailing list