Skip to content

Commit 30aa4dc

Browse files
[lldb][swift] Create thread plan for stepping through Allocating Init
These trampolines have a well defined target, which can be extracted from the demangle tree. A similar strategy is already adopted for stepping into conformance code. This allows LLDB to create a RunToAddress plan with a target address, which is obtained by searching the Module for functions matching `<class_name>.Init`. We already had a test for this, but we rely on the "step avoid libraries" setting to emulate the case of "avoid libraries without debug info". However, they are not exactly equivalent, so the test mislead us into believing stepping was working. A future patch should try to fold the setting-checking code into ThreadPlanShouldStopHere::DefaultShouldStopCallback. With this patch, the test now works regardless of whether debug info is present or not. To make this work, this commit also teaches the Swift language runtime how to recognize "type metadata accessors". These are thunks in the sense that they jump to some other code, but not user code. Their purposes is to eventually send the "self" message to the SwiftObject the first time they run. As such, they should just be stepped out from, which is accomplished by having the runtime recognize them as trampolines but not provide any specific actions, relying on the default behavior of ShouldStopHere. Prior to this change, this would sometimes work accidentally because all the functions involved had line 0 debug information, and the debugger would keep stepping through _all_ of them. However, in debug builds of x86, this strategy failed; sometimes the computed Symbol size is larger than the line entry for these functions, and so the detection "all line 0" fails. Relying on this is error-prone and slow, as it causes the debugger to stop many times.
1 parent c89ea13 commit 30aa4dc

File tree

3 files changed

+50
-23
lines changed

3 files changed

+50
-23
lines changed

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

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ enum class ThunkAction {
5353
Unknown = 0,
5454
GetThunkTarget,
5555
StepIntoConformance,
56+
StepIntoAllocatingInit,
5657
StepThrough,
5758
};
5859

@@ -336,7 +337,7 @@ static const char *GetThunkKindName(ThunkKind kind) {
336337
case ThunkKind::Unknown:
337338
return "Unknown";
338339
case ThunkKind::AllocatingInit:
339-
return "StepThrough";
340+
return "StepIntoAllocatingInit";
340341
case ThunkKind::PartialApply:
341342
return "GetThunkTarget";
342343
case ThunkKind::ObjCAttribute:
@@ -353,7 +354,7 @@ static ThunkAction GetThunkAction(ThunkKind kind) {
353354
case ThunkKind::Unknown:
354355
return ThunkAction::Unknown;
355356
case ThunkKind::AllocatingInit:
356-
return ThunkAction::StepThrough;
357+
return ThunkAction::StepIntoAllocatingInit;
357358
case ThunkKind::PartialApply:
358359
return ThunkAction::GetThunkTarget;
359360
case ThunkKind::ObjCAttribute:
@@ -589,18 +590,50 @@ static lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
589590
thread, sym_addr_range, sc, function_name.c_str(), eOnlyDuringStepping,
590591
eLazyBoolNo, eLazyBoolNo);
591592
}
593+
case ThunkAction::StepIntoAllocatingInit: {
594+
LLDB_LOGF(log, "Stepping into allocating init: \"%s\"", symbol_name);
595+
swift::Demangle::Context ctx;
596+
NodePointer demangled_node =
597+
SwiftLanguageRuntime::DemangleSymbolAsNode(symbol_name, ctx);
598+
599+
using Kind = Node::Kind;
600+
NodePointer class_node = childAtPath(
601+
demangled_node, {Kind::Allocator, Kind::Class, Kind::Identifier});
602+
if (!class_node || !class_node->hasText()) {
603+
std::string node_str = getNodeTreeAsString(demangled_node);
604+
LLDB_LOGF(log,
605+
"Failed to extract constructor name from demangle node: %s",
606+
node_str.c_str());
607+
return nullptr;
608+
}
609+
610+
ModuleFunctionSearchOptions options{/*include_symbols*/ true,
611+
/*include_inlines*/ true};
612+
std::string ctor_name = llvm::formatv("{0}.init", class_node->getText());
613+
SymbolContextList sc_list;
614+
sc.module_sp->FindFunctions(RegularExpression(ctor_name), options, sc_list);
615+
std::vector<addr_t> load_addresses;
616+
Target &target = thread.GetProcess()->GetTarget();
617+
for (const SymbolContext &ctor_sc : sc_list) {
618+
const Symbol *ctor_symbol = ctor_sc.symbol;
619+
if (ctor_symbol)
620+
load_addresses.push_back(ctor_symbol->GetLoadAddress(&target));
621+
}
622+
if (load_addresses.empty())
623+
return nullptr;
624+
return std::make_shared<ThreadPlanRunToAddress>(thread, load_addresses,
625+
stop_others);
626+
}
592627
case ThunkAction::StepThrough: {
593628
if (log)
594629
log->Printf("Stepping through thunk: %s kind: %s", symbol_name,
595630
GetThunkKindName(thunk_kind));
596631
AddressRange sym_addr_range(sc.symbol->GetAddress(),
597632
sc.symbol->GetByteSize());
598-
ThreadPlanSP new_plan_sp = std::make_shared<ThreadPlanStepInRange>(thread, sym_addr_range, sc,
599-
nullptr, eOnlyDuringStepping,
600-
eLazyBoolNo, eLazyBoolNo);
601-
static_cast<ThreadPlanStepInRange *>(new_plan_sp.get())
602-
->GetFlags().Clear(ThreadPlanShouldStopHere::eStepOutPastThunks);
603-
return new_plan_sp;
633+
ThreadPlanSP new_plan_sp = std::make_shared<ThreadPlanStepInRange>(
634+
thread, sym_addr_range, sc, nullptr, eOnlyDuringStepping, eLazyBoolNo,
635+
eLazyBoolNo);
636+
return new_plan_sp;
604637
}
605638
}
606639

