-
Notifications
You must be signed in to change notification settings - Fork 341
[lldb][Swift] Small steps towards fixing StepOut in async functions #9138
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
[lldb][Swift] Small steps towards fixing StepOut in async functions #9138
Conversation
This commit message has a mistake:
should be "whose CFAs are on the heap," |
Oops, good catch! I'll update it before posting the real PR |
lldb/source/Target/StackID.cpp
Outdated
if (lhs_cfa_on_stack && rhs_cfa_on_stack) | ||
return {}; |
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 early exit is redundant to the final return {}
.
also, this function could return bool
, since it currently only ever returns two values: true
or nullopt
. But if it can only return two values, shouldn't it be true/false?
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.
It is true that this function only returns two values (on this patch, subsequent patches will change this though).
However, nullopt
here really means: "the question isYoungerHeapCFAs is not applicable". So returning "false" would give the false impression that "isYoungerHeapCFAs" is returning "it's not younger".
The call site needs a way of knowing that it needs to try other comparisons, which is what nullopt represents here.
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.
subsequent patches will change this though
imo that's where this change should happen
I also think this would be best served by an enum, which can be explicit about the case meanings. I think optional<bool>
should be avoided in any language.
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.
another option: instead of mixing two predicates into one, have one predicate which checks if a CFA is heap, and another that checks if one heap CFAs is younger than another heap CFA.
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.
one predicate which checks if a CFA is heap, and another that checks if one heap CFAs is younger than another heap CFA.
Are you suggesting something like this?
if (are both heap cfas(cfa1, cfa2))
return compare heap cfas (cfa1, cfa2)
The problem with that is that we still need to handle the case where one of them is on the heap and the other one is not. We could do:
if (at least one is heap cfaa(cfa1, cfa2))
return compare heap cfas (cfa1, cfa2)
But then the "compare heap cfas" function would need to repeat the check to figure out which one is on the heap.
The other problem with this approach is that there will be another third case where we "don't have an opinion", which is when we were chasing continuation pointers and decided to give up because we reached some upper limit on the number of tries.
I'll try the enumeration approach if you believe that makes sense, though IMO the optional<bool>
is quite expressive here
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.
Changed in the newest version
|
||
@swiftTest | ||
@skipIf(oslist=["windows", "linux"]) | ||
@skipIf("rdar://133849022") |
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.
Given the PR description:
This PR adds tests demonstrating where the debugger fails
should this be an expectedFailure?
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 was debating this but we would risk having sporadic xpasses: if the stars align and the "heap cfas" numeric values are such that certain StackIDs comparisons behave in some pattern, the test could pass.
@adrian-prantl have you had a chance to look at this? |
# Expected frames: | ||
# ASYNC___0___ <- ASYNC___1___ <- ASYNC___2___ <- ASYNC___3___ <- Main | ||
num_frames = 5 | ||
func_names = list(map(lambda frame: frame.GetFunctionName(), frames[:num_frames])) |
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.
a more pythonic formation:
func_names = list(map(lambda frame: frame.GetFunctionName(), frames[:num_frames])) | |
func_names = [frame.GetFunctionName() for frame in frames[:num_frames]] |
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.
Nice!
func_names = list(map(lambda frame: frame.GetFunctionName(), frames[:num_frames])) | ||
for idx in range(num_frames - 1): | ||
self.assertRegex(func_names[idx], f"ASYNC___{idx}___") | ||
self.assertRegex(func_names[num_frames - 1], "Main") |
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.
these assertRegex
can be assertIn
since there's no regular expressions in play.
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.
Good point!
da5facc
to
52f2fbc
Compare
Addressed review comments |
52f2fbc
to
f44c46e
Compare
Changed "IsYounger" function to return an enumeration |
lldb/source/Target/StackID.cpp
Outdated
if (lhs.IsCFAOnStack(process) && !rhs.IsCFAOnStack(process)) | ||
if (lhs_cfa_on_stack && !rhs_cfa_on_stack) | ||
return HeapCFAComparisonResult::Younger; | ||
return {}; |
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.
should this be NoOpinion
?
(I'm not sure what {}
evaluates to for an enum, but I presume the first case)
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.
Oooops.
num_frames = 5 | ||
func_names = [frame.GetFunctionName() for frame in frames[:num_frames]] | ||
for idx in range(num_frames - 1): | ||
self.assertRegex(func_names[idx], f"ASYNC___{idx}___") |
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 can also be assertIn
if you want
…rison The code moved in this patch is a swift customization, even though it is missing the customization markers. This simple refactor should also make it clear that there is a bug in this function: it is not symmetric.
While this could, theoretically, be sent upstream, we have unfortunately renamed some of the core APIs downstream ("IsYounger" doesn't exist upstream, nor anything "heap cfa" related).
f44c46e
to
874b20b
Compare
Update test and address error in the previous update |
This PR adds tests demonstrating where the debugger fails, as well as exposing swift customizations that are not properly tagged.
The two final fixes we need are listed below. I have not included them in this PR because I am missing unittests for the StackID changes in them and because this PR stands on its own regardless of whether the fixes below are considered appropriate or not.
This work builds on top the previously merged PRs improving the swift debugging story:
#9112
#9107
swiftlang/swift#75881
#9025