https://github.com/pdepetro created https://github.com/llvm/llvm-project/pull/184815
@jimingham You had suggested to @clayborg that we should call DidFork(), DidVFork() and DidVForkDone() after a corresponding stop in expression evaluation. Those functions require parameters that are private to the specific child StopInfo class. I went with reusing the existing StopInfo::PerformAction() virtual function, rather than create a new one just for this purpose. >From ef1fe58c7bc375bb1eb11df926242a82836082d1 Mon Sep 17 00:00:00 2001 From: Philip DePetro <[email protected]> Date: Thu, 19 Feb 2026 07:45:23 -0800 Subject: [PATCH 1/3] [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 <[email protected]> Date: Thu, 19 Feb 2026 09:12:35 -0800 Subject: [PATCH 2/3] 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 <[email protected]> Date: Wed, 4 Mar 2026 12:17:43 -0800 Subject: [PATCH 3/3] 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() { _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