@@ -613,7 +646,15 @@ bool SwiftLanguageRuntime::IsSymbolARuntimeThunk(const Symbol &symbol) {
613646
if (symbol_name.empty())
614647
return false;
615648
swift::Demangle::Context demangle_ctx;
616-
return demangle_ctx.isThunkSymbol(symbol_name);
649+
if (demangle_ctx.isThunkSymbol(symbol_name))
650+
return true;
651+
652+
// These are not Thunks in the sense that they don't jump to *user* code.
653+
// But they are language symbols that should be identified as something to act
654+
// on; here, the default action of stepping out is appropriate.
655+
Node *node = demangle_ctx.demangleSymbolAsNode(symbol_name);
656+
return node && node->getKind() == Node::Kind::Global &&
657+
hasChild(node, Node::Kind::TypeMetadataAccessFunction);
617658
}
618659

619660
bool SwiftLanguageRuntime::IsSwiftMangledName(llvm::StringRef name) {

lldb/test/API/lang/swift/cxx_interop/backward/stepping/TestSwiftBackwardInteropStepping.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ def test_method_step_over_class(self):
5353
thread = self.setup('Break here for method - class')
5454
self.check_step_over(thread, 'testMethod')
5555

56-
@expectedFailureAll(bugnumber="rdar://106670255")
5756
@swiftTest
5857
def test_init_step_in_class(self):
5958
thread = self.setup('Break here for constructor - class')
@@ -115,7 +114,6 @@ def test_method_step_over_struct_class(self):
115114
thread = self.setup('Break here for method - struct')
116115
self.check_step_over(thread, 'testMethod')
117116

118-
@expectedFailureAll(bugnumber="rdar://106670255")
119117
@swiftTest
120118
def test_init_step_in_struct_class(self):
121119
thread = self.setup('Break here for constructor - struct')

lldb/test/API/lang/swift/step_through_allocating_init/TestStepThroughAllocatingInit.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,6 @@ def setUp(self):
2525
lldbtest.TestBase.setUp(self)
2626
self.main_source = "main.swift"
2727
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))
4028

4129
def do_test(self, use_api):
4230
"""Tests that we can step reliably in swift code."""

0 commit comments

Comments
 (0)