Skip to content

Commit 03da97c

Browse files
committed
Control the "step out through thunk" logic explicitly when pushing thread plans (llvm#129301)
Jonas recently added a trampoline handling strategy for simple language thunks that does: "step through language thunks stepping in one level deep and stopping if you hit user code". That was actually pulled over from the swift implementation. However, this strategy and the strategy we have to "step out past language thunks" when stepping out come into conflict if the thunk you are stepping through calls some other function before dispatching to the intended method. When you step out of the called function back into the thunk, should you keep stepping out past the thunk or not? In most cases, you want to step out past the thunk, but in this particular case you don't. This patch adds a way to inform the thread plan (or really it's ShouldStopHere behavior) of which behavior it should have, and passes the don't step through thunks to the step through plan it uses to step through thunks. I didn't add a test for this because I couldn't find a C++ thunk that calls another function before getting to the target function. I asked the clang folks here if they could think of a case where clang would do this, and they couldn't. If anyone can think of such a construct, it will be easy to write the step through test for it... This does happen in swift, however, so when I cherry-pick this to the swift fork I'll test it there. (cherry picked from commit ddbce2f) (cherry picked from commit 169fb46)
1 parent 293ff41 commit 03da97c

File tree

4 files changed

+102
-1
lines changed

4 files changed

+102
-1
lines changed

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,9 +525,12 @@ static lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
525525
GetThunkKindName(thunk_kind));
526526
AddressRange sym_addr_range(sc.symbol->GetAddress(),
527527
sc.symbol->GetByteSize());
528-
return std::make_shared<ThreadPlanStepInRange>(thread, sym_addr_range, sc,
528+
ThreadPlanSP new_plan_sp = std::make_shared<ThreadPlanStepInRange>(thread, sym_addr_range, sc,
529529
nullptr, eOnlyDuringStepping,
530530
eLazyBoolNo, eLazyBoolNo);
531+
static_cast<ThreadPlanStepInRange *>(new_plan_sp.get())
532+
->GetFlags().Clear(ThreadPlanShouldStopHere::eStepOutPastThunks);
533+
return new_plan_sp;
531534
}
532535
}
533536

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
SWIFT_SOURCES := main.swift
2+
include Makefile.rules
3+
4+
cleanup:
5+
rm -f Makefile *.d
6+
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
import lldbsuite.test.lldbtest as lldbtest
4+
import lldbsuite.test.lldbutil as lldbutil
5+
import os
6+
import platform
7+
8+
class TestStepThroughAllocatingInit(lldbtest.TestBase):
9+
mydir = lldbtest.TestBase.compute_mydir(__file__)
10+
11+
@swiftTest
12+
def test_swift_stepping_api(self):
13+
"""Test that step in using the Python API steps through thunk."""
14+
self.build()
15+
self.do_test(True)
16+
17+
@swiftTest
18+
def test_swift_stepping_cli(self):
19+
"""Same test with the cli - it goes a slightly different path than
20+
the API."""
21+
self.build()
22+
self.do_test(False)
23+
24+
def setUp(self):
25+
lldbtest.TestBase.setUp(self)
26+
self.main_source = "main.swift"
27+
self.main_source_spec = lldb.SBFileSpec(self.main_source)
28+
# If you are running against a debug swift you are going to
29+
# end up stepping into the stdlib and that will make stepping
30+
# tests impossible to write. So avoid that.
31+
32+
if platform.system() == 'Darwin':
33+
lib_name = "libswiftCore.dylib"
34+
else:
35+
lib_name = "libswiftCore.so"
36+
37+
self.dbg.HandleCommand(
38+
"settings set "
39+
"target.process.thread.step-avoid-libraries {}".format(lib_name))
40+
41+
def do_test(self, use_api):
42+
"""Tests that we can step reliably in swift code."""
43+
exe_name = "a.out"
44+
exe = self.getBuildArtifact(exe_name)
45+
46+
target, process, thread, breakpoint = lldbutil.run_to_source_breakpoint(self,
47+
'Break here to step into init', self.main_source_spec)
48+
49+
# Step into the function.
50+
if use_api:
51+
thread.StepInto()
52+
else:
53+
self.runCmd("thread step-in")
54+
frame_0 = thread.frames[0]
55+
self.assertIn('Foo.init()', frame_0.GetFunctionName())
56+
57+
# Check that our parent frame is indeed allocating_init (otherwise we aren't
58+
# testing what we think we're testing...
59+
frame_1 = thread.frames[1]
60+
self.assertIn("allocating_init", frame_1.GetFunctionName())
61+
62+
# Step one line so some_string is initialized, make sure we can
63+
# get its value:
64+
thread.StepOver()
65+
frame_0 = thread.frames[0]
66+
self.assertIn('Foo.init()', frame_0.GetFunctionName())
67+
var = frame_0.FindVariable("some_string")
68+
self.assertTrue(var.GetError().Success())
69+
self.assertEqual(var.GetSummary(), '"foo"')
70+
71+
# Now make sure that stepping out steps past the thunk:
72+
thread.StepOut()
73+
frame_0 = thread.frames[0]
74+
self.assertIn("doSomething", frame_0.GetFunctionName())
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class Foo {
2+
init() {
3+
let some_string = "foo"
4+
print(some_string)
5+
}
6+
}
7+
8+
func bar(_ _: Foo) {
9+
print("bar")
10+
}
11+
12+
func doSomething()
13+
{
14+
let f = Foo() // Break here to step into init
15+
bar(f)
16+
}
17+
18+
doSomething()

0 commit comments

Comments
 (0)