Skip to content

Commit 0f00a51

Browse files
Merge pull request #9574 from felipepiovezan/felipe/improve_q_funclet_unwinding
[lldb][swift] Fix unwinding of Q funclets by comparing PC to continuation ptr
2 parents 6b99ebc + 2451d19 commit 0f00a51

File tree

2 files changed

+39
-22
lines changed

2 files changed

+39
-22
lines changed

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

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2716,6 +2716,37 @@ static llvm::Expected<addr_t> ReadAsyncContextRegisterFromUnwind(
27162716
"SwiftLanguageRuntime: Unsupported register location type = %d", loctype);
27172717
}
27182718

2719+
/// Returns true if the async register should be dereferenced once to obtain the
2720+
/// CFA of the currently executing function. This is the case at the start of
2721+
/// "Q" funclets, before the low level code changes the meaning of the async
2722+
/// register to not require the indirection.
2723+
/// This implementation detects the transition point by comparing the
2724+
/// continuation pointer in the async context with the currently executing
2725+
/// funclet given by the SymbolContext sc. If they are the same, the PC is
2726+
/// before the transition point.
2727+
/// FIXME: this fails in some recursive async functions. See: rdar://139676623
2728+
static llvm::Expected<bool> IsIndirectContext(Process &process,
2729+
StringRef mangled_name,
2730+
addr_t async_reg,
2731+
SymbolContext &sc) {
2732+
if (!SwiftLanguageRuntime::IsSwiftAsyncAwaitResumePartialFunctionSymbol(
2733+
mangled_name))
2734+
return false;
2735+
2736+
llvm::Expected<addr_t> continuation_ptr = ReadPtrFromAddr(
2737+
process, async_reg, /*offset*/ process.GetAddressByteSize());
2738+
if (!continuation_ptr)
2739+
return continuation_ptr.takeError();
2740+
2741+
if (sc.function)
2742+
return sc.function->GetAddressRange().ContainsLoadAddress(
2743+
*continuation_ptr, &process.GetTarget());
2744+
assert(sc.symbol);
2745+
Address continuation_addr;
2746+
continuation_addr.SetLoadAddress(*continuation_ptr, &process.GetTarget());
2747+
return sc.symbol->ContainsFileAddress(continuation_addr.GetFileAddress());
2748+
}
2749+
27192750
// Examine the register state and detect the transition from a real
27202751
// stack frame to an AsyncContext frame, or a frame in the middle of
27212752
// the AsyncContext chain, and return an UnwindPlan for these situations.
@@ -2776,15 +2807,20 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
27762807
// for await resume ("Q") funclets ("indirect context").
27772808
// 2. The async context for the currently executing async function, for all
27782809
// other funclets ("Y" and "Yx" funclets, where "x" is a number).
2779-
bool indirect_context =
2780-
IsSwiftAsyncAwaitResumePartialFunctionSymbol(mangled_name.GetStringRef());
27812810

27822811
llvm::Expected<addr_t> async_reg = ReadAsyncContextRegisterFromUnwind(
27832812
sc, *process_sp, pc, func_start_addr, *regctx, *regnums);
27842813
if (!async_reg)
27852814
return log_expected(async_reg.takeError());
2815+
2816+
llvm::Expected<bool> maybe_indirect_context =
2817+
IsIndirectContext(*process_sp, mangled_name, *async_reg, sc);
2818+
if (!maybe_indirect_context)
2819+
return log_expected(maybe_indirect_context.takeError());
2820+
27862821
llvm::Expected<addr_t> async_ctx =
2787-
indirect_context ? ReadPtrFromAddr(*m_process, *async_reg) : *async_reg;
2822+
*maybe_indirect_context ? ReadPtrFromAddr(*m_process, *async_reg)
2823+
: *async_reg;
27882824
if (!async_ctx)
27892825
return log_expected(async_ctx.takeError());
27902826

lldb/test/API/lang/swift/async/unwind/unwind_in_all_instructions/TestSwiftAsyncUnwindAllInstructions.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -62,26 +62,7 @@ def set_breakpoints_all_funclets(self, target):
6262
breakpoints.add(bp.GetID())
6363
return breakpoints
6464

65-
# FIXME: there are challenges when unwinding Q funclets ("await resume"),
66-
# see rdar://137048317. For now, we only know how to unwind during and
67-
# shortly after the prologue. This function returns "should skip" if we're
68-
# at a PC that is too far from the prologue (~16 bytes). This is a
69-
# rough approximation that seems to work for both x86 and arm.
70-
def should_skip_Q_funclet(self, thread):
71-
current_frame = thread.frames[0]
72-
function = current_frame.GetFunction()
73-
if "await resume" not in function.GetName():
74-
return False
75-
76-
max_prologue_offset = 16
77-
prologue_end = function.GetStartAddress()
78-
prologue_end.OffsetAddress(function.GetPrologueByteSize() + max_prologue_offset)
79-
current_pc = current_frame.GetPCAddress()
80-
return current_pc.GetFileAddress() >= prologue_end.GetFileAddress()
81-
8265
def check_unwind_ok(self, thread, bpid):
83-
if self.should_skip_Q_funclet(thread):
84-
return
8566
# Check that we see the virtual backtrace:
8667
expected_funcnames = [
8768
"ASYNC___1___",

0 commit comments

Comments
 (0)