Skip to content

Commit 85069c8

Browse files
[LLDB][swift] Skip prologue when unwiding virtual frames
Variables are never available in the prologue of a function. By unwinding the pc of the virtual frames as the first instruction of the continuation function, LLDB is unable to display any variable information. This patch addresses the issue by computing the continuation pointer from the context of the function being unwound, and then querying the continuation's SymbolContext to determine its prologue size.
1 parent 4e5e68f commit 85069c8

File tree

4 files changed

+100
-8
lines changed

4 files changed

+100
-8
lines changed

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

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2745,9 +2745,31 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
27452745
regnums->dummy_regnum, g_dummy_dwarf_expression,
27462746
sizeof(g_dummy_dwarf_expression), false);
27472747
}
2748-
row->SetRegisterLocationToAtCFAPlusOffset(regnums->pc_regnum, ptr_size,
2749-
false);
27502748

2749+
std::optional<addr_t> pc_after_prologue = [&]() -> std::optional<addr_t> {
2750+
// In the prologue, use the async_reg as is, it has not been clobbered.
2751+
if (in_prologue)
2752+
return TrySkipVirtualParentProlog(GetAsyncContext(regctx), *process_sp,
2753+
indirect_context);
2754+
2755+
// Both ABIs (x86_64 and aarch64) guarantee the async reg is saved at:
2756+
// *(fp - 8).
2757+
Status error;
2758+
addr_t async_reg_entry_value = LLDB_INVALID_ADDRESS;
2759+
process_sp->ReadMemory(fp - ptr_size, &async_reg_entry_value, ptr_size,
2760+
error);
2761+
if (error.Fail())
2762+
return {};
2763+
return TrySkipVirtualParentProlog(async_reg_entry_value, *process_sp,
2764+
indirect_context);
2765+
}();
2766+
2767+
if (pc_after_prologue)
2768+
row->SetRegisterLocationToIsConstant(regnums->pc_regnum, *pc_after_prologue,
2769+
false);
2770+
else
2771+
row->SetRegisterLocationToAtCFAPlusOffset(regnums->pc_regnum, ptr_size,
2772+
false);
27512773
row->SetUnspecifiedRegistersAreUndefined(true);
27522774

