Skip to content
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

Closed
wants to merge 4 commits into from

Conversation

Zoxc
Copy link
Contributor

@Zoxc Zoxc commented Oct 30, 2017

For the following program,

fn main() {
    panic!("hello world");
}

we now get this stack trace:

thread 'main' panicked at 'hello world', test.rs:2:4
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
   0: test::main
             at .\test.rs:2

instead of this one:

thread 'main' panicked at 'hello world', test.rs:2:4
stack backtrace:
   0: std::sys_common::backtrace::_print
             at C:\projects\rust\src\libstd\sys_common\backtrace.rs:92
   1: std::panicking::default_hook::{{closure}}
             at C:\projects\rust\src\libstd\panicking.rs:380
   2: std::panicking::default_hook
             at C:\projects\rust\src\libstd\panicking.rs:397
   3: std::panicking::rust_panic_with_hook
             at C:\projects\rust\src\libstd\panicking.rs:577
   4: std::panicking::begin_panic<str*>
             at C:\projects\rust\src\libstd\panicking.rs:538
   5: test::main
             at .\test.rs:2
   6: panic_unwind::__rust_maybe_catch_panic
             at C:\projects\rust\src\libpanic_unwind\lib.rs:99
   7: std::rt::lang_start
             at C:\projects\rust\src\libstd\rt.rs:52
   8: __scrt_common_main_seh
             at f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:283
   9: BaseThreadInitThunk

This introduces a str macro fragment used to redirect panic! called with string literals to a non-generic function. This redirection is also an optimization (and matches what libcore does).

@kennytm kennytm added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 30, 2017
@kennytm
Copy link
Member

kennytm commented Oct 30, 2017

cc #40264 (@Yamakaky)

cc #35625 (:literal macro specifier)

@Yamakaky
Copy link
Contributor

Yamakaky commented Oct 30, 2017

SGTM!
BTW, is the backtrace pruning feature on nightly? When i try, it does the old behaviour.

@@ -509,6 +509,7 @@ fn may_begin_with(name: &str, token: &Token) -> bool {
}

match name {
"str" => token.can_begin_str(),
Copy link
Contributor

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".

@Zoxc Zoxc force-pushed the backtraces branch 2 times, most recently from 2507e67 to d046344 Compare October 31, 2017 16:38
@Zoxc
Copy link
Contributor Author

Zoxc commented Oct 31, 2017

I removed the str macro fragment specifier from this PR.

This does not seem to work on OS X, probably because the backtraces are incorrect there.

@Zoxc
Copy link
Contributor Author

Zoxc commented Nov 1, 2017

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 mark_start (calls main and thread functions) are also emitted from backtraces.

@Zoxc
Copy link
Contributor Author

Zoxc commented Nov 1, 2017

r? @alexcrichton

#[cold]
fn panic_dispatch(fmt: fmt::Arguments,
file_line_col: &(&'static str, u32, u32),
entry_point: &usize) -> ! {
Copy link
Member

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?

Copy link
Contributor Author

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?

Copy link
Member

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?

Copy link
Member

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?

@@ -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>,
Copy link
Member

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?

Copy link
Contributor Author

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.

pub fn mark_start(f: &mut FnMut()) {
f();
unsafe {
asm!("" ::: "memory" : "volatile"); // A dummy statement to prevent tail call optimization
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't.

struct Function(*const ());
unsafe impl Sync for Function {}

static MARK_START: Function = Function(mark_start as *const ());
Copy link
Member

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?

Copy link
Contributor Author

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.

@@ -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) {
Copy link
Member

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?

Copy link
Contributor Author

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.

@bors
Copy link
Contributor

bors commented Nov 9, 2017

☔ The latest upstream changes (presumably #45725) made this pull request unmergeable. Please resolve the merge conflicts.

@Zoxc Zoxc force-pushed the backtraces branch 2 times, most recently from 15bff34 to 915b654 Compare November 15, 2017 17:35
@Zoxc
Copy link
Contributor Author

Zoxc commented Nov 15, 2017

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

@Zoxc Zoxc force-pushed the backtraces branch 2 times, most recently from d63555a to 03e28c9 Compare November 15, 2017 18:28
unsafe { panic_impl(fmt, file, line, col) }
}
unsafe { panic_impl(fmt, file, line, col, entry_point) }
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing newline

@Zoxc Zoxc changed the title Ignore panic functions in backtraces Ignore panic functions in backtraces. Print inlined functions on Windows Nov 16, 2017
@kennytm
Copy link
Member

kennytm commented Nov 22, 2017

Ping @alexcrichton (also @retep998) is there any unresolved questions for this PR?

@kennytm
Copy link
Member

kennytm commented Nov 29, 2017

Triage ping @alexcrichton! There are some responses from @Zoxc on #45637 (review) and #45637 (comment)

cc @rust-lang/libs

@kennytm kennytm added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Nov 29, 2017
fn __rust_begin_short_backtrace<F: FnOnce()>(f: F) {
f()
/// Clean the backtrace by using std::rt::mark_backtrace_start
#[inline(always)]
Copy link
Member

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?

Copy link
Contributor Author

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.

@alexcrichton
Copy link
Member

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"?

@Zoxc
Copy link
Contributor Author

Zoxc commented Dec 1, 2017

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.

@alexcrichton
Copy link
Member

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.


#[cold]
#[inline(never)]
#[cfg_attr(not(stage0), notail_when_called)]
Copy link
Member

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?

@alexcrichton
Copy link
Member

Looks good to me, thanks! A few comments and a fixup to some indentation and otherwise looks ready to go.

@@ -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
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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 :/.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Member

@eddyb eddyb Dec 14, 2017

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.

Copy link
Contributor Author

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

Copy link
Member

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() {}
Copy link
Member

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.

@@ -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);
Copy link
Member

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.

@bors
Copy link
Contributor

bors commented Dec 17, 2017

☔ The latest upstream changes (presumably #46436) made this pull request unmergeable. Please resolve the merge conflicts.

@Zoxc
Copy link
Contributor Author

Zoxc commented Dec 24, 2017

@alexcrichton It turns out notail cannot be applied to invoke instructions in LLVM, and LLVM can convert invoke instructions into call instructions (without notail).

@bors
Copy link
Contributor

bors commented Dec 25, 2017

☔ The latest upstream changes (presumably #46914) made this pull request unmergeable. Please resolve the merge conflicts.

@alexcrichton
Copy link
Member

@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?

@kennytm kennytm added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jan 10, 2018
@kennytm kennytm added S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. labels Jan 17, 2018
@kennytm
Copy link
Member

kennytm commented Jan 17, 2018

Hi @Zoxc, is this PR currently blocked by #47252, and is there a workaround for notail invoke or a LLVM bug report we can follow as mentioned in #45637 (comment)? Thanks!

@Zoxc
Copy link
Contributor Author

Zoxc commented Jan 17, 2018

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.

bors added a commit that referenced this pull request Jan 25, 2018
Print inlined functions on Windows

Split from #45637

r? @alexcrichton
bors added a commit that referenced this pull request Jan 26, 2018
Print inlined functions on Windows

Split from #45637

r? @alexcrichton
@shepmaster
Copy link
Member

Ping from triage, @Zoxc ! Have you "split out the fixes to mark the start of backtraces"? If so, should this PR be closed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants