[Lldb-commits] [lldb] [lldb] Step through inlined code when stepping out of inline frame N > 0 (PR #226910)

via lldb-commits lldb-commits at lists.llvm.org
Mon Sep 28 00:48:51 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Felipe de Azevedo Piovezan (felipepiovezan)

<details>
<summary>Changes</summary>

To step out of an inline frame N > 0, ThreadPlanStepOut first steps out to frame N, then queues a plan that steps through the inlined block. Due to a bug, ShouldStop marks the plan complete right after it queues that plan, so the thread stops as soon as it reaches frame N, with no stop reason.

In fact, the comment next to that code describes the intended behavior while implementing something different: only call the StepOut plan done if the "step out from line" plan cannot be queued.

For example, consider this backtrace:
```
  frame #<!-- -->0: 0x00000001000003c0 deep`sink(x=81) at deep.c:4:6 [opt]
  frame #<!-- -->1: 0x0000000100000418 deep`level3(a=48) at deep.c:11:3 [opt] [inlined]
  frame #<!-- -->2: 0x0000000100000404 deep`level2(b=37) at deep.c:20:3 [opt] [inlined]
  frame #<!-- -->3: 0x00000001000003f0 deep`level1(c=42) at deep.c:28:3 [opt] [inlined]
  frame #<!-- -->4: 0x00000001000003dc deep`main at deep.c:33:3 [opt]
```

This patch changes behavior as follows:

```
command                  before                          after
frame select 1; finish   level3 line 12, no stop reason  level2 line 21, step out
frame select 2; finish   level3 line 12, no stop reason  level1 line 29, step out
```

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


4 Files Affected:

- (modified) lldb/source/Target/ThreadPlanStepOut.cpp (+5-7) 
- (added) lldb/test/API/functionalities/thread/step_out_inline/Makefile (+3) 
- (added) lldb/test/API/functionalities/thread/step_out_inline/TestThreadStepOutInline.py (+30) 
- (added) lldb/test/API/functionalities/thread/step_out_inline/main.c (+18) 


``````````diff
diff --git a/lldb/source/Target/ThreadPlanStepOut.cpp b/lldb/source/Target/ThreadPlanStepOut.cpp
index a98eb862a7e27..34608000f1d4f 100644
--- a/lldb/source/Target/ThreadPlanStepOut.cpp
+++ b/lldb/source/Target/ThreadPlanStepOut.cpp
@@ -379,14 +379,12 @@ bool ThreadPlanStepOut::ShouldStop(Event *event_ptr) {
   bool done = false;
   if (m_step_out_to_inline_plan_sp) {
     if (m_step_out_to_inline_plan_sp->MischiefManaged()) {
+      m_step_out_to_inline_plan_sp.reset();
       // Now step through the inlined stack we are in:
-      if (QueueInlinedStepPlan(true)) {
-        // If we can't queue a plan to do this, then just call ourselves done.
-        m_step_out_to_inline_plan_sp.reset();
-        SetPlanComplete(false);
-        return true;
-      } else
-        done = true;
+      if (QueueInlinedStepPlan(true))
+        return false;
+      // If we can't queue a plan to do this, then just call ourselves done.
+      done = true;
     } else
       return m_step_out_to_inline_plan_sp->ShouldStop(event_ptr);
   } else if (m_step_through_inline_plan_sp) {
diff --git a/lldb/test/API/functionalities/thread/step_out_inline/Makefile b/lldb/test/API/functionalities/thread/step_out_inline/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/functionalities/thread/step_out_inline/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/thread/step_out_inline/TestThreadStepOutInline.py b/lldb/test/API/functionalities/thread/step_out_inline/TestThreadStepOutInline.py
new file mode 100644
index 0000000000000..c66dc73eb3e11
--- /dev/null
+++ b/lldb/test/API/functionalities/thread/step_out_inline/TestThreadStepOutInline.py
@@ -0,0 +1,30 @@
+"""
+Test stepping out of an inline frame that is not frame zero.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestThreadStepOutInline(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def step_out_of_frame(self, frame_idx, function, marker):
+        self.build()
+        _, _, thread, _ = lldbutil.run_to_name_breakpoint(self, "sink")
+        self.assertTrue(thread.GetFrameAtIndex(frame_idx).IsInlined())
+
+        thread.StepOutOfFrame(thread.GetFrameAtIndex(frame_idx))
+
+        self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete)
+        frame = thread.GetFrameAtIndex(0)
+        self.assertEqual(frame.GetFunctionName(), function)
+        self.assertEqual(frame.GetLineEntry().GetLine(), line_number("main.c", marker))
+
+    def test_step_out_of_frame_1(self):
+        self.step_out_of_frame(1, "level1", "// after level2")
+
+    def test_step_out_of_frame_2(self):
+        self.step_out_of_frame(2, "main", "// after level1")
diff --git a/lldb/test/API/functionalities/thread/step_out_inline/main.c b/lldb/test/API/functionalities/thread/step_out_inline/main.c
new file mode 100644
index 0000000000000..beb1353abb37b
--- /dev/null
+++ b/lldb/test/API/functionalities/thread/step_out_inline/main.c
@@ -0,0 +1,18 @@
+volatile int g;
+
+__attribute__((noinline)) void sink(int x) { g = x; }
+
+static inline __attribute__((always_inline)) void level2(int b) {
+  sink(b);
+  g = b + 1;
+}
+
+static inline __attribute__((always_inline)) void level1(int c) {
+  level2(c + 1);
+  g = c + 1; // after level2
+}
+
+int main(void) {
+  level1(42);
+  return 0; // after level1
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/226910


More information about the lldb-commits mailing list