Skip to content

Commit 7f8c8bc

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 StepThrough plan with a target symbol, instead of a plan that keep taking steps until it reaches some interesting location. 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 572a8dc commit 7f8c8bc

File tree

3 files changed

+40
-22
lines changed

3 files changed

+40
-22
lines changed

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

+40-8
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,17 +590,40 @@ 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 function_node = childAtPath(
601+
demangled_node, {Kind::Allocator, Kind::Class, Kind::Identifier});
602+
if (!function_node || !function_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+
StringRef class_name = function_node->getText();
610+
std::string ctor_name = (class_name + ".init").str();
611+
AddressRange sym_addr_range(sc.symbol->GetAddress(),
612+
sc.symbol->GetByteSize());
613+
ThreadPlanSP new_plan_sp = std::make_shared<ThreadPlanStepInRange>(
614+
thread, sym_addr_range, sc, ctor_name.c_str(), eOnlyDuringStepping,
615+
eLazyBoolNo, eLazyBoolNo);
616+
return new_plan_sp;
617+
}
592618
case ThunkAction::StepThrough: {
593619
if (log)
594620
log->Printf("Stepping through thunk: %s kind: %s", symbol_name,
595621
GetThunkKindName(thunk_kind));
596622
AddressRange sym_addr_range(sc.symbol->GetAddress(),
597623
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);
624+
ThreadPlanSP new_plan_sp = std::make_shared<ThreadPlanStepInRange>(
625+
thread, sym_addr_range, sc, nullptr, eOnlyDuringStepping, eLazyBoolNo,
626+
eLazyBoolNo);
603627
return new_plan_sp;
604628
}
605629
}
@@ -613,7 +637,15 @@ bool SwiftLanguageRuntime::IsSymbolARuntimeThunk(const Symbol &symbol) {
613637
if (symbol_name.empty())
614638
return false;
615639
swift::Demangle::Context demangle_ctx;
616-
return demangle_ctx.isThunkSymbol(symbol_name);
640+
if (demangle_ctx.isThunkSymbol(symbol_name))
641+
return true;
642+
643+
// These are not Thunks in the sense that they don't jump to *user* code.
644+
// But they are language symbols that should be identified as something to act
645+
// on; here, the default action of stepping out is appropriate.
646+
Node *node = demangle_ctx.demangleSymbolAsNode(symbol_name);
647+
return node && node->getKind() == Node::Kind::Global &&
648+
hasChild(node, Node::Kind::TypeMetadataAccessFunction);
617649
}
618650

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

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

-2
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

-12
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)