27532775
UnwindPlanSP plan = std::make_shared<UnwindPlan>(lldb::eRegisterKindDWARF);
@@ -2773,13 +2795,15 @@ UnwindPlanSP SwiftLanguageRuntime::GetFollowAsyncContextUnwindPlan(
27732795
if (!regnums)
27742796
return UnwindPlanSP();
27752797

2798+
const bool is_indirect =
2799+
regctx->ReadRegisterAsUnsigned(regnums->dummy_regnum, (uint64_t)-1ll) ==
2800+
(uint64_t)-1ll;
27762801
// In the general case, the async register setup by the frame above us
27772802
// should be dereferenced twice to get our context, except when the frame
27782803
// above us is an async frame on the OS stack that takes its context directly
27792804
// (see discussion in GetRuntimeUnwindPlan()). The availability of
27802805
// dummy_regnum is used as a marker for this situation.
2781-
if (regctx->ReadRegisterAsUnsigned(regnums->dummy_regnum, (uint64_t)-1ll) !=
2782-
(uint64_t)-1ll) {
2806+
if (!is_indirect) {
27832807
row->GetCFAValue().SetIsRegisterDereferenced(regnums->async_ctx_regnum);
27842808
row->SetRegisterLocationToSame(regnums->async_ctx_regnum, false);
27852809
} else {
@@ -2816,8 +2840,21 @@ UnwindPlanSP SwiftLanguageRuntime::GetFollowAsyncContextUnwindPlan(
28162840
regnums->async_ctx_regnum, expression, expr_size - 1, false);
28172841
}
28182842

2819-
row->SetRegisterLocationToAtCFAPlusOffset(regnums->pc_regnum, ptr_size,
2820-
false);
2843+
// Suppose this is unwinding frame #2 of a call stack. The value given for
2844+
// the async register has two possible values, depending on what frame #1
2845+
// expects:
2846+
// 1. The CFA of frame #1, direct ABI, dereferencing it once produces CFA of
2847+
// Frame #2.
2848+
// 2. The CFA of frame #0, indirect ABI, dereferencing it twice produces CFA
2849+
// of Frame #2.
2850+
const unsigned num_indirections = 1 + is_indirect;
2851+
if (std::optional<addr_t> pc_after_prologue = TrySkipVirtualParentProlog(
2852+
GetAsyncContext(regctx), *process_sp, num_indirections))
2853+
row->SetRegisterLocationToIsConstant(regnums->pc_regnum, *pc_after_prologue,
2854+
false);
2855+
else
2856+
row->SetRegisterLocationToAtCFAPlusOffset(regnums->pc_regnum, ptr_size,
2857+
false);
28212858

28222859
row->SetUnspecifiedRegistersAreUndefined(true);
28232860

@@ -2831,4 +2868,51 @@ UnwindPlanSP SwiftLanguageRuntime::GetFollowAsyncContextUnwindPlan(
28312868
return plan;
28322869
}
28332870

2871+
std::optional<lldb::addr_t> SwiftLanguageRuntime::TrySkipVirtualParentProlog(
2872+
lldb::addr_t async_reg_val, Process &process, unsigned num_indirections) {
2873+
assert(num_indirections <= 2 &&
2874+
"more than two dereferences should not be needed");
2875+
if (async_reg_val == LLDB_INVALID_ADDRESS || async_reg_val == 0)
2876+
return {};
2877+
2878+
const auto ptr_size = process.GetAddressByteSize();
2879+
Status error;
2880+
2881+
// Compute the CFA of this frame.
2882+
addr_t cfa = async_reg_val;
2883+
for (; num_indirections != 0; --num_indirections) {
2884+
process.ReadMemory(cfa, &cfa, ptr_size, error);
2885+
if (error.Fail())
2886+
return {};
2887+
}
2888+
2889+
// The last funclet will have a zero CFA, we don't want to read that.
2890+
if (cfa == 0)
2891+
return {};
2892+
2893+
// Get the PC of the parent frame, i.e. the continuation pointer, which is
2894+
// the second field of the CFA.
2895+
addr_t pc_location = cfa + ptr_size;
2896+
addr_t pc_value = LLDB_INVALID_ADDRESS;
2897+
process.ReadMemory(pc_location, &pc_value, ptr_size, error);
2898+
if (error.Fail())
2899+
return {};
2900+
2901+
Address pc;
2902+
Target &target = process.GetTarget();
2903+
pc.SetLoadAddress(pc_value, &target);
2904+
if (!pc.IsValid())
2905+
return {};
2906+
2907+
SymbolContext sc;
2908+
if (!pc.CalculateSymbolContext(&sc,
2909+
eSymbolContextFunction | eSymbolContextSymbol))
2910+
return {};
2911+
if (!sc.symbol && !sc.function)
2912+
return {};
2913+
2914+
auto prologue_size = sc.symbol ? sc.symbol->GetPrologueByteSize()
2915+
: sc.function->GetPrologueByteSize();
2916+
return pc_value + prologue_size;
2917+
}
28342918
} // namespace lldb_private

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,13 @@ class SwiftLanguageRuntime : public LanguageRuntime {
490490
GetFollowAsyncContextUnwindPlan(lldb::ProcessSP process_sp,
491491
RegisterContext *regctx, ArchSpec &arch,
492492
bool &behaves_like_zeroth_frame);
493+
494+
/// Given the async register of a funclet, extract its continuation pointer,
495+
/// compute the prologue size of the continuation function, and return the
496+
/// address of the first non-prologue instruction.
497+
std::optional<lldb::addr_t>
498+
TrySkipVirtualParentProlog(lldb::addr_t async_reg_val, Process &process,
499+
unsigned num_indirections);
493500
};
494501

495502
} // namespace lldb_private

lldb/test/API/lang/swift/async/frame/variables_multiple_frames/TestSwiftAsyncFrameVarMultipleFrames.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def check_variables(self, async_frames, expected_values):
4444
lldbutil.check_variable(self, myvar, False, value=expected_value)
4545

4646
@swiftTest
47-
@skipIf(oslist=["windows", "linux", "macos"])
47+
@skipIf(oslist=["windows", "linux"])
4848
def test(self):
4949
"""Test `frame variable` in async functions"""
5050
self.build()

lldb/test/API/lang/swift/async/unwind/backtrace_locals/TestSwiftAsyncBacktraceLocals.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ def run_fibo_tests(self, target, process):
7676
error = lldb.SBError()
7777
ret_addr = process.ReadPointerFromMemory(
7878
cfa[fibonacci_number-1] + target.addr_size, error)
79+
prologue_to_skip = frame.GetFunction().GetPrologueByteSize()
7980
self.assertSuccess(error, "Managed to read context memory")
80-
self.assertEqual(ret_addr, frame.GetPC())
81+
self.assertEqual(ret_addr + prologue_to_skip, frame.GetPC())
8182

8283
self.assertIn("Main.main", thread.GetFrameAtIndex(n+1).GetFunctionName())
8384

0 commit comments

Comments
 (0)