-
Notifications
You must be signed in to change notification settings - Fork 341
Fix swift async frames unwinding unwinding #9025
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix swift async frames unwinding unwinding #9025
Conversation
|
// Creates an expression accessing (fp - 8), with an optional dereference | ||
// operation. This is only valid for x86_64 or aarch64. | ||
llvm::ArrayRef<uint8_t> | ||
getAsyncRegFromFramePointerDWARFExpr(llvm::Triple::ArchType triple, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lldb functions start with an Upper case letter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in the new version
9964bc1
to
317e4a6
Compare
@@ -2586,6 +2582,39 @@ lldb::addr_t SwiftLanguageRuntime::GetAsyncContext(RegisterContext *regctx) { | |||
return LLDB_INVALID_ADDRESS; | |||
} | |||
|
|||
// Creates an expression accessing (fp - 8), with an optional dereference |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
///
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
// works. | ||
static const uint8_t g_cfa_dwarf_expression_x86_64[] = { | ||
llvm::dwarf::DW_OP_breg6, // DW_OP_breg6, register 6 == rbp | ||
0x78, // sleb128 -8 (ptrsize) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this comment. Yes, it's 128-8, but sleb128 is a variable-length encoding, so I'm confused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also understand this is not new in your patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate? -8 is 0x78 in sleb128 encoding, and it takes a single byte to represent it.
The comment is explaining where the magic 0x78 comes from.
@@ -2765,9 +2761,9 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp, | |||
|
|||
// Creates an UnwindPlan for following the AsyncContext chain | |||
// up the stack, from a current AsyncContext frame. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a doxygen comment on the declaration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
||
auto pc_after_prologue = [&]() -> std::optional<addr_t> { | ||
// If we're in the prologue, we can just use our x22, it is unmodified. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a generic name for x22 (since this is arch specific?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
context_reg
or some thing like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
calling is "async_reg" to match other comments in this file which refer to it as "asynchronous register"
// Otherwise, check the ABI guaranteed slot for the saved x22 reg: *(fp - 8). | ||
Status error; | ||
addr_t x22_entry_value; | ||
process_sp->ReadMemory(fp - ptr_size, &x22_entry_value, ptr_size, error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should comment that this is true on all ABIs, otherwise this screams for being an ABI callback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
row->SetRegisterLocationToAtCFAPlusOffset(regnums->pc_regnum, ptr_size, | ||
false); | ||
// Since we pretend x22 is the register "passed" to our child frame, there | ||
// is always at least one indirection. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add the missing detail to this comment that explains why the indirection follows from it being a virtual frame?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reworded this entire paragraph to remove this part of the explanation that requires a lot of background information.
if (async_reg_val == LLDB_INVALID_ADDRESS || async_reg_val == 0) | ||
return {}; | ||
|
||
const auto ptr_size = 8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get this from process?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
return {}; | ||
|
||
Address pc; | ||
Target &target(process.GetTarget()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Target &target = process.GetTarget();
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
317e4a6
to
1aa6a85
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Address review comments
return {}; | ||
|
||
Address pc; | ||
Target &target(process.GetTarget()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
if (async_reg_val == LLDB_INVALID_ADDRESS || async_reg_val == 0) | ||
return {}; | ||
|
||
const auto ptr_size = 8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
// works. | ||
static const uint8_t g_cfa_dwarf_expression_x86_64[] = { | ||
llvm::dwarf::DW_OP_breg6, // DW_OP_breg6, register 6 == rbp | ||
0x78, // sleb128 -8 (ptrsize) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate? -8 is 0x78 in sleb128 encoding, and it takes a single byte to represent it.
The comment is explaining where the magic 0x78 comes from.
|
||
auto pc_after_prologue = [&]() -> std::optional<addr_t> { | ||
// If we're in the prologue, we can just use our x22, it is unmodified. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
calling is "async_reg" to match other comments in this file which refer to it as "asynchronous register"
// Otherwise, check the ABI guaranteed slot for the saved x22 reg: *(fp - 8). | ||
Status error; | ||
addr_t x22_entry_value; | ||
process_sp->ReadMemory(fp - ptr_size, &x22_entry_value, ptr_size, error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
row->SetRegisterLocationToAtCFAPlusOffset(regnums->pc_regnum, ptr_size, | ||
false); | ||
// Since we pretend x22 is the register "passed" to our child frame, there | ||
// is always at least one indirection. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reworded this entire paragraph to remove this part of the explanation that requires a lot of background information.
@@ -2765,9 +2761,9 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp, | |||
|
|||
// Creates an UnwindPlan for following the AsyncContext chain | |||
// up the stack, from a current AsyncContext frame. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@@ -2586,6 +2582,39 @@ lldb::addr_t SwiftLanguageRuntime::GetAsyncContext(RegisterContext *regctx) { | |||
return LLDB_INVALID_ADDRESS; | |||
} | |||
|
|||
// Creates an expression accessing (fp - 8), with an optional dereference |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
This will enable subsequent commits to introduce calls to other member functions from that method.
1aa6a85
to
8e0cfd2
Compare
Removed the last two commits, which dealt with Q funclets as frame 0 and required compiler changes. |
@swift-ci test |
|
||
// Compute the CFA of this frame. | ||
addr_t cfa = async_reg_val; | ||
while (num_indirections--) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact that this underflows on the last iteration bothers me a bit.
for (; num_indirections != 0; --num_indirections)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, good catch!
|
||
func ASYNC___2___() async -> Int { | ||
var myvar = 222; | ||
let result = await ASYNC___1___() //breakpoint1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// breakpoint1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
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.
8e0cfd2
to
85069c8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed review comments
|
||
func ASYNC___2___() async -> Int { | ||
var myvar = 222; | ||
let result = await ASYNC___1___() //breakpoint1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
|
||
// Compute the CFA of this frame. | ||
addr_t cfa = async_reg_val; | ||
while (num_indirections--) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, good catch!
@swift-ci test |
@swift-ci test platform windows |
@swift-ci test windows platform |
@swift-ci test windows platform |
This series of patches aims to solve the problem where LLDB is unable to print variables in backtraces involving swift async functions. The only frame where this works today is the first async frame, which is a real frame and is physically on the stack. We fail to do so for all virtual frames.
The underlying problem is simple: when unwinding the PC, we use the first instruction of the continuation funclets. This instruction is part of the prologue, where no variables are in scope.
The solution is similarly simple: we just offset the PC by the size of the prologue of the continuation funclet.
These commits are meant to be reviewed independently, please the commit messages too.
The first commit is a cherry-pick from: llvm#100624