-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Draft of branch coverage support #118305
Draft of branch coverage support #118305
Conversation
@rustbot label +A-code-coverage |
@rustbot author |
tests/coverage/branch.coverage
Outdated
LL| | if | ||
LL| | ! | ||
LL| 15| cond | ||
------------------ | ||
| Branch (LL:9): [True: 10, False: 5] | ||
------------------ |
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.
Two problems here:
- The span doesn't include
!
. - The true/false counts are reversed, since they're tracking
cond
instead of!cond
.
tests/coverage/branch.coverage
Outdated
LL| | if | ||
LL| 15| a | ||
------------------ | ||
| Branch (LL:9): [True: 12, False: 3] | ||
------------------ | ||
LL| | && | ||
LL| 12| b | ||
------------------ | ||
| Branch (LL:9): [True: 8, False: 4] | ||
------------------ |
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 all looks correct, which is nice.
tests/coverage/branch.coverage
Outdated
LL| | if | ||
LL| | let | ||
LL| | true | ||
LL| | = | ||
LL| 15| cond |
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 currently doesn't get branch coverage at all. I'd like to fix that if possible, but for me it's a lower priority than getting the logical ops correct.
cc @Swatinem in case you're curious. The MIR building stuff will have to change once I figure out how to handle the logical ops better. |
☔ The latest upstream changes (presumably #118300) made this pull request unmergeable. Please resolve the merge conflicts. |
Well, this definitely indicates a need to add macro-expanded spans to the list of things I need to figure out. The existing coverage code has some functions in |
I tried passing each of the branch spans through |
996fbe5
to
9000afd
Compare
Now that the So I'm thinking it might make sense for me to polish the current functionality into a review-ready state, so that people can start playing with branch coverage on nightly and start flushing out any lingering correctness issues. |
This comment has been minimized.
This comment has been minimized.
811cb89
to
934e104
Compare
I found a way to fix the mappings for We also need to propagate it through the |
Make the success arms of `if lhs || rhs` meet up in a separate block Extracted from rust-lang#118305, where this is necessary to avoid introducing a bug when injecting marker statements into the then/else arms. --- In the previous code (rust-lang#111752), the success block of `lhs` would jump directly to the success block of `rhs`. However, `rhs_success_block` could already contain statements that are specific to the RHS, and the direct goto causes them to be executed in the LHS success path as well. This patch therefore creates a fresh block that the LHS and RHS success blocks can both jump to. --- I think the reason we currently get away with this is that `rhs_success_block` usually doesn't contain anything other than StorageDead statements for locals used by the RHS, and those statements don't seem to cause problems in the LHS success path (which never makes those locals live). But if we start adding meaningful statements for branch coverage (or MC/DC coverage), it's important to keep the LHS and RHS blocks separate.
Make the success arms of `if lhs || rhs` meet up in a separate block Extracted from rust-lang#118305, where this is necessary to avoid introducing a bug when injecting marker statements into the then/else arms. --- In the previous code (rust-lang#111752), the success block of `lhs` would jump directly to the success block of `rhs`. However, `rhs_success_block` could already contain statements that are specific to the RHS, and the direct goto causes them to be executed in the LHS success path as well. This patch therefore creates a fresh block that the LHS and RHS success blocks can both jump to. --- I think the reason we currently get away with this is that `rhs_success_block` usually doesn't contain anything other than StorageDead statements for locals used by the RHS, and those statements don't seem to cause problems in the LHS success path (which never makes those locals live). But if we start adding meaningful statements for branch coverage (or MC/DC coverage), it's important to keep the LHS and RHS blocks separate.
Rollup merge of rust-lang#121784 - Zalathar:if-or-converge, r=Nadrieril Make the success arms of `if lhs || rhs` meet up in a separate block Extracted from rust-lang#118305, where this is necessary to avoid introducing a bug when injecting marker statements into the then/else arms. --- In the previous code (rust-lang#111752), the success block of `lhs` would jump directly to the success block of `rhs`. However, `rhs_success_block` could already contain statements that are specific to the RHS, and the direct goto causes them to be executed in the LHS success path as well. This patch therefore creates a fresh block that the LHS and RHS success blocks can both jump to. --- I think the reason we currently get away with this is that `rhs_success_block` usually doesn't contain anything other than StorageDead statements for locals used by the RHS, and those statements don't seem to cause problems in the LHS success path (which never makes those locals live). But if we start adding meaningful statements for branch coverage (or MC/DC coverage), it's important to keep the LHS and RHS blocks separate.
☔ The latest upstream changes (presumably #121859) made this pull request unmergeable. Please resolve the merge conflicts. |
☔ The latest upstream changes (presumably #121998) made this pull request unmergeable. Please resolve the merge conflicts. |
☔ The latest upstream changes (presumably #122151) made this pull request unmergeable. Please resolve the merge conflicts. |
e72afef
to
d40cf6d
Compare
Switched to a different mechanism for handling |
9fa37bb
to
59eb74a
Compare
This will allow MIR building to check whether a function is eligible for coverage instrumentation, and avoid collecting branch coverage info if it is not.
Now that I've resolved my own concerns, I think it might be time to close this and open a new PR for actual review. |
coverage: Initial support for branch coverage instrumentation (This is a review-ready version of the changes that were drafted in rust-lang#118305.) This PR adds support for branch coverage instrumentation, gated behind the unstable flag value `-Zcoverage-options=branch`. (Coverage instrumentation must also be enabled with `-Cinstrument-coverage`.) During THIR-to-MIR lowering (MIR building), if branch coverage is enabled, we collect additional information about branch conditions and their corresponding then/else blocks. We inject special marker statements into those blocks, so that the `InstrumentCoverage` MIR pass can reliably identify them even after the initially-built MIR has been simplified and renumbered. The rest of the changes are mostly just plumbing needed to gather up the information that was collected during MIR building, and include it in the coverage metadata that we embed in the final binary. Note that `llvm-cov show` doesn't print branch coverage information in its source views by default; that needs to be explicitly enabled with `--show-branches=count` or similar. --- The current implementation doesn't have any support for instrumenting `if let` or let-chains. I think it's still useful without that, and adding it would be non-trivial, so I'm happy to leave that for future work.
Rollup merge of rust-lang#122322 - Zalathar:branch, r=oli-obk coverage: Initial support for branch coverage instrumentation (This is a review-ready version of the changes that were drafted in rust-lang#118305.) This PR adds support for branch coverage instrumentation, gated behind the unstable flag value `-Zcoverage-options=branch`. (Coverage instrumentation must also be enabled with `-Cinstrument-coverage`.) During THIR-to-MIR lowering (MIR building), if branch coverage is enabled, we collect additional information about branch conditions and their corresponding then/else blocks. We inject special marker statements into those blocks, so that the `InstrumentCoverage` MIR pass can reliably identify them even after the initially-built MIR has been simplified and renumbered. The rest of the changes are mostly just plumbing needed to gather up the information that was collected during MIR building, and include it in the coverage metadata that we embed in the final binary. Note that `llvm-cov show` doesn't print branch coverage information in its source views by default; that needs to be explicitly enabled with `--show-branches=count` or similar. --- The current implementation doesn't have any support for instrumenting `if let` or let-chains. I think it's still useful without that, and adding it would be non-trivial, so I'm happy to leave that for future work.
EDIT: I've closed this and re-opened a non-draft version at #122322.
This is a work-in-progress snapshot of my implementation of branch coverage.
It works, and produces correct results in many cases. However, it sometimes produces confusing or incorrect results forif
expressions that contain the!
/&&
/||
operators, since the THIR-to-MIR lowering of those is non-trivial.