Skip to content

Commit 169fb46

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)
1 parent 541d218 commit 169fb46

File tree

7 files changed

+120
-8
lines changed

7 files changed

+120
-8
lines changed

lldb/include/lldb/Target/ThreadPlanShouldStopHere.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ class ThreadPlanShouldStopHere {
5959
eNone = 0,
6060
eAvoidInlines = (1 << 0),
6161
eStepInAvoidNoDebug = (1 << 1),
62-
eStepOutAvoidNoDebug = (1 << 2)
62+
eStepOutAvoidNoDebug = (1 << 2),
63+
eStepOutPastThunks = (1 << 3)
6364
};
6465

6566
// Constructors and Destructors

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

lldb/source/Target/ThreadPlanShouldStopHere.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "lldb/Target/ThreadPlanShouldStopHere.h"
1010
#include "lldb/Symbol/Function.h"
1111
#include "lldb/Symbol/Symbol.h"
12+
#include "lldb/Target/Language.h"
1213
#include "lldb/Target/LanguageRuntime.h"
1314
#include "lldb/Target/Process.h"
1415
#include "lldb/Target/RegisterContext.h"
@@ -86,7 +87,12 @@ bool ThreadPlanShouldStopHere::DefaultShouldStopHereCallback(
8687
if (symbol) {
8788
ProcessSP process_sp(current_plan->GetThread().GetProcess());
8889
for (auto *runtime : process_sp->GetLanguageRuntimes()) {
89-
if (runtime->IsSymbolARuntimeThunk(*symbol))
90+
if (runtime->IsSymbolARuntimeThunk(*symbol) &&
91+
flags.Test(ThreadPlanShouldStopHere::eStepOutPastThunks)) {
92+
LLDB_LOGF(
93+
log, "Stepping out past a language thunk %s for: %s",
94+
frame->GetFunctionName(),
95+
Language::GetNameForLanguageType(runtime->GetLanguageType()));
9096
should_stop_here = false;
9197
}
9298
}
@@ -139,11 +145,14 @@ ThreadPlanSP ThreadPlanShouldStopHere::DefaultStepFromHereCallback(
139145
if (sc.symbol) {
140146
ProcessSP process_sp(current_plan->GetThread().GetProcess());
141147
for (auto *runtime : process_sp->GetLanguageRuntimes()) {
142-
if (runtime->IsSymbolARuntimeThunk(*sc.symbol)) {
143-
if (log)
144-
log->Printf("In runtime thunk %s - stepping out.",
145-
sc.symbol->GetName().GetCString());
148+
if (runtime->IsSymbolARuntimeThunk(*sc.symbol) &&
149+
flags.Test(ThreadPlanShouldStopHere::eStepOutPastThunks)) {
150+
LLDB_LOGF(
151+
log, "Stepping out past a language thunk %s for: %s",
152+
frame->GetFunctionName(),
153+
Language::GetNameForLanguageType(runtime->GetLanguageType()));
146154
just_step_out = true;
155+
break;
147156
}
148157
}
149158
// If the whole function is marked line 0 just step out, that's easier &

lldb/source/Target/ThreadPlanStepInRange.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ using namespace lldb;
2929
using namespace lldb_private;
3030

3131
uint32_t ThreadPlanStepInRange::s_default_flag_values =
32-
ThreadPlanShouldStopHere::eStepInAvoidNoDebug;
32+
ThreadPlanShouldStopHere::eStepInAvoidNoDebug |
33+
ThreadPlanShouldStopHere::eStepOutPastThunks;
3334

3435
// ThreadPlanStepInRange: Step through a stack range, either stepping over or
3536
// into based on the value of \a type.
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)