-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Rustc adds line-number information for unhittable panic handlers #55352
Comments
Simple repro in https://github.com/bossmc/kcov-rust-issue. Reproduce with |
This still reproduces. I want to dig in with a little private investigation to understand the scenarios where the cleanup path is marked as unreached, and to know how one should be expected to avoid such marking in cases like this (e.g. should we require a I also want to understand the scenarios where people want to know about code paths that were silently injected by the compiler. I do believe there are cases where people want to know about that, but I also think the majority of programmers are trying to avoid actively thinking about cleanup paths, and for the most part we want to encourage that... |
There are two "markings" at play here:
Which are you asking about? Are you exploring if Rustc can detect the unhittable landing pads and not emit debug information for them? Or if kcov can tell if a given debug info line corresponds to a landing pad and therefore not report it? |
I'm asking about the expected/desired experience for an end-developer. I expect some people are going to want to test the control-flow paths that correspond to panics for certain library routines. But I also expect some set of people to treat some panic paths as something they do not attempt to test (because, for example, they are truly unreachable -- as is the case in the situation outlined in this issue). What is the right way to serve both groups? Is it to ask people to add certain attributes to their source code (and then rustc incorporates those attributes into the metadata that it generates for kcov)? Or should we expect the source code in both of these scenarios to remain unchanged? (To be clear: @bossmc 's original description included bullet points that explore this question with ideas of paths to follow. I just want to start from a higher level: To ask what the ideal experience should be in the two scenarios I outlined, and then try to work backwards from there.) |
First off, root issue I was investigating was kcov producing bad coverage information for Rust binaries. I've worked out why kcov is producing the results it is and they're "correct" given what Rustc is doing. I'm not sure where the fix (if any) needs to be made, but I'm starting with Rustc as kcov's strategy looks sound.
Consider the following code:
Kcov will mark the "Uncovered" line as unhit. This is surprising, as that line was definitely passed during the execution of the program. Lines can be omitted from kcov coverage (with
// KCOV_EXCL_LINE
) but this leads to hundreds of those markers scattered around the code, potentially hiding real coverage lapses/lowering maintainability of the codebase.Kcov determines coverage by looking at the
.debug_lines
section of the binary, which contains a mapping from address in the binary to line of code. It then sets a break point at every listed address before running the program. Each time a break point is hit, kcov marks the associated line of code as hit, and clears the breakpoint (as hitting it again tells us nothing, and breakpoints are slow). After the process completes, any lines remaining were not hit.This means that kcov's "was this line hit" logic is pretty solid, assuming the
.debug_lines
section is accurate.When Rust generates a block that calls any function after creating any binding to a type with a
Drop
implementation, it also generates a unwind-cleanup block to call thatdrop()
in the event of the functionpanic
-ing. In the above case, the generated code is something like (assuming Rust had exceptions):This makes sense, if there is a panic (which might get handled up the stack somewhere) we should delete bindings that are memory-safe (yay Rust!) to free up memory that's not going to be accessible any more.
Unfortunately:
}
line marked above) so unlessget_age
panics, this code won't be hit.get_age
never panics so this cleanup code can't be hit (without changing theget_age()
function at least)In a release build, the cleanup handler is stripped because LLVM notices that there's no way for anything in the
try
block to panic, so the handler is not needed. In a debug build, it doesn't do this optimisation and leaves genuinely unhittable code in the binary, but associates it with a line of code, causing many false positives in kcov's output (especially as kcov uses debug builds to prevent dead-code elimination removing untested code).What to do differently? I'm not sure, but here are some ideas:
The text was updated successfully, but these errors were encountered: