Skip to content

Commit f0041b4

Browse files
Merge pull request #10505 from felipepiovezan/felipe/fix_conformance_stepping
[cherry-pick][lldb] Fix stepping through conformance methods
2 parents c66f74a + 1bd67e3 commit f0041b4

File tree

7 files changed

+152
-23
lines changed

7 files changed

+152
-23
lines changed

lldb/include/lldb/Target/ThreadPlanStepOut.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,22 @@ namespace lldb_private {
1717

1818
class ThreadPlanStepOut : public ThreadPlan, public ThreadPlanShouldStopHere {
1919
public:
20+
/// Creates a thread plan to step out from frame_idx, skipping parent frames
21+
/// if they are artificial or hidden frames. Also skips frames without debug
22+
/// info based on step_out_avoids_code_without_debug_info.
2023
ThreadPlanStepOut(Thread &thread, SymbolContext *addr_context,
2124
bool first_insn, bool stop_others, Vote report_stop_vote,
2225
Vote report_run_vote, uint32_t frame_idx,
2326
LazyBool step_out_avoids_code_without_debug_info,
2427
bool continue_to_next_branch = false,
2528
bool gather_return_value = true);
2629

30+
/// Creates a thread plan to step out from frame_idx to frame_idx + 1.
31+
ThreadPlanStepOut(Thread &thread, bool stop_others, Vote report_stop_vote,
32+
Vote report_run_vote, uint32_t frame_idx,
33+
bool continue_to_next_branch = false,
34+
bool gather_return_value = true);
35+
2736
~ThreadPlanStepOut() override;
2837

2938
void GetDescription(Stream *s, lldb::DescriptionLevel level) override;
@@ -88,6 +97,10 @@ class ThreadPlanStepOut : public ThreadPlan, public ThreadPlanShouldStopHere {
8897
LazyBool step_out_avoids_code_without_debug_info);
8998

9099
void SetupAvoidNoDebug(LazyBool step_out_avoids_code_without_debug_info);
100+
101+
void SetupReturnAddress(lldb::StackFrameSP return_frame_sp,
102+
lldb::StackFrameSP immediate_return_from_sp,
103+
uint32_t frame_idx, bool continue_to_next_branch);
91104
// Need an appropriate marker for the current stack so we can tell step out
92105
// from step in.
93106

lldb/source/Target/Thread.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,9 +1392,8 @@ ThreadPlanSP Thread::QueueThreadPlanForStepOutNoShouldStop(
13921392
const bool calculate_return_value =
13931393
false; // No need to calculate the return value here.
13941394
ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut(
1395-
*this, addr_context, first_insn, stop_other_threads, report_stop_vote,
1396-
report_run_vote, frame_idx, eLazyBoolNo, continue_to_next_branch,
1397-
calculate_return_value));
1395+
*this, stop_other_threads, report_stop_vote, report_run_vote, frame_idx,
1396+
continue_to_next_branch, calculate_return_value));
13981397

13991398
ThreadPlanStepOut *new_plan =
14001399
static_cast<ThreadPlanStepOut *>(thread_plan_sp.get());

lldb/source/Target/ThreadPlanStepInRange.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ bool ThreadPlanStepInRange::DefaultShouldStopHereCallback(
460460
if (!should_stop_here)
461461
return false;
462462

463-
if (should_stop_here && current_plan->GetKind() == eKindStepInRange &&
463+
if (current_plan->GetKind() == eKindStepInRange &&
464464
operation == eFrameCompareYounger) {
465465
ThreadPlanStepInRange *step_in_range_plan =
466466
static_cast<ThreadPlanStepInRange *>(current_plan);

lldb/source/Target/ThreadPlanStepOut.cpp

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,32 @@ using namespace lldb_private;
3838

3939
uint32_t ThreadPlanStepOut::s_default_flag_values = 0;
4040

41+
/// Computes the target frame this plan should step out to.
42+
static StackFrameSP
43+
ComputeTargetFrame(Thread &thread, uint32_t start_frame_idx,
44+
std::vector<StackFrameSP> &skipped_frames) {
45+
uint32_t frame_idx = start_frame_idx + 1;
46+
StackFrameSP return_frame_sp = thread.GetStackFrameAtIndex(frame_idx);
47+
if (!return_frame_sp)
48+
return nullptr;
49+
50+
while (return_frame_sp->IsArtificial() || return_frame_sp->IsHidden()) {
51+
skipped_frames.push_back(return_frame_sp);
52+
53+
frame_idx++;
54+
return_frame_sp = thread.GetStackFrameAtIndex(frame_idx);
55+
56+
// We never expect to see an artificial frame without a regular ancestor.
57+
// Defensively refuse to step out.
58+
if (!return_frame_sp) {
59+
LLDB_LOG(GetLog(LLDBLog::Step),
60+
"Can't step out of frame with artificial ancestors");
61+
return nullptr;
62+
}
63+
}
64+
return return_frame_sp;
65+
}
66+
4167
// ThreadPlanStepOut: Step out of the current frame
4268
ThreadPlanStepOut::ThreadPlanStepOut(
4369
Thread &thread, SymbolContext *context, bool first_insn, bool stop_others,
@@ -54,33 +80,46 @@ ThreadPlanStepOut::ThreadPlanStepOut(
5480
m_stop_others(stop_others), m_immediate_step_from_function(nullptr),
5581
m_is_swift_error_value(false),
5682
m_calculate_return_value(gather_return_value) {
57-
Log *log = GetLog(LLDBLog::Step);
5883
SetFlagsToDefault();
5984
SetupAvoidNoDebug(step_out_avoids_code_without_debug_info);
6085

6186
m_step_from_insn = thread.GetRegisterContext()->GetPC(0);
6287

63-
uint32_t return_frame_index = frame_idx + 1;
64-
StackFrameSP return_frame_sp(thread.GetStackFrameAtIndex(return_frame_index));
88+
StackFrameSP return_frame_sp =
89+
ComputeTargetFrame(thread, frame_idx, m_stepped_past_frames);
6590
StackFrameSP immediate_return_from_sp(thread.GetStackFrameAtIndex(frame_idx));
6691

67-
if (!return_frame_sp || !immediate_return_from_sp)
68-
return; // we can't do anything here. ValidatePlan() will return false.
92+
SetupReturnAddress(return_frame_sp, immediate_return_from_sp, frame_idx,
93+
continue_to_next_branch);
94+
}
6995

70-
// While stepping out, behave as-if artificial frames are not present.
71-
while (return_frame_sp->IsArtificial() || return_frame_sp->IsHidden()) {
72-
m_stepped_past_frames.push_back(return_frame_sp);
96+
ThreadPlanStepOut::ThreadPlanStepOut(Thread &thread, bool stop_others,
97+
Vote report_stop_vote,
98+
Vote report_run_vote, uint32_t frame_idx,
99+
bool continue_to_next_branch,
100+
bool gather_return_value)
101+
: ThreadPlan(ThreadPlan::eKindStepOut, "Step out", thread, report_stop_vote,
102+
report_run_vote),
103+
ThreadPlanShouldStopHere(this), m_return_bp_id(LLDB_INVALID_BREAK_ID),
104+
m_return_addr(LLDB_INVALID_ADDRESS), m_stop_others(stop_others),
105+
m_immediate_step_from_function(nullptr),
106+
m_calculate_return_value(gather_return_value) {
107+
SetFlagsToDefault();
108+
m_step_from_insn = thread.GetRegisterContext()->GetPC(0);
73109

74-
++return_frame_index;
75-
return_frame_sp = thread.GetStackFrameAtIndex(return_frame_index);
110+
StackFrameSP return_frame_sp = thread.GetStackFrameAtIndex(frame_idx + 1);
111+
StackFrameSP immediate_return_from_sp =
112+
thread.GetStackFrameAtIndex(frame_idx);
76113

77-
// We never expect to see an artificial frame without a regular ancestor.
78-
// If this happens, log the issue and defensively refuse to step out.
79-
if (!return_frame_sp) {
80-
LLDB_LOG(log, "Can't step out of frame with artificial ancestors");
81-
return;
82-
}
83-
}
114+
SetupReturnAddress(return_frame_sp, immediate_return_from_sp, frame_idx,
115+
continue_to_next_branch);
116+
}
117+
118+
void ThreadPlanStepOut::SetupReturnAddress(
119+
StackFrameSP return_frame_sp, StackFrameSP immediate_return_from_sp,
120+
uint32_t frame_idx, bool continue_to_next_branch) {
121+
if (!return_frame_sp || !immediate_return_from_sp)
122+
return; // we can't do anything here. ValidatePlan() will return false.
84123

85124
m_step_out_to_id = return_frame_sp->GetStackID();
86125
m_immediate_step_from_id = immediate_return_from_sp->GetStackID();
@@ -94,8 +133,8 @@ ThreadPlanStepOut::ThreadPlanStepOut(
94133
// First queue a plan that gets us to this inlined frame, and when we get
95134
// there we'll queue a second plan that walks us out of this frame.
96135
m_step_out_to_inline_plan_sp = std::make_shared<ThreadPlanStepOut>(
97-
thread, nullptr, false, stop_others, eVoteNoOpinion, eVoteNoOpinion,
98-
frame_idx - 1, eLazyBoolNo, continue_to_next_branch);
136+
GetThread(), nullptr, false, m_stop_others, eVoteNoOpinion,
137+
eVoteNoOpinion, frame_idx - 1, eLazyBoolNo, continue_to_next_branch);
99138
static_cast<ThreadPlanStepOut *>(m_step_out_to_inline_plan_sp.get())
100139
->SetShouldStopHereCallbacks(nullptr, nullptr);
101140
m_step_out_to_inline_plan_sp->SetPrivate(true);
@@ -135,6 +174,7 @@ ThreadPlanStepOut::ThreadPlanStepOut(
135174

136175
// Perform some additional validation on the return address.
137176
uint32_t permissions = 0;
177+
Log *log = GetLog(LLDBLog::Step);
138178
if (!m_process.GetLoadAddressPermissions(m_return_addr, permissions)) {
139179
LLDB_LOGF(log, "ThreadPlanStepOut(%p): Return address (0x%" PRIx64
140180
") permissions not found.", static_cast<void *>(this),
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
SWIFT_SOURCES := main.swift
2+
include Makefile.rules
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import lldb
2+
from lldbsuite.test.lldbtest import *
3+
from lldbsuite.test.decorators import *
4+
import lldbsuite.test.lldbutil as lldbutil
5+
6+
7+
@skipIf(oslist=["windows", "linux"])
8+
class TestSwiftSteppingThroughWitness(TestBase):
9+
10+
def setUp(self):
11+
TestBase.setUp(self)
12+
self.runCmd(
13+
"settings set target.process.thread.step-avoid-libraries libswift_Concurrency.dylib"
14+
)
15+
16+
@swiftTest
17+
def test_step_in_and_out(self):
18+
"""Test that stepping in and out of protocol methods work"""
19+
self.build()
20+
_, _, thread, _ = lldbutil.run_to_source_breakpoint(
21+
self, "break here", lldb.SBFileSpec("main.swift")
22+
)
23+
24+
thread.StepInto()
25+
stop_reason = thread.GetStopReason()
26+
self.assertStopReason(stop_reason, lldb.eStopReasonPlanComplete)
27+
28+
frame0 = thread.frames[0]
29+
frame1 = thread.frames[1]
30+
self.assertIn("SlowRandomNumberGenerator.random", frame0.GetFunctionName())
31+
self.assertIn(
32+
"protocol witness for a.RandomNumberGenerator.random",
33+
frame1.GetFunctionName(),
34+
)
35+
36+
thread.StepOut()
37+
stop_reason = thread.GetStopReason()
38+
self.assertStopReason(stop_reason, lldb.eStopReasonPlanComplete)
39+
frame0 = thread.frames[0]
40+
self.assertIn("doMath", frame0.GetFunctionName())
41+
42+
@swiftTest
43+
def test_step_over(self):
44+
"""Test that stepping over protocol methods work"""
45+
self.build()
46+
_, _, thread, _ = lldbutil.run_to_source_breakpoint(
47+
self, "break here", lldb.SBFileSpec("main.swift")
48+
)
49+
50+
thread.StepOver()
51+
stop_reason = thread.GetStopReason()
52+
self.assertStopReason(stop_reason, lldb.eStopReasonPlanComplete)
53+
frame0 = thread.frames[0]
54+
self.assertIn("doMath", frame0.GetFunctionName())
55+
56+
line_entry = frame0.GetLineEntry()
57+
self.assertEqual(14, line_entry.GetLine())
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
protocol RandomNumberGenerator {
2+
func random(in range: ClosedRange<Int>) async -> Int
3+
}
4+
5+
class SlowRandomNumberGenerator: RandomNumberGenerator {
6+
func random(in range: ClosedRange<Int>) async -> Int {
7+
try? await Task.sleep(for: .milliseconds(500))
8+
return Int.random(in: range)
9+
}
10+
}
11+
12+
func doMath<RNG: RandomNumberGenerator>(with rng: RNG) async {
13+
let y = await rng.random(in: 101...200) // break here
14+
print("Y is \(y)")
15+
}
16+
17+
let rng = SlowRandomNumberGenerator()
18+
await doMath(with: rng)

0 commit comments

Comments
 (0)