Skip to content

Conversation

@jasonmolenda
Copy link
Collaborator

I will be changing breakpoint hitting behavior soon, where currently lldb reports a breakpoint as being hit when a thread is at a BreakpointSite, but possibly has not executed the breakpoint instruction and trapped yet, to having lldb only report a breakpoint hit when the breakpoint instruction has actually been executed.

One corner case bug with this change is that when you are stopped at a breakpoint (that has been hit) on the last instruction of a function, and you do finish, a ThreadPlanStepOut is pushed to the thread's plan stack to put a breakpoint on the return address and resume execution. And when the thread is asked to resume, it sees that it is at a BreakpointSite that has been hit, and pushes a
ThreadPlanStepOverBreakpoint on the thread. The StepOverBreakpoint
plan sees that the thread's state is eStateRunning (not eStateStepping),
so it marks itself as "auto continue" -- so once the breakpoint has
been stepped over, we will execution on the thread.

With current lldb stepping behavior ("a thread at a BreakpointSite is said to have stopped with a breakpoint-hit stop reason, even if the breakpoint hasn't been executed yet"),
ThreadPlanStepOverBreakpoint::DoPlanExplainsStop has a special bit of code which detects when the thread stops with a eStopReasonBreakpoint. It first checks if the pc is the same as when we started -- did our "step instruction" not actually step? -- says the stop reason is explained. Otherwise it sets auto-continue to false (because we've hit an unexpected breakpoint, and we have advanced past our original pc, and returns false - the stop reason is not explained.

So we do the "finish", lldb instruction steps, we stop at the return-address breakpoint and lldb sets the thread's stop reason to breakpoint-hit. ThreadPlanStepOverBreakpoint sees an eStopReasonBreakpoint, sets its auto-continue to false, and says we stopped for osme reason other than this plan. (and it will also report IsPlanStale()==true so it will remove itself) Meanwhile the ThreadPlanStepOut sees that it has stopped in the StackID it wanted to run to, and return success.

This all changes when stopping at a breakpoint site doesn't report breakpoint-hit until we actually execute the instruction. Now the ThraedPlanStepOverBreakpoint looks at the thread's stop reason, it's eStopReasonTrace (we've instruction stepped), and so it leaves its auto-continue to true. ThreadPlanStepOut sees that it has reached its goal StackID, removes its breakpoint, and says it is done. Thread::ShouldStop thinks the auto-continue == yes vote from ThreadPlanStepOverBreakpoint wins, and we lose control of the process.

This patch changes ThreadPlanStepOut to require that both (1) we are at the StackID of the caller function, where we wanted to end up, and (2) we have actually hit the breakpoint that we inserted.

This in effect means that now lldb instruction-steps over the breakpoint in the callee function, stops at the return address of the caller function. StepOverBreakpoint has completed. StepOut is still running, and we continue the thread again. We immediatley hit the breakpoint (that we're sitting at), and now ThreadPlanStepOut marks itself as completed, and we return control to the user.

Jim suggests that ThreadPlanStepOverBreakpoint is a bit unusual because it's not something pushed on the stack by a higher-order thread plan that "owns" it, it is inserted by the Thread as it is about to resume, if we're at a BreakpointSite. It has no connection to the thread plans above it, but tries to set the auto-continue mode based on the state of the thread when it is inserted (and tries to detect an unexpected breakpoint and unset that auto-continue it previously decided on, because it now realizes it should not influence execution control any more). Instead maybe the
ThreadPlanStepOverBreakpoint should be inserted as a child plan of whatever the lowest plan is on the stack at the point it is added.

I added an API test that will catch this bug in the new thread breakpoint algorithm.

I will be changing breakpoint hitting behavior soon, where currently
lldb reports a breakpoint as being hit when a thread is *at* a
BreakpointSite, but possibly has not executed the breakpoint
instruction and trapped yet, to having lldb only report a breakpoint
hit when the breakpoint instruction has actually been executed.

One corner case bug with this change is that when you are stopped
at a breakpoint (that has been hit) on the last instruction of a
function, and you do `finish`, a ThreadPlanStepOut is pushed to the
thread's plan stack to put a breakpoint on the return address and
resume execution.  And when the thread is asked to resume, it sees
that it is at a BreakpointSite that has been hit, and pushes a
ThreadPlanStepOverBreakpoint on the thread.   The StepOverBreakpoint
plan sees that the thread's state is eStateRunning (not eStateStepping),
so it marks itself as "auto continue" -- so once the breakpoint has
been stepped over, we will execution on the thread.

With current lldb stepping behavior ("a thread *at* a BreakpointSite
is said to have stopped with a breakpoint-hit stop reason, even if
the breakpoint hasn't been executed yet"),
`ThreadPlanStepOverBreakpoint::DoPlanExplainsStop` has a special
bit of code which detects when the thread stops with a
eStopReasonBreakpoint.  It first checks if the pc is the same as
when we started -- did our "step instruction" not actually step?
-- says the stop reason is explained.  Otherwise it sets auto-continue
to false (because we've hit an *unexpected* breakpoint, and we have
advanced past our original pc, and returns false - the stop reason
is not explained.

So we do the "finish", lldb instruction steps, we stop *at* the
return-address breakpoint and lldb sets the thread's stop reason
to breakpoint-hit.  ThreadPlanStepOverBreakpoint sees an
eStopReasonBreakpoint, sets its auto-continue to false, and says
we stopped for osme reason other than this plan.  (and it will also
report `IsPlanStale()==true` so it will remove itself)  Meanwhile
the ThreadPlanStepOut sees that it has stopped in the StackID it
wanted to run to, and return success.

This all changes when stopping at a breakpoint site doesn't report
breakpoint-hit until we actually execute the instruction.  Now the
ThraedPlanStepOverBreakpoint looks at the thread's stop reason,
it's eStopReasonTrace (we've instruction stepped), and so it leaves
its auto-continue to `true`.  ThreadPlanStepOut sees that it has
reached its goal StackID, removes its breakpoint, and says it is done.
Thread::ShouldStop thinks the auto-continue == yes vote from
ThreadPlanStepOverBreakpoint wins, and we lose control of the process.

This patch changes ThreadPlanStepOut to require that *both* (1)
we are at the StackID of the caller function, where we wanted to
end up, and (2) we have actually hit the breakpoint that we inserted.

This in effect means that now lldb instruction-steps over the breakpoint
in the callee function, stops at the return address of the caller
function.  StepOverBreakpoint has completed.  StepOut is still running,
and we continue the thread again.  We immediatley hit the breakpoint
(that we're sitting at), and now ThreadPlanStepOut marks itself as
completed, and we return control to the user.

Jim suggests that ThreadPlanStepOverBreakpoint is a bit unusual
because it's not something pushed on the stack by a higher-order
thread plan that "owns" it, it is inserted by the Thread as it is
about to resume, if we're at a BreakpointSite.  It has no connection
to the thread plans above it, but tries to set the auto-continue
mode based on the state of the thread when it is inserted (and tries
to detect an unexpected breakpoint and unset that auto-continue it
previously decided on, because it now realizes it should not influence
execution control any more).  Instead maybe the
ThreadPlanStepOverBreakpoint should be inserted as a child plan of
whatever the lowest plan is on the stack at the point it is added.

I added an API test that will catch this bug in the new thread
breakpoint algorithm.
@llvmbot
Copy link
Member

llvmbot commented Feb 12, 2025

@llvm/pr-subscribers-lldb

Author: Jason Molenda (jasonmolenda)

Changes

I will be changing breakpoint hitting behavior soon, where currently lldb reports a breakpoint as being hit when a thread is at a BreakpointSite, but possibly has not executed the breakpoint instruction and trapped yet, to having lldb only report a breakpoint hit when the breakpoint instruction has actually been executed.

One corner case bug with this change is that when you are stopped at a breakpoint (that has been hit) on the last instruction of a function, and you do finish, a ThreadPlanStepOut is pushed to the thread's plan stack to put a breakpoint on the return address and resume execution. And when the thread is asked to resume, it sees that it is at a BreakpointSite that has been hit, and pushes a
ThreadPlanStepOverBreakpoint on the thread. The StepOverBreakpoint
plan sees that the thread's state is eStateRunning (not eStateStepping),
so it marks itself as "auto continue" -- so once the breakpoint has
been stepped over, we will execution on the thread.

With current lldb stepping behavior ("a thread at a BreakpointSite is said to have stopped with a breakpoint-hit stop reason, even if the breakpoint hasn't been executed yet"),
ThreadPlanStepOverBreakpoint::DoPlanExplainsStop has a special bit of code which detects when the thread stops with a eStopReasonBreakpoint. It first checks if the pc is the same as when we started -- did our "step instruction" not actually step? -- says the stop reason is explained. Otherwise it sets auto-continue to false (because we've hit an unexpected breakpoint, and we have advanced past our original pc, and returns false - the stop reason is not explained.

So we do the "finish", lldb instruction steps, we stop at the return-address breakpoint and lldb sets the thread's stop reason to breakpoint-hit. ThreadPlanStepOverBreakpoint sees an eStopReasonBreakpoint, sets its auto-continue to false, and says we stopped for osme reason other than this plan. (and it will also report IsPlanStale()==true so it will remove itself) Meanwhile the ThreadPlanStepOut sees that it has stopped in the StackID it wanted to run to, and return success.

This all changes when stopping at a breakpoint site doesn't report breakpoint-hit until we actually execute the instruction. Now the ThraedPlanStepOverBreakpoint looks at the thread's stop reason, it's eStopReasonTrace (we've instruction stepped), and so it leaves its auto-continue to true. ThreadPlanStepOut sees that it has reached its goal StackID, removes its breakpoint, and says it is done. Thread::ShouldStop thinks the auto-continue == yes vote from ThreadPlanStepOverBreakpoint wins, and we lose control of the process.

This patch changes ThreadPlanStepOut to require that both (1) we are at the StackID of the caller function, where we wanted to end up, and (2) we have actually hit the breakpoint that we inserted.

This in effect means that now lldb instruction-steps over the breakpoint in the callee function, stops at the return address of the caller function. StepOverBreakpoint has completed. StepOut is still running, and we continue the thread again. We immediatley hit the breakpoint (that we're sitting at), and now ThreadPlanStepOut marks itself as completed, and we return control to the user.

Jim suggests that ThreadPlanStepOverBreakpoint is a bit unusual because it's not something pushed on the stack by a higher-order thread plan that "owns" it, it is inserted by the Thread as it is about to resume, if we're at a BreakpointSite. It has no connection to the thread plans above it, but tries to set the auto-continue mode based on the state of the thread when it is inserted (and tries to detect an unexpected breakpoint and unset that auto-continue it previously decided on, because it now realizes it should not influence execution control any more). Instead maybe the
ThreadPlanStepOverBreakpoint should be inserted as a child plan of whatever the lowest plan is on the stack at the point it is added.

I added an API test that will catch this bug in the new thread breakpoint algorithm.


Full diff: https://github.com/llvm/llvm-project/pull/126838.diff

4 Files Affected:

  • (modified) lldb/source/Target/ThreadPlanStepOut.cpp (+6-2)
  • (added) lldb/test/API/functionalities/thread/finish-from-empty-func/Makefile (+3)
  • (added) lldb/test/API/functionalities/thread/finish-from-empty-func/TestEmptyFuncThreadStepOut.py (+43)
  • (added) lldb/test/API/functionalities/thread/finish-from-empty-func/main.c (+8)
diff --git a/lldb/source/Target/ThreadPlanStepOut.cpp b/lldb/source/Target/ThreadPlanStepOut.cpp
index c0ea53e4a8cbb..2376b52cfc03a 100644
--- a/lldb/source/Target/ThreadPlanStepOut.cpp
+++ b/lldb/source/Target/ThreadPlanStepOut.cpp
@@ -364,8 +364,12 @@ bool ThreadPlanStepOut::ShouldStop(Event *event_ptr) {
   }
 
   if (!done) {
-    StackID frame_zero_id = GetThread().GetStackFrameAtIndex(0)->GetStackID();
-    done = !(frame_zero_id < m_step_out_to_id);
+    StopInfoSP stop_info_sp = GetPrivateStopInfo();
+    if (stop_info_sp.get() &&
+        stop_info_sp->GetStopReason() == eStopReasonBreakpoint) {
+      StackID frame_zero_id = GetThread().GetStackFrameAtIndex(0)->GetStackID();
+      done = !(frame_zero_id < m_step_out_to_id);
+    }
   }
 
   // The normal step out computations think we are done, so all we need to do
diff --git a/lldb/test/API/functionalities/thread/finish-from-empty-func/Makefile b/lldb/test/API/functionalities/thread/finish-from-empty-func/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/functionalities/thread/finish-from-empty-func/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/thread/finish-from-empty-func/TestEmptyFuncThreadStepOut.py b/lldb/test/API/functionalities/thread/finish-from-empty-func/TestEmptyFuncThreadStepOut.py
new file mode 100644
index 0000000000000..bf57070e336e7
--- /dev/null
+++ b/lldb/test/API/functionalities/thread/finish-from-empty-func/TestEmptyFuncThreadStepOut.py
@@ -0,0 +1,43 @@
+"""
+Test finish out of an empty function (may be one-instruction long)
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class FinishFromEmptyFunctionTestCase(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def test_finish_from_empty_function(self):
+        """Test that when stopped at a breakpoint in an empty function, finish leaves it correctly."""
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+        target, process, thread, _ = lldbutil.run_to_name_breakpoint(
+            self, "done", exe_name=exe
+        )
+        if self.TraceOn():
+            self.runCmd("bt")
+
+        correct_stepped_out_line = line_number("main.c", "leaving main")
+        return_statement_line = line_number("main.c", "return 0")
+        safety_bp = target.BreakpointCreateByLocation(
+            lldb.SBFileSpec("main.c"), return_statement_line
+        )
+        self.assertTrue(safety_bp.IsValid())
+
+        error = lldb.SBError()
+        thread.StepOut(error)
+        self.assertTrue(error.Success())
+
+        if self.TraceOn():
+            self.runCmd("bt")
+
+        frame = thread.GetSelectedFrame()
+        self.assertEqual(
+            frame.line_entry.GetLine(),
+            correct_stepped_out_line,
+            "Step-out lost control of execution, ran too far",
+        )
diff --git a/lldb/test/API/functionalities/thread/finish-from-empty-func/main.c b/lldb/test/API/functionalities/thread/finish-from-empty-func/main.c
new file mode 100644
index 0000000000000..bc66a548a89df
--- /dev/null
+++ b/lldb/test/API/functionalities/thread/finish-from-empty-func/main.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+void done() {}
+int main() {
+  puts("in main");
+  done();
+  puts("leaving main");
+  return 0;
+}

@jasonmolenda
Copy link
Collaborator Author

This was the last of the issues that were hit (found via a test by @mstorsjo ) for this PR from July 2024 #96260

I've been iterating on that PR to address all of the issues, but have landed all of the fixes as separate PRs on llvm-project main, I've been using #105594 to track those fixes (and I developed them all there as I was working on it)

I'll probably create a clean PR (which is basically just a rebasing of the July 2024 patch) once this has landed and all known issues have been addressed on llvm-project main.

Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

StackID frame_zero_id = GetThread().GetStackFrameAtIndex(0)->GetStackID();
done = !(frame_zero_id < m_step_out_to_id);
StopInfoSP stop_info_sp = GetPrivateStopInfo();
if (stop_info_sp.get() &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (stop_info_sp.get() &&
if (stop_info_sp &&

@jasonmolenda jasonmolenda merged commit cbb4e99 into llvm:main Feb 12, 2025
4 of 5 checks passed
@jasonmolenda jasonmolenda deleted the thread-step-out-work-correctly-with-new-breakpoint-behavior branch February 12, 2025 21:48
@llvm-ci
Copy link
Collaborator

llvm-ci commented Feb 12, 2025

LLVM Buildbot has detected a new failure on builder cross-project-tests-sie-ubuntu running on doug-worker-1a while building lldb at step 5 "build-unified-tree".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/181/builds/13554

Here is the relevant piece of the build log for the reference
Step 5 (build-unified-tree) failure: build (failure)
...
3.616 [105/8/16] Linking CXX executable bin/llvm-debuginfo-analyzer
3.626 [104/8/17] Linking CXX executable bin/llvm-ctxprof-util
3.740 [103/8/18] Linking CXX executable bin/lli-child-target
3.802 [102/8/19] Linking CXX executable bin/llvm-ar
3.805 [101/8/20] Generating ../../bin/llvm-lib
3.808 [100/8/21] Generating ../../bin/llvm-ranlib
3.812 [99/8/22] Generating ../../bin/llvm-dlltool
3.860 [98/8/23] Linking CXX executable bin/llvm-profdata
3.957 [97/8/24] Linking CXX executable bin/apinotes-test
5.170 [96/8/25] Building CXX object tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o
FAILED: tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o 
/opt/ccache/bin/g++ -DGTEST_HAS_RTTI=0 -DHAVE_ROUND -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/build/tools/lldb/source/Target -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/llvm-project/lldb/source/Target -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/llvm-project/lldb/include -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/build/tools/lldb/include -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/build/include -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/llvm-project/llvm/include -I/usr/include/python3.8 -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/llvm-project/llvm/../clang/include -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/build/tools/lldb/../clang/include -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/llvm-project/lldb/source -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/build/tools/lldb/source -isystem /usr/include/libxml2 -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -fno-lifetime-dse -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-uninitialized -Wno-nonnull -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -fdiagnostics-color -ffunction-sections -fdata-sections -Wno-unknown-pragmas -Wno-strict-aliasing -Wno-stringop-truncation -O3 -DNDEBUG  -fno-exceptions -funwind-tables -fno-rtti -UNDEBUG -std=c++17 -MD -MT tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o -MF tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o.d -o tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o -c /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp
/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp: In member function ‘virtual bool lldb_private::ThreadPlanStepOut::ShouldStop(lldb_private::Event*)’:
/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp:368:9: error: ‘stop_info’ was not declared in this scope; did you mean ‘stop_info_sp’?
  368 |     if (stop_info && stop_info_sp->GetStopReason() == eStopReasonBreakpoint) {
      |         ^~~~~~~~~
      |         stop_info_sp
5.170 [96/7/26] Linking CXX shared module lib/CheckerDependencyHandlingAnalyzerPlugin.so
5.184 [96/6/27] Linking CXX shared module lib/SampleAnalyzerPlugin.so
5.215 [96/5/28] Linking CXX shared module lib/CheckerOptionHandlingAnalyzerPlugin.so
5.280 [96/4/29] Linking CXX executable bin/diagtool
5.881 [96/3/30] Linking CXX executable bin/clang-diff
14.483 [96/2/31] Building CXX object lib/CodeGen/AsmPrinter/CMakeFiles/LLVMAsmPrinter.dir/AsmPrinter.cpp.o
15.240 [96/1/32] Building CXX object lib/LTO/CMakeFiles/LLVMLTO.dir/LTO.cpp.o
ninja: build stopped: subcommand failed.

@llvm-ci
Copy link
Collaborator

llvm-ci commented Feb 12, 2025

LLVM Buildbot has detected a new failure on builder cross-project-tests-sie-ubuntu-dwarf5 running on doug-worker-1b while building lldb at step 5 "build-unified-tree".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/163/builds/13395

Here is the relevant piece of the build log for the reference
Step 5 (build-unified-tree) failure: build (failure)
...
5.772 [102/8/19] Linking CXX executable bin/llvm-ar
5.788 [101/8/20] Generating ../../bin/llvm-dlltool
5.804 [100/8/21] Generating ../../bin/llvm-ranlib
5.820 [99/8/22] Generating ../../bin/llvm-lib
5.856 [98/8/23] Linking CXX executable bin/apinotes-test
5.921 [97/8/24] Linking CXX executable bin/llvm-profdata
5.944 [96/8/25] Linking CXX executable bin/amdgpu-arch
6.182 [95/8/26] Linking CXX executable bin/llvm-cat
6.199 [94/8/27] Linking CXX executable bin/llvm-as
7.466 [93/8/28] Building CXX object tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o
FAILED: tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o 
/opt/ccache/bin/g++ -DGTEST_HAS_RTTI=0 -DHAVE_ROUND -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/tools/lldb/source/Target -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/lldb/source/Target -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/lldb/include -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/tools/lldb/include -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/include -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/llvm/include -I/usr/include/python3.10 -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/llvm/../clang/include -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/tools/lldb/../clang/include -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/lldb/source -I/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/build/tools/lldb/source -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -fno-lifetime-dse -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-uninitialized -Wno-nonnull -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Wno-unknown-pragmas -Wno-strict-aliasing -Wno-stringop-truncation -O3 -DNDEBUG  -fno-exceptions -funwind-tables -fno-rtti -UNDEBUG -std=c++17 -MD -MT tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o -MF tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o.d -o tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o -c /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp
/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp: In member function ‘virtual bool lldb_private::ThreadPlanStepOut::ShouldStop(lldb_private::Event*)’:
/home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp:368:9: error: ‘stop_info’ was not declared in this scope; did you mean ‘stop_info_sp’?
  368 |     if (stop_info && stop_info_sp->GetStopReason() == eStopReasonBreakpoint) {
      |         ^~~~~~~~~
      |         stop_info_sp
7.844 [93/7/29] Linking CXX shared module lib/CheckerDependencyHandlingAnalyzerPlugin.so
8.060 [93/6/30] Linking CXX shared module lib/SampleAnalyzerPlugin.so
8.133 [93/5/31] Linking CXX shared module lib/CheckerOptionHandlingAnalyzerPlugin.so
8.248 [93/4/32] Linking CXX executable bin/diagtool
8.564 [93/3/33] Linking CXX executable bin/clang-refactor
20.987 [93/2/34] Building CXX object lib/CodeGen/AsmPrinter/CMakeFiles/LLVMAsmPrinter.dir/AsmPrinter.cpp.o
21.708 [93/1/35] Building CXX object lib/LTO/CMakeFiles/LLVMLTO.dir/LTO.cpp.o
ninja: build stopped: subcommand failed.

@llvm-ci
Copy link
Collaborator

llvm-ci commented Feb 12, 2025

LLVM Buildbot has detected a new failure on builder lldb-x86_64-debian running on lldb-x86_64-debian while building lldb at step 4 "build".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/162/builds/16093

Here is the relevant piece of the build log for the reference
Step 4 (build) failure: build (failure)
...
4.320 [55/21/60] Linking CXX executable bin/llvm-objdump
4.323 [54/21/61] Linking CXX executable bin/llvm-mca
4.334 [54/20/62] Generating ../../bin/llvm-otool
4.354 [54/19/63] Linking CXX executable bin/llvm-ar
4.358 [51/21/64] Linking CXX executable bin/llvm-mc
4.367 [51/20/65] Generating ../../bin/llvm-ranlib
4.367 [51/19/66] Generating ../../bin/llvm-lib
4.367 [51/18/67] Generating ../../bin/llvm-dlltool
4.432 [51/17/68] Linking CXX executable bin/llvm-nm
4.437 [51/16/69] Building CXX object tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o
FAILED: tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o 
/usr/bin/clang++ -DGTEST_HAS_RTTI=0 -DHAVE_ROUND -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/home/worker/2.0.1/lldb-x86_64-debian/build/tools/lldb/source/Target -I/home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/source/Target -I/home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/include -I/home/worker/2.0.1/lldb-x86_64-debian/build/tools/lldb/include -I/home/worker/2.0.1/lldb-x86_64-debian/build/include -I/home/worker/2.0.1/lldb-x86_64-debian/llvm-project/llvm/include -I/usr/include/python3.11 -I/home/worker/2.0.1/lldb-x86_64-debian/llvm-project/llvm/../clang/include -I/home/worker/2.0.1/lldb-x86_64-debian/build/tools/lldb/../clang/include -I/home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/source -I/home/worker/2.0.1/lldb-x86_64-debian/build/tools/lldb/source -isystem /usr/include/libxml2 -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Wno-unknown-pragmas -Wno-strict-aliasing -Wno-vla-extension -O3 -DNDEBUG  -fno-exceptions -funwind-tables -fno-rtti -UNDEBUG -std=c++17 -MD -MT tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o -MF tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o.d -o tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o -c /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp
/home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp:368:9: error: use of undeclared identifier 'stop_info'; did you mean 'stop_info_sp'?
    if (stop_info && stop_info_sp->GetStopReason() == eStopReasonBreakpoint) {
        ^~~~~~~~~
        stop_info_sp
/home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp:367:16: note: 'stop_info_sp' declared here
    StopInfoSP stop_info_sp = GetPrivateStopInfo();
               ^
1 error generated.
4.437 [51/15/70] Linking CXX executable bin/sancov
4.443 [51/14/71] Linking CXX executable bin/llvm-profgen
4.469 [51/13/72] Linking CXX executable bin/llvm-debuginfo-analyzer
4.492 [51/12/73] Linking CXX executable bin/llvm-cfi-verify
4.567 [51/11/74] Linking CXX executable bin/llvm-extract
4.578 [51/10/75] Linking CXX executable bin/llvm-jitlink
4.696 [51/9/76] Linking CXX shared module lib/CheckerOptionHandlingAnalyzerPlugin.so
4.725 [51/8/77] Linking CXX executable bin/diagtool
4.751 [51/7/78] Linking CXX shared module lib/CheckerDependencyHandlingAnalyzerPlugin.so
4.844 [51/6/79] Linking CXX shared module lib/SampleAnalyzerPlugin.so
5.191 [51/5/80] Linking CXX executable bin/clang-diff
5.191 [51/4/81] Linking CXX executable bin/clang-refactor
5.245 [51/3/82] Linking CXX executable bin/clang-installapi
11.240 [51/2/83] Building CXX object lib/CodeGen/AsmPrinter/CMakeFiles/LLVMAsmPrinter.dir/AsmPrinter.cpp.o
12.560 [51/1/84] Building CXX object lib/LTO/CMakeFiles/LLVMLTO.dir/LTO.cpp.o
ninja: build stopped: subcommand failed.

@llvm-ci
Copy link
Collaborator

llvm-ci commented Feb 13, 2025

LLVM Buildbot has detected a new failure on builder llvm-x86_64-debian-dylib running on gribozavr4 while building lldb at step 5 "build-unified-tree".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/60/builds/19470

Here is the relevant piece of the build log for the reference
Step 5 (build-unified-tree) failure: build (failure)
...
72.530 [121/8/7015] Building CXX object tools/lldb/source/Plugins/ExpressionParser/Clang/CMakeFiles/lldbPluginExpressionParserClang.dir/ClangModulesDeclVendor.cpp.o
72.533 [121/7/7016] Building CXX object tools/lldb/source/Plugins/ExpressionParser/Clang/CMakeFiles/lldbPluginExpressionParserClang.dir/ClangExpressionParser.cpp.o
72.832 [121/6/7017] Linking CXX executable bin/clang-tidy
72.930 [121/5/7018] Building CXX object tools/lldb/source/Version/CMakeFiles/lldbVersion.dir/Version.cpp.o
72.934 [120/5/7019] Linking CXX static library lib/liblldbVersion.a
72.938 [119/5/7020] Linking CXX executable bin/clangd-indexer
72.942 [119/4/7021] Linking CXX static library lib/liblldbValueObject.a
73.302 [119/3/7022] Linking CXX executable bin/clangd-fuzzer
73.361 [119/2/7023] Linking CXX executable bin/clangd
76.165 [119/1/7024] Building CXX object tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o
FAILED: tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o 
CCACHE_CPP2=yes CCACHE_HASHDIR=yes /usr/bin/ccache /usr/bin/clang++ -DGTEST_HAS_RTTI=0 -DHAVE_ROUND -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/b/1/llvm-x86_64-debian-dylib/build/tools/lldb/source/Target -I/b/1/llvm-x86_64-debian-dylib/llvm-project/lldb/source/Target -I/b/1/llvm-x86_64-debian-dylib/llvm-project/lldb/include -I/b/1/llvm-x86_64-debian-dylib/build/tools/lldb/include -I/b/1/llvm-x86_64-debian-dylib/build/include -I/b/1/llvm-x86_64-debian-dylib/llvm-project/llvm/include -I/b/1/llvm-x86_64-debian-dylib/llvm-project/llvm/../clang/include -I/b/1/llvm-x86_64-debian-dylib/build/tools/lldb/../clang/include -I/b/1/llvm-x86_64-debian-dylib/llvm-project/lldb/source -I/b/1/llvm-x86_64-debian-dylib/build/tools/lldb/source -isystem /usr/include/libxml2 -fPIC -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Wno-unknown-pragmas -Wno-strict-aliasing -Wno-vla-extension -O3 -DNDEBUG  -fno-exceptions -funwind-tables -fno-rtti -UNDEBUG -std=c++17 -MD -MT tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o -MF tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o.d -o tools/lldb/source/Target/CMakeFiles/lldbTarget.dir/ThreadPlanStepOut.cpp.o -c /b/1/llvm-x86_64-debian-dylib/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp
/b/1/llvm-x86_64-debian-dylib/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp:368:9: error: use of undeclared identifier 'stop_info'; did you mean 'stop_info_sp'?
    if (stop_info && stop_info_sp->GetStopReason() == eStopReasonBreakpoint) {
        ^~~~~~~~~
        stop_info_sp
/b/1/llvm-x86_64-debian-dylib/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp:367:16: note: 'stop_info_sp' declared here
    StopInfoSP stop_info_sp = GetPrivateStopInfo();
               ^
1 error generated.
ninja: build stopped: subcommand failed.

flovent pushed a commit to flovent/llvm-project that referenced this pull request Feb 13, 2025
…vm#126838)

I will be changing breakpoint hitting behavior soon, where currently
lldb reports a breakpoint as being hit when a thread is *at* a
BreakpointSite, but possibly has not executed the breakpoint instruction
and trapped yet, to having lldb only report a breakpoint hit when the
breakpoint instruction has actually been executed.

One corner case bug with this change is that when you are stopped at a
breakpoint (that has been hit) on the last instruction of a function,
and you do `finish`, a ThreadPlanStepOut is pushed to the thread's plan
stack to put a breakpoint on the return address and resume execution.
And when the thread is asked to resume, it sees that it is at a
BreakpointSite that has been hit, and pushes a
ThreadPlanStepOverBreakpoint on the thread.   The StepOverBreakpoint
plan sees that the thread's state is eStateRunning (not eStateStepping),
so it marks itself as "auto continue" -- so once the breakpoint has
been stepped over, we will execution on the thread.

With current lldb stepping behavior ("a thread *at* a BreakpointSite is
said to have stopped with a breakpoint-hit stop reason, even if the
breakpoint hasn't been executed yet"),
`ThreadPlanStepOverBreakpoint::DoPlanExplainsStop` has a special bit of
code which detects when the thread stops with a eStopReasonBreakpoint.
It first checks if the pc is the same as when we started -- did our
"step instruction" not actually step? -- says the stop reason is
explained. Otherwise it sets auto-continue to false (because we've hit
an *unexpected* breakpoint, and we have advanced past our original pc,
and returns false - the stop reason is not explained.

So we do the "finish", lldb instruction steps, we stop *at* the
return-address breakpoint and lldb sets the thread's stop reason to
breakpoint-hit. ThreadPlanStepOverBreakpoint sees an
eStopReasonBreakpoint, sets its auto-continue to false, and says we
stopped for osme reason other than this plan. (and it will also report
`IsPlanStale()==true` so it will remove itself) Meanwhile the
ThreadPlanStepOut sees that it has stopped in the StackID it wanted to
run to, and return success.

This all changes when stopping at a breakpoint site doesn't report
breakpoint-hit until we actually execute the instruction. Now the
ThraedPlanStepOverBreakpoint looks at the thread's stop reason, it's
eStopReasonTrace (we've instruction stepped), and so it leaves its
auto-continue to `true`. ThreadPlanStepOut sees that it has reached its
goal StackID, removes its breakpoint, and says it is done.
Thread::ShouldStop thinks the auto-continue == yes vote from
ThreadPlanStepOverBreakpoint wins, and we lose control of the process.

This patch changes ThreadPlanStepOut to require that *both* (1) we are
at the StackID of the caller function, where we wanted to end up, and
(2) we have actually hit the breakpoint that we inserted.

This in effect means that now lldb instruction-steps over the breakpoint
in the callee function, stops at the return address of the caller
function. StepOverBreakpoint has completed. StepOut is still running,
and we continue the thread again. We immediatley hit the breakpoint
(that we're sitting at), and now ThreadPlanStepOut marks itself as
completed, and we return control to the user.

Jim suggests that ThreadPlanStepOverBreakpoint is a bit unusual because
it's not something pushed on the stack by a higher-order thread plan
that "owns" it, it is inserted by the Thread as it is about to resume,
if we're at a BreakpointSite. It has no connection to the thread plans
above it, but tries to set the auto-continue mode based on the state of
the thread when it is inserted (and tries to detect an unexpected
breakpoint and unset that auto-continue it previously decided on,
because it now realizes it should not influence execution control any
more). Instead maybe the
ThreadPlanStepOverBreakpoint should be inserted as a child plan of
whatever the lowest plan is on the stack at the point it is added.

I added an API test that will catch this bug in the new thread
breakpoint algorithm.
joaosaffran pushed a commit to joaosaffran/llvm-project that referenced this pull request Feb 14, 2025
…vm#126838)

I will be changing breakpoint hitting behavior soon, where currently
lldb reports a breakpoint as being hit when a thread is *at* a
BreakpointSite, but possibly has not executed the breakpoint instruction
and trapped yet, to having lldb only report a breakpoint hit when the
breakpoint instruction has actually been executed.

One corner case bug with this change is that when you are stopped at a
breakpoint (that has been hit) on the last instruction of a function,
and you do `finish`, a ThreadPlanStepOut is pushed to the thread's plan
stack to put a breakpoint on the return address and resume execution.
And when the thread is asked to resume, it sees that it is at a
BreakpointSite that has been hit, and pushes a
ThreadPlanStepOverBreakpoint on the thread.   The StepOverBreakpoint
plan sees that the thread's state is eStateRunning (not eStateStepping),
so it marks itself as "auto continue" -- so once the breakpoint has
been stepped over, we will execution on the thread.

With current lldb stepping behavior ("a thread *at* a BreakpointSite is
said to have stopped with a breakpoint-hit stop reason, even if the
breakpoint hasn't been executed yet"),
`ThreadPlanStepOverBreakpoint::DoPlanExplainsStop` has a special bit of
code which detects when the thread stops with a eStopReasonBreakpoint.
It first checks if the pc is the same as when we started -- did our
"step instruction" not actually step? -- says the stop reason is
explained. Otherwise it sets auto-continue to false (because we've hit
an *unexpected* breakpoint, and we have advanced past our original pc,
and returns false - the stop reason is not explained.

So we do the "finish", lldb instruction steps, we stop *at* the
return-address breakpoint and lldb sets the thread's stop reason to
breakpoint-hit. ThreadPlanStepOverBreakpoint sees an
eStopReasonBreakpoint, sets its auto-continue to false, and says we
stopped for osme reason other than this plan. (and it will also report
`IsPlanStale()==true` so it will remove itself) Meanwhile the
ThreadPlanStepOut sees that it has stopped in the StackID it wanted to
run to, and return success.

This all changes when stopping at a breakpoint site doesn't report
breakpoint-hit until we actually execute the instruction. Now the
ThraedPlanStepOverBreakpoint looks at the thread's stop reason, it's
eStopReasonTrace (we've instruction stepped), and so it leaves its
auto-continue to `true`. ThreadPlanStepOut sees that it has reached its
goal StackID, removes its breakpoint, and says it is done.
Thread::ShouldStop thinks the auto-continue == yes vote from
ThreadPlanStepOverBreakpoint wins, and we lose control of the process.

This patch changes ThreadPlanStepOut to require that *both* (1) we are
at the StackID of the caller function, where we wanted to end up, and
(2) we have actually hit the breakpoint that we inserted.

This in effect means that now lldb instruction-steps over the breakpoint
in the callee function, stops at the return address of the caller
function. StepOverBreakpoint has completed. StepOut is still running,
and we continue the thread again. We immediatley hit the breakpoint
(that we're sitting at), and now ThreadPlanStepOut marks itself as
completed, and we return control to the user.

Jim suggests that ThreadPlanStepOverBreakpoint is a bit unusual because
it's not something pushed on the stack by a higher-order thread plan
that "owns" it, it is inserted by the Thread as it is about to resume,
if we're at a BreakpointSite. It has no connection to the thread plans
above it, but tries to set the auto-continue mode based on the state of
the thread when it is inserted (and tries to detect an unexpected
breakpoint and unset that auto-continue it previously decided on,
because it now realizes it should not influence execution control any
more). Instead maybe the
ThreadPlanStepOverBreakpoint should be inserted as a child plan of
whatever the lowest plan is on the stack at the point it is added.

I added an API test that will catch this bug in the new thread
breakpoint algorithm.
sivan-shani pushed a commit to sivan-shani/llvm-project that referenced this pull request Feb 24, 2025
…vm#126838)

I will be changing breakpoint hitting behavior soon, where currently
lldb reports a breakpoint as being hit when a thread is *at* a
BreakpointSite, but possibly has not executed the breakpoint instruction
and trapped yet, to having lldb only report a breakpoint hit when the
breakpoint instruction has actually been executed.

One corner case bug with this change is that when you are stopped at a
breakpoint (that has been hit) on the last instruction of a function,
and you do `finish`, a ThreadPlanStepOut is pushed to the thread's plan
stack to put a breakpoint on the return address and resume execution.
And when the thread is asked to resume, it sees that it is at a
BreakpointSite that has been hit, and pushes a
ThreadPlanStepOverBreakpoint on the thread.   The StepOverBreakpoint
plan sees that the thread's state is eStateRunning (not eStateStepping),
so it marks itself as "auto continue" -- so once the breakpoint has
been stepped over, we will execution on the thread.

With current lldb stepping behavior ("a thread *at* a BreakpointSite is
said to have stopped with a breakpoint-hit stop reason, even if the
breakpoint hasn't been executed yet"),
`ThreadPlanStepOverBreakpoint::DoPlanExplainsStop` has a special bit of
code which detects when the thread stops with a eStopReasonBreakpoint.
It first checks if the pc is the same as when we started -- did our
"step instruction" not actually step? -- says the stop reason is
explained. Otherwise it sets auto-continue to false (because we've hit
an *unexpected* breakpoint, and we have advanced past our original pc,
and returns false - the stop reason is not explained.

So we do the "finish", lldb instruction steps, we stop *at* the
return-address breakpoint and lldb sets the thread's stop reason to
breakpoint-hit. ThreadPlanStepOverBreakpoint sees an
eStopReasonBreakpoint, sets its auto-continue to false, and says we
stopped for osme reason other than this plan. (and it will also report
`IsPlanStale()==true` so it will remove itself) Meanwhile the
ThreadPlanStepOut sees that it has stopped in the StackID it wanted to
run to, and return success.

This all changes when stopping at a breakpoint site doesn't report
breakpoint-hit until we actually execute the instruction. Now the
ThraedPlanStepOverBreakpoint looks at the thread's stop reason, it's
eStopReasonTrace (we've instruction stepped), and so it leaves its
auto-continue to `true`. ThreadPlanStepOut sees that it has reached its
goal StackID, removes its breakpoint, and says it is done.
Thread::ShouldStop thinks the auto-continue == yes vote from
ThreadPlanStepOverBreakpoint wins, and we lose control of the process.

This patch changes ThreadPlanStepOut to require that *both* (1) we are
at the StackID of the caller function, where we wanted to end up, and
(2) we have actually hit the breakpoint that we inserted.

This in effect means that now lldb instruction-steps over the breakpoint
in the callee function, stops at the return address of the caller
function. StepOverBreakpoint has completed. StepOut is still running,
and we continue the thread again. We immediatley hit the breakpoint
(that we're sitting at), and now ThreadPlanStepOut marks itself as
completed, and we return control to the user.

Jim suggests that ThreadPlanStepOverBreakpoint is a bit unusual because
it's not something pushed on the stack by a higher-order thread plan
that "owns" it, it is inserted by the Thread as it is about to resume,
if we're at a BreakpointSite. It has no connection to the thread plans
above it, but tries to set the auto-continue mode based on the state of
the thread when it is inserted (and tries to detect an unexpected
breakpoint and unset that auto-continue it previously decided on,
because it now realizes it should not influence execution control any
more). Instead maybe the
ThreadPlanStepOverBreakpoint should be inserted as a child plan of
whatever the lowest plan is on the stack at the point it is added.

I added an API test that will catch this bug in the new thread
breakpoint algorithm.
igorkudrin added a commit to igorkudrin/llvm-project that referenced this pull request Oct 3, 2025
The test did not work as intended when the empty function 'done()'
contained epilog code, because a breakpoint was set on the first
instruction of the epilog instead of on the last instruction of the
function. This caused the test to pass even with the fix from llvm#126838
reverted.
igorkudrin added a commit to igorkudrin/llvm-project that referenced this pull request Oct 5, 2025
When a thread reaches a breakpoint at the return address set by
`ThreadPlanStepOut`, `ThreadPlanStepOut::ShouldStop()` calls
`ThreadPlanShouldStopHere::InvokeShouldStopHereCallback()`, and if it
returns `false`, `ThreadPlanShouldStopHere::QueueStepOutFromHerePlan()`
is called to queue a new plan to skip the corresponding range. Once the
new plan finishes, `ThreadPlanStepOut::ShouldStop()` should recheck the
stop condition; however, there is no branch that sets `done` to `true`.
Before llvm#126838, the method checked if it had reached a suitable frame.
After the patch, the check is only performed at a breakpoint; thus, the
execution continues.

This patch causes `ThreadPlanStepOut::ShouldStop()` to recheck the stop
condition when `m_step_out_further_plan_sp` completes.
igorkudrin added a commit that referenced this pull request Oct 8, 2025
…at (#161982)

When a thread reaches a breakpoint at the return address set by
`ThreadPlanStepOut`, `ThreadPlanStepOut::ShouldStop()` calls
`ThreadPlanShouldStopHere::InvokeShouldStopHereCallback()`, and if it
returns `false`, `ThreadPlanShouldStopHere::QueueStepOutFromHerePlan()`
is called to queue a new plan to skip the corresponding range. Once the
new plan finishes, `ThreadPlanStepOut::ShouldStop()` should recheck the
stop condition; however, there is no code path in the method that sets
`done` to `true`. Before #126838, if `done` was `false`, the method checked
if a suitable frame had been reached. After the patch, the check is only
performed at a breakpoint; thus, the execution continues.

This patch causes `ThreadPlanStepOut::ShouldStop()` to recheck the stop
condition when `m_step_out_further_plan_sp` completes.
svkeerthy pushed a commit that referenced this pull request Oct 9, 2025
…at (#161982)

When a thread reaches a breakpoint at the return address set by
`ThreadPlanStepOut`, `ThreadPlanStepOut::ShouldStop()` calls
`ThreadPlanShouldStopHere::InvokeShouldStopHereCallback()`, and if it
returns `false`, `ThreadPlanShouldStopHere::QueueStepOutFromHerePlan()`
is called to queue a new plan to skip the corresponding range. Once the
new plan finishes, `ThreadPlanStepOut::ShouldStop()` should recheck the
stop condition; however, there is no code path in the method that sets
`done` to `true`. Before #126838, if `done` was `false`, the method checked
if a suitable frame had been reached. After the patch, the check is only
performed at a breakpoint; thus, the execution continues.

This patch causes `ThreadPlanStepOut::ShouldStop()` to recheck the stop
condition when `m_step_out_further_plan_sp` completes.
clingfei pushed a commit to clingfei/llvm-project that referenced this pull request Oct 10, 2025
…at (llvm#161982)

When a thread reaches a breakpoint at the return address set by
`ThreadPlanStepOut`, `ThreadPlanStepOut::ShouldStop()` calls
`ThreadPlanShouldStopHere::InvokeShouldStopHereCallback()`, and if it
returns `false`, `ThreadPlanShouldStopHere::QueueStepOutFromHerePlan()`
is called to queue a new plan to skip the corresponding range. Once the
new plan finishes, `ThreadPlanStepOut::ShouldStop()` should recheck the
stop condition; however, there is no code path in the method that sets
`done` to `true`. Before llvm#126838, if `done` was `false`, the method checked
if a suitable frame had been reached. After the patch, the check is only
performed at a breakpoint; thus, the execution continues.

This patch causes `ThreadPlanStepOut::ShouldStop()` to recheck the stop
condition when `m_step_out_further_plan_sp` completes.
akadutta pushed a commit to akadutta/llvm-project that referenced this pull request Oct 14, 2025
…at (llvm#161982)

When a thread reaches a breakpoint at the return address set by
`ThreadPlanStepOut`, `ThreadPlanStepOut::ShouldStop()` calls
`ThreadPlanShouldStopHere::InvokeShouldStopHereCallback()`, and if it
returns `false`, `ThreadPlanShouldStopHere::QueueStepOutFromHerePlan()`
is called to queue a new plan to skip the corresponding range. Once the
new plan finishes, `ThreadPlanStepOut::ShouldStop()` should recheck the
stop condition; however, there is no code path in the method that sets
`done` to `true`. Before llvm#126838, if `done` was `false`, the method checked
if a suitable frame had been reached. After the patch, the check is only
performed at a breakpoint; thus, the execution continues.

This patch causes `ThreadPlanStepOut::ShouldStop()` to recheck the stop
condition when `m_step_out_further_plan_sp` completes.
igorkudrin added a commit that referenced this pull request Oct 22, 2025
The test did not work as intended when the empty function `done()`
contained prologue/epilogue code, because a breakpoint was set before
the last instruction of the function, which caused the test to pass even
with the fix from #126838 having been reverted.

The test is intended to check a case when a breakpoint is set on a
return instruction, which is the very last instruction of a function.
When stepping out from this breakpoint, there is interaction between
`ThreadPlanStepOut` and `ThreadPlanStepOverBreakpoint` that could lead
to missing the stop location in the outer frame; the detailed
explanation can be found in #126838.

On `Linux/AArch64`, the source is compiled into:
```
> objdump -d main.o
0000000000000000 <done>:
   0:   d65f03c0        ret
```
So, when the command `bt set -n done` from the original test sets a
breakpoint to the first instruction of `done()`, this instruction
luckily also happens to be the last one.

On `Linux/x86_64`, it compiles into:
```
> objdump -d main.o
0000000000000000 <done>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   5d                      pop    %rbp
   5:   c3                      ret
```
In this case, setting a breakpoint by function name means setting it
several instructions before `ret`, which does not provoke the
interaction between `ThreadPlanStepOut` and
`ThreadPlanStepOverBreakpoint`.
Lukacma pushed a commit to Lukacma/llvm-project that referenced this pull request Oct 29, 2025
The test did not work as intended when the empty function `done()`
contained prologue/epilogue code, because a breakpoint was set before
the last instruction of the function, which caused the test to pass even
with the fix from llvm#126838 having been reverted.

The test is intended to check a case when a breakpoint is set on a
return instruction, which is the very last instruction of a function.
When stepping out from this breakpoint, there is interaction between
`ThreadPlanStepOut` and `ThreadPlanStepOverBreakpoint` that could lead
to missing the stop location in the outer frame; the detailed
explanation can be found in llvm#126838.

On `Linux/AArch64`, the source is compiled into:
```
> objdump -d main.o
0000000000000000 <done>:
   0:   d65f03c0        ret
```
So, when the command `bt set -n done` from the original test sets a
breakpoint to the first instruction of `done()`, this instruction
luckily also happens to be the last one.

On `Linux/x86_64`, it compiles into:
```
> objdump -d main.o
0000000000000000 <done>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   5d                      pop    %rbp
   5:   c3                      ret
```
In this case, setting a breakpoint by function name means setting it
several instructions before `ret`, which does not provoke the
interaction between `ThreadPlanStepOut` and
`ThreadPlanStepOverBreakpoint`.
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 30, 2025
The test did not work as intended when the empty function `done()`
contained prologue/epilogue code, because a breakpoint was set before
the last instruction of the function, which caused the test to pass even
with the fix from llvm#126838 having been reverted.

The test is intended to check a case when a breakpoint is set on a
return instruction, which is the very last instruction of a function.
When stepping out from this breakpoint, there is interaction between
`ThreadPlanStepOut` and `ThreadPlanStepOverBreakpoint` that could lead
to missing the stop location in the outer frame; the detailed
explanation can be found in llvm#126838.

On `Linux/AArch64`, the source is compiled into:
```
> objdump -d main.o
0000000000000000 <done>:
   0:   d65f03c0        ret
```
So, when the command `bt set -n done` from the original test sets a
breakpoint to the first instruction of `done()`, this instruction
luckily also happens to be the last one.

On `Linux/x86_64`, it compiles into:
```
> objdump -d main.o
0000000000000000 <done>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   5d                      pop    %rbp
   5:   c3                      ret
```
In this case, setting a breakpoint by function name means setting it
several instructions before `ret`, which does not provoke the
interaction between `ThreadPlanStepOut` and
`ThreadPlanStepOverBreakpoint`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants