-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Ignore panic functions in backtraces. Print inlined functions on Windows #45637
Conversation
SGTM! |
src/libsyntax/ext/tt/macro_parser.rs
Outdated
@@ -509,6 +509,7 @@ fn may_begin_with(name: &str, token: &Token) -> bool { | |||
} | |||
|
|||
match name { | |||
"str" => token.can_begin_str(), |
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.
All the previous matchers went through RFC process.
Well, at least I'm pretty sure this addition should not be hidden in a PR named "Ignore panic functions in backtraces".
2507e67
to
d046344
Compare
I removed the This does not seem to work on OS X, probably because the backtraces are incorrect there. |
I changed how the implemented worked. Now panic functions passes a pointer to themselves into the panic handler. Backtraces then ignore functions in the backtrace before that pointer. Any functions after |
#[cold] | ||
fn panic_dispatch(fmt: fmt::Arguments, | ||
file_line_col: &(&'static str, u32, u32), | ||
entry_point: &usize) -> ! { |
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.
How come this is passed as &usize
? Can't this just be a usize
?
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 think passing locals by reference also prevents tail call optimization. I'm not really sure that this or the asm!
trick works for function which never return. Currently these do not get tail call optimization applied, but I don't see a reason for that. Perhaps LLVM skips that optimization on cold functions?
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'm a little wary to have this and rely on this, are we sure that this doesn't get optimized away even with LTO? If we leave this in can it at least be documented in the source as to why it's a reference?
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.
My guess for why this isn't optimized is because fmt::Arguments
is a pretty big struct that's copied around the stack, and LLVM is probably conservative with the tail-call for now. Despite that though this seems like something that's pretty brittle to rely on?
src/libstd/panicking.rs
Outdated
@@ -546,8 +554,9 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u3 | |||
/// run panic hooks, and then delegate to the actual implementation of panics. | |||
#[inline(never)] | |||
#[cold] | |||
fn rust_panic_with_hook(msg: Box<Any + Send>, | |||
file_line_col: &(&'static str, u32, u32)) -> ! { | |||
pub(crate) fn rust_panic_with_hook(msg: Box<Any + Send>, |
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.
How come pub(crate)
is needed 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.
Probably just a leftover from the earlier version.
src/libstd/sys_common/backtrace.rs
Outdated
pub fn mark_start(f: &mut FnMut()) { | ||
f(); | ||
unsafe { | ||
asm!("" ::: "memory" : "volatile"); // A dummy statement to prevent tail call optimization |
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.
Does #[inline(never)]
not work for inhibiting inlining and providing this as a marker?
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 does not. It just prevents inlining, not the removal of stack 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.
Instead of asm!
, does std::sync::atomic::compiler_fence()
work?
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 shouldn't.
src/libstd/sys_common/backtrace.rs
Outdated
struct Function(*const ()); | ||
unsafe impl Sync for Function {} | ||
|
||
static MARK_START: Function = Function(mark_start as *const ()); |
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.
Couldn't this just store fn(...)
instead of using casts and unsafe impls?
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 think so. An earlier version had an array of functions, which needed casts.
src/libtest/lib.rs
Outdated
@@ -1484,7 +1484,10 @@ pub fn run_test(opts: &TestOpts, | |||
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. | |||
#[inline(never)] | |||
fn __rust_begin_short_backtrace<F: FnOnce()>(f: F) { |
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 presumably no longer works now that the libstd implementation was updated?
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.
Yeah this is now broken. We could work around it by exporting mark_start
as an unstable item.
☔ The latest upstream changes (presumably #45725) made this pull request unmergeable. Please resolve the merge conflicts. |
15bff34
to
915b654
Compare
Now this also prints inlined functions in backtraces on Windows. @alexcrichton I'd like this to get merged in time for the next beta so I can get more complete backtraces in rustc. cc @retep998 |
d63555a
to
03e28c9
Compare
unsafe { panic_impl(fmt, file, line, col) } | ||
} | ||
unsafe { panic_impl(fmt, file, line, col, entry_point) } | ||
} |
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.
Missing newline
Ping @alexcrichton (also @retep998) is there any unresolved questions for this PR? |
Triage ping @alexcrichton! There are some responses from @Zoxc on #45637 (review) and #45637 (comment) cc @rust-lang/libs |
src/libtest/lib.rs
Outdated
fn __rust_begin_short_backtrace<F: FnOnce()>(f: F) { | ||
f() | ||
/// Clean the backtrace by using std::rt::mark_backtrace_start | ||
#[inline(always)] |
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.
Does this need to be either #[inline]
or #[inline(always)]
? If so, can it be documented as to why?
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 think the attribute is needed here.
Nice on the windows bits! Could those perhaps be moved to a separate PR? They seem unambiguously good to me while I'm still pretty uneasy on the other parts. It seems like relying on the exact addresses of functions is quite brittle? I'm worried that this will cause regressions over time as we continue to upgrade LLVM. Is there perhaps something we can do to make this more bullet-proof rather than "insert something and pray llvm doesn't optimize it"? |
I don't think there's anything we can do to make it bulletproof without modifying LLVM. If it breaks, it will just return to the status quo, so I don't think that's a good reason to avoid landing this. Backtraces are unfortunately best effort due to other reasons anyway. |
I'm personally sort of wary for that though in that this if we go down this road it's a commitment for us to make that we will receive, review, and maintain patches to fix this functionality as it regresses over time. In that sense I'm not comfortable myself r+'ing this but would want a larger sign-off from perhaps @rust-lang/libs on this functionality. |
src/libstd/panicking.rs
Outdated
|
||
#[cold] | ||
#[inline(never)] | ||
#[cfg_attr(not(stage0), notail_when_called)] |
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.
Could you add comment as well to functions in this crate for why this attribute is applied?
Looks good to me, thanks! A few comments and a fixup to some indentation and otherwise looks ready to go. |
src/librustc_trans/mir/block.rs
Outdated
@@ -137,6 +137,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { | |||
this.set_debug_loc(&ret_bcx, terminator.source_info); | |||
this.store_return(&ret_bcx, ret_dest, &fn_ty.ret, invokeret); | |||
} | |||
|
|||
None |
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 probably be invokeret
.
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.
That would be bad, since InvokeInst
is unrelated to CallInst
and does not have a notail
attribute.
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.
They're both CallSite
though?
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 think that has a setTailCallKind
method.
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.
Oh, my bad, I just checked again and that is indeed that case. That's a big troubling, because an invoke
can become a call
through optimizations :/.
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 probably panic if we need to add notail on invokes then. I'll do that to check that we don't emit any invokes 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.
It seems like we do emit invokes 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.
I guess the attribute should require non-Rust ABI which implies nounwind
?
Err, it should always be an invoke if it's calling a panic entry-point with unwinding enabled.
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.
Isn't invoke
only used when you need cleanup? Normal call
instructions can also unwind and should be used 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.
Right, unwinding + cleanup.
// except according to those terms. | ||
|
||
#[notail_when_called] | ||
fn 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.
Missing newline at the end of file.
src/rustllvm/RustWrapper.cpp
Outdated
@@ -1226,6 +1226,10 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, | |||
unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Bundles, Name)); | |||
} | |||
|
|||
extern "C" void LLVMRustSetCallNoTail(LLVMValueRef Call) { | |||
dyn_cast<CallInst>(unwrap(Call))->setTailCallKind(CallInst::TCK_NoTail); |
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.
Okay, then I'd make this if (auto CI = dyn_cast<CallInst>(unwrap(Call))) CI->...
just so you can pass an invoke instruction.
☔ The latest upstream changes (presumably #46436) made this pull request unmergeable. Please resolve the merge conflicts. |
@alexcrichton It turns out |
☔ The latest upstream changes (presumably #46914) made this pull request unmergeable. Please resolve the merge conflicts. |
@Zoxc that's unfortunate :( Would it be possible to land the fixes for inlined functions on Windows and perhaps follow-up with an upstream bug with LLVM to add this feature? |
Hi @Zoxc, is this PR currently blocked by #47252, and is there a workaround for |
There isn't any workaround nor have I filed a LLVM issue. I should still split out the fixes to mark the start of backtraces though, which addresses #47429. |
Print inlined functions on Windows Split from #45637 r? @alexcrichton
Print inlined functions on Windows Split from #45637 r? @alexcrichton
Ping from triage, @Zoxc ! Have you "split out the fixes to mark the start of backtraces"? If so, should this PR be closed? |
For the following program,
we now get this stack trace:
instead of this one:
This introduces a
str
macro fragment used to redirectpanic!
called with string literals to a non-generic function. This redirection is also an optimization (and matches what libcore does).