llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Philip DePetro (pdepetro) <details> <summary>Changes</summary> @<!-- -->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. --- Full diff: https://github.com/llvm/llvm-project/pull/184815.diff 5 Files Affected: - (modified) lldb/include/lldb/Target/StopInfo.h (+1) - (modified) lldb/source/Target/ThreadPlanCallFunction.cpp (+15) - (added) lldb/test/API/commands/expression/expr-with-fork/Makefile (+4) - (added) lldb/test/API/commands/expression/expr-with-fork/TestExprWithFork.py (+49) - (added) lldb/test/API/commands/expression/expr-with-fork/main.cpp (+21) ``````````diff 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. 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..80ee288ee709a --- /dev/null +++ b/lldb/test/API/commands/expression/expr-with-fork/TestExprWithFork.py @@ -0,0 +1,49 @@ +""" +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, 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 new file mode 100644 index 0000000000000..4e210df3d7682 --- /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, bool use_vfork) { + pid_t pid = use_vfork ? vfork() : fork(); + if (pid == -1) + return -1; + if (pid == 0) { + // child + _exit(value); + } + // parent + int status; + waitpid(pid, &status, 0); + return WEXITSTATUS(status); +} + +int main() { + int x = 42; + return 0; // break here +} `````````` </details> https://github.com/llvm/llvm-project/pull/184815 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